assert 2.4.0 → 2.5.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 (52) hide show
  1. data/README.md +8 -8
  2. data/lib/assert.rb +9 -62
  3. data/lib/assert/assert_runner.rb +15 -30
  4. data/lib/assert/assertions.rb +105 -41
  5. data/lib/assert/cli.rb +1 -1
  6. data/lib/assert/config.rb +56 -0
  7. data/lib/assert/context.rb +44 -150
  8. data/lib/assert/context/setup_dsl.rb +70 -0
  9. data/lib/assert/context/subject_dsl.rb +39 -0
  10. data/lib/assert/context/suite_dsl.rb +20 -0
  11. data/lib/assert/context/test_dsl.rb +51 -0
  12. data/lib/assert/runner.rb +8 -2
  13. data/lib/assert/suite.rb +33 -12
  14. data/lib/assert/test.rb +11 -8
  15. data/lib/assert/utils.rb +23 -11
  16. data/lib/assert/version.rb +1 -1
  17. data/lib/assert/view.rb +9 -7
  18. data/lib/assert/view/base.rb +26 -4
  19. data/lib/assert/view/default_view.rb +1 -1
  20. data/lib/assert/view/helpers/ansi_styles.rb +1 -1
  21. data/lib/assert/view/helpers/common.rb +16 -6
  22. data/test/helper.rb +1 -94
  23. data/test/support/factory.rb +70 -0
  24. data/test/system/running_tests.rb +55 -28
  25. data/test/unit/assert_tests.rb +6 -33
  26. data/test/unit/assertions/assert_block_tests.rb +3 -3
  27. data/test/unit/assertions/assert_empty_tests.rb +6 -4
  28. data/test/unit/assertions/assert_equal_tests.rb +19 -22
  29. data/test/unit/assertions/assert_file_exists_tests.rb +6 -4
  30. data/test/unit/assertions/assert_includes_tests.rb +8 -4
  31. data/test/unit/assertions/assert_instance_of_tests.rb +8 -6
  32. data/test/unit/assertions/assert_kind_of_tests.rb +8 -5
  33. data/test/unit/assertions/assert_match_tests.rb +8 -4
  34. data/test/unit/assertions/assert_nil_tests.rb +6 -4
  35. data/test/unit/assertions/assert_raises_tests.rb +2 -2
  36. data/test/unit/assertions/assert_respond_to_tests.rb +7 -5
  37. data/test/unit/assertions/assert_same_tests.rb +75 -6
  38. data/test/unit/assertions/assert_true_false_tests.rb +116 -0
  39. data/test/unit/assertions_tests.rb +7 -5
  40. data/test/unit/config_tests.rb +58 -0
  41. data/test/unit/context/{setup_teardown_singleton_tests.rb → setup_dsl_tests.rb} +17 -19
  42. data/test/unit/context/subject_dsl_tests.rb +78 -0
  43. data/test/unit/context/suite_dsl_tests.rb +47 -0
  44. data/test/unit/context/{test_should_singleton_tests.rb → test_dsl_tests.rb} +33 -34
  45. data/test/unit/context_tests.rb +19 -15
  46. data/test/unit/runner_tests.rb +9 -3
  47. data/test/unit/suite_tests.rb +20 -23
  48. data/test/unit/test_tests.rb +22 -14
  49. data/test/unit/utils_tests.rb +15 -21
  50. data/test/unit/view_tests.rb +12 -5
  51. metadata +23 -10
  52. data/test/unit/context/basic_singleton_tests.rb +0 -86
