assert 0.7.3 → 0.8.0

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