blackstart 0.1.0 → 0.5.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.txt +91 -67
- data/blackstart.gemspec +1 -1
- data/lib/blackstart.rb +42 -59
- data/test/blackstart_test.rb +265 -212
- metadata +4 -4
data/README.txt
CHANGED
@@ -2,151 +2,175 @@ Blackstart
|
|
2
2
|
|
3
3
|
Blackstart is a small, subdued library for automated testing in Ruby. It
|
4
4
|
doesn't depend on anything beyond the Ruby platform and doesn't modify the
|
5
|
-
environment
|
5
|
+
environment beyond its conventional namespace. It's tested with a primitive
|
6
6
|
program, not via an automated-testing library.
|
7
7
|
|
8
|
-
The blackstart is a small, subdued bird that eats bugs. A black start is when
|
9
|
-
power plant
|
8
|
+
The blackstart is a small, subdued bird that eats bugs. A black start is when
|
9
|
+
an inactive power plant restarts by means of an independent power source rather
|
10
|
+
than the electrical grid.
|
10
11
|
|
11
12
|
Here's an example of a test program that uses Blackstart:
|
12
13
|
|
13
14
|
require "blackstart"
|
14
15
|
|
15
|
-
exit Blackstart.run
|
16
|
-
|
16
|
+
exit Blackstart.run [
|
17
|
+
proc {
|
17
18
|
unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
|
18
19
|
raise "join did not work as expected"
|
19
20
|
end
|
20
|
-
|
21
|
+
},
|
21
22
|
|
22
|
-
|
23
|
+
proc {
|
23
24
|
unless "sample" == "simple".gsub(/i/, "a")
|
24
25
|
raise "gsub did not work as expected"
|
25
26
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
Blackstart.add adds procs to a collection and returns that collection. In the
|
30
|
-
example, each proc is designed to run a test and, if it fails, raise an error
|
31
|
-
to signal this.
|
27
|
+
}
|
28
|
+
]
|
32
29
|
|
33
30
|
Blackstart.run runs a sequence of tests and reports information about any that
|
34
|
-
fail
|
35
|
-
|
36
|
-
|
31
|
+
fail. It returns false if there were failures, true otherwise.
|
32
|
+
|
33
|
+
An array of procs is an easy way to represent a sequence of tests. In general,
|
34
|
+
the sequence can be any object that responds to an each message in the
|
35
|
+
conventional way and each test object in the sequence can be any object that
|
36
|
+
(1) is a proc or converts to one via to_proc and (2) converts to a string via
|
37
|
+
to_s.
|
38
|
+
|
39
|
+
Each test is run by converting the test object to a proc if necessary and then
|
40
|
+
calling it in the context of a new instance of Blackstart::Scratchpad; if and
|
41
|
+
only if this raises an error (any exception that's a kind of StandardError),
|
42
|
+
Blackstart.run interprets it as a failed test.
|
43
|
+
|
44
|
+
After each test failure, Blackstart.run sends a puts message with failure
|
45
|
+
information to its second parameter, which is $stdout by default. The failure
|
46
|
+
information includes a description of the test (the test object converted to a
|
47
|
+
string) and a description of the error raised (its class, message, and
|
48
|
+
backtrace).
|
37
49
|
|
38
|
-
The library provides little beyond this, but it's easy to do
|
39
|
-
|
50
|
+
The library provides little beyond this, but it's easy to do sophisticated
|
51
|
+
things with it. Some examples follow.
|
40
52
|
|
41
53
|
|
42
54
|
- Exiting with the appropriate status
|
43
55
|
|
44
|
-
|
45
|
-
|
46
|
-
|
56
|
+
You may want your test program to exit with a successful status only if there
|
57
|
+
were no failures, perhaps so it can work as part of a testing script. You can
|
58
|
+
do this by using the object returned by Blackstart.run as the argument to
|
59
|
+
Kernel#exit. Blackstart.run returns false if there were failures, true
|
60
|
+
otherwise.
|
47
61
|
|
48
62
|
|
49
63
|
- Defining helpers
|
50
64
|
|
51
|
-
You may want all your tests to
|
65
|
+
You may want to enable all your tests to assert something, generate test data,
|
52
66
|
or perform some other task by sending a message. You could implement this
|
53
67
|
statelessly in a singleton object or all instances of Object, for example, but
|
54
|
-
there's another option that may be more convenient. Blackstart.run calls
|
55
|
-
test proc
|
56
|
-
instance methods in that class
|
68
|
+
there's another option that may be more convenient. When Blackstart.run calls a
|
69
|
+
test proc, it sets the self object to a new instance of Blackstart::Scratchpad.
|
70
|
+
You can define instance methods in that class. For example:
|
57
71
|
|
58
|
-
class Blackstart::
|
72
|
+
class Blackstart::Scratchpad
|
59
73
|
def assert boolean
|
60
74
|
raise "assertion failed" unless boolean
|
61
75
|
end
|
62
76
|
|
63
77
|
def make_products
|
64
78
|
@cabbage = { :description => "head of cabbage", :price => 125 }
|
65
|
-
@orange = { :description => "Cara
|
79
|
+
@orange = { :description => "Cara Cara navel orange", :price => 100 }
|
66
80
|
nil
|
67
81
|
end
|
68
82
|
end
|
69
83
|
|
70
|
-
All
|
71
|
-
|
72
|
-
|
84
|
+
All your test procs can use them by sending messages to self. These methods,
|
85
|
+
like the test proc itself, can see and modify instance variables and other
|
86
|
+
elements of the scratchpad's state. The scratchpad is disposable:
|
87
|
+
Blackstart.run discards it after the test is complete.
|
73
88
|
|
74
89
|
|
75
90
|
- Running code before and after each test
|
76
91
|
|
77
|
-
You
|
78
|
-
|
92
|
+
You may want to run code before and after each test -- for example, to create
|
93
|
+
objects needed in tests or clean up resources without requiring every test to
|
94
|
+
do these explicitly. You can do this by defining a custom
|
95
|
+
Blackstart::Scratchpad#instance_exec. For example:
|
79
96
|
|
80
|
-
class Blackstart::
|
97
|
+
class Blackstart::Scratchpad
|
81
98
|
def instance_exec(*)
|
82
|
-
puts "
|
99
|
+
puts "before test"
|
83
100
|
@variable = "example"
|
84
101
|
super
|
85
102
|
ensure
|
86
|
-
puts "
|
103
|
+
puts "after test"
|
87
104
|
end
|
88
105
|
end
|
89
106
|
|
107
|
+
Use this hack with care because the test proc could send instance_exec messages
|
108
|
+
to self.
|
109
|
+
|
90
110
|
|
91
111
|
- Building a test collection in stages
|
92
112
|
|
93
|
-
You
|
94
|
-
|
113
|
+
You may want to build a test collection in stages rather than all at once. This
|
114
|
+
can be done straightforwardly:
|
95
115
|
|
96
116
|
require "blackstart"
|
97
117
|
|
98
|
-
|
118
|
+
TESTS = []
|
99
119
|
|
100
|
-
|
101
|
-
|
120
|
+
TESTS.concat [
|
121
|
+
proc {
|
102
122
|
unless "Hello, World!" == ["He", "", "o, Wor", "d!"].join("l")
|
103
123
|
raise "join did not work as expected"
|
104
124
|
end
|
105
|
-
|
106
|
-
|
125
|
+
}
|
126
|
+
]
|
107
127
|
|
108
|
-
|
109
|
-
|
128
|
+
TESTS.concat [
|
129
|
+
proc {
|
110
130
|
unless "sample" == "simple".gsub(/i/, "a")
|
111
131
|
raise "gsub did not work as expected"
|
112
132
|
end
|
113
|
-
|
114
|
-
|
133
|
+
}
|
134
|
+
]
|
115
135
|
|
116
|
-
exit Blackstart.run
|
136
|
+
exit Blackstart.run TESTS
|
117
137
|
|
118
|
-
This
|
119
|
-
load
|
138
|
+
This pattern is useful if you want to define your tests in multiple files: you
|
139
|
+
create a collection with an agreed-upon name, load multiple files, each of
|
140
|
+
which adds test objects to that collection, and then run all the tests.
|
120
141
|
|
121
142
|
|
122
143
|
- Running tests in random order
|
123
144
|
|
145
|
+
To check if you have any tests that depend on other tests having run, or not
|
146
|
+
having run, earlier, you may want to run your tests in random order.
|
124
147
|
Blackstart.run runs a sequence of tests in order, but you can pass it a
|
125
|
-
randomly-ordered sequence. Array#shuffle may be helpful for this. If you
|
126
|
-
|
127
|
-
so you can
|
148
|
+
randomly-ordered sequence. Array#shuffle may be helpful for this. If you use
|
149
|
+
Array#shuffle or something similar, you may also want to print the random seed
|
150
|
+
just before the tests are shuffled so you can rerun your tests in the same
|
151
|
+
order by setting the random seed.
|
128
152
|
|
129
153
|
|
130
154
|
- Reporting detailed test descriptions
|
131
155
|
|
132
|
-
After a test fails, Blackstart.run writes a
|
133
|
-
output stream. It gets this
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
immediately clear what was being tested.
|
156
|
+
After a test fails, Blackstart.run writes a string describing the test to the
|
157
|
+
output stream. It gets this string by sending a to_s message to the test
|
158
|
+
object. When the test object is an instance of Proc, the returned string
|
159
|
+
typically includes the file path and line number where it was defined: helpful,
|
160
|
+
but it won't be immediately clear what was being tested.
|
138
161
|
|
139
|
-
You can improve this by
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
162
|
+
You can improve this by designing your own test objects. Blackstart.run does
|
163
|
+
not strictly need a sequence of procs; it also works with a sequence of objects
|
164
|
+
that convert to procs in response to to_proc messages. Your custom test objects
|
165
|
+
can respond to to_s with detailed descriptions instead of mere file paths and
|
166
|
+
line numbers.
|
144
167
|
|
145
168
|
|
146
169
|
- Handling failures differently
|
147
170
|
|
148
171
|
Blackstart.run handles each failure by sending a puts message to the output
|
149
|
-
stream, one of its parameters. By default,
|
150
|
-
|
151
|
-
can specify any object as the output
|
152
|
-
-- allowing you to handle failure
|
172
|
+
stream, one of its parameters, with failure information. By default, the output
|
173
|
+
stream is $stdout, so the default behavior is to print unadorned failure
|
174
|
+
information to standard output. But you can specify any object as the output
|
175
|
+
stream -- even if it's not really a stream -- allowing you to handle failure
|
176
|
+
information however you want.
|
data/blackstart.gemspec
CHANGED
data/lib/blackstart.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
##
|
2
|
-
# This module provides facilities for
|
2
|
+
# This module provides facilities for running automated tests.
|
3
3
|
|
4
4
|
module Blackstart
|
5
5
|
|
6
6
|
##
|
7
|
-
#
|
7
|
+
# Yields to the block and returns a string describing the error raised, if
|
8
|
+
# any.
|
8
9
|
#
|
9
|
-
# In detail: This method
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
10
|
+
# In detail: This method yields to the block with no arguments. If that
|
11
|
+
# raises an error -- meaning an exception whose class is StandardError or a
|
12
|
+
# descendant of StandardError -- this method attempts to make and return a
|
13
|
+
# string describing that error; the description consists of the error's
|
14
|
+
# class, message, and backtrace (if any). If an exception is raised while
|
15
|
+
# making an error description, this method raises that exception. If
|
16
|
+
# yielding to the block raises a non-error exception, this method raises that
|
17
|
+
# exception. If yielding to the block does not raise an exception, this
|
18
|
+
# method returns nil.
|
17
19
|
|
18
|
-
def self.vet
|
19
|
-
|
20
|
+
def self.vet
|
21
|
+
yield
|
20
22
|
nil
|
21
23
|
rescue ::StandardError
|
22
24
|
# Like IO#puts, separate lines with a line feed character rather than $\.
|
@@ -24,64 +26,45 @@ module Blackstart
|
|
24
26
|
end
|
25
27
|
|
26
28
|
##
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# In detail: This method sends call with one positional argument, an adder
|
30
|
-
# object, to the object representing the block, director. This block can send
|
31
|
-
# call messages to the adder to add objects representing the respective
|
32
|
-
# blocks to the collection, sink. With each sending of call, the object to be
|
33
|
-
# added will be either a proc (if call is sent with a block) or nil (if call
|
34
|
-
# is sent without a block). If any non-block arguments are sent with the call
|
35
|
-
# message, an exception is raised. To add the object, the adder sends a <<
|
36
|
-
# message to sink with the object as the positional argument and no other
|
37
|
-
# arguments. The adder returns nil in response to a call message if it
|
38
|
-
# returns at all. This method returns sink.
|
39
|
-
#
|
40
|
-
# By default, sink is a new empty array.
|
41
|
-
|
42
|
-
def self.add sink = [], &director
|
43
|
-
director.call ::Kernel.lambda { |&prc|
|
44
|
-
sink << prc
|
45
|
-
nil
|
46
|
-
}
|
47
|
-
sink
|
48
|
-
end
|
49
|
-
|
50
|
-
##
|
51
|
-
# A class for the contexts (the self objects) of tests.
|
29
|
+
# A class for tests' disposable helper objects.
|
52
30
|
|
53
|
-
class
|
31
|
+
class Scratchpad
|
54
32
|
end
|
55
33
|
|
56
34
|
##
|
57
|
-
# Runs
|
35
|
+
# Runs tests, writes any failure information to the output stream, and
|
58
36
|
# returns a boolean indicating whether there were no failures.
|
59
37
|
#
|
60
|
-
# In detail: This method
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
# arguments
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
38
|
+
# In detail: This method sends an each message with a block to the sequence
|
39
|
+
# of test objects, source, expecting it to yield to the block once for each
|
40
|
+
# test object in the sequence with that test object as the first argument.
|
41
|
+
# Each test involves creating a new scratchpad -- an instance of
|
42
|
+
# Blackstart::Scratchpad -- and sending an instance_exec message to it with
|
43
|
+
# the test object as a block argument, which converts the test object to a
|
44
|
+
# proc via to_proc if it's not already a proc. The resulting proc is called
|
45
|
+
# with no arguments; the self object is set to the scratchpad. A failure is
|
46
|
+
# when the test (which includes any initial conversion to a proc) raises an
|
47
|
+
# error: an exception whose class is StandardError or a descendant of
|
48
|
+
# StandardError. After a failure, this method sends a puts message to the
|
49
|
+
# output stream, ostream, with these arguments (positional, in order): a
|
50
|
+
# string "FAILED TEST:"; the test object converted to a string via to_s; a
|
51
|
+
# string "...ERROR:"; a string combining the class, message, and backtrace of
|
52
|
+
# the error; and an empty string. If a test raises a non-error exception,
|
53
|
+
# this method immediately raises that exception. After it has run all the
|
54
|
+
# tests and handled any failures, this method returns false if there were
|
55
|
+
# failures, true otherwise. This method immediately raises any exception
|
56
|
+
# raised outside of a test.
|
73
57
|
#
|
74
58
|
# By default, ostream is $stdout.
|
75
59
|
|
76
60
|
def self.run source, ostream = $stdout
|
77
61
|
success = true
|
78
|
-
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
if err_desc = vet { context.instance_exec(&prc) }
|
62
|
+
source.each do |tst|
|
63
|
+
# Create the scratchpad outside the vet block so that this method will
|
64
|
+
# raise any exception that results instead of potentially treating it as
|
65
|
+
# a test failure.
|
66
|
+
scratchpad = Scratchpad.new
|
67
|
+
if err_desc = vet { scratchpad.instance_exec(&tst) }
|
85
68
|
success = false
|
86
69
|
ostream.puts "FAILED TEST:", tst.to_s, "...ERROR:", err_desc.to_s, ""
|
87
70
|
end
|
data/test/blackstart_test.rb
CHANGED
@@ -6,9 +6,8 @@ require "blackstart"
|
|
6
6
|
# Blackstart should be a module.
|
7
7
|
fail "" unless Module.equal? Blackstart.class
|
8
8
|
|
9
|
-
# Blackstart should say it responds to vet
|
9
|
+
# Blackstart should say it responds to vet and run.
|
10
10
|
fail "" unless Blackstart.respond_to? :vet
|
11
|
-
fail "" unless Blackstart.respond_to? :add
|
12
11
|
fail "" unless Blackstart.respond_to? :run
|
13
12
|
|
14
13
|
# Test Blackstart.vet:
|
@@ -27,10 +26,14 @@ else
|
|
27
26
|
fail ""
|
28
27
|
end
|
29
28
|
|
30
|
-
# If
|
31
|
-
#
|
29
|
+
# If yielding to the block raises an error, Blackstart.vet should return a
|
30
|
+
# string describing that error.
|
32
31
|
class ::Object
|
33
|
-
|
32
|
+
# Should return an error description when no block is given.
|
33
|
+
fail "" unless String.equal? Blackstart.vet.class
|
34
|
+
|
35
|
+
# Use frozen strings and backtrace arrays so that an exception will be raised
|
36
|
+
# if they're modified.
|
34
37
|
|
35
38
|
e_class = Class.new StandardError
|
36
39
|
def e_class.to_s
|
@@ -41,164 +44,70 @@ class ::Object
|
|
41
44
|
def e_nil.backtrace
|
42
45
|
nil
|
43
46
|
end
|
44
|
-
|
47
|
+
error = Blackstart.vet { ::Kernel.raise e_nil }
|
48
|
+
fail "" unless String.equal? error.class
|
49
|
+
fail "" unless "FakeError: a" == error
|
45
50
|
|
46
51
|
e_empty = e_class.new "b".freeze
|
47
52
|
def e_empty.backtrace
|
48
53
|
[].freeze
|
49
54
|
end
|
50
|
-
|
55
|
+
error = Blackstart.vet { ::Kernel.raise e_empty }
|
56
|
+
fail "" unless String.equal? error.class
|
57
|
+
fail "" unless "FakeError: b" == error
|
51
58
|
|
52
59
|
e_full = e_class.new "c".freeze
|
53
60
|
def e_full.backtrace
|
54
61
|
["d".freeze, "e".freeze].freeze
|
55
62
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# If sending call with no arguments to the block raises a non-StandardError
|
61
|
-
# exception, Blackstart.vet should not rescue it.
|
62
|
-
class ::Object
|
63
|
-
non_standard_error = Class.new Exception
|
64
|
-
begin
|
65
|
-
Blackstart.vet { ::Kernel.raise non_standard_error }
|
66
|
-
rescue non_standard_error
|
67
|
-
else
|
68
|
-
fail ""
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# If sending call with no arguments to the block does not raise an exception,
|
73
|
-
# Blackstart.vet should return nil.
|
74
|
-
fail "" unless nil.equal? Blackstart.vet { nil }
|
75
|
-
fail "" unless nil.equal? Blackstart.vet { true }
|
76
|
-
|
77
|
-
# Test Blackstart.add:
|
78
|
-
|
79
|
-
# When there is no block, Blackstart.add should raise an exception.
|
80
|
-
begin
|
81
|
-
Blackstart.add
|
82
|
-
rescue NoMethodError
|
83
|
-
else
|
84
|
-
fail ""
|
85
|
-
end
|
86
|
-
begin
|
87
|
-
Blackstart.add []
|
88
|
-
rescue NoMethodError
|
89
|
-
else
|
90
|
-
fail ""
|
91
|
-
end
|
92
|
-
|
93
|
-
# Blackstart.add should raise an exception if superfluous arguments are sent.
|
94
|
-
begin
|
95
|
-
Blackstart.add [], "invalid"
|
96
|
-
rescue ArgumentError
|
97
|
-
else
|
98
|
-
fail ""
|
63
|
+
error = Blackstart.vet { ::Kernel.raise e_full }
|
64
|
+
fail "" unless String.equal? error.class
|
65
|
+
fail "" unless "FakeError: c\nd\ne" == error
|
99
66
|
end
|
100
|
-
begin
|
101
|
-
Blackstart.add([], "invalid") {}
|
102
|
-
rescue ArgumentError
|
103
|
-
else
|
104
|
-
fail ""
|
105
|
-
end
|
106
|
-
|
107
|
-
# When no collection is specified and the block does nothing, Blackstart.add
|
108
|
-
# should return an empty array.
|
109
|
-
fail "" unless [] == Blackstart.add {}
|
110
67
|
|
111
|
-
#
|
112
|
-
# Blackstart.
|
113
|
-
# sending <<.
|
68
|
+
# If yielding to the block raises an error and that error raises an exception
|
69
|
+
# when queried, Blackstart.vet should raise that second exception.
|
114
70
|
class ::Object
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
adder.call(&prc2)
|
121
|
-
adder.call
|
71
|
+
e1 = StandardError.new
|
72
|
+
e2_class = Class.new StandardError
|
73
|
+
e1.instance_variable_set :@next_exception_class, e2_class
|
74
|
+
def e1.message
|
75
|
+
::Kernel.raise @next_exception_class
|
122
76
|
end
|
123
|
-
fail "" unless sink.equal? retval
|
124
|
-
fail "" unless [42, prc1, prc2, nil] == sink
|
125
|
-
end
|
126
|
-
|
127
|
-
# When the block sends call to the adder with arguments other than a block, it
|
128
|
-
# should raise an exception and not add anything to the collection.
|
129
|
-
class ::Object
|
130
|
-
sink = []
|
131
77
|
begin
|
132
|
-
Blackstart.
|
133
|
-
rescue
|
134
|
-
else
|
135
|
-
fail ""
|
136
|
-
end
|
137
|
-
begin
|
138
|
-
Blackstart.add(sink) { |adder| adder.call "invalid" }
|
139
|
-
rescue ArgumentError
|
78
|
+
Blackstart.vet { ::Kernel.raise e1 }
|
79
|
+
rescue e2_class
|
140
80
|
else
|
141
81
|
fail ""
|
142
82
|
end
|
143
|
-
fail "" unless 0 == sink.length
|
144
83
|
end
|
145
84
|
|
146
|
-
#
|
147
|
-
# should
|
85
|
+
# If yielding to the block raises a non-StandardError exception, Blackstart.vet
|
86
|
+
# should raise it.
|
148
87
|
class ::Object
|
149
|
-
|
150
|
-
Blackstart.add do |adder|
|
151
|
-
retval_block = adder.call {}
|
152
|
-
retval_no_block = adder.call
|
153
|
-
end
|
154
|
-
fail "" unless nil.equal? retval_block
|
155
|
-
fail "" unless nil.equal? retval_no_block
|
156
|
-
end
|
157
|
-
|
158
|
-
# In general, when there is a block that raises an exception, Blackstart.add
|
159
|
-
# should not rescue it.
|
160
|
-
class ::Object
|
161
|
-
e = StandardError.new
|
88
|
+
non_standard_error = Class.new Exception
|
162
89
|
begin
|
163
|
-
Blackstart.
|
164
|
-
rescue
|
165
|
-
fail "" unless e.equal? $!
|
90
|
+
Blackstart.vet { ::Kernel.raise non_standard_error }
|
91
|
+
rescue non_standard_error
|
166
92
|
else
|
167
93
|
fail ""
|
168
94
|
end
|
169
95
|
end
|
170
96
|
|
171
|
-
#
|
172
|
-
#
|
173
|
-
|
174
|
-
|
175
|
-
Blackstart.add(nil) { |adder| adder.call {} }
|
176
|
-
rescue NoMethodError
|
177
|
-
else
|
178
|
-
fail ""
|
179
|
-
end
|
97
|
+
# If yielding to the block does not raise an exception, Blackstart.vet should
|
98
|
+
# return nil. The object returned shouldn't matter.
|
99
|
+
fail "" unless nil.equal? Blackstart.vet { nil }
|
100
|
+
fail "" unless nil.equal? Blackstart.vet { true }
|
180
101
|
|
181
|
-
#
|
182
|
-
# Blackstart.add should append to a new array that gets returned.
|
183
|
-
class ::Object
|
184
|
-
prc = proc {}
|
185
|
-
fail "" unless [prc] == Blackstart.add { |adder| adder.call(&prc) }
|
186
|
-
end
|
102
|
+
# Test Blackstart::Scratchpad:
|
187
103
|
|
188
|
-
#
|
104
|
+
# Blackstart::Scratchpad.new should return an instance of
|
105
|
+
# Blackstart::Scratchpad, which implies that Blackstart::Scratchpad should be a
|
106
|
+
# class.
|
107
|
+
fail "" unless Blackstart::Scratchpad.equal? Blackstart::Scratchpad.new.class
|
189
108
|
|
190
|
-
#
|
191
|
-
|
192
|
-
fail "" unless Blackstart::Context.equal? Blackstart::Context.new.class
|
193
|
-
|
194
|
-
# Blackstart::Context.new should raise an exception if any non-block arguments
|
195
|
-
# are sent.
|
196
|
-
begin
|
197
|
-
Blackstart::Context.new nil
|
198
|
-
rescue ArgumentError
|
199
|
-
else
|
200
|
-
fail ""
|
201
|
-
end
|
109
|
+
# A new scratchpad should have no instance variables.
|
110
|
+
fail "" unless 0 == Blackstart::Scratchpad.new.instance_variables.length
|
202
111
|
|
203
112
|
# Test Blackstart.run:
|
204
113
|
|
@@ -206,7 +115,7 @@ end
|
|
206
115
|
# return the appropriate object based on the behavior of the tests.
|
207
116
|
class ::Object
|
208
117
|
begin
|
209
|
-
# Set $stdout to a test spy and
|
118
|
+
# Set $stdout to a test spy and restore it in the ensure clause.
|
210
119
|
original_stdout = $stdout
|
211
120
|
spy_stdout = $stdout.clone
|
212
121
|
spy_stdout.instance_variable_set :@_test_calls, stdout_calls = []
|
@@ -216,155 +125,235 @@ class ::Object
|
|
216
125
|
end
|
217
126
|
$stdout = spy_stdout
|
218
127
|
|
219
|
-
|
220
|
-
|
221
|
-
|
128
|
+
invalid_ostream = Object.new
|
129
|
+
def invalid_ostream.puts(*)
|
130
|
+
::Kernel.raise ::NoMethodError
|
131
|
+
end
|
132
|
+
|
133
|
+
# When there are no tests, Blackstart.run should not notify the output
|
134
|
+
# stream of any failures.
|
135
|
+
source = []
|
136
|
+
fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
|
222
137
|
fail "" unless true.equal? Blackstart.run(source, spy_stdout)
|
223
138
|
fail "" unless true.equal? Blackstart.run(source)
|
224
139
|
fail "" unless 0 == stdout_calls.length
|
225
140
|
|
226
|
-
#
|
227
|
-
|
228
|
-
|
141
|
+
# When there are tests but none raise exceptions, Blackstart.run should not
|
142
|
+
# notify the output stream of any failures. The objects returned by the
|
143
|
+
# test procs shouldn't affect Blackstart.run's behavior.
|
144
|
+
source = [proc {}, proc { 42 }]
|
145
|
+
fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
|
229
146
|
fail "" unless true.equal? Blackstart.run(source, spy_stdout)
|
230
147
|
fail "" unless true.equal? Blackstart.run(source)
|
231
148
|
fail "" unless 0 == stdout_calls.length
|
232
149
|
|
233
|
-
# Define some
|
234
|
-
|
150
|
+
# Define some test objects. Freeze them, the strings to which they convert,
|
151
|
+
# some exception details, and the strings to which the exception classes
|
152
|
+
# convert to check that they don't get modified.
|
153
|
+
e_class = Class.new StandardError do
|
154
|
+
def backtrace
|
155
|
+
["line1".freeze, "line2".freeze].freeze
|
156
|
+
end
|
157
|
+
end
|
235
158
|
def e_class.to_s
|
236
|
-
"FakeError"
|
159
|
+
"FakeError".freeze
|
237
160
|
end
|
238
|
-
fail_prc1 = proc { ::Kernel.raise e_class, "fake message 1" }
|
161
|
+
fail_prc1 = proc { ::Kernel.raise e_class, "fake message 1".freeze }
|
239
162
|
def fail_prc1.to_s
|
240
|
-
"to_s 1"
|
163
|
+
"to_s 1".freeze
|
241
164
|
end
|
165
|
+
fail_prc1.freeze
|
166
|
+
pass_prc = proc {}.freeze
|
242
167
|
fail_prc2 = Object.new # Not a Proc instance, but converts to one.
|
243
168
|
fail_prc2.instance_variable_set :@_test_e_class, e_class
|
244
169
|
def fail_prc2.to_proc
|
245
170
|
e_class = @_test_e_class
|
246
|
-
::Proc.new { ::Kernel.raise e_class, "fake message 2" }
|
171
|
+
::Proc.new { ::Kernel.raise e_class, "fake message 2".freeze }.freeze
|
247
172
|
end
|
248
173
|
def fail_prc2.to_s
|
249
|
-
"to_s 2"
|
174
|
+
"to_s 2".freeze
|
250
175
|
end
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
# Output stream that does not conform to the expected interface. (And in
|
257
|
-
# general, the only exceptions Blackstart.run should rescue are
|
258
|
-
# StandardError exceptions raised by tests.)
|
259
|
-
begin
|
260
|
-
Blackstart.run source, Object.new
|
261
|
-
rescue NoMethodError
|
262
|
-
else
|
263
|
-
fail ""
|
176
|
+
fail_prc2.freeze
|
177
|
+
non_prc = Object.new # Does not convert to a proc.
|
178
|
+
def non_prc.to_proc
|
179
|
+
::Kernel.raise ::NoMethodError
|
264
180
|
end
|
265
|
-
|
181
|
+
def non_prc.to_s
|
182
|
+
"to_s 3".freeze
|
183
|
+
end
|
184
|
+
non_prc.freeze
|
185
|
+
bad_prc = Object.new # Pretends to convert to a proc but doesn't.
|
186
|
+
def bad_prc.to_proc
|
187
|
+
0
|
188
|
+
end
|
189
|
+
def bad_prc.to_s
|
190
|
+
"to_s 4".freeze
|
191
|
+
end
|
192
|
+
bad_prc.freeze
|
266
193
|
|
267
|
-
#
|
194
|
+
# A mix of tests that raise errors and tests that return. Freeze the array
|
195
|
+
# to check that it's not being modified.
|
196
|
+
source = [fail_prc1, pass_prc, fail_prc2, non_prc, bad_prc].freeze
|
197
|
+
|
198
|
+
# An output stream that conforms to the expected interface and does not
|
199
|
+
# raise an exception should receive all failure information.
|
268
200
|
spy_ostream = Object.new
|
269
201
|
spy_ostream.instance_variable_set :@_test_calls, calls = []
|
270
202
|
def spy_ostream.puts *args
|
271
203
|
@_test_calls << args
|
272
|
-
nil
|
204
|
+
# A real standard output stream would return nil here (and spy_stdout
|
205
|
+
# does that), but Blackstart.run's behavior should not be affected by the
|
206
|
+
# returned object. Returning this unusual object tests that.
|
207
|
+
::Object.new
|
273
208
|
end
|
209
|
+
# Freeze the output stream to check that it's not being modified.
|
210
|
+
spy_ostream.freeze
|
274
211
|
fail "" unless false.equal? Blackstart.run(source, spy_ostream)
|
275
212
|
fail "" unless 0 == stdout_calls.length
|
276
|
-
fail "" unless
|
213
|
+
fail "" unless 4 == calls.length
|
277
214
|
fail "" unless 5 == calls[0].length
|
215
|
+
fail "" unless calls[0].all? { |o| ::String.equal? o.class }
|
278
216
|
fail "" unless "FAILED TEST:" == calls[0][0]
|
279
217
|
fail "" unless "to_s 1" == calls[0][1]
|
280
218
|
fail "" unless "...ERROR:" == calls[0][2]
|
281
|
-
fail "" unless
|
219
|
+
fail "" unless "FakeError: fake message 1\nline1\nline2" == calls[0][3]
|
282
220
|
fail "" unless "" == calls[0][4]
|
283
221
|
fail "" unless 5 == calls[1].length
|
222
|
+
fail "" unless calls[1].all? { |o| ::String.equal? o.class }
|
284
223
|
fail "" unless "FAILED TEST:" == calls[1][0]
|
285
224
|
fail "" unless "to_s 2" == calls[1][1]
|
286
225
|
fail "" unless "...ERROR:" == calls[1][2]
|
287
|
-
fail "" unless
|
226
|
+
fail "" unless "FakeError: fake message 2\nline1\nline2" == calls[1][3]
|
288
227
|
fail "" unless "" == calls[1][4]
|
289
|
-
|
290
|
-
|
291
|
-
|
228
|
+
fail "" unless 5 == calls[2].length
|
229
|
+
fail "" unless calls[2].all? { |o| ::String.equal? o.class }
|
230
|
+
fail "" unless "FAILED TEST:" == calls[2][0]
|
231
|
+
fail "" unless "to_s 3" == calls[2][1]
|
232
|
+
fail "" unless "...ERROR:" == calls[2][2]
|
233
|
+
# (No need to check the error description.)
|
234
|
+
fail "" unless "" == calls[2][4]
|
235
|
+
fail "" unless 5 == calls[3].length
|
236
|
+
fail "" unless calls[3].all? { |o| ::String.equal? o.class }
|
237
|
+
fail "" unless "FAILED TEST:" == calls[3][0]
|
238
|
+
fail "" unless "to_s 4" == calls[3][1]
|
239
|
+
fail "" unless "...ERROR:" == calls[3][2]
|
240
|
+
# (No need to check the error description.)
|
241
|
+
fail "" unless "" == calls[3][4]
|
242
|
+
|
243
|
+
# The faked standard output stream.
|
292
244
|
stdout_calls.clear
|
293
245
|
fail "" unless false.equal? Blackstart.run(source)
|
294
|
-
fail "" unless
|
246
|
+
fail "" unless 4 == stdout_calls.length
|
295
247
|
fail "" unless 5 == stdout_calls[0].length
|
248
|
+
fail "" unless stdout_calls[0].all? { |o| ::String.equal? o.class }
|
296
249
|
fail "" unless "FAILED TEST:" == stdout_calls[0][0]
|
297
250
|
fail "" unless "to_s 1" == stdout_calls[0][1]
|
298
251
|
fail "" unless "...ERROR:" == stdout_calls[0][2]
|
299
|
-
fail "" unless
|
252
|
+
fail "" unless "FakeError: fake message 1\nline1\nline2" ==
|
253
|
+
stdout_calls[0][3]
|
300
254
|
fail "" unless "" == stdout_calls[0][4]
|
301
255
|
fail "" unless 5 == stdout_calls[1].length
|
256
|
+
fail "" unless stdout_calls[1].all? { |o| ::String.equal? o.class }
|
302
257
|
fail "" unless "FAILED TEST:" == stdout_calls[1][0]
|
303
258
|
fail "" unless "to_s 2" == stdout_calls[1][1]
|
304
259
|
fail "" unless "...ERROR:" == stdout_calls[1][2]
|
305
|
-
fail "" unless
|
260
|
+
fail "" unless "FakeError: fake message 2\nline1\nline2" ==
|
261
|
+
stdout_calls[1][3]
|
306
262
|
fail "" unless "" == stdout_calls[1][4]
|
263
|
+
fail "" unless 5 == stdout_calls[2].length
|
264
|
+
fail "" unless stdout_calls[2].all? { |o| ::String.equal? o.class }
|
265
|
+
fail "" unless "FAILED TEST:" == stdout_calls[2][0]
|
266
|
+
fail "" unless "to_s 3" == stdout_calls[2][1]
|
267
|
+
fail "" unless "...ERROR:" == stdout_calls[2][2]
|
268
|
+
# (No need to check the error description.)
|
269
|
+
fail "" unless "" == stdout_calls[2][4]
|
270
|
+
fail "" unless 5 == stdout_calls[3].length
|
271
|
+
fail "" unless stdout_calls[3].all? { |o| ::String.equal? o.class }
|
272
|
+
fail "" unless "FAILED TEST:" == stdout_calls[3][0]
|
273
|
+
fail "" unless "to_s 4" == stdout_calls[3][1]
|
274
|
+
fail "" unless "...ERROR:" == stdout_calls[3][2]
|
275
|
+
# (No need to check the error description.)
|
276
|
+
fail "" unless "" == stdout_calls[3][4]
|
307
277
|
stdout_calls.clear
|
308
278
|
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
|
313
|
-
|
279
|
+
# An output stream that raises an exception when notified of a failure
|
280
|
+
# should cause Blackstart.run to raise that exception and stop processing
|
281
|
+
# tests. (In general, the only exceptions Blackstart.run should rescue are
|
282
|
+
# StandardError exceptions raised by tests.)
|
283
|
+
second_test_run = false
|
284
|
+
source = [proc { ::Kernel.raise "" }, proc { second_test_run = true }]
|
314
285
|
begin
|
315
|
-
Blackstart.run source,
|
316
|
-
rescue
|
286
|
+
Blackstart.run source, invalid_ostream
|
287
|
+
rescue NoMethodError
|
317
288
|
else
|
318
289
|
fail ""
|
319
290
|
end
|
291
|
+
fail "" if second_test_run
|
320
292
|
fail "" unless 0 == stdout_calls.length
|
321
293
|
|
322
|
-
#
|
323
|
-
#
|
324
|
-
# exceptions
|
294
|
+
# When a test raises a non-StandardError exception, Blackstart.run should
|
295
|
+
# raise that exception and stop processing tests. (In general, the only
|
296
|
+
# exceptions Blackstart.run should rescue are StandardError exceptions
|
297
|
+
# raised by tests.)
|
298
|
+
second_test_run = false
|
299
|
+
non_standard_error = Class.new Exception
|
300
|
+
source = [proc { ::Kernel.raise non_standard_error },
|
301
|
+
proc { second_test_run = true }]
|
325
302
|
begin
|
326
|
-
Blackstart.run
|
327
|
-
rescue
|
303
|
+
Blackstart.run source, spy_stdout
|
304
|
+
rescue non_standard_error
|
328
305
|
else
|
329
306
|
fail ""
|
330
307
|
end
|
308
|
+
fail "" if second_test_run
|
331
309
|
fail "" unless 0 == stdout_calls.length
|
332
310
|
|
333
|
-
#
|
334
|
-
#
|
335
|
-
# (
|
311
|
+
# When the test sequence does not conform to the interface and raises an
|
312
|
+
# exception during iteration, Blackstart.run should raise that exception.
|
313
|
+
# (In general, the only exceptions Blackstart.run should rescue are
|
336
314
|
# StandardError exceptions raised by tests.)
|
337
|
-
source =
|
338
|
-
|
339
|
-
|
340
|
-
rescue ArgumentError
|
341
|
-
else
|
342
|
-
fail ""
|
315
|
+
source = Object.new
|
316
|
+
def source.each(*)
|
317
|
+
::Kernel.raise ::NoMethodError
|
343
318
|
end
|
344
|
-
source = [0]
|
345
319
|
begin
|
346
320
|
Blackstart.run source, spy_stdout
|
347
|
-
rescue
|
321
|
+
rescue NoMethodError
|
348
322
|
else
|
349
323
|
fail ""
|
350
324
|
end
|
351
|
-
|
352
|
-
|
353
|
-
|
325
|
+
fail "" unless 0 == stdout_calls.length
|
326
|
+
|
327
|
+
# When a test fails and converting the test object to a string raises an
|
328
|
+
# exception, Blackstart.run should raise that exception and stop processing
|
329
|
+
# tests. (In general, the only exceptions Blackstart.run should rescue are
|
330
|
+
# StandardError exceptions raised by tests.)
|
331
|
+
second_test_run = false
|
332
|
+
no_desc = proc { ::Kernel.raise "" }
|
333
|
+
def no_desc.to_s
|
334
|
+
::Kernel.raise ::NoMethodError
|
354
335
|
end
|
355
|
-
source = [
|
336
|
+
source = [no_desc, proc { second_test_run = true }]
|
356
337
|
begin
|
357
338
|
Blackstart.run source, spy_stdout
|
358
|
-
rescue
|
339
|
+
rescue NoMethodError
|
359
340
|
else
|
360
341
|
fail ""
|
361
342
|
end
|
343
|
+
fail "" if second_test_run
|
362
344
|
fail "" unless 0 == stdout_calls.length
|
363
345
|
ensure
|
364
346
|
$stdout = original_stdout
|
365
347
|
end
|
366
348
|
end
|
367
349
|
|
350
|
+
# Blackstart.run should run tests in order.
|
351
|
+
class ::Object
|
352
|
+
data = []
|
353
|
+
Blackstart.run [proc { data << 1 }, proc { data << 2 }], nil
|
354
|
+
fail "" unless [1, 2] == data
|
355
|
+
end
|
356
|
+
|
368
357
|
# Blackstart.run should call each test proc with no arguments.
|
369
358
|
class ::Object
|
370
359
|
args = nil
|
@@ -375,14 +364,55 @@ class ::Object
|
|
375
364
|
fail "" unless [] == args
|
376
365
|
end
|
377
366
|
|
378
|
-
# Blackstart.run should
|
379
|
-
#
|
367
|
+
# Blackstart.run should work with any source that responds to each. The object
|
368
|
+
# returned in response to the each message should not affect Blackstart.run's
|
369
|
+
# behavior.
|
370
|
+
class ::Object
|
371
|
+
source = Object.new
|
372
|
+
def source.each
|
373
|
+
ran = false
|
374
|
+
yield proc { ran = true }
|
375
|
+
@ran = ran
|
376
|
+
self
|
377
|
+
end
|
378
|
+
fail "" unless true.equal? Blackstart.run(source, nil)
|
379
|
+
fail "" unless source.instance_variable_get :@ran
|
380
|
+
|
381
|
+
source = Object.new
|
382
|
+
def source.each
|
383
|
+
yield proc {}
|
384
|
+
nil
|
385
|
+
end
|
386
|
+
fail "" unless true.equal? Blackstart.run(source, nil)
|
387
|
+
end
|
388
|
+
|
389
|
+
# Blackstart.run should send exactly one each message to the source.
|
390
|
+
class ::Object
|
391
|
+
source = Object.new
|
392
|
+
source.instance_variable_set :@count, 0
|
393
|
+
def source.each
|
394
|
+
@count += 1
|
395
|
+
self
|
396
|
+
end
|
397
|
+
Blackstart.run source, nil
|
398
|
+
fail "" unless 1 == source.instance_variable_get(:@count)
|
399
|
+
end
|
400
|
+
|
401
|
+
# When Blackstart.run calls a test proc, the self object should be a unique
|
402
|
+
# instance of Blackstart::Scratchpad.
|
380
403
|
class ::Object
|
381
|
-
|
382
|
-
Blackstart.run [proc {
|
383
|
-
fail "" unless Blackstart::
|
384
|
-
fail "" unless Blackstart::
|
385
|
-
fail "" if
|
404
|
+
sp1 = sp2 = nil
|
405
|
+
Blackstart.run [proc { sp1 = self }, proc { sp2 = self }], nil
|
406
|
+
fail "" unless Blackstart::Scratchpad.equal? sp1.class
|
407
|
+
fail "" unless Blackstart::Scratchpad.equal? sp2.class
|
408
|
+
fail "" if sp1.equal? sp2
|
409
|
+
end
|
410
|
+
|
411
|
+
# When Blackstart.run runs a test, the scratchpad should not be frozen.
|
412
|
+
class ::Object
|
413
|
+
frozen = true
|
414
|
+
Blackstart.run [proc { frozen = frozen? }], nil
|
415
|
+
fail "" if frozen
|
386
416
|
end
|
387
417
|
|
388
418
|
# Blackstart.run should raise an exception if too few or too many non-block
|
@@ -393,11 +423,34 @@ rescue ArgumentError
|
|
393
423
|
else
|
394
424
|
fail ""
|
395
425
|
end
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
426
|
+
class ::Object
|
427
|
+
source = []
|
428
|
+
begin
|
429
|
+
Blackstart.run source, nil, nil
|
430
|
+
rescue ArgumentError
|
431
|
+
else
|
432
|
+
fail ""
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# The following section modifies the state of the library, so it must be run
|
437
|
+
# after all the ordinary tests.
|
438
|
+
|
439
|
+
# Blackstart.run should run each test by sending instance_exec to a scratchpad.
|
440
|
+
class Blackstart::Scratchpad
|
441
|
+
def foo
|
442
|
+
2
|
443
|
+
end
|
444
|
+
|
445
|
+
def instance_exec(*)
|
446
|
+
@ivar = 1
|
447
|
+
super
|
448
|
+
end
|
449
|
+
end
|
450
|
+
class ::Object
|
451
|
+
result = nil
|
452
|
+
Blackstart.run [proc { result = [@ivar, foo()] }], nil
|
453
|
+
fail "" unless [1, 2] == result
|
401
454
|
end
|
402
455
|
|
403
456
|
# If this message doesn't get written, there was a problem.
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blackstart
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Aaron Beckerman
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2024-
|
18
|
+
date: 2024-12-21 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|