assert 0.7.3 → 0.8.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 (40) hide show
  1. data/.gitignore +16 -4
  2. data/{CHANGELOG.rdoc → CHANGELOG.md} +14 -3
  3. data/LICENSE +22 -0
  4. data/README.md +261 -0
  5. data/Rakefile +1 -1
  6. data/assert.gemspec +2 -1
  7. data/lib/assert/assertions.rb +16 -0
  8. data/lib/assert/autorun.rb +11 -7
  9. data/lib/assert/macros/methods.rb +49 -3
  10. data/lib/assert/rake_tasks.rb +13 -7
  11. data/lib/assert/rake_tasks/irb.rb +1 -1
  12. data/lib/assert/rake_tasks/scope.rb +55 -28
  13. data/lib/assert/rake_tasks/test_task.rb +4 -1
  14. data/lib/assert/result.rb +7 -9
  15. data/lib/assert/result_set.rb +7 -4
  16. data/lib/assert/runner.rb +10 -10
  17. data/lib/assert/setup/helpers.rb +5 -3
  18. data/lib/assert/setup/runner.rb +2 -3
  19. data/lib/assert/setup/suite.rb +2 -5
  20. data/lib/assert/setup/view.rb +26 -3
  21. data/lib/assert/test.rb +56 -37
  22. data/lib/assert/version.rb +1 -1
  23. data/lib/assert/view/base.rb +75 -0
  24. data/lib/assert/view/default_view.rb +75 -0
  25. data/lib/assert/view/helpers/ansi_styles.rb +25 -0
  26. data/lib/assert/view/helpers/capture_output.rb +23 -0
  27. data/lib/assert/view/helpers/common.rb +154 -0
  28. data/test/assertions/assert_file_exists_test.rb +43 -0
  29. data/test/assertions/assert_not_file_exists_test.rb +43 -0
  30. data/test/assertions_test.rb +4 -1
  31. data/test/macro_test.rb +25 -0
  32. data/test/rake_tasks/irb_test.rb +2 -2
  33. data/test/rake_tasks/scope_test.rb +9 -9
  34. data/test/result_set_test.rb +13 -23
  35. data/test/runner_test.rb +1 -1
  36. data/test/test/{running_test.rb → running_tests.rb} +14 -14
  37. data/test/view/base_tests.rb +65 -0
  38. metadata +29 -18
  39. data/Gemfile.lock +0 -23
  40. data/README.rdoc +0 -183
@@ -1,3 +1,3 @@
1
1
  module Assert
