assert 2.14.0 → 2.15.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/assert.gemspec +0 -2
  4. data/lib/assert/assert_runner.rb +1 -1
  5. data/lib/assert/assertions.rb +1 -1
  6. data/lib/assert/config.rb +6 -1
  7. data/lib/assert/config_helpers.rb +75 -0
  8. data/lib/assert/context.rb +20 -14
  9. data/lib/assert/context/test_dsl.rb +17 -13
  10. data/lib/assert/{view/default_view.rb → default_view.rb} +9 -17
  11. data/lib/assert/factory.rb +2 -7
  12. data/lib/assert/file_line.rb +7 -3
  13. data/lib/assert/macro.rb +1 -1
  14. data/lib/assert/macros/methods.rb +1 -1
  15. data/lib/assert/result.rb +84 -90
  16. data/lib/assert/runner.rb +8 -2
  17. data/lib/assert/suite.rb +15 -5
  18. data/lib/assert/test.rb +112 -75
  19. data/lib/assert/version.rb +1 -1
  20. data/lib/assert/view.rb +108 -21
  21. data/lib/assert/view_helpers.rb +238 -0
  22. data/test/support/factory.rb +23 -6
  23. data/test/system/test_tests.rb +359 -0
  24. data/test/unit/assertions_tests.rb +1 -1
  25. data/test/unit/config_helpers_tests.rb +95 -0
  26. data/test/unit/config_tests.rb +5 -1
  27. data/test/unit/context/test_dsl_tests.rb +25 -17
  28. data/test/unit/context_tests.rb +45 -10
  29. data/test/unit/factory_tests.rb +9 -11
  30. data/test/unit/file_line_tests.rb +22 -0
  31. data/test/unit/result_tests.rb +219 -160
  32. data/test/unit/runner_tests.rb +19 -5
  33. data/test/unit/suite_tests.rb +23 -4
  34. data/test/unit/test_tests.rb +167 -33
  35. data/test/unit/view_helpers_tests.rb +210 -0
  36. data/test/unit/view_tests.rb +66 -26
  37. metadata +12 -23
  38. data/lib/assert/view/base.rb +0 -91
  39. data/lib/assert/view/helpers/ansi_styles.rb +0 -25
  40. data/lib/assert/view/helpers/common.rb +0 -209
  41. data/test/system/running_tests.rb +0 -404
@@ -9,84 +9,100 @@ module Assert::Result
9
9
  class Skip < Base; end
10
10
 
11
11
  def self.types
12
- { :pass => Pass,
13
- :fail => Fail,
14
- :ignore => Ignore,
15
- :skip => Skip,
16
- :error => Error
17
- }
12
+ @types ||= Hash.new{ |h, k| Base }.tap do |hash|
13
+ hash[:pass] = Pass
14
+ hash[:fail] = Fail
15
+ hash[:ignore] = Ignore
16
+ hash[:skip] = Skip
17
+ hash[:error] = Error
18
+ end.freeze
18
19
  end
19
20
 
20
- class Base
21
+ def self.new(data = nil)
22
+ data ||= {}
23
+ self.types[data[:type]].new(data)
24
+ end
21
25
 
22
- attr_reader :test, :message, :backtrace
26
+ class Base
23
27
 
24
- def initialize(test, message, backtrace=nil)
25
- @test = test
26
- @backtrace = Backtrace.new(backtrace)
27
- @message = message && !message.empty? ? message : nil
28
- end
28
+ def self.type; :unknown; end
29
+ def self.name; ''; end
29
30
 
30
- Assert::Result.types.keys.each do |meth|
31
- define_method("#{meth}?") { false }
31
+ def self.for_test(test, message, bt)
32
+ self.new({
33
+ :test_name => test.name,
34
+ :message => message,
35
+ :backtrace => Backtrace.new(bt)
36
+ })
32
37
  end
33
38
 
34
- def test_name
35
- @test.name
39
+ def initialize(build_data)
40
+ @build_data = build_data
36
41
  end
37
42
 
