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