onceover 3.11.1 → 3.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 0de0a768f2e22ad3164a270df1a526f814f6ddc21e8333cebca698e55cab3425
4
- data.tar.gz: f1eca8504701aaedd2a84b5e949ad3defd013a190014f2276d93bde738216d2b
2
+ SHA1:
3
+ metadata.gz: 3e2b22d1ea208cdaeeeb71b5a99e2f55f6056cb2
4
+ data.tar.gz: d29bad7de9cfd34c55ece9b69bbb73b5b48cb182
5
5
  SHA512:
6
- metadata.gz: d06d28ae8e4bf7065ed9a25caa4b9f98dcab8fd5f825c84dccc501bce4274752cd35590b44d8c245b314b6b023f552a6a1adab0a084d9d2a109178b97df1baf5
7
- data.tar.gz: 6bf9cb7ca6c63c9557e433e597e73b0e7a8c0c9cb1dda28552ca9fce366d6ecfff01c36854e34fe300b392c0d6ca91817f513bcb056ce2332d33d10c36441da9
6
+ metadata.gz: add796e1d03519fb411c5b2bc5a27b5ff0f00e52b9cea682aeb06b21ec31d36ddfcbbf5209a88fe9eda2a26650d49be47d611096f264b21e97302134faa7b8f0
7
+ data.tar.gz: 73ec3b013c0c02598fa04d99dee1721b91c40d40ec2bd174e24393177f3052ee084d222afbf8791bbaeda8ae0263679e2a88e5c53b78e31ad3728c934b142d55
data/Gemfile CHANGED
@@ -2,6 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'pry-coolline', '> 0.0', '< 1.0.0'
6
+
5
7
  if ENV['PUPPET_VERSION']
6
8
  gem 'puppet', ENV['PUPPET_VERSION']
7
9
  end
@@ -0,0 +1,36 @@
1
+ @formatting
2
+ Feature: Format errors nicely
3
+ I would like Onceover to format errors nicely so that they are easily read
4
+
5
+ Background:
6
+ Given the OnceoverFormatter
7
+
8
+ Scenario: Parse a missing class error
9
+ When Puppet throws the error: "error during compilation: Evaluation Error: Error while evaluating a Function Call, Could not find class ::role::websevrer for server01.foo.com (line: 17, column: 1) on node server01.foo.com"
10
+ Then the error should parse successfully
11
+ And it should find 1 error
12
+ And the parsed error should contain the following keys: text, line, column
13
+
14
+ Scenario: Parse an incorrect parameter error
15
+ When Puppet throws the error: "error during compilation: Evaluation Error: Error while evaluating a Resource Statement, Class[Docker]: has no parameter named 'package_name' (file: /some/path/tp/the/file/module/manifests/profile/docker.pp, line: 8, column: 1) on node server01.foo.com"
16
+ Then the error should parse successfully
17
+ And it should find 1 error
18
+ And the parsed error should contain the following keys: text, file, line, column
19
+
20
+ Scenario: Parse a duplication declaration error
21
+ When Puppet throws the error: "error during compilation: Evaluation Error: Error while evaluating a Resource Statement, Duplicate declaration: Package[virtualenv] is already declared at (file: /some/path/tp/the/file/foo/manifests/profile/ti2.pp, line: 31); cannot redeclare (file: /some/path/tp/the/file/python/manifests/install.pp, line: 54) (file: /some/path/tp/the/file/python/manifests/install.pp, line: 54, column: 3) on node server01.foo.co"
22
+ Then the error should parse successfully
23
+ And it should find 2 errors
24
+ And the parsed errors should contain the following keys: text, file, line, column
25
+
26
+ Scenario: Parse a lookup failure error
27
+ When Puppet throws the error: "error during compilation: Function lookup() did not find a value for the name 'archvsync::repositories' on node server01.foo.com"
28
+ Then the error should parse successfully
29
+ And it should find 1 errors
30
+ And the parsed errors should contain the following keys: text
31
+
32
+ Scenario: Fail to parse an error and survive anyway
33
+ When Puppet throws the error: "Derp derp derp. This error is 💩"
34
+ Then the error should parse successfully
35
+ And it should find 1 errors
36
+ And the parsed errors should contain the following keys: text
@@ -0,0 +1,41 @@
1
+ Given(/^the OnceoverFormatter$/) do
2
+ require 'rspec'
3
+ require 'onceover/rspec/formatters'
4
+
5
+ RSpec.configure do |c|
6
+ # Create onceover settings to be accessed by formatters
7
+ c.add_setting :onceover_tempdir
8
+ c.add_setting :onceover_root
9
+ c.add_setting :onceover_environmentpath
10
+
11
+ c.onceover_tempdir = "/Users/foo/git/controlrepo/.onceover"
12
+ c.onceover_root = "/Users/foo/git/controlrepo"
13
+ c.onceover_environmentpath = "etc/puppetlabs/code/environments"
14
+ end
15
+
16
+ @formatter = OnceoverFormatter.new(STDOUT)
17
+ end
18
+
19
+ When(/^Puppet throws the error: "(.*)"$/) do |error|
20
+ @error = error
21
+ end
22
+
23
+ Then(/^the error should parse successfully$/) do
24
+ expect do
25
+ @parsed_error = @formatter.parse_errors(@error)
26
+ end.to_not raise_error
27
+ end
28
+
29
+ Then(/^it should find (\d+) errors?$/) do |number|
30
+ expect(@parsed_error.length).to be(number.to_i)
31
+ end
32
+
33
+ Then(/^the parsed errors? should contain the following keys: (.*)$/) do |keys|
34
+ # Split the keys into an array
35
+ keys = keys.split(',').map(&:strip)
36
+ @parsed_error.each do |error|
37
+ keys.each do |k|
38
+ expect(error).to have_key(k.to_sym)
39
+ end
40
+ end
41
+ end
@@ -41,7 +41,7 @@ This includes deploying using r10k and running all custom tests.
41
41
  summary 'Runs spec tests'