38
- def to_sym; nil; end
43
+ def type; @type ||= (@build_data[:type] || self.class.type).to_sym; end
44
+ def name; @name ||= (@build_data[:name] || self.class.name.to_s); end
45
+ def test_name; @test_name ||= (@build_data[:test_name] || ''); end
46
+ def message; @message ||= (@build_data[:message] || ''); end
47
+ def backtrace; @backtrace ||= (@build_data[:backtrace] || Backtrace.new([])); end
48
+ def trace; @trace ||= (@build_data[:trace] || build_trace(self.backtrace)); end
39
49
 
40
- def to_s
41
- [ "#{self.name.upcase}: #{self.test_name}",
42
- self.message,
43
- self.trace
44
- ].compact.join("\n")
50
+ Assert::Result.types.keys.each do |type|
51
+ define_method("#{type}?"){ self.type == type }
45
52
  end
46
53
 
47
- def name
48
- ""
54
+ # we choose to implement this way instead of using an `attr_writer` to be
55
+ # consistant with how you override exception backtraces.
56
+ def set_backtrace(bt)
57
+ @backtrace = Backtrace.new(bt)
58
+ @trace = build_trace(@backtrace)
49
59
  end
50
60
 
51
- def trace
52
- (self.backtrace.filtered.first || self.test.context_info.called_from).to_s
61
+ def data
62
+ { :type => self.type,
63
+ :name => self.name,
64
+ :test_name => self.test_name,
65
+ :message => self.message,
66
+ :backtrace => self.backtrace,
67
+ :trace => self.trace,
68
+ }
53
69
  end
54
70
 
55
- # chose to implement this way instead of using an `attr_writer` to be
56
- # consistant with how you override exception backtraces.
57
- def set_backtrace(bt)
58
- @backtrace = Backtrace.new(bt || [])
71
+ def to_sym; self.type; end
72
+
73
+ def to_s
74
+ [ "#{self.name.upcase}: #{self.test_name}",
75
+ self.message,
76
+ self.trace
77
+ ].reject(&:empty?).join("\n")
59
78
  end
60
79
 
61
- def ==(other)
62
- self.class == other.class && self.message == other.message
80
+ def ==(other_result)
81
+ self.type == other_result.type && self.message == other_result.message
63
82
  end
64
83
 
65
84
  def inspect
66
85
  "#<#{self.class}:#{'0x0%x' % (object_id << 1)} @message=#{self.message.inspect}>"
67
86
  end
68
87
 
88
+ private
89
+
90
+ # by default, a result's trace is the first line of its filtered backtrace
91
+ def build_trace(backtrace); backtrace.filtered.first.to_s; end
92
+
69
93
  end
70
94
 
71
95
  class Pass < Base
72
96
 
73
- def pass?; true; end
74
- def to_sym; :pass; end
75
-
76
- def name
77
- "Pass"
78
- end
97
+ def self.type; :pass; end
98
+ def self.name; 'Pass'; end
79
99
 
80
100
  end
81
101
 
82
102
  class Ignore < Base
83
103
 
84
- def ignore?; true; end
85
- def to_sym; :ignore; end
86
-
87
- def name
88
- "Ignore"
89
- end
104
+ def self.type; :ignore; end
105
+ def self.name; 'Ignore'; end
90
106
 
91
107
  end
92
108
 
@@ -95,24 +111,20 @@ module Assert::Result
95
111
 
96
112
  class Fail < Base
97
113
 
114
+ def self.type; :fail; end
115
+ def self.name; 'Fail'; end
116
+
98
117
  # fail results can be generated manually or by raising Assert::Result::TestFailure
99
- def initialize(test, message_or_exception, backtrace=nil)
118
+ def self.for_test(test, message_or_exception, bt = nil)
100
119
  if message_or_exception.kind_of?(TestFailure)
101
- super(test, message_or_exception.message, message_or_exception.backtrace || [])
120
+ super(test, message_or_exception.message, message_or_exception.backtrace)
102
121
  elsif message_or_exception.kind_of?(Exception)
