onceover 3.11.1 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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