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.
- data/.gitignore +16 -4
- data/{CHANGELOG.rdoc → CHANGELOG.md} +14 -3
- data/LICENSE +22 -0
- data/README.md +261 -0
- data/Rakefile +1 -1
- data/assert.gemspec +2 -1
- data/lib/assert/assertions.rb +16 -0
- data/lib/assert/autorun.rb +11 -7
- data/lib/assert/macros/methods.rb +49 -3
- data/lib/assert/rake_tasks.rb +13 -7
- data/lib/assert/rake_tasks/irb.rb +1 -1
- data/lib/assert/rake_tasks/scope.rb +55 -28
- data/lib/assert/rake_tasks/test_task.rb +4 -1
- data/lib/assert/result.rb +7 -9
- data/lib/assert/result_set.rb +7 -4
- data/lib/assert/runner.rb +10 -10
- data/lib/assert/setup/helpers.rb +5 -3
- data/lib/assert/setup/runner.rb +2 -3
- data/lib/assert/setup/suite.rb +2 -5
- data/lib/assert/setup/view.rb +26 -3
- data/lib/assert/test.rb +56 -37
- data/lib/assert/version.rb +1 -1
- data/lib/assert/view/base.rb +75 -0
- data/lib/assert/view/default_view.rb +75 -0
- data/lib/assert/view/helpers/ansi_styles.rb +25 -0
- data/lib/assert/view/helpers/capture_output.rb +23 -0
- data/lib/assert/view/helpers/common.rb +154 -0
- data/test/assertions/assert_file_exists_test.rb +43 -0
- data/test/assertions/assert_not_file_exists_test.rb +43 -0
- data/test/assertions_test.rb +4 -1
- data/test/macro_test.rb +25 -0
- data/test/rake_tasks/irb_test.rb +2 -2
- data/test/rake_tasks/scope_test.rb +9 -9
- data/test/result_set_test.rb +13 -23
- data/test/runner_test.rb +1 -1
- data/test/test/{running_test.rb → running_tests.rb} +14 -14
- data/test/view/base_tests.rb +65 -0
- metadata +29 -18
- data/Gemfile.lock +0 -23
- data/README.rdoc +0 -183
data/lib/assert/version.rb
CHANGED
@@ -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
|