103
122
  raise ArgumentError, "generate fail results by raising Assert::Result::TestFailure"
104
123
  else
105
- super(test, message_or_exception, backtrace)
124
+ super(test, message_or_exception, bt)
106
125
  end
107
126
  end
108
127
 
109
- def fail?; true; end
110
- def to_sym; :fail; end
111
-
112
- def name
113
- "Fail"
114
- end
115
-
116
128
  end
117
129
 
118
130
  # raised by the 'skip' context helper to break test execution
@@ -120,71 +132,52 @@ module Assert::Result
120
132
 
121
133
  class Skip < Base
122
134
 
135
+ def self.type; :skip; end
136
+ def self.name; 'Skip'; end
137
+
123
138
  # skip results are generated by raising Assert::Result::TestSkipped
124
- def initialize(test, exception)
139
+ def self.for_test(test, exception)
125
140
  if exception.kind_of?(TestSkipped)
126
- super(test, exception.message, exception.backtrace || [])
141
+ super(test, exception.message, exception.backtrace)
127
142
  else
128
143
  raise ArgumentError, "generate skip results by raising Assert::Result::TestSkipped"
129
144
  end
130
145
  end
131
146
 
132
- def skip?; true; end
133
- def to_sym; :skip; end
134
-
135
- def name
136
- "Skip"
137
- end
138
-
139
147
  end
140
148
 
141
149
  class Error < Base
142
150
 
151
+ def self.type; :error; end
152
+ def self.name; 'Error'; end
153
+
143
154
  # error results are generated by raising exceptions in tests
144
- def initialize(test, exception)
155
+ def self.for_test(test, exception)
145
156
  if exception.kind_of?(Exception)
146
- super(test, "#{exception.message} (#{exception.class.name})", exception.backtrace || [])
157
+ super(test, "#{exception.message} (#{exception.class.name})", exception.backtrace)
147
158
  else
148
159
  raise ArgumentError, "generate error results by raising an exception"
149
160
  end
150
161
  end
151
162
 
152
- def error?; true; end
153
- def to_sym; :error; end
154
-
155
- def name
156
- "Error"
157
- end
163
+ private
158
164
 
159
165
  # override of the base, always show the full unfiltered backtrace for errors
160
- def trace
161
- self.backtrace.to_s
162
- end
166
+ def build_trace(backtrace); backtrace.to_s; end
163
167
 
164
168
  end
165
169
 
166
- # Utility Classes
167
-
168
- class Set < ::Array
169
- attr_accessor :callback
170
+ class Backtrace < ::Array
170
171
 
171
- def initialize(callback=nil)
172
- @callback = callback
173
- super()
174
- end
172
+ DELIM = "\n".freeze
175
173
 
176
- def <<(result)
177
- super
178
- @callback.call(result) if @callback
179
- end
180
- end
174
+ def self.parse(bt); self.new(bt.to_s.split(DELIM)); end
181
175
 
182
- class Backtrace < ::Array
183
176
  def initialize(value = nil)
184
177
  super([*(value || "No backtrace")])
185
178
  end
186
179
 
187
- def to_s; self.join("\n"); end
180
+ def to_s; self.join(DELIM); end
188
181
 
189
182
  def filtered
190
183
  self.class.new(self.reject { |line| filter_out?(line) })
@@ -199,6 +192,7 @@ module Assert::Result
199
192
  assert_bin_regex = /bin\/assert\:/
200
193
  line.rindex(assert_lib_path, 0) || line =~ assert_bin_regex
201
194
  end
195
+
202
196
  end
203
197
 
204
198
  end
@@ -1,8 +1,10 @@
1
+ require 'assert/config_helpers'
1
2
  require 'assert/suite'
2
3
 
3
4
  module Assert
4
5
 
5
6
  class Runner
7
+ include Assert::ConfigHelpers
6
8
 
7
9
  attr_reader :config
8
10
 
@@ -10,8 +12,12 @@ module Assert
10
12
  @config = config
11
13
  end
12
14
 