@@ -0,0 +1,51 @@
1
+ require 'assert/macro'
2
+ require 'assert/suite'
3
+ require 'assert/test'
4
+
5
+ module Assert; end
6
+ class Assert::Context
7
+
8
+ module TestDSL
9
+
10
+ def test(desc_or_macro, called_from=nil, first_caller=nil, &block)
11
+ if desc_or_macro.kind_of?(Assert::Macro)
12
+ instance_eval(&desc_or_macro)
13
+ elsif block_given?
14
+ ci = Assert::Suite::ContextInfo.new(self, called_from, first_caller || caller.first)
15
+ test_name = desc_or_macro
16
+
17
+ # create a test from the given code block
18
+ self.suite.tests << Assert::Test.new(test_name, ci, self.suite.config, &block)
19
+ else
20
+ test_eventually(desc_or_macro, called_from, first_caller || caller.first, &block)
21
+ end
22
+ end
23
+
24
+ def test_eventually(desc_or_macro, called_from=nil, first_caller=nil, &block)
25
+ ci = Assert::Suite::ContextInfo.new(self, called_from, first_caller || caller.first)
26
+ test_name = desc_or_macro.kind_of?(Assert::Macro) ? desc_or_macro.name : desc_or_macro
27
+ skip_block = block.nil? ? Proc.new { skip 'TODO' } : Proc.new { skip }
28
+
29
+ # create a test from a proc that just skips
30
+ self.suite.tests << Assert::Test.new(test_name, ci, self.suite.config, &skip_block)
31
+ end
32
+ alias_method :test_skip, :test_eventually
33
+
34
+ def should(desc_or_macro, called_from=nil, first_caller=nil, &block)
35
+ if !desc_or_macro.kind_of?(Assert::Macro)
36
+ desc_or_macro = "should #{desc_or_macro}"
37
+ end
38
+ test(desc_or_macro, called_from, first_caller || caller.first, &block)
39
+ end
40
+
41
+ def should_eventually(desc_or_macro, called_from=nil, first_caller=nil, &block)
42
+ if !desc_or_macro.kind_of?(Assert::Macro)
43
+ desc_or_macro = "should #{desc_or_macro}"
44
+ end
45
+ test_eventually(desc_or_macro, called_from, first_caller || caller.first, &block)
46
+ end
47
+ alias_method :should_skip, :should_eventually
48
+
49
+ end
50
+
51
+ end
data/lib/assert/runner.rb CHANGED
@@ -1,8 +1,14 @@
1
+ require 'assert/suite'
2
+
1
3
  module Assert
2
4
 
3
5
  class Runner
4
6
 
5
- # Runner runs a suite of tests.
7
+ attr_reader :config
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ end
6
12
 
7
13
  def run(suite, view)
8
14
  raise ArgumentError if !suite.kind_of?(Suite)
@@ -27,7 +33,7 @@ module Assert
27
33
  protected
28
34
 
29
35
  def tests_to_run(suite)
30
- srand Assert.config.runner_seed
36
+ srand self.config.runner_seed
31
37
  suite.tests.sort.sort_by { rand suite.tests.size }
32
38
  end
33
39
 
data/lib/assert/suite.rb CHANGED
@@ -3,24 +3,15 @@ require 'assert/test'
3
3
  module Assert
4
4
  class Suite
5
5
 
6
- class ContextInfo
7
- attr_reader :called_from, :klass, :file
8
-
9
- def initialize(klass, called_from=nil, first_caller=nil)
10
- @called_from = called_from || first_caller
11
- @klass = klass
12
- @file = @called_from.gsub(/\:[0-9]+.*$/, '') if @called_from
13
- end
14
- end
15
-
16
6
  TEST_METHOD_REGEX = /^test./
17
7
 
18
8
  # A suite is a set of tests to run. When a test class subclasses
19
9
  # the Context class, that test class is pushed to the suite.
20
10
 
21
- attr_accessor :tests, :test_methods, :start_time, :end_time
11
+ attr_accessor :config, :tests, :test_methods, :start_time, :end_time
22
12
 
23
- def initialize
13
+ def initialize(config)
14
+ @config = config
24
15
  @tests = []
25
16
  @test_methods = []
26
17
  @start_time = 0
@@ -31,6 +22,14 @@ module Assert
31
22
  @end_time - @start_time
32
23
  end
33
24
 
