assert 2.14.0 → 2.15.0

Sign up to get free protection for your applications and to get access to all the features.
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)