13
- def run(suite, view)
15
+ def run
16
+ suite, view = @config.suite, @config.view
14
17
  raise ArgumentError if !suite.kind_of?(Suite)
18
+ if tests?
19
+ view.puts "Running tests in random order, seeded with \"#{runner_seed}\""
20
+ end
15
21
  view.fire(:on_start)
16
22
 
17
23
  begin
@@ -39,7 +45,7 @@ module Assert
39
45
 
40
46
  def tests_to_run(suite)
41
47
  srand self.config.runner_seed
42
- suite.tests.sort.sort_by { rand suite.tests.size }
48
+ suite.tests.sort.sort_by{ rand suite.tests.size }
43
49
  end
44
50
 
45
51
  end
@@ -14,8 +14,8 @@ module Assert
14
14
  @config = config
15
15
  @tests = []
16
16
  @test_methods = []
17
- @start_time = 0
18
- @end_time = 0
17
+ @start_time = Time.now
18
+ @end_time = @start_time
19
19
  end
20
20
 
21
21
  def run_time
@@ -32,8 +32,12 @@ module Assert
32
32
 
33
33
  alias_method :ordered_tests, :tests
34
34
 
35
+ def ordered_tests_by_run_time
36
+ self.ordered_tests.sort{ |a, b| a.run_time <=> b.run_time }
37
+ end
38
+
35
39
  def results
36
- tests.inject([]) {|results, test| results += test.results}
40
+ tests.inject([]){ |results, test| results += test.results }
37
41
  end
38
42
  alias_method :ordered_results, :results
39
43
 
@@ -62,7 +66,7 @@ module Assert
62
66
  self.tests.size
63
67
  end
64
68
 
65
- def result_count(type=nil)
69
+ def result_count(type = nil)
66
70
  if type
67
71
  self.tests.inject(0) do |count, test|
68
72
  count += test.result_count(type)
@@ -130,13 +134,19 @@ module Assert
130
134
  end
131
135
 
132
136
  class ContextInfo
137
+
133
138
  attr_reader :called_from, :klass, :file
134
139
 
135
- def initialize(klass, called_from=nil, first_caller=nil)
140
+ def initialize(klass, called_from = nil, first_caller = nil)
136
141
  @called_from = called_from || first_caller
137
142
  @klass = klass
138
143
  @file = @called_from.gsub(/\:[0-9]+.*$/, '') if @called_from
139
144
  end
145
+
146
+ def test_name(name)
147
+ [klass.description.to_s, name.to_s].compact.reject(&:empty?).join(' ')
148
+ end
149
+
140
150
  end
141
151
 
142
152
  end
@@ -6,62 +6,113 @@ module Assert
6
6
 
7
7
  class Test
8
8
 
9
- # a Test is some code/method to run in the scope of a Context. After a
10
- # a test runs, it should have some assertions which are its results.
9
+ # a Test is some code/method to run in the scope of a Context that may
10
+ # produce results
11
11
 
12
- attr_reader :context_info, :config
13
- attr_reader :name, :file_line, :code
14
- attr_accessor :results, :output, :run_time
12
+ def self.result_count_meth(type)
13
+ "#{type}_result_count".to_sym
14
+ end
15
+
16
+ def self.name_file_line_context_data(ci, name)
17
+ { :name => ci.test_name(name),
18
+ :file_line => ci.called_from
19
+ }
20
+ end
21
+
22
+ def self.for_block(name, context_info, config, &block)
23
+ self.new(self.name_file_line_context_data(context_info, name).merge({
24
+ :context_info => context_info,
25
+ :config => config,
26
+ :code => block
27
+ }))
28
+ end
29
+
30
+ def self.for_method(method_name, context_info, config)
31
+ self.new(self.name_file_line_context_data(context_info, method_name).merge({
32
+ :context_info => context_info,
33
+ :config => config,
34
+ :code => proc{ self.send(method_name) }
35
+ }))
36
+ end
37
+
38
+ attr_reader :results
39
+ attr_writer :total_result_count
40
+
41
+ def initialize(build_data = nil)
42
+ @build_data, @results = build_data || {}, []
43
+ end
44
+
45
+ def file_line; @file_line ||= FileLine.parse((@build_data[:file_line] || '').to_s); end
46
+ def name; @name ||= (@build_data[:name] || ''); end
47
+ def output; @output ||= (@build_data[:output] || ''); end
48
+ def run_time; @run_time ||= (@build_data[:run_time] || 0); end
49
+
50
+ def total_result_count
51
+ @total_result_count ||= (@build_data[:total_result_count] || 0)
52
+ end
15
53
 