25
+ def test_rate
26
+ get_rate(self.tests.size, self.run_time.to_f)
27
+ end
28
+
29
+ def result_rate
30
+ get_rate(self.results.size, self.run_time.to_f)
31
+ end
32
+
34
33
  alias_method :ordered_tests, :tests
35
34
 
36
35
  def results
@@ -91,6 +90,12 @@ module Assert
91
90
  end
92
91
  alias_method :shutdown, :teardown
93
92
 
93
+ def inspect
94
+ "#<#{self.class}:#{'0x0%x' % (object_id << 1)}"\
95
+ " test_count=#{self.test_count.inspect}"\
96
+ " result_count=#{self.result_count.inspect}>"
97
+ end
98
+
94
99
  protected
95
100
 
96
101
  def setups
@@ -118,6 +123,22 @@ module Assert
118
123
  methods.uniq.delete_if {|method_name| method_name !~ TEST_METHOD_REGEX }
119
124
  end
120
125
 
126
+ private
127
+
128
+ def get_rate(count, time)
129
+ time == 0 ? 0.0 : (count.to_f / time.to_f)
130
+ end
131
+
132
+ class ContextInfo
133
+ attr_reader :called_from, :klass, :file
134
+
135
+ def initialize(klass, called_from=nil, first_caller=nil)
136
+ @called_from = called_from || first_caller
137
+ @klass = klass
138
+ @file = @called_from.gsub(/\:[0-9]+.*$/, '') if @called_from
139
+ end
140
+ end
141
+
121
142
  end
122
143
 
123
144
  end
data/lib/assert/test.rb CHANGED
@@ -7,15 +7,18 @@ module Assert
7
7
  # a Test is some code/method to run in the scope of a Context. After a
8
8
  # a test runs, it should have some assertions which are its results.
9
9
 
10
- attr_reader :name, :code, :context_info
10
+ attr_reader :name, :context_info, :config, :code
11
11
  attr_accessor :results, :output
12
12
 
13
- def initialize(name, suite_context_info, code = nil, &block)
14
- @context_info = suite_context_info
15
- @name = name_from_context(name)
16
- @code = (code || block)
13
+ def initialize(name, suite_ci, config, opts = nil, &block)
14
+ @context_info = suite_ci
15
+ @name, @config = name_from_context(name), config
16
+
17
+ o = opts || {}
18
+ @code = o[:code] || block || Proc.new{}
19
+
17
20
  @results = Result::Set.new
18
- @output = ""
21
+ @output = ""
19
22
  end
20
23
 
21
24
  def context_class
@@ -25,7 +28,7 @@ module Assert
25
28
  def run(&result_callback)
26
29
  # setup the a new test run
27
30
  @results = Result::Set.new(result_callback)
28
- run_scope = self.context_class.new(self)
31
+ run_scope = self.context_class.new(self, self.config)
29
32
 
30
33
  # run the test, capturing its output
31
34
  begin
@@ -114,7 +117,7 @@ module Assert
114
117
  end
115
118
 
116
119
  def capture_output(&block)
117
- if Assert.config.capture_output == true
120
+ if self.config.capture_output == true
118
121
  orig_stdout = $stdout.clone
119
122
  $stdout = capture_io
120
123
  block.call
data/lib/assert/utils.rb CHANGED
@@ -6,9 +6,8 @@ module Assert
6
6
 
7
7
  # show objects in a human-readable manner. Either inspects or pretty-prints
8
8
  # them depending on settings.
9
-
10
- def self.show(obj)
11
- out = Assert.config.pp_objects ? Assert.config.pp_proc.call(obj) : obj.inspect
9
+ def self.show(obj, config)
10
+ out = config.pp_objects ? config.pp_proc.call(obj) : obj.inspect
12
11
  out = out.encode(Encoding.default_external) if defined?(Encoding)
13
12
  out
14
13
  end
@@ -16,13 +15,11 @@ module Assert
16
15
  # show objects in a human-readable manner and make the output diff-able. This