42
42
 
43
43
  optional :p, :parallel, 'Runs spec tests in parallel. This increases speed at the cost of poorly formatted logs and irrelevant junit output.'
44
- optional nil, :format, 'Which RSpec formatter to use, valid options are: documentation, progress, FailureCollector. You also specify this multiple times', multiple: true, default: :defaults
44
+ optional nil, :format, 'Which RSpec formatter to use, valid options are: documentation, progress, FailureCollector, OnceoverFormatter. You also specify this multiple times', multiple: true, default: :defaults
45
45
 
46
46
  run do |opts, args, cmd|
47
47
  repo = Onceover::Controlrepo.new(opts)
@@ -297,7 +297,7 @@ class Onceover
297
297
  threads.map(&:join)
298
298
  puppetfile_string = queue.pop
299
299
 
300
- File.open(@puppetfile, 'w') {|f| f.write(puppetfile_string.join("\n")) }
300
+ File.open(@puppetfile, 'w') {|f| f.puts(puppetfile_string.join("\n")) }
301
301
  puts "#{'changed'.yellow} #{@puppetfile}"
302
302
  end
303
303
 
@@ -0,0 +1,239 @@
1
+ require 'pathname'
2
+
3
+ class OnceoverFormatter
4
+ RSpec::Core::Formatters.register self, :example_group_started,
5
+ :example_passed, :example_failed, :example_pending, :dump_failures#, :dump_summary
6
+
7
+ COMPILATION_ERROR = %r{error during compilation: (?<error>.*)}
8
+ ERROR_WITH_LOCATION = %r{(?<error>.*?)\s(at )?(\((file: (?<file>.*?), )?line: (?<line>\d+)(, column: (?<column>\d+))?\))(; )?}
9
+ ERROR_WITHOUT_LOCATION = %r{(?<error>.*?)\son node}
10
+
11
+ def initialize output
12
+ @output = output
13
+ @previous_role = nil
14
+ end
15
+
16
+ def example_group_started notification
17
+ if notification.group.parent_groups == [notification.group]
18
+ # If this is the highest level group (The role)
19
+ role = notification.group.description
20
+ if role != @previous_role
21
+ @output << "\n"
22
+ @output << class_name("#{notification.group.description}:")
23
+
24
+ # Calculate the padding required
25
+ padding = (longest_group - role.length) + 1
26
+ # Create padding
27
+ padding.times { @output << ' ' }
28
+
29
+ # Save the role name
30
+ @previous_role = role
31
+ end
32
+ else
33
+ # If not then this will be a test for that role
34
+ @output << '? '
35
+ end
36
+ end
37
+
38
+ def example_passed notification
39
+ @output << "\b\b"
40
+ @output << "#{green('P')} "
41
+ end
42
+
43
+ def example_failed notification
44
+ @output << "\b\b"
45
+ @output << "#{red('F')} "
46
+ end
47
+
48
+ def example_pending notification
49
+ @output << "\b\b"
50
+ @output << "#{yellow('?')} "
51
+ end
52
+
53
+ def dump_failures notification
54
+ require 'onceover/controlrepo'
55
+
56
+ # Group by role
57
+ grouped = notification.failed_examples.group_by { |e| e.metadata[:example_group][:parent_example_group][:description]}
58
+
59
+ # Further group by error
60
+ grouped.each do |role, failures|
61
+ grouped[role] = failures.uniq { |f| f.metadata[:execution_result].exception.to_s }
62
+ end
63
+
64
+ # Put some spacing before the results
65
+ @output << "\n\n\n"
66
+
67
+ grouped.each do |role, failures|
68
+ role = {
69
+ name: role,
70
+ errors: failures.map { |f| parse_errors(f.metadata[:execution_result].exception.to_s)}.flatten,
71
+ }
72
+
73
+ @output << Onceover::Controlrepo.evaluate_template('error_summary.yaml.erb', binding)
74
+ end
75
+ end
76
+
77
+ def parse_errors(raw_error)
78
+ # Check if the error is a compilation error
79
+ match = COMPILATION_ERROR.match(raw_error)
80
+ if match
81
+ compilation_error = match['error']
82
+ # Check if we car parse it
83
+ if ERROR_WITH_LOCATION.match(compilation_error)
84
+ scanned_errors = match['error'].scan(ERROR_WITH_LOCATION)
85
+
86
+ # Delete any matches where there was no error text
87
+ scanned_errors.delete_if { |e| e.first.empty? }
88
+
89
+ scanned_errors.map do |error_matches|
90
+ {
91
+ text: error_matches[0],
92
+ file: calculate_relative_source(error_matches[1]),
93
+ line: error_matches[2],
94
+ column: error_matches[3],
95
+ }
96
+ end
97
+ elsif ERROR_WITHOUT_LOCATION.match(compilation_error)
98
+ scanned_errors = match['error'].scan(ERROR_WITHOUT_LOCATION)
99
+
100
+ # Delete any matches where there was no error text
101
+ scanned_errors.delete_if { |e| e.first.empty? }
102
+
103
+ scanned_errors.map do |error_matches|
104
+ {
105
+ text: error_matches[0],
106
+ }
107
+ end
108
+ else
109
+ [{
110
+ text: raw_error,
111
+ }]
112
+ end
113
+ else
114
+ [{
115
+ text: raw_error,
116
+ }]
117
+ end
118
+ end
119
+
120
+ # This method calculates where the original source file is relative to the
121
+ # user's current location. This is more compliacted than it sounds because
122
+ # if we are running from the root of the controlrepo and we have an error in:
123
+ #
124
+ # /Users/dylan/git/puppet_controlrepo/.onceover/etc/puppetlabs/code/environments/production/site/role/manifests/lb.pp
125
+ #
126
+ # We need that to end up pointing at the original source file not the cached
127
+ # one i.e.
128
+ #
129
+ # site/role/manifests/lb.pp
130
+ #
131
+ def calculate_relative_source(file)
132
+ return nil if file.nil?
133
+
134
+ file = Pathname.new(file)
135
+ tempdir = Pathname.new(RSpec.configuration.onceover_tempdir)
136
+ root = Pathname.new(RSpec.configuration.onceover_root)
137
+ environmentpath = Pathname.new(RSpec.configuration.onceover_environmentpath)
138
+
139
+ # Calculate the full relative path
140
+ file.relative_path_from(tempdir + environmentpath + "production").to_s
141
+ end
142
+
143
+ private
144
+
145
+ # Below are defined the styles for the output
146
+ def class_name(text)
147
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :bold)
148
+ end
149
+
150
+ def black(text)
151
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :black)
152
+ end
153
+
154
+ def red(text)
155
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :red)
156
+ end
157
+
158
+ def green(text)
159
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :green)
160
+ end
161
+
162
+ def yellow(text)
163
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :yellow)
164
+ end
165
+
166
+ def blue(text)
167
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :blue)
168
+ end
169
+
170
+ def magenta(text)
171
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :magenta)
172
+ end
173
+
174
+ def cyan(text)
175
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :cyan)
176
+ end
177
+
178
+ def white(text)
179
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :white)
180
+ end
181
+
182
+ def bold(text)
183
+ RSpec::Core::Formatters::ConsoleCodes.wrap(text, :bold)
184
+ end
185
+
186
+ def longest_group
187
+ RSpec.configuration.world.example_groups.max { |a,b| a.description.length <=> b.description.length}.description.length
188
+ end
189
+
190
+ end
191
+
192
+ # class OnceoverFormatterParallel < OnceoverFormatter
193
+ # require 'yaml'
194
+
195
+ # def example_group_started notification
196
+ # # Do nothing
197
+ # end
198
+
199
+ # def example_passed notification
200
+ # @output << green('P')
201
+ # end
202
+
203
+ # def example_failed notification
204
+ # @output << red('F')
205
+ # end
206
+
207
+ # def example_pending notification
208
+ # @output << yellow('?')
209
+ # end
210
+
211
+ # def dump_failures
212
+ # # TODO: This should write to a file and then get picked up and formatted by onceover itself
213
+ # # might need to use a module for the formatting
214
+ # require 'pry'
215
+ # binding.pry
216
+ # RSpec.configuration.onceover_tempdir
217
+ # end
218
+
219
+ # end
220
+
221
+ class FailureCollector
222
+ RSpec::Core::Formatters.register self, :dump_failures
223
+
224
+ def initialize(output)
225
+ FileUtils.touch(File.expand_path("#{RSpec.configuration.onceover_tempdir}/failures.out"))
226
+ end
227
+
228
+ def dump_failures(failures)
229
+ open(File.expand_path("#{RSpec.configuration.onceover_tempdir}/failures.out"), 'a') { |f|
230
+ failures.failed_examples.each do |fe|
231
+ f.puts
232
+ f.puts "#{fe.metadata[:description]}"
233
+ f.puts "#{fe.metadata[:execution_result].exception.to_s}"
234
+ f.puts "#{fe.metadata[:file_path]}:#{fe.metadata[:line_number]}"
235
+ f.puts "------------------------------------------------------"
236
+ end
237
+ }
238
+ end
239
+ end
@@ -53,7 +53,7 @@ class Onceover
53
53
 