16
- def initialize(name, suite_ci, config, opts = nil, &block)
17
- @context_info = suite_ci
18
- @config = config
19
- @name, @file_line = name_file_line_from_context(@context_info, name)
54
+ Assert::Result.types.keys.each do |type|
55
+ n = result_count_meth(type)
56
+ define_method(n) do
57
+ instance_variable_get("@#{n}") || instance_variable_set("@#{n}", @build_data[n] || 0)
58
+ end
59
+ end
20
60
 
21
- o = opts || {}
22
- @code = o[:code] || block || Proc.new{}
61
+ def context_info; @context_info ||= @build_data[:context_info]; end
62
+ def config; @config ||= @build_data[:config]; end
63
+ def code; @code ||= @build_data[:code]; end
23
64
 
24
- @results = Result::Set.new
25
- @output = ""
26
- @run_time = 0
27
- @result_rate = 0
65
+ def data
66
+ { :file_line => self.file_line.to_s,
67
+ :name => self.name.to_s,
68
+ :output => self.output.to_s,
69
+ :run_time => self.run_time
70
+ }.merge(result_count_data(:total_result_count => self.total_result_count))
28
71
  end
29
72
 
30
73
  def context_class; self.context_info.klass; end
31
74
  def file; self.file_line.file; end
32
75
  def line_number; self.file_line.line; end
33
76
 
34
- def run(&result_callback)
35
- @results = Result::Set.new(result_callback)
77
+ def result_rate
78
+ get_rate(self.result_count, self.run_time)
79
+ end
80
+
81
+ def result_count(type = nil)
82
+ if Assert::Result.types.keys.include?(type)
83
+ self.send(result_count_meth(type))
84
+ else
85
+ self.total_result_count
86
+ end
87
+ end
88
+
89
+ def capture_result(result, callback)
90
+ self.results << result
91
+ self.total_result_count += 1
92
+ n = result_count_meth(result.to_sym)
93
+ instance_variable_set("@#{n}", (instance_variable_get("@#{n}") || 0) + 1)
94
+ callback.call(result)
95
+ end
36
96
 
37
- scope = self.context_class.new(self, self.config)
97
+ def run(&result_callback)
98
+ result_callback ||= proc{ |result| } # do nothing by default
99
+ scope = self.context_class.new(self, self.config, result_callback)
38
100
  start_time = Time.now
39
101
  capture_output do
40
- self.context_class.send('run_arounds', scope){ run_test_main(scope) }
102
+ self.context_class.send('run_arounds', scope) do # TODO: why `send`?
103
+ run_test(scope, result_callback)
104
+ end
41
105
  end
42
106
  @run_time = Time.now - start_time
43
-
44
107
  @results
45
108
  end
46
109
 
47
110
  Assert::Result.types.each do |name, klass|
48
111
  define_method "#{name}_results" do
49
- @results.select{|r| r.kind_of?(klass) }
50
- end
51
- end
52
-
53
- def result_count(type=nil)
54
- if Assert::Result.types.include?(type)
55
- self.send("#{type}_results").size
56
- else
57
- @results.size
112
+ self.results.select{|r| r.kind_of?(klass) }
58
113
  end
59
114
  end
60
115
 
61
- def result_rate
62
- get_rate(self.result_count, self.run_time)
63
- end
64
-
65
116
  def <=>(other_test)
66
117
  self.name <=> other_test.name
67
118
  end
@@ -75,57 +126,40 @@ module Assert
75
126
 