17
16
  # expands on the basic `show` util by escaping newlines and making object id
18
17
  # hex-values generic.
19
-
20
- def self.show_for_diff(obj)
21
- show(obj).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ':0xXXXXXX')
18
+ def self.show_for_diff(obj, config)
19
+ show(obj, config).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ':0xXXXXXX')
22
20
  end
23
21
 
24
22
  # open a tempfile and yield it
25
-
26
23
  def self.tempfile(name, content)
27
24
  require "tempfile"
28
25
  Tempfile.open(name) do |tmpfile|
@@ -32,14 +29,12 @@ module Assert
32
29
  end
33
30
 
34
31
  # Get a proc that uses stdlib `PP.pp` to pretty print objects
35
-
36
32
  def self.stdlib_pp_proc(width = nil)
37
33
  require 'pp'
38
- Proc.new{ |obj| "\n#{PP.pp(obj, '', width || 79).strip}\n" }
34
+ Proc.new{ |obj| PP.pp(obj, '', width || 79).strip }
39
35
  end
40
36
 
41
37
  # Return true if if either show output has newlines or is bigger than 29 chars
42
-
43
38
  def self.default_use_diff_proc
44
39
  Proc.new do |exp_show_output, act_show_output|
45
40
  exp_show_output.include?("\n") || exp_show_output.size > 29 ||
@@ -47,12 +42,13 @@ module Assert
47
42
  end
48
43
  end
49
44
 
45
+ # use `diff` system cmd to show exp/act show output
50
46
  def self.syscmd_diff_proc(syscmd = "diff --unified=-1")
51
47
  Proc.new do |exp_show_output, act_show_output|
52
48
  result = ""
53
49
  tempfile('exp_show_output', exp_show_output) do |a|
54
50
  tempfile('act_show_output', act_show_output) do |b|
55
- result = `#{syscmd} #{a.path} #{b.path}`
51
+ result = `#{syscmd} #{a.path} #{b.path}`.strip
56
52
  result.sub!(/^\-\-\- .+/, "--- expected")
57
53
  result.sub!(/^\+\+\+ .+/, "+++ actual")
58
54
  result = "--- expected\n+++ actual" if result.empty?
@@ -62,6 +58,22 @@ module Assert
62
58
  end
63
59
  end
64
60
 
61
+ # use git to determine which files have changes
62
+ def self.git_changed_proc
63
+ Proc.new do |config, test_paths|
64
+ files = []
65
+ cmd = [
66
+ "git diff --no-ext-diff --name-only", # changed files
67
+ "git ls-files --others --exclude-standard" # added files
68
+ ].map{ |c| "#{c} -- #{test_paths.join(' ')}" }.join(' && ')
69
+ Assert::CLI.bench('Load only changed files') do
70
+ files = `#{cmd}`.split("\n")
71
+ end
72
+ puts Assert::CLI.debug_msg(" `#{cmd}`") if config.debug
73
+ files
74
+ end
75
+ end
76
+
65
77
  end
66
78
 
67
79
  # alias for brevity
@@ -1,3 +1,3 @@
1
1
  module Assert
2
- VERSION = "2.4.0"
2
+ VERSION = "2.5.0"
3
3
  end
data/lib/assert/view.rb CHANGED
@@ -6,16 +6,18 @@ module Assert::View
6
6
  # require views by passing either a full path to the view ruby file
7
7
  # or passing the name of a view installed in ~/.assert/views
8
8
 
9
- def self.require_user_view(view)
10
- views_file = File.join(Assert.config.user_test_dir, 'views', view, 'lib', view)
9
+ def self.require_user_view(view_name)
10
+ views_file = File.expand_path(
11
+ File.join("#{ENV['HOME']}/.assert/views", view_name, 'lib', view_name)
12
+ )
11
13
 
