treetop 0.1.0 → 1.0.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/README +3 -0
- data/Rakefile +35 -0
- data/bin/tt +25 -0
- data/lib/treetop.rb +10 -6
- data/lib/treetop/compiler.rb +7 -0
- data/lib/treetop/compiler/grammar_compiler.rb +21 -0
- data/lib/treetop/compiler/lexical_address_space.rb +17 -0
- data/lib/treetop/compiler/load_grammar.rb +7 -0
- data/lib/treetop/compiler/metagrammar.rb +2441 -0
- data/lib/treetop/compiler/metagrammar.treetop +384 -0
- data/lib/treetop/compiler/node_classes.rb +18 -0
- data/lib/treetop/compiler/node_classes/anything_symbol.rb +10 -0
- data/lib/treetop/compiler/node_classes/atomic_expression.rb +9 -0
- data/lib/treetop/compiler/node_classes/character_class.rb +10 -0
- data/lib/treetop/compiler/node_classes/choice.rb +31 -0
- data/lib/treetop/compiler/node_classes/declaration_sequence.rb +24 -0
- data/lib/treetop/compiler/node_classes/grammar.rb +28 -0
- data/lib/treetop/compiler/node_classes/inline_module.rb +27 -0
- data/lib/treetop/compiler/node_classes/nonterminal.rb +11 -0
- data/lib/treetop/compiler/node_classes/optional.rb +19 -0
- data/lib/treetop/compiler/node_classes/parenthesized_expression.rb +9 -0
- data/lib/treetop/compiler/node_classes/parsing_expression.rb +132 -0
- data/lib/treetop/compiler/node_classes/parsing_rule.rb +55 -0
- data/lib/treetop/compiler/node_classes/predicate.rb +45 -0
- data/lib/treetop/compiler/node_classes/repetition.rb +56 -0
- data/lib/treetop/compiler/node_classes/sequence.rb +64 -0
- data/lib/treetop/compiler/node_classes/terminal.rb +10 -0
- data/lib/treetop/compiler/node_classes/treetop_file.rb +9 -0
- data/lib/treetop/compiler/ruby_builder.rb +109 -0
- data/lib/treetop/ruby_extensions.rb +2 -0
- data/lib/treetop/ruby_extensions/string.rb +19 -0
- data/lib/treetop/runtime.rb +9 -0
- data/lib/treetop/runtime/compiled_parser.rb +66 -0
- data/lib/treetop/runtime/node_cache.rb +27 -0
- data/lib/treetop/runtime/parse_cache.rb +19 -0
- data/lib/treetop/runtime/parse_failure.rb +32 -0
- data/lib/treetop/runtime/parse_result.rb +30 -0
- data/lib/treetop/runtime/syntax_node.rb +53 -0
- data/lib/treetop/runtime/terminal_parse_failure.rb +33 -0
- data/lib/treetop/runtime/terminal_syntax_node.rb +12 -0
- data/test/compilation_target/target.rb +143 -0
- data/test/compilation_target/target.treetop +15 -0
- data/test/compilation_target/target_test.rb +56 -0
- data/test/compiler/and_predicate_test.rb +33 -0
- data/test/compiler/anything_symbol_test.rb +24 -0
- data/test/compiler/character_class_test.rb +45 -0
- data/test/compiler/choice_test.rb +49 -0
- data/test/compiler/circular_compilation_test.rb +20 -0
- data/test/compiler/failure_propagation_functional_test.rb +20 -0
- data/test/compiler/grammar_compiler_test.rb +58 -0
- data/test/compiler/grammar_test.rb +33 -0
- data/test/compiler/nonterminal_symbol_test.rb +15 -0
- data/test/compiler/not_predicate_test.rb +35 -0
- data/test/compiler/one_or_more_test.rb +30 -0
- data/test/compiler/optional_test.rb +32 -0
- data/test/compiler/parsing_rule_test.rb +30 -0
- data/test/compiler/sequence_test.rb +68 -0
- data/test/compiler/terminal_symbol_test.rb +35 -0
- data/test/compiler/test_grammar.treetop +7 -0
- data/test/compiler/zero_or_more_test.rb +51 -0
- data/test/composition/a.treetop +11 -0
- data/test/composition/b.treetop +11 -0
- data/test/composition/c.treetop +10 -0
- data/test/composition/d.treetop +10 -0
- data/test/composition/grammar_composition_test.rb +23 -0
- data/test/parser/syntax_node_test.rb +53 -0
- data/test/parser/terminal_parse_failure_test.rb +22 -0
- data/test/ruby_extensions/string_test.rb +33 -0
- data/test/screw/Rakefile +16 -0
- data/test/screw/unit.rb +37 -0
- data/test/screw/unit/assertion_failed_error.rb +14 -0
- data/test/screw/unit/assertions.rb +615 -0
- data/test/screw/unit/auto_runner.rb +227 -0
- data/test/screw/unit/collector.rb +45 -0
- data/test/screw/unit/collector/dir.rb +107 -0
- data/test/screw/unit/collector/objectspace.rb +28 -0
- data/test/screw/unit/error.rb +48 -0
- data/test/screw/unit/failure.rb +45 -0
- data/test/screw/unit/sugar.rb +25 -0
- data/test/screw/unit/test_case.rb +176 -0
- data/test/screw/unit/test_result.rb +73 -0
- data/test/screw/unit/test_suite.rb +70 -0
- data/test/screw/unit/ui.rb +4 -0
- data/test/screw/unit/ui/console/test_runner.rb +118 -0
- data/test/screw/unit/ui/fox/test_runner.rb +268 -0
- data/test/screw/unit/ui/gtk/test_runner.rb +416 -0
- data/test/screw/unit/ui/gtk2/testrunner.rb +465 -0
- data/test/screw/unit/ui/test_runner_mediator.rb +58 -0
- data/test/screw/unit/ui/test_runner_utilities.rb +46 -0
- data/test/screw/unit/ui/tk/test_runner.rb +260 -0
- data/test/screw/unit/util.rb +4 -0
- data/test/screw/unit/util/backtrace_filter.rb +40 -0
- data/test/screw/unit/util/observable.rb +82 -0
- data/test/screw/unit/util/proc_wrapper.rb +48 -0
- data/test/test_helper.rb +89 -0
- metadata +127 -69
- data/lib/treetop/api.rb +0 -3
- data/lib/treetop/api/load_grammar.rb +0 -16
- data/lib/treetop/api/malformed_grammar_exception.rb +0 -9
- data/lib/treetop/grammar.rb +0 -7
- data/lib/treetop/grammar/grammar.rb +0 -48
- data/lib/treetop/grammar/grammar_builder.rb +0 -35
- data/lib/treetop/grammar/parsing_expression_builder.rb +0 -5
- data/lib/treetop/grammar/parsing_expression_builder_helper.rb +0 -121
- data/lib/treetop/grammar/parsing_expressions.rb +0 -18
- data/lib/treetop/grammar/parsing_expressions/and_predicate.rb +0 -17
- data/lib/treetop/grammar/parsing_expressions/anything_symbol.rb +0 -20
- data/lib/treetop/grammar/parsing_expressions/character_class.rb +0 -24
- data/lib/treetop/grammar/parsing_expressions/node_instantiating_parsing_expression.rb +0 -14
- data/lib/treetop/grammar/parsing_expressions/node_propagating_parsing_expression.rb +0 -4
- data/lib/treetop/grammar/parsing_expressions/nonterminal_symbol.rb +0 -42
- data/lib/treetop/grammar/parsing_expressions/not_predicate.rb +0 -18
- data/lib/treetop/grammar/parsing_expressions/one_or_more.rb +0 -12
- data/lib/treetop/grammar/parsing_expressions/optional.rb +0 -14
- data/lib/treetop/grammar/parsing_expressions/ordered_choice.rb +0 -27
- data/lib/treetop/grammar/parsing_expressions/parsing_expression.rb +0 -36
- data/lib/treetop/grammar/parsing_expressions/predicate.rb +0 -25
- data/lib/treetop/grammar/parsing_expressions/repeating_parsing_expression.rb +0 -29
- data/lib/treetop/grammar/parsing_expressions/sequence.rb +0 -41
- data/lib/treetop/grammar/parsing_expressions/terminal_parsing_expression.rb +0 -11
- data/lib/treetop/grammar/parsing_expressions/terminal_symbol.rb +0 -31
- data/lib/treetop/grammar/parsing_expressions/zero_or_more.rb +0 -11
- data/lib/treetop/grammar/parsing_rule.rb +0 -10
- data/lib/treetop/metagrammar.rb +0 -2
- data/lib/treetop/metagrammar/metagrammar.rb +0 -14
- data/lib/treetop/metagrammar/metagrammar.treetop +0 -320
- data/lib/treetop/parser.rb +0 -11
- data/lib/treetop/parser/node_cache.rb +0 -25
- data/lib/treetop/parser/parse_cache.rb +0 -17
- data/lib/treetop/parser/parse_failure.rb +0 -22
- data/lib/treetop/parser/parse_result.rb +0 -26
- data/lib/treetop/parser/parser.rb +0 -24
- data/lib/treetop/parser/sequence_syntax_node.rb +0 -14
- data/lib/treetop/parser/syntax_node.rb +0 -31
- data/lib/treetop/parser/terminal_parse_failure.rb +0 -18
- data/lib/treetop/parser/terminal_syntax_node.rb +0 -7
- data/lib/treetop/protometagrammar.rb +0 -16
- data/lib/treetop/protometagrammar/anything_symbol_expression_builder.rb +0 -13
- data/lib/treetop/protometagrammar/block_expression_builder.rb +0 -17
- data/lib/treetop/protometagrammar/character_class_expression_builder.rb +0 -25
- data/lib/treetop/protometagrammar/grammar_expression_builder.rb +0 -38
- data/lib/treetop/protometagrammar/nonterminal_symbol_expression_builder.rb +0 -45
- data/lib/treetop/protometagrammar/ordered_choice_expression_builder.rb +0 -21
- data/lib/treetop/protometagrammar/parsing_rule_expression_builder.rb +0 -23
- data/lib/treetop/protometagrammar/parsing_rule_sequence_expression_builder.rb +0 -14
- data/lib/treetop/protometagrammar/prefix_expression_builder.rb +0 -25
- data/lib/treetop/protometagrammar/primary_expression_builder.rb +0 -71
- data/lib/treetop/protometagrammar/protometagrammar.rb +0 -25
- data/lib/treetop/protometagrammar/sequence_expression_builder.rb +0 -37
- data/lib/treetop/protometagrammar/suffix_expression_builder.rb +0 -33
- data/lib/treetop/protometagrammar/terminal_symbol_expression_builder.rb +0 -52
- data/lib/treetop/protometagrammar/trailing_block_expression_builder.rb +0 -30
- data/lib/treetop/ruby_extension.rb +0 -11
@@ -0,0 +1,45 @@
|
|
1
|
+
module Screw
|
2
|
+
module Unit
|
3
|
+
|
4
|
+
# Encapsulates a test failure. Created by Screw::Unit::TestCase
|
5
|
+
# when an assertion fails.
|
6
|
+
class Failure
|
7
|
+
attr_reader :test_name, :location, :message
|
8
|
+
|
9
|
+
SINGLE_CHARACTER = 'F'
|
10
|
+
|
11
|
+
# Creates a new Failure with the given location and
|
12
|
+
# message.
|
13
|
+
def initialize(test_name, location, message)
|
14
|
+
@test_name = test_name
|
15
|
+
@location = location
|
16
|
+
@message = message
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a single character representation of a failure.
|
20
|
+
def single_character_display
|
21
|
+
SINGLE_CHARACTER
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a brief version of the error description.
|
25
|
+
def short_display
|
26
|
+
"#@test_name: #{@message.split("\n")[0]}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a verbose version of the error description.
|
30
|
+
def long_display
|
31
|
+
location_display = if(location.size == 1)
|
32
|
+
location[0].sub(/\A(.+:\d+).*/, ' [\\1]')
|
33
|
+
else
|
34
|
+
"\n [#{location.join("\n ")}]"
|
35
|
+
end
|
36
|
+
"Failure:\n#@test_name#{location_display}:\n#@message"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Overridden to return long_display.
|
40
|
+
def to_s
|
41
|
+
long_display
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Object
|
2
|
+
def context(description, options = {}, &block)
|
3
|
+
superclass = options[:extend] || Screw::Unit::TestCase
|
4
|
+
test_case_class = Class.new(superclass, &block)
|
5
|
+
test_case_class.test_case_description = description
|
6
|
+
Object.send(:const_set, unique_test_case_name(description.sanitize.camelize), test_case_class)
|
7
|
+
end
|
8
|
+
|
9
|
+
def unique_test_case_name(name, n=1)
|
10
|
+
candidate_name = (n > 1 ? "#{name}#{n}" : name).to_sym
|
11
|
+
if Object.const_defined?(candidate_name)
|
12
|
+
unique_test_case_name(name, n + 1)
|
13
|
+
else
|
14
|
+
candidate_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
alias :describe :context
|
19
|
+
end
|
20
|
+
|
21
|
+
class String
|
22
|
+
def sanitize
|
23
|
+
self.gsub(/[^\w]+/, "_")
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module Screw
|
2
|
+
module Unit
|
3
|
+
|
4
|
+
# Ties everything together. If you subclass and add your own
|
5
|
+
# test methods, it takes care of making them into tests and
|
6
|
+
# wrapping those tests into a suite. It also does the
|
7
|
+
# nitty-gritty of actually running an individual test and
|
8
|
+
# collecting its results into a Screw::Unit::TestResult object.
|
9
|
+
class TestCase
|
10
|
+
class << self
|
11
|
+
|
12
|
+
attr_writer :test_case_description
|
13
|
+
|
14
|
+
def test_case_description
|
15
|
+
@test_case_description || self.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
# Rolls up all of the test* methods in the fixture into
|
19
|
+
# one suite, creating a new instance of the fixture for
|
20
|
+
# each method.
|
21
|
+
def suite
|
22
|
+
method_names = public_instance_methods(true)
|
23
|
+
tests = method_names.delete_if {|method_name| method_name !~ /^test./}
|
24
|
+
suite = TestSuite.new(name)
|
25
|
+
tests.sort.each do
|
26
|
+
|test|
|
27
|
+
catch(:invalid_test) do
|
28
|
+
suite << new(test)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
return suite
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_names
|
35
|
+
@test_names ||= Hash.new
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a test method by prefixing string with 'test_' and replacing non word characters with underbars
|
39
|
+
def test(name_string, &block)
|
40
|
+
method_name = unique_test_name("test_" + name_string.sanitize)
|
41
|
+
test_names[method_name] = name_string
|
42
|
+
define_method(method_name, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def unique_test_name(name, n=1)
|
46
|
+
candidate_name = (n > 1 ? "#{name}#{n}" : name).to_sym
|
47
|
+
if method_defined?(candidate_name)
|
48
|
+
unique_test_name(name, n + 1)
|
49
|
+
else
|
50
|
+
candidate_name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
alias :it :test
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
include Assertions
|
60
|
+
include Spec::Matchers
|
61
|
+
include Util::BacktraceFilter
|
62
|
+
|
63
|
+
attr_reader :method_name
|
64
|
+
|
65
|
+
STARTED = name + "::STARTED"
|
66
|
+
FINISHED = name + "::FINISHED"
|
67
|
+
|
68
|
+
##
|
69
|
+
# These exceptions are not caught by #run.
|
70
|
+
|
71
|
+
PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt,
|
72
|
+
SystemExit]
|
73
|
+
|
74
|
+
# Creates a new instance of the fixture for running the
|
75
|
+
# test represented by test_method_name.
|
76
|
+
def initialize(test_method_name)
|
77
|
+
unless(respond_to?(test_method_name) and
|
78
|
+
(method(test_method_name).arity == 0 ||
|
79
|
+
method(test_method_name).arity == -1))
|
80
|
+
throw :invalid_test
|
81
|
+
end
|
82
|
+
@method_name = test_method_name
|
83
|
+
@test_passed = true
|
84
|
+
end
|
85
|
+
|
86
|
+
# Runs the individual test method represented by this
|
87
|
+
# instance of the fixture, collecting statistics, failures
|
88
|
+
# and errors in result.
|
89
|
+
def run(result)
|
90
|
+
yield(STARTED, name)
|
91
|
+
@_result = result
|
92
|
+
begin
|
93
|
+
setup
|
94
|
+
__send__(@method_name)
|
95
|
+
rescue AssertionFailedError => e
|
96
|
+
add_failure(e.message, e.backtrace)
|
97
|
+
rescue Exception
|
98
|
+
raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
|
99
|
+
add_error($!)
|
100
|
+
ensure
|
101
|
+
begin
|
102
|
+
teardown
|
103
|
+
rescue AssertionFailedError => e
|
104
|
+
add_failure(e.message, e.backtrace)
|
105
|
+
rescue Exception
|
106
|
+
raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
|
107
|
+
add_error($!)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
result.add_run
|
111
|
+
yield(FINISHED, name)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Called before every test method runs. Can be used
|
115
|
+
# to set up fixture information.
|
116
|
+
def setup
|
117
|
+
end
|
118
|
+
|
119
|
+
# Called after every test method runs. Can be used to tear
|
120
|
+
# down fixture information.
|
121
|
+
def teardown
|
122
|
+
end
|
123
|
+
|
124
|
+
def default_test
|
125
|
+
flunk("No tests were specified")
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns whether this individual test passed or
|
129
|
+
# not. Primarily for use in teardown so that artifacts
|
130
|
+
# can be left behind if the test fails.
|
131
|
+
def passed?
|
132
|
+
return @test_passed
|
133
|
+
end
|
134
|
+
private :passed?
|
135
|
+
|
136
|
+
def size
|
137
|
+
1
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_assertion
|
141
|
+
@_result.add_assertion
|
142
|
+
end
|
143
|
+
private :add_assertion
|
144
|
+
|
145
|
+
def add_failure(message, all_locations=caller())
|
146
|
+
@test_passed = false
|
147
|
+
@_result.add_failure(Failure.new(name, filter_backtrace(all_locations), message))
|
148
|
+
end
|
149
|
+
private :add_failure
|
150
|
+
|
151
|
+
def add_error(exception)
|
152
|
+
@test_passed = false
|
153
|
+
@_result.add_error(Error.new(name, exception))
|
154
|
+
end
|
155
|
+
private :add_error
|
156
|
+
|
157
|
+
# Returns a human-readable name for the specific test that
|
158
|
+
# this instance of TestCase represents.
|
159
|
+
def name
|
160
|
+
"#{self.class.test_case_description}: #{self.class.test_names[@method_name] || @method_name}"
|
161
|
+
end
|
162
|
+
|
163
|
+
# Overridden to return #name.
|
164
|
+
def to_s
|
165
|
+
name
|
166
|
+
end
|
167
|
+
|
168
|
+
# It's handy to be able to compare TestCase instances.
|
169
|
+
def ==(other)
|
170
|
+
return false unless(other.kind_of?(self.class))
|
171
|
+
return false unless(@method_name == other.method_name)
|
172
|
+
self.class == other.class
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Screw
|
2
|
+
module Unit
|
3
|
+
|
4
|
+
# Collects Screw::Unit::Failure and Screw::Unit::Error so that
|
5
|
+
# they can be displayed to the user. To this end, observers
|
6
|
+
# can be added to it, allowing the dynamic updating of, say, a
|
7
|
+
# UI.
|
8
|
+
class TestResult
|
9
|
+
include Util::Observable
|
10
|
+
|
11
|
+
CHANGED = "CHANGED"
|
12
|
+
FAULT = "FAULT"
|
13
|
+
|
14
|
+
attr_reader(:run_count, :assertion_count)
|
15
|
+
|
16
|
+
# Constructs a new, empty TestResult.
|
17
|
+
def initialize
|
18
|
+
@run_count, @assertion_count = 0, 0
|
19
|
+
@failures, @errors = Array.new, Array.new
|
20
|
+
end
|
21
|
+
|
22
|
+
# Records a test run.
|
23
|
+
def add_run
|
24
|
+
@run_count += 1
|
25
|
+
notify_listeners(CHANGED, self)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Records a Screw::Unit::Failure.
|
29
|
+
def add_failure(failure)
|
30
|
+
@failures << failure
|
31
|
+
notify_listeners(FAULT, failure)
|
32
|
+
notify_listeners(CHANGED, self)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Records a Screw::Unit::Error.
|
36
|
+
def add_error(error)
|
37
|
+
@errors << error
|
38
|
+
notify_listeners(FAULT, error)
|
39
|
+
notify_listeners(CHANGED, self)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Records an individual assertion.
|
43
|
+
def add_assertion
|
44
|
+
@assertion_count += 1
|
45
|
+
notify_listeners(CHANGED, self)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a string contain the recorded runs, assertions,
|
49
|
+
# failures and errors in this TestResult.
|
50
|
+
def to_s
|
51
|
+
"#{run_count} tests, #{assertion_count} assertions, #{failure_count} failures, #{error_count} errors"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns whether or not this TestResult represents
|
55
|
+
# successful completion.
|
56
|
+
def passed?
|
57
|
+
return @failures.empty? && @errors.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the number of failures this TestResult has
|
61
|
+
# recorded.
|
62
|
+
def failure_count
|
63
|
+
return @failures.size
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the number of errors this TestResult has
|
67
|
+
# recorded.
|
68
|
+
def error_count
|
69
|
+
return @errors.size
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Screw
|
2
|
+
module Unit
|
3
|
+
|
4
|
+
# A collection of tests which can be #run.
|
5
|
+
#
|
6
|
+
# Note: It is easy to confuse a TestSuite instance with
|
7
|
+
# something that has a static suite method; I know because _I_
|
8
|
+
# have trouble keeping them straight. Think of something that
|
9
|
+
# has a suite method as simply providing a way to get a
|
10
|
+
# meaningful TestSuite instance.
|
11
|
+
class TestSuite
|
12
|
+
attr_reader :name, :tests
|
13
|
+
|
14
|
+
STARTED = name + "::STARTED"
|
15
|
+
FINISHED = name + "::FINISHED"
|
16
|
+
|
17
|
+
# Creates a new TestSuite with the given name.
|
18
|
+
def initialize(name="Unnamed TestSuite")
|
19
|
+
@name = name
|
20
|
+
@tests = []
|
21
|
+
end
|
22
|
+
|
23
|
+
# Runs the tests and/or suites contained in this
|
24
|
+
# TestSuite.
|
25
|
+
def run(result, &progress_block)
|
26
|
+
yield(STARTED, name)
|
27
|
+
@tests.each do |test|
|
28
|
+
test.run(result, &progress_block)
|
29
|
+
end
|
30
|
+
yield(FINISHED, name)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Adds the test to the suite.
|
34
|
+
def <<(test)
|
35
|
+
@tests << test
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete(test)
|
40
|
+
@tests.delete(test)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Retuns the rolled up number of tests in this suite;
|
44
|
+
# i.e. if the suite contains other suites, it counts the
|
45
|
+
# tests within those suites, not the suites themselves.
|
46
|
+
def size
|
47
|
+
total_size = 0
|
48
|
+
@tests.each { |test| total_size += test.size }
|
49
|
+
total_size
|
50
|
+
end
|
51
|
+
|
52
|
+
def empty?
|
53
|
+
tests.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
# Overridden to return the name given the suite at
|
57
|
+
# creation.
|
58
|
+
def to_s
|
59
|
+
@name
|
60
|
+
end
|
61
|
+
|
62
|
+
# It's handy to be able to compare TestSuite instances.
|
63
|
+
def ==(other)
|
64
|
+
return false unless(other.kind_of?(self.class))
|
65
|
+
return false unless(@name == other.name)
|
66
|
+
@tests == other.tests
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Screw
|
2
|
+
module Unit
|
3
|
+
module UI
|
4
|
+
module Console
|
5
|
+
|
6
|
+
# Runs a Screw::Unit::TestSuite on the console.
|
7
|
+
class TestRunner
|
8
|
+
extend TestRunnerUtilities
|
9
|
+
|
10
|
+
# Creates a new TestRunner for running the passed
|
11
|
+
# suite. If quiet_mode is true, the output while
|
12
|
+
# running is limited to progress dots, errors and
|
13
|
+
# failures, and the final result. io specifies
|
14
|
+
# where runner output should go to; defaults to
|
15
|
+
# STDOUT.
|
16
|
+
def initialize(suite, output_level=NORMAL, io=STDOUT)
|
17
|
+
if (suite.respond_to?(:suite))
|
18
|
+
@suite = suite.suite
|
19
|
+
else
|
20
|
+
@suite = suite
|
21
|
+
end
|
22
|
+
@output_level = output_level
|
23
|
+
@io = io
|
24
|
+
@already_outputted = false
|
25
|
+
@faults = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Begins the test run.
|
29
|
+
def start
|
30
|
+
setup_mediator
|
31
|
+
attach_to_mediator
|
32
|
+
return start_mediator
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def setup_mediator
|
37
|
+
@mediator = create_mediator(@suite)
|
38
|
+
suite_name = @suite.to_s
|
39
|
+
if ( @suite.kind_of?(Module) )
|
40
|
+
suite_name = @suite.name
|
41
|
+
end
|
42
|
+
output("Loaded suite #{suite_name}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_mediator(suite)
|
46
|
+
return TestRunnerMediator.new(suite)
|
47
|
+
end
|
48
|
+
|
49
|
+
def attach_to_mediator
|
50
|
+
@mediator.add_listener(TestResult::FAULT, &method(:add_fault))
|
51
|
+
@mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
|
52
|
+
@mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
|
53
|
+
@mediator.add_listener(TestCase::STARTED, &method(:test_started))
|
54
|
+
@mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
|
55
|
+
end
|
56
|
+
|
57
|
+
def start_mediator
|
58
|
+
return @mediator.run_suite
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_fault(fault)
|
62
|
+
@faults << fault
|
63
|
+
output_single(fault.single_character_display, PROGRESS_ONLY)
|
64
|
+
@already_outputted = true
|
65
|
+
end
|
66
|
+
|
67
|
+
def started(result)
|
68
|
+
@result = result
|
69
|
+
output("Started")
|
70
|
+
end
|
71
|
+
|
72
|
+
def finished(elapsed_time)
|
73
|
+
nl
|
74
|
+
output("Finished in #{elapsed_time} seconds.")
|
75
|
+
@faults.each_with_index do |fault, index|
|
76
|
+
nl
|
77
|
+
output("%3d) %s" % [index + 1, fault.long_display])
|
78
|
+
end
|
79
|
+
nl
|
80
|
+
output(@result)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_started(name)
|
84
|
+
output_single(name + ": ", VERBOSE)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_finished(name)
|
88
|
+
output_single(".", PROGRESS_ONLY) unless (@already_outputted)
|
89
|
+
nl(VERBOSE)
|
90
|
+
@already_outputted = false
|
91
|
+
end
|
92
|
+
|
93
|
+
def nl(level=NORMAL)
|
94
|
+
output("", level)
|
95
|
+
end
|
96
|
+
|
97
|
+
def output(something, level=NORMAL)
|
98
|
+
@io.puts(something) if (output?(level))
|
99
|
+
@io.flush
|
100
|
+
end
|
101
|
+
|
102
|
+
def output_single(something, level=NORMAL)
|
103
|
+
@io.write(something) if (output?(level))
|
104
|
+
@io.flush
|
105
|
+
end
|
106
|
+
|
107
|
+
def output?(level)
|
108
|
+
level <= @output_level
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if __FILE__ == $0
|
117
|
+
Screw::Unit::UI::Console::TestRunner.start_command_line_test
|
118
|
+
end
|