54
54
  # Set dynamic defaults for format
55
55
  if opts[:format] == [:defaults]
56
- @formatters = opts[:parallel] ? ['documentation', 'FailureCollector'] : ['documentation']
56
+ @formatters = opts[:parallel] ? ['documentation', 'FailureCollector'] : ['OnceoverFormatter']
57
57
  else
58
58
  @formatters = opts[:format]
59
59
  end
data/onceover.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "onceover"
7
- s.version = "3.11.1"
7
+ s.version = "3.12.0"
8
8
  s.authors = ["Dylan Ratcliffe"]
9
9
  s.email = ["dylan.ratcliffe@puppet.com"]
10
10
  s.homepage = "https://github.com/dylanratcliffe/onceover"
@@ -0,0 +1,14 @@
1
+ <%= bold(role[:name]) %>: <%= red('failed') %>
2
+ errors:
3
+ <% role[:errors].each do |error| -%>
4
+ <%= red(error[:text]) %>
5
+ <% if error[:file] -%>
6
+ file: <%= bold(error[:file]) %>
7
+ <% end -%>
8
+ <% if error[:line] -%>
9
+ line: <%= bold(error[:line]) %>
10
+ <% end -%>
11
+ <% if error[:column] -%>
12
+ column: <%= bold(error[:column]) %>
13
+ <% end -%>
14
+ <% end -%>
@@ -14,30 +14,18 @@ end
14
14
 