2
- VERSION = "0.7.3"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -0,0 +1,75 @@
1
+ require 'assert/result'
2
+ require 'assert/options'
3
+
4
+ module Assert::View
5
+
6
+ class Base
7
+
8
+ # setup some default options for all views
9
+
10
+ include Assert::Options
11
+ options do
12
+ default_pass_abbrev '.'
13
+ default_fail_abbrev 'F'
14
+ default_ignore_abbrev 'I'
15
+ default_skip_abbrev 'S'
16
+ default_error_abbrev 'E'
17
+ end
18
+
19
+ # include a bunch of common helper methods
20
+
21
+ require 'assert/view/helpers/common'
22
+ include Helpers::Common
23
+
24
+ attr_accessor :suite, :output_io
25
+
26
+ def initialize(output_io, suite=Assert.suite)
27
+ @output_io = output_io
28
+ @suite = suite
29
+
30
+ if @output_io.respond_to?(:sync=)
31
+ @output_io.sync = true
32
+ end
33
+ end
34
+
35
+ def view; self; end
36
+
37
+ def fire(callback, *args)
38
+ self.send(callback, *args)
39
+ end
40
+
41
+ # Callbacks
42
+
43
+ # define callback handlers to output information. handlers are
44
+ # instance_eval'd in the scope of the view instance. any stdout is captured
45
+ # and sent to the io stream.
46
+
47
+ # available callbacks from the runner:
48
+ # * `before_load`: called at the beginning, before the suite is loaded
49
+ # * `after_load`: called after the suite is loaded, just before `on_start`
50
+ # functionally equivalent to `on_start`
51
+ # * `on_start`: called when a loaded test suite starts running
52
+ # * `before_test`: called before a test starts running
53
+ # the test is passed as an arg
54
+ # * `on_result`: called when a running tests generates a result
55
+ # the result is passed as an arg
56
+ # * `after_test`: called after a test finishes running
57
+ # the test is passed as an arg
58
+ # * `on_finish`: called when the test suite is finished running
59
+
60
+ def before_load; end
61
+ def after_load; end
62
+ def on_start; end
63
+ def before_test(test); end
64
+ def on_result(result); end
65
+ def after_test(test); end
66
+ def on_finish; end
67
+
68
+ # IO capture
69
+
70
+ def puts(*args); @output_io.puts(*args); end
71
+ def print(*args); @output_io.print(*args); end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,75 @@
1
+ require 'assert/view/base'
2
+
3
+ module Assert::View
4
+
5
+ # This is the default view used by assert. It renders ansi test output
6
+ # designed for terminal viewing.
7
+
8
+ class DefaultView < Base
9
+ require 'assert/view/helpers/capture_output'
10
+ include Helpers::CaptureOutput
11
+
12
+ require 'assert/view/helpers/ansi_styles'
13
+ include Helpers::AnsiStyles
14
+
15
+ options do
16
+ styled true
17
+ pass_styles :green
18
+ fail_styles :red, :bold
19
+ error_styles :yellow, :bold
20
+ skip_styles :cyan
21
+ ignore_styles :magenta
22
+ end
23
+
24
+ def after_load
25
+ puts "Loaded suite (#{test_count_statement})"
26
+ end
27
+
28
+ def on_start
29
+ if tests?
30
+ puts "Running tests in random order, seeded with \"#{runner_seed}\""
31
+ end
32
+ end
33
+
34
+ def on_result(result)
35
+ result_abbrev = options.send("#{result.to_sym}_abbrev")
36
+ styled_abbrev = ansi_styled_msg(result_abbrev, result_ansi_styles(result))
37
+
38
+ print styled_abbrev
39
+ end
40
+
41
+ def on_finish
42
+ if tests?
43
+ print "\n"
44
+ puts
45
+
46
+ # output detailed results for the tests in reverse test/result order
47
+ tests = suite.ordered_tests.reverse
48
+ result_details_for(tests, :reversed).each do |details|
49
+ if show_result_details?(details.result)
50
+ # output the styled result details
51
+ result = details.result
52
+ puts ansi_styled_msg(result.to_s, result_ansi_styles(result))
53
+
54
+ # output any captured stdout
55
+ output = details.output
56
+ puts captured_output(output) if output && !output.empty?
57
+
58
+ puts
59
+ end
60
+ end
61
+ end
62
+
63
+ # style the summaries of each result set
64
+ styled_results_sentence = results_summary_sentence do |summary, sym|
65
+ ansi_styled_msg(summary, result_ansi_styles(sym))
66
+ end
67
+
68
+ puts "#{result_count_statement}: #{styled_results_sentence}"
69
+ puts
70
+ puts "(#{run_time} seconds)"
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,25 @@
1
+ require 'ansi/code'
2
+
3
+ module Assert::View::Helpers
4
+
5
+ module AnsiStyles
6
+
7
+ def result_ansi_styles(result)
8
+ view.options.styled ? view.options.send("#{result.to_sym}_styles") : []
9
+ end
10
+
11
+ def ansi_styled_msg(msg, styles=[])
12
+ if !(style = ansi_style(*styles)).empty?
13
+ style + msg + ANSI.send(:reset)
14
+ else
15
+ msg
16
+ end
17
+ end
18
+
19
+ def ansi_style(*ansi_codes)
20
+ ansi_codes.collect{|code| ANSI.send(code) rescue nil}.compact.join('')
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,23 @@
1
+ module Assert::View::Helpers
2
+
3
+ module CaptureOutput
4
+
5
+ def captured_output(output)
6
+ if !output.empty?
7
+ # TODO: move to the base view
8
+ [ captured_output_start_msg,
9
+ output + captured_output_end_msg
10
+ ].join("\n")
11
+ end
12
+ end
13
+
14
+ def captured_output_start_msg
15
+ "--- stdout ---"
16
+ end
17
+ def captured_output_end_msg
18
+ "--------------"
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,154 @@
1
+ module Assert::View::Helpers
2
+
3
+ class ResultDetails
4
+
5
+ attr_reader :result, :test_index, :test, :output
6
+
7
+ def initialize(result, test, test_index)
8
+ @result = result
9
+ @test = test
10
+ @test_index = test_index
11
+ @output = test.output
12
+ end
13
+ end
14
+
15
+ module Common
16
+
17
+ # get the formatted suite run time
18
+ def run_time(format='%.6f')
19
+ format % self.suite.run_time
20
+ end
21
+
22
+ def runner_seed
23
+ self.suite.runner_seed
24
+ end
25
+
26
+ def count(type)
27
+ self.suite.count(type)
28
+ end
29
+
30
+ def tests?
31
+ self.count(:tests) > 0
32
+ end
33
+
34
+ def all_pass?
35
+ self.count(:pass) == self.count(:results)
36
+ end
37
+
38
+ # get a uniq list of contexts for the test suite
39
+ def suite_contexts
40
+ @suite_contexts ||= self.suite.tests.inject([]) do |contexts, test|
41
+ contexts << test.context_info.klass
42
+ end.uniq
43
+ end
44
+
45
+ def ordered_suite_contexts
46
+ self.suite_contexts.sort{|a,b| a.to_s <=> b.to_s}
47
+ end
48
+
49
+ # get a uniq list of files containing contexts for the test suite
50
+ def suite_files
51
+ @suite_files ||= self.suite.tests.inject([]) do |files, test|
52
+ files << test.context_info.file
53
+ end.uniq
54
+ end
55
+
56
+ def ordered_suite_files
57
+ self.suite_files.sort{|a,b| a.to_s <=> b.to_s}
58
+ end
59
+
60
+ # get all the result details for a set of tests
61
+ def result_details_for(tests, result_order=:normal)
62
+ test_index = 0
63
+ tests.collect do |test|
64
+ test_index += 1
65
+
66
+ details = test.results.
67
+ collect { |result| ResultDetails.new(result, test, test_index) }
68
+
69
+ details.reverse! if result_order == :reversed
70
+
71
+ details
72
+ end.compact.flatten
73
+ end
74
+
75
+ # get all the result details for a set of tests matching a file or context
76
+ def matched_result_details_for(match, tests, result_order=:normal)
77
+ context_match = match.kind_of?(Class) && match.ancestors.include?(Assert::Context)
78
+ file_match = match.kind_of?(String)
79
+
80
+ matching_tests = if context_match
81
+ tests.select {|test| test.context_info.klass == match}
82
+ elsif file_match
83
+ tests.select {|test| test.context_info.file == match}
84
+ else
85
+ tests
86
+ end
87
+
88
+ result_details_for(matching_tests, result_order)
89
+ end
90
+
91
+ # only show result details for failed or errored results
92
+ # show result details if a skip or passed result was issues w/ a message
93
+ def show_result_details?(result)
94
+ ([:fail, :error].include?(result.to_sym)) ||
95
+ ([:skip, :ignore].include?(result.to_sym) && result.message)
96
+ end
97
+
98
+ # return a list of result symbols that have actually occurred
99
+ def ocurring_result_types
100
+ @result_types ||= [
101
+ :pass, :fail, :ignore, :skip, :error
102
+ ].select { |result_sym| self.count(result_sym) > 0 }
103
+ end
104
+
105
+ # print a result summary message for a given result type
106
+ def result_summary_msg(result_type)
107
+ if result_type == :pass && self.all_pass?
108
+ self.all_pass_result_summary_msg
109
+ else
110
+ "#{self.count(result_type)} #{result_type.to_s}"
111
+ end
112
+ end
113
+
114
+ # generate an appropriate result summary msg for all tests passing
115
+ def all_pass_result_summary_msg
116
+ if self.count(:results) < 1
117
+ "uhh..."
118
+ elsif self.count(:results) == 1
119
+ "pass"
120
+ else
121
+ "all pass"
122
+ end
123
+ end
124
+
125
+ # generate a sentence fragment describing the breakdown of test results
126
+ # if a block is given, yield each msg in the breakdown for custom formatting
127
+ def results_summary_sentence
128
+ summaries = self.ocurring_result_types.collect do |result_sym|
129
+ summary_msg = self.result_summary_msg(result_sym)
130
+ block_given? ? yield(summary_msg, result_sym) : summary_msg
131
+ end
132
+ self.to_sentence(summaries)
133
+ end
134
+
135
+ def test_count_statement
136
+ "#{self.count(:tests)} test#{'s' if self.count(:tests) != 1}"
137
+ end
138
+
139
+ def result_count_statement
140
+ "#{self.count(:results)} result#{'s' if self.count(:results) != 1}"
141
+ end
142
+
143
+ # generate a comma-seperated sentence fragment given a list of things
144
+ def to_sentence(things)
145
+ if things.size <= 2
146
+ things.join(things.size == 2 ? ' and ' : '')
147
+ else
148
+ [things[0..-2].join(", "), things.last].join(", and ")
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+ end
@@ -0,0 +1,43 @@
1
+ require 'assert'
2
+
3
+ class Assert::Assertions::AssertFileExistsTests < Assert::Context
4
+ desc "the assert_file_exists helper run in a test"
5
+ setup do
6
+ fail_desc = @fail_desc = "assert file exists empty fail desc"
7
+ fail_args = @fail_args = [ '/a/path/to/some/file/that/no/exists', fail_desc ]
8
+ @test = Factory.test do
9
+ assert_file_exists(__FILE__) # pass
10
+ assert_file_exists(*fail_args) # fail
11
+ end
12
+ @test.run
13
+ end
14
+ subject{ @test }
15
+
16
+ should "have 2 total results" do
17
+ assert_equal 2, subject.result_count
18
+ end
19
+ should "have 1 pass result" do
20
+ assert_equal 1, subject.result_count(:pass)
21
+ end
22
+ should "have 1 fail result" do
23
+ assert_equal 1, subject.result_count(:fail)
24
+ end
25
+
26
+ class FailMessageTest < AssertFileExistsTests
27
+ desc "with a failed result"
28
+ setup do
29
+ @expected = [
30
+ @fail_args[1],
31
+ "Expected #{@fail_args[0].inspect} to exist."
32
+ ].join("\n")
33
+ @fail_message = @test.fail_results.first.message
34
+ end
35
+ subject{ @fail_message }
36
+
37
+ should "have a fail message with an explanation of what failed and my fail description" do
38
+ assert_equal @expected, subject
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,43 @@
1
+ require 'assert'
2
+
3
+ class Assert::Assertions::AssertNotFileExistsTests < Assert::Context
4
+ desc "the assert_not_file_exists helper run in a test"
5
+ setup do
6
+ fail_desc = @fail_desc = "assert not file exists empty fail desc"
7
+ fail_args = @fail_args = [ __FILE__, fail_desc ]
8
+ @test = Factory.test do
9
+ assert_not_file_exists('/a/path/to/some/file/that/no/exists') # pass
10
+ assert_not_file_exists(*fail_args) # fail
11
+ end
12
+ @test.run
13
+ end
14
+ subject{ @test }
15
+
16
+ should "have 2 total results" do
17
+ assert_equal 2, subject.result_count
18
+ end
19
+ should "have 1 pass result" do
20
+ assert_equal 1, subject.result_count(:pass)
21
+ end
22
+ should "have 1 fail result" do
23
+ assert_equal 1, subject.result_count(:fail)
24
+ end
25
+
26
+ class FailMessageTest < AssertNotFileExistsTests
27
+ desc "with a failed result"
28
+ setup do
29
+ @expected = [
30
+ @fail_args[1],
31
+ "Expected #{@fail_args[0].inspect} to not exist."
32
+ ].join("\n")
33
+ @fail_message = @test.fail_results.first.message
34
+ end
35
+ subject{ @fail_message }
36
+
37
+ should "have a fail message with an explanation of what failed and my fail description" do
38
+ assert_equal @expected, subject
39
+ end
40
+
41
+ end
42
+
43
+ end