12
- if File.exists?(view) || File.exists?(view+'.rb')
13
- require view
14
- elsif File.exists?(views_file+'.rb')
14
+ if File.exists?(view_name) || File.exists?(view_name + '.rb')
15
+ require view_name
16
+ elsif File.exists?(views_file + '.rb')
15
17
  require views_file
16
18
  else
17
- msg = "[WARN] Can't find or require #{view.inspect} view."
18
- msg << " Did you install it in `~/.assert/views`?" if !view.match(/\A\//)
19
+ msg = "[WARN] Can't find or require #{view_name.inspect} view."
20
+ msg << " Did you install it in `~/.assert/views`?" if !view_name.match(/\A\//)
19
21
  warn msg
20
22
  end
21
23
  end
@@ -1,3 +1,5 @@
1
+ require 'assert/config'
2
+ require 'assert/suite'
1
3
  require 'assert/result'
2
4
 
3
5
  module Assert::View
@@ -17,13 +19,33 @@ module Assert::View
17
19
  option 'skip_abbrev', 'S'
18
20
  option 'error_abbrev', 'E'
19
21
 
20
- def initialize(output_io, suite=nil)
21
- @output_io, @suite = output_io, suite
22
+ attr_reader :config, :suite
23
+
24
+ def initialize(output_io, *args)
25
+ @output_io = output_io
26
+ @suite, @config = [
27
+ args.last.kind_of?(Assert::Suite) ? args.pop : nil,
28
+ args.last.kind_of?(Assert::Config) ? args.pop : nil
29
+ ]
30
+
22
31
  @output_io.sync = true if @output_io.respond_to?(:sync=)
23
32
  end
24
33
 
25
- def view; self; end
26
- def suite; @suite || Assert.suite; end
34
+ def is_tty?
35
+ !!@output_io.isatty
36
+ end
37
+
38
+ def view
39
+ self
40
+ end
41
+
42
+ def config
43
+ @config ||= Assert.config
44
+ end
45
+
46
+ def suite
47
+ @suite ||= Assert.suite
48
+ end
27
49
 
28
50
  def fire(callback, *args)
29
51
  self.send(callback, *args)
@@ -68,7 +68,7 @@ module Assert::View
68
68
 
69
69
  puts "#{result_count_statement}: #{styled_results_sentence}"
70
70
  puts
71
- puts "(#{run_time} seconds)"
71
+ puts "(#{run_time} seconds, #{test_rate} tests/s, #{result_rate} results/s)"
72
72
  end
73
73
 
74
74
  end
@@ -9,7 +9,7 @@ module Assert::View::Helpers
9
9
  end
10
10
 
11
11
  def ansi_styled_msg(msg, styles=[])
12
- if !(style = ansi_style(*styles)).empty?
12
+ if !(style = ansi_style(*styles)).empty? && self.is_tty?
13
13
  style + msg + ANSI.send(:reset)
14
14
  else
15
15
  msg
@@ -6,13 +6,8 @@ module Assert::View::Helpers
6
6
  receiver.class_eval{ extend ClassMethods }
7
7
  end
8
8
 
9
- # get the formatted suite run time
10
- def run_time(format='%.6f')
11
- format % self.suite.run_time
12
- end
13
-
14
9
  def runner_seed
15
- Assert.config.runner_seed
10
+ self.config.runner_seed
16
11
  end
17
12
 
18
13
  def count(type)
@@ -27,6 +22,21 @@ module Assert::View::Helpers
27
22
  self.count(:pass) == self.count(:results)
28
23
  end
29
24
 
25
+ # get the formatted suite run time
26
+ def run_time(format = '%.6f')
27
+ format % self.suite.run_time
28
+ end
29
+
30
+ # get the formatted suite test rate
31
+ def test_rate(format = '%.6f')
32
+ format % self.suite.test_rate
33
+ end
34
+
35
+ # get the formatted suite result rate
36
+ def result_rate(format = '%.6f')
37
+ format % self.suite.result_rate
38
+ end
39
+
30
40
  # get a uniq list of contexts for the test suite
31
41
  def suite_contexts
32
42
  @suite_contexts ||= self.suite.tests.inject([]) do |contexts, test|
data/test/helper.rb CHANGED
@@ -7,97 +7,4 @@ $LOAD_PATH.unshift(ROOT_PATH)
7
7
 
8
8
  # require pry for debugging (`binding.pry`)
9
9
  require 'pry'
10
-
11
- # Force tests to run without halting on failures (needed so all tests will run
12
- # properly). For halt on fail behavior testing, the context of those tests
13
- # configures Assert temporarily as needed.
14
-
15
- class Assert::Context
16
- def setup
17
- Assert.config.halt_on_fail false
18
- # Note: don't mess with the `capture_output` setting in this setup block. Doing
19
- # so will break the capture output tests. If you really need to set it one
20
- # way or the other, do so in the `.assert.rb` local settings file.
21
- end
22
-
23
- # a context for use in testing all the different context singleton methods
24
- class ContextSingletonTests < Assert::Context
25
- desc "Assert context singleton"
26
- setup do
27
- @orig_assert_suite = Assert.suite
28
- Assert.config.suite TEST_ASSERT_SUITE
29
- @test = Factory.test
30
- @context_class = @test.context_class
31
- end
32
- teardown do
33
- TEST_ASSERT_SUITE.tests.clear
34
- Assert.config.suite @orig_assert_suite
35
- end
36
- subject{ @context_class }
37
-
38
- end
39
-
40
- end
41
-
42
-
43
- # A Suite for use in the tests. It is seperate from `Assert.suite`
44
- # (which is the actual suite being used to run the tests). Don't use
45
- # `Assert.suite` in your tests, use TEST_ASSERT_SUITE
46
-
47
- TEST_ASSERT_SUITE = Assert::Suite.new
48
-
49
- # A context for use in the tests and also in the `context_class` factory. This
50
- # will ensure any contexts defined as part of the tests will add their methods
51
- # to `TEST_ASSERT_SUITE`
52
-
53
- class TestContext < Assert::Context
54
- def self.method_added(meth)
55
- if meth.to_s =~ Assert::Suite::TEST_METHOD_REGEX
56
- ci = Assert::Suite::ContextInfo.new(self, Factory.context_info_called_from)
57
- TEST_ASSERT_SUITE.tests << Assert::Test.new(meth.to_s, ci, meth)
58
- end
59
- end
60
- end
61
-
62
- module Factory
63
-
64
- def self.context_info_called_from
65
- "/path/to_file.rb:1234"
66
- end
67
-
68
- def self.context_info(context_class)
69
- Assert::Suite::ContextInfo.new(context_class, context_info_called_from)
70
- end
71
-
72
- # Generate an anonymous `Context` inherited from `TestContext` by default.
73
- # This provides a common interface for all contexts used in testing.
74
-
75
- def self.context_class(inherit_from=nil, &block)
76
- inherit_from ||= TestContext
77
- klass = Class.new(inherit_from, &block)
78
- default = (const_name = "FactoryAssertContext").dup
79
-
80
- while(Object.const_defined?(const_name)) do
81
- const_name = "FactoryAssertContext#{rand(Time.now.to_i)}"
82
- end
83
- Object.const_set(const_name, klass)
84
- klass
85
- end
86
-
87
- # Generate a no-op test for use in testing.
88
-
89
- def self.test(*args, &block)
90
- name = (args[0] || "a test").to_s
91
- context_info = args[1] || self.context_info(self.context_class)
92
- test_block = (block || args[2] || ::Proc.new{})
93
-
94
- Assert::Test.new(name, context_info, &test_block)
95
- end
96
-
97
- # Generate a skip result for use in testing.
98
-
99
- def self.skip_result(name, exception)
100
- Assert::Result::Skip.new(Factory.test(name), exception)
101
- end
102
-
103
- end
10
+ require 'test/support/factory'