15
15
  require 'puppetlabs_spec_helper/module_spec_helper'
16
16
  require 'rspec_junit_formatter'
17
+ require 'onceover/rspec/formatters'
17
18
 
18
- class FailureCollector
19
- RSpec::Core::Formatters.register self, :dump_failures
20
-
21
- def initialize(output)
22
- @output = output
23
- puts "Initializing FailureCollector"
24
- FileUtils.touch(File.expand_path('<%= repo.tempdir %>/failures.out'))
25
- end
19
+ RSpec.configure do |c|
20
+ # Create onceover settings to be accessed by formatters
21
+ c.add_setting :onceover_tempdir
22
+ c.add_setting :onceover_root
23
+ c.add_setting :onceover_environmentpath
26
24
 
27
- def dump_failures(failures)
28
- open(File.expand_path('<%= repo.tempdir %>/failures.out'), 'a') { |f|
29
- failures.failed_examples.each do |fe|
30
- f.puts
31
- f.puts "#{fe.metadata[:description]}"
32
- f.puts "#{fe.metadata[:execution_result].exception.to_s}"
33
- f.puts "#{fe.metadata[:file_path]}:#{fe.metadata[:line_number]}"
34
- f.puts "------------------------------------------------------"
35
- end
36
- }
37
- end
38
- end
25
+ c.onceover_tempdir = <%= repo.tempdir.inspect %>
26
+ c.onceover_root = <%= repo.root.inspect %>
27
+ c.onceover_environmentpath = <%= repo.environmentpath.inspect %>
39
28
 
40
- RSpec.configure do |c|
41
29
  # Also add JUnit output in case people want to use that
42
30
  c.add_formatter('RSpecJUnitFormatter','<%= repo.tempdir %>/spec.xml')
43
31
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onceover
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.11.1
4
+ version: 3.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dylan Ratcliffe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-26 00:00:00.000000000 Z
11
+ date: 2019-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -343,12 +343,14 @@ files:
343
343
  - factsets/solaris-11.2-sparc-64.json
344
344
  - factsets/windows-10-64.json
345
345
  - features/cache.feature
346
+ - features/formatting.feature
346
347
  - features/help.feature
347
348
  - features/init.feature
348
349
  - features/run.feature
349
350
  - features/show.feature
350
351
  - features/step_definitions/cache.rb
351
352
  - features/step_definitions/common.rb
353
+ - features/step_definitions/formatter.rb
352
354
  - features/step_definitions/init.rb
353
355
  - features/step_definitions/run.rb
354
356
  - features/support/cache_helper.rb
@@ -370,6 +372,7 @@ files:
370
372
  - lib/onceover/logger.rb
371
373
  - lib/onceover/node.rb
372
374
  - lib/onceover/rake_tasks.rb
375
+ - lib/onceover/rspec/formatters.rb
373
376
  - lib/onceover/runner.rb
374
377
  - lib/onceover/test.rb
375
378
  - lib/onceover/testconfig.rb
@@ -423,6 +426,7 @@ files:
423
426
  - templates/Rakefile.erb
424
427
  - templates/acceptance_test_spec.rb.erb
425
428
  - templates/controlrepo.yaml.erb
429
+ - templates/error_summary.yaml.erb
426
430
  - templates/factsets_README.md.erb
427
431
  - templates/nodeset.yaml.erb
428
432
  - templates/pre_conditions_README.md.erb
@@ -449,7 +453,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
449
453
  - !ruby/object:Gem::Version
450
454
  version: '0'
451
455
  requirements: []
452
- rubygems_version: 3.0.1
456
+ rubyforge_project:
457
+ rubygems_version: 2.2.5
453
458
  signing_key:
454
459
  specification_version: 4
455
460
  summary: Testing tools for Puppet controlrepos