76
127
  private
77
128
 
78
- def run_test_main(scope)
129
+ def run_test(scope, result_callback)
79
130
  begin
80
- run_test_setup(scope)
81
- run_test_code(scope)
131
+ # run any assert style 'setup do' setups
132
+ self.context_class.send('run_setups', scope) # TODO: why `send`?
133
+ # run any test/unit style 'def setup' setups
134
+ scope.setup if scope.respond_to?(:setup)
135
+ # run the code block
136
+ scope.instance_eval(&(self.code || proc{}))
82
137
  rescue Result::TestFailure => err
83
- @results << Result::Fail.new(self, err)
138
+ capture_result(Result::Fail.for_test(self, err), result_callback)
84
139
  rescue Result::TestSkipped => err
85
- @results << Result::Skip.new(self, err)
140
+ capture_result(Result::Skip.for_test(self, err), result_callback)
86
141
  rescue SignalException => err
87
142
  raise(err)
88
143
  rescue Exception => err
89
- @results << Result::Error.new(self, err)
144
+ capture_result(Result::Error.for_test(self, err), result_callback)
90
145
  ensure
91
146
  begin
92
- run_test_teardown(scope)
147
+ # run any assert style 'teardown do' teardowns
148
+ self.context_class.send('run_teardowns', scope) # TODO: why `send`?
149
+ # run any test/unit style 'def teardown' teardowns
150
+ scope.teardown if scope.respond_to?(:teardown)
93
151
  rescue Result::TestFailure => err
94
- @results << Result::Fail.new(self, err)
152
+ capture_result(Result::Fail.for_test(self, err), result_callback)
95
153
  rescue Result::TestSkipped => err
96
- @results << Result::Skip.new(self, err)
154
+ capture_result(Result::Skip.for_test(self, err), result_callback)
97
155
  rescue SignalException => err
98
156
  raise(err)
99
- rescue Exception => teardown_err
100
- @results << Result::Error.new(self, teardown_err)
157
+ rescue Exception => err
158
+ capture_result(Result::Error.for_test(self, err), result_callback)
101
159
  end
102
160
  end
103
161
  end
104
162
 
105
- def run_test_setup(scope)
106
- # run any assert style 'setup do' setups
107
- self.context_class.send('run_setups', scope)
108
-
109
- # run any classic test/unit style 'def setup' setups
110
- scope.setup if scope.respond_to?(:setup)
111
- end
112
-
113
- def run_test_code(scope)
114
- if @code.kind_of?(::Proc)
115
- scope.instance_eval(&@code)
116
- elsif scope.respond_to?(@code.to_s)
117
- scope.send(@code.to_s)
118
- end
119
- end
120
-
121
- def run_test_teardown(scope)
122
- # run any classic test/unit style 'def teardown' teardowns
123
- scope.teardown if scope.respond_to?(:teardown)
124
-
125
- # run any assert style 'teardown do' teardowns
126
- self.context_class.send('run_teardowns', scope)
127
- end
128
-
129
163
  def capture_output(&block)
130
164
  if self.config.capture_output == true
131
165
  orig_stdout = $stdout.clone
@@ -138,15 +172,18 @@ module Assert
138
172
  end
139
173
 
140
174
  def capture_io
141
- StringIO.new(@output, "a+")
175
+ StringIO.new(self.output, "a+")
176
+ end
177
+
178
+ def result_count_data(seed)
179
+ Assert::Result.types.keys.inject(seed) do |d, t|
180
+ d[result_count_meth(t)] = self.send(result_count_meth(t))
181
+ d
182
+ end
142
183
  end
143
184
 
144
- def name_file_line_from_context(context_info, name)
145
- [ [ context_info.klass.description,
146
- name
147
- ].compact.reject(&:empty?).join(" "),
148
- FileLine.parse(context_info.called_from)
149
- ]
185
+ def result_count_meth(type)
186
+ self.class.result_count_meth(type)
150
187
  end
151
188
 
152
189
  def get_rate(count, time)