blackstart 0.2.0 → 0.6.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/LICENSE.txt +1 -1
- data/README.txt +90 -66
- data/lib/blackstart.rb +44 -61
- data/test/blackstart_test.rb +267 -205
- metadata +6 -7
- data/blackstart.gemspec +0 -11
data/LICENSE.txt
CHANGED
data/README.txt
CHANGED
@@ -2,82 +2,99 @@ 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
|
6
|
-
program, not via
|
5
|
+
environment beyond its conventional namespace. It's tested with a primitive
|
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
99
|
puts "before test"
|
83
100
|
@variable = "example"
|
@@ -87,66 +104,73 @@ Blackstart::Context#instance_exec. For example:
|
|
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
|
-
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/lib/blackstart.rb
CHANGED
@@ -1,87 +1,70 @@
|
|
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
|
-
rescue ::StandardError
|
23
|
+
rescue ::StandardError => e
|
22
24
|
# Like IO#puts, separate lines with a line feed character rather than $\.
|
23
|
-
["#{
|
25
|
+
["#{e.class}: #{e.message}"].concat(e.backtrace || []).join "\n"
|
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,158 +44,73 @@ 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 ""
|
99
|
-
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
|
-
|
111
|
-
# When a collection is specified and the block tries to add to the collection,
|
112
|
-
# Blackstart.add should add corresponding procs or nil to the collection by
|
113
|
-
# sending <<.
|
114
|
-
class ::Object
|
115
|
-
sink = [42]
|
116
|
-
prc1 = proc {}
|
117
|
-
prc2 = proc {}
|
118
|
-
retval = Blackstart.add sink do |adder|
|
119
|
-
adder.call(&prc1)
|
120
|
-
adder.call(&prc2)
|
121
|
-
adder.call
|
122
|
-
end
|
123
|
-
fail "" unless sink.equal? retval
|
124
|
-
fail "" unless [42, prc1, prc2, nil] == sink
|
63
|
+
error = Blackstart.vet { ::Kernel.raise e_full }
|
64
|
+
fail "" unless String.equal? error.class
|
65
|
+
fail "" unless "FakeError: c\nd\ne" == error
|
125
66
|
end
|
126
67
|
|
127
|
-
#
|
128
|
-
#
|
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.
|
129
70
|
class ::Object
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
fail ""
|
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
|
136
76
|
end
|
137
77
|
begin
|
138
|
-
Blackstart.
|
139
|
-
rescue
|
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
|
-
end
|
145
|
-
|
146
|
-
# When Blackstart.add's block sends call to the adder and it returns, nil
|
147
|
-
# should be the object it returns.
|
148
|
-
class ::Object
|
149
|
-
retval_block = retval_no_block = true
|
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
83
|
end
|
157
84
|
|
158
|
-
#
|
159
|
-
# should
|
85
|
+
# If yielding to the block raises a non-StandardError exception, Blackstart.vet
|
86
|
+
# should raise it.
|
160
87
|
class ::Object
|
161
|
-
|
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
|
-
# Blackstart::
|
191
|
-
|
192
|
-
fail "" unless Blackstart::Context.equal? Blackstart::Context.new.class
|
109
|
+
# Blackstart::Scratchpad should not be frozen.
|
110
|
+
fail "" if Blackstart::Scratchpad.frozen?
|
193
111
|
|
194
|
-
# A new
|
195
|
-
fail "" unless 0 == Blackstart::
|
112
|
+
# A new scratchpad should have no instance variables.
|
113
|
+
fail "" unless 0 == Blackstart::Scratchpad.new.instance_variables.length
|
196
114
|
|
197
115
|
# Test Blackstart.run:
|
198
116
|
|
@@ -200,7 +118,7 @@ fail "" unless 0 == Blackstart::Context.new.instance_variables.length
|
|
200
118
|
# return the appropriate object based on the behavior of the tests.
|
201
119
|
class ::Object
|
202
120
|
begin
|
203
|
-
# Set $stdout to a test spy and
|
121
|
+
# Set $stdout to a test spy and restore it in the ensure clause.
|
204
122
|
original_stdout = $stdout
|
205
123
|
spy_stdout = $stdout.clone
|
206
124
|
spy_stdout.instance_variable_set :@_test_calls, stdout_calls = []
|
@@ -210,155 +128,235 @@ class ::Object
|
|
210
128
|
end
|
211
129
|
$stdout = spy_stdout
|
212
130
|
|
213
|
-
|
214
|
-
|
215
|
-
|
131
|
+
invalid_ostream = Object.new
|
132
|
+
def invalid_ostream.puts(*)
|
133
|
+
::Kernel.raise ::NoMethodError
|
134
|
+
end
|
135
|
+
|
136
|
+
# When there are no tests, Blackstart.run should not notify the output
|
137
|
+
# stream of any failures.
|
138
|
+
source = []
|
139
|
+
fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
|
216
140
|
fail "" unless true.equal? Blackstart.run(source, spy_stdout)
|
217
141
|
fail "" unless true.equal? Blackstart.run(source)
|
218
142
|
fail "" unless 0 == stdout_calls.length
|
219
143
|
|
220
|
-
#
|
221
|
-
|
222
|
-
|
144
|
+
# When there are tests but none raise exceptions, Blackstart.run should not
|
145
|
+
# notify the output stream of any failures. The objects returned by the
|
146
|
+
# test procs shouldn't affect Blackstart.run's behavior.
|
147
|
+
source = [proc {}, proc { 42 }]
|
148
|
+
fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
|
223
149
|
fail "" unless true.equal? Blackstart.run(source, spy_stdout)
|
224
150
|
fail "" unless true.equal? Blackstart.run(source)
|
225
151
|
fail "" unless 0 == stdout_calls.length
|
226
152
|
|
227
|
-
# Define some
|
228
|
-
|
153
|
+
# Define some test objects. Freeze them, the strings to which they convert,
|
154
|
+
# some exception details, and the strings to which the exception classes
|
155
|
+
# convert to check that they don't get modified.
|
156
|
+
e_class = Class.new StandardError do
|
157
|
+
def backtrace
|
158
|
+
["line1".freeze, "line2".freeze].freeze
|
159
|
+
end
|
160
|
+
end
|
229
161
|
def e_class.to_s
|
230
|
-
"FakeError"
|
162
|
+
"FakeError".freeze
|
231
163
|
end
|
232
|
-
fail_prc1 = proc { ::Kernel.raise e_class, "fake message 1" }
|
164
|
+
fail_prc1 = proc { ::Kernel.raise e_class, "fake message 1".freeze }
|
233
165
|
def fail_prc1.to_s
|
234
|
-
"to_s 1"
|
166
|
+
"to_s 1".freeze
|
235
167
|
end
|
168
|
+
fail_prc1.freeze
|
169
|
+
pass_prc = proc {}.freeze
|
236
170
|
fail_prc2 = Object.new # Not a Proc instance, but converts to one.
|
237
171
|
fail_prc2.instance_variable_set :@_test_e_class, e_class
|
238
172
|
def fail_prc2.to_proc
|
239
173
|
e_class = @_test_e_class
|
240
|
-
::Proc.new { ::Kernel.raise e_class, "fake message 2" }
|
174
|
+
::Proc.new { ::Kernel.raise e_class, "fake message 2".freeze }.freeze
|
241
175
|
end
|
242
176
|
def fail_prc2.to_s
|
243
|
-
"to_s 2"
|
177
|
+
"to_s 2".freeze
|
244
178
|
end
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
# Output stream that does not conform to the expected interface. (And in
|
251
|
-
# general, the only exceptions Blackstart.run should rescue are
|
252
|
-
# StandardError exceptions raised by tests.)
|
253
|
-
begin
|
254
|
-
Blackstart.run source, Object.new
|
255
|
-
rescue NoMethodError
|
256
|
-
else
|
257
|
-
fail ""
|
179
|
+
fail_prc2.freeze
|
180
|
+
non_prc = Object.new # Does not convert to a proc.
|
181
|
+
def non_prc.to_proc
|
182
|
+
::Kernel.raise ::NoMethodError
|
258
183
|
end
|
259
|
-
|
184
|
+
def non_prc.to_s
|
185
|
+
"to_s 3".freeze
|
186
|
+
end
|
187
|
+
non_prc.freeze
|
188
|
+
bad_prc = Object.new # Pretends to convert to a proc but doesn't.
|
189
|
+
def bad_prc.to_proc
|
190
|
+
0
|
191
|
+
end
|
192
|
+
def bad_prc.to_s
|
193
|
+
"to_s 4".freeze
|
194
|
+
end
|
195
|
+
bad_prc.freeze
|
196
|
+
|
197
|
+
# A mix of tests that raise errors and tests that return. Freeze the array
|
198
|
+
# to check that it's not being modified.
|
199
|
+
source = [fail_prc1, pass_prc, fail_prc2, non_prc, bad_prc].freeze
|
260
200
|
|
261
|
-
#
|
201
|
+
# An output stream that conforms to the expected interface and does not
|
202
|
+
# raise an exception should receive all failure information.
|
262
203
|
spy_ostream = Object.new
|
263
204
|
spy_ostream.instance_variable_set :@_test_calls, calls = []
|
264
205
|
def spy_ostream.puts *args
|
265
206
|
@_test_calls << args
|
266
|
-
nil
|
207
|
+
# A real standard output stream would return nil here (and spy_stdout
|
208
|
+
# does that), but Blackstart.run's behavior should not be affected by the
|
209
|
+
# returned object. Returning this unusual object tests that.
|
210
|
+
::Object.new
|
267
211
|
end
|
212
|
+
# Freeze the output stream to check that it's not being modified.
|
213
|
+
spy_ostream.freeze
|
268
214
|
fail "" unless false.equal? Blackstart.run(source, spy_ostream)
|
269
215
|
fail "" unless 0 == stdout_calls.length
|
270
|
-
fail "" unless
|
216
|
+
fail "" unless 4 == calls.length
|
271
217
|
fail "" unless 5 == calls[0].length
|
218
|
+
fail "" unless calls[0].all? { |o| ::String.equal? o.class }
|
272
219
|
fail "" unless "FAILED TEST:" == calls[0][0]
|
273
220
|
fail "" unless "to_s 1" == calls[0][1]
|
274
221
|
fail "" unless "...ERROR:" == calls[0][2]
|
275
|
-
fail "" unless
|
222
|
+
fail "" unless "FakeError: fake message 1\nline1\nline2" == calls[0][3]
|
276
223
|
fail "" unless "" == calls[0][4]
|
277
224
|
fail "" unless 5 == calls[1].length
|
225
|
+
fail "" unless calls[1].all? { |o| ::String.equal? o.class }
|
278
226
|
fail "" unless "FAILED TEST:" == calls[1][0]
|
279
227
|
fail "" unless "to_s 2" == calls[1][1]
|
280
228
|
fail "" unless "...ERROR:" == calls[1][2]
|
281
|
-
fail "" unless
|
229
|
+
fail "" unless "FakeError: fake message 2\nline1\nline2" == calls[1][3]
|
282
230
|
fail "" unless "" == calls[1][4]
|
283
|
-
|
284
|
-
|
285
|
-
|
231
|
+
fail "" unless 5 == calls[2].length
|
232
|
+
fail "" unless calls[2].all? { |o| ::String.equal? o.class }
|
233
|
+
fail "" unless "FAILED TEST:" == calls[2][0]
|
234
|
+
fail "" unless "to_s 3" == calls[2][1]
|
235
|
+
fail "" unless "...ERROR:" == calls[2][2]
|
236
|
+
# (No need to check the error description.)
|
237
|
+
fail "" unless "" == calls[2][4]
|
238
|
+
fail "" unless 5 == calls[3].length
|
239
|
+
fail "" unless calls[3].all? { |o| ::String.equal? o.class }
|
240
|
+
fail "" unless "FAILED TEST:" == calls[3][0]
|
241
|
+
fail "" unless "to_s 4" == calls[3][1]
|
242
|
+
fail "" unless "...ERROR:" == calls[3][2]
|
243
|
+
# (No need to check the error description.)
|
244
|
+
fail "" unless "" == calls[3][4]
|
245
|
+
|
246
|
+
# The faked standard output stream.
|
286
247
|
stdout_calls.clear
|
287
248
|
fail "" unless false.equal? Blackstart.run(source)
|
288
|
-
fail "" unless
|
249
|
+
fail "" unless 4 == stdout_calls.length
|
289
250
|
fail "" unless 5 == stdout_calls[0].length
|
251
|
+
fail "" unless stdout_calls[0].all? { |o| ::String.equal? o.class }
|
290
252
|
fail "" unless "FAILED TEST:" == stdout_calls[0][0]
|
291
253
|
fail "" unless "to_s 1" == stdout_calls[0][1]
|
292
254
|
fail "" unless "...ERROR:" == stdout_calls[0][2]
|
293
|
-
fail "" unless
|
255
|
+
fail "" unless "FakeError: fake message 1\nline1\nline2" ==
|
256
|
+
stdout_calls[0][3]
|
294
257
|
fail "" unless "" == stdout_calls[0][4]
|
295
258
|
fail "" unless 5 == stdout_calls[1].length
|
259
|
+
fail "" unless stdout_calls[1].all? { |o| ::String.equal? o.class }
|
296
260
|
fail "" unless "FAILED TEST:" == stdout_calls[1][0]
|
297
261
|
fail "" unless "to_s 2" == stdout_calls[1][1]
|
298
262
|
fail "" unless "...ERROR:" == stdout_calls[1][2]
|
299
|
-
fail "" unless
|
263
|
+
fail "" unless "FakeError: fake message 2\nline1\nline2" ==
|
264
|
+
stdout_calls[1][3]
|
300
265
|
fail "" unless "" == stdout_calls[1][4]
|
266
|
+
fail "" unless 5 == stdout_calls[2].length
|
267
|
+
fail "" unless stdout_calls[2].all? { |o| ::String.equal? o.class }
|
268
|
+
fail "" unless "FAILED TEST:" == stdout_calls[2][0]
|
269
|
+
fail "" unless "to_s 3" == stdout_calls[2][1]
|
270
|
+
fail "" unless "...ERROR:" == stdout_calls[2][2]
|
271
|
+
# (No need to check the error description.)
|
272
|
+
fail "" unless "" == stdout_calls[2][4]
|
273
|
+
fail "" unless 5 == stdout_calls[3].length
|
274
|
+
fail "" unless stdout_calls[3].all? { |o| ::String.equal? o.class }
|
275
|
+
fail "" unless "FAILED TEST:" == stdout_calls[3][0]
|
276
|
+
fail "" unless "to_s 4" == stdout_calls[3][1]
|
277
|
+
fail "" unless "...ERROR:" == stdout_calls[3][2]
|
278
|
+
# (No need to check the error description.)
|
279
|
+
fail "" unless "" == stdout_calls[3][4]
|
301
280
|
stdout_calls.clear
|
302
281
|
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
306
|
-
|
307
|
-
|
282
|
+
# An output stream that raises an exception when notified of a failure
|
283
|
+
# should cause Blackstart.run to raise that exception and stop processing
|
284
|
+
# tests. (In general, the only exceptions Blackstart.run should rescue are
|
285
|
+
# StandardError exceptions raised by tests.)
|
286
|
+
second_test_run = false
|
287
|
+
source = [proc { ::Kernel.raise "" }, proc { second_test_run = true }]
|
308
288
|
begin
|
309
|
-
Blackstart.run source,
|
310
|
-
rescue
|
289
|
+
Blackstart.run source, invalid_ostream
|
290
|
+
rescue NoMethodError
|
311
291
|
else
|
312
292
|
fail ""
|
313
293
|
end
|
294
|
+
fail "" if second_test_run
|
314
295
|
fail "" unless 0 == stdout_calls.length
|
315
296
|
|
316
|
-
#
|
317
|
-
#
|
318
|
-
# exceptions
|
297
|
+
# When a test raises a non-StandardError exception, Blackstart.run should
|
298
|
+
# raise that exception and stop processing tests. (In general, the only
|
299
|
+
# exceptions Blackstart.run should rescue are StandardError exceptions
|
300
|
+
# raised by tests.)
|
301
|
+
second_test_run = false
|
302
|
+
non_standard_error = Class.new Exception
|
303
|
+
source = [proc { ::Kernel.raise non_standard_error },
|
304
|
+
proc { second_test_run = true }]
|
319
305
|
begin
|
320
|
-
Blackstart.run
|
321
|
-
rescue
|
306
|
+
Blackstart.run source, spy_stdout
|
307
|
+
rescue non_standard_error
|
322
308
|
else
|
323
309
|
fail ""
|
324
310
|
end
|
311
|
+
fail "" if second_test_run
|
325
312
|
fail "" unless 0 == stdout_calls.length
|
326
313
|
|
327
|
-
#
|
328
|
-
#
|
329
|
-
# (
|
314
|
+
# When the test sequence does not conform to the interface and raises an
|
315
|
+
# exception during iteration, Blackstart.run should raise that exception.
|
316
|
+
# (In general, the only exceptions Blackstart.run should rescue are
|
330
317
|
# StandardError exceptions raised by tests.)
|
331
|
-
source =
|
332
|
-
|
333
|
-
|
334
|
-
rescue ArgumentError
|
335
|
-
else
|
336
|
-
fail ""
|
318
|
+
source = Object.new
|
319
|
+
def source.each(*)
|
320
|
+
::Kernel.raise ::NoMethodError
|
337
321
|
end
|
338
|
-
source = [0]
|
339
322
|
begin
|
340
323
|
Blackstart.run source, spy_stdout
|
341
|
-
rescue
|
324
|
+
rescue NoMethodError
|
342
325
|
else
|
343
326
|
fail ""
|
344
327
|
end
|
345
|
-
|
346
|
-
|
347
|
-
|
328
|
+
fail "" unless 0 == stdout_calls.length
|
329
|
+
|
330
|
+
# When a test fails and converting the test object to a string raises an
|
331
|
+
# exception, Blackstart.run should raise that exception and stop processing
|
332
|
+
# tests. (In general, the only exceptions Blackstart.run should rescue are
|
333
|
+
# StandardError exceptions raised by tests.)
|
334
|
+
second_test_run = false
|
335
|
+
no_desc = proc { ::Kernel.raise "" }
|
336
|
+
def no_desc.to_s
|
337
|
+
::Kernel.raise ::NoMethodError
|
348
338
|
end
|
349
|
-
source = [
|
339
|
+
source = [no_desc, proc { second_test_run = true }]
|
350
340
|
begin
|
351
341
|
Blackstart.run source, spy_stdout
|
352
|
-
rescue
|
342
|
+
rescue NoMethodError
|
353
343
|
else
|
354
344
|
fail ""
|
355
345
|
end
|
346
|
+
fail "" if second_test_run
|
356
347
|
fail "" unless 0 == stdout_calls.length
|
357
348
|
ensure
|
358
349
|
$stdout = original_stdout
|
359
350
|
end
|
360
351
|
end
|
361
352
|
|
353
|
+
# Blackstart.run should run tests in order.
|
354
|
+
class ::Object
|
355
|
+
data = []
|
356
|
+
Blackstart.run [proc { data << 1 }, proc { data << 2 }], nil
|
357
|
+
fail "" unless [1, 2] == data
|
358
|
+
end
|
359
|
+
|
362
360
|
# Blackstart.run should call each test proc with no arguments.
|
363
361
|
class ::Object
|
364
362
|
args = nil
|
@@ -369,14 +367,55 @@ class ::Object
|
|
369
367
|
fail "" unless [] == args
|
370
368
|
end
|
371
369
|
|
372
|
-
# Blackstart.run should
|
373
|
-
#
|
370
|
+
# Blackstart.run should work with any source that responds to each. The object
|
371
|
+
# returned in response to the each message should not affect Blackstart.run's
|
372
|
+
# behavior.
|
374
373
|
class ::Object
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
374
|
+
source = Object.new
|
375
|
+
def source.each
|
376
|
+
ran = false
|
377
|
+
yield proc { ran = true }
|
378
|
+
@ran = ran
|
379
|
+
self
|
380
|
+
end
|
381
|
+
fail "" unless true.equal? Blackstart.run(source, nil)
|
382
|
+
fail "" unless source.instance_variable_get :@ran
|
383
|
+
|
384
|
+
source = Object.new
|
385
|
+
def source.each
|
386
|
+
yield proc {}
|
387
|
+
nil
|
388
|
+
end
|
389
|
+
fail "" unless true.equal? Blackstart.run(source, nil)
|
390
|
+
end
|
391
|
+
|
392
|
+
# Blackstart.run should send exactly one each message to the source.
|
393
|
+
class ::Object
|
394
|
+
source = Object.new
|
395
|
+
source.instance_variable_set :@count, 0
|
396
|
+
def source.each
|
397
|
+
@count += 1
|
398
|
+
self
|
399
|
+
end
|
400
|
+
Blackstart.run source, nil
|
401
|
+
fail "" unless 1 == source.instance_variable_get(:@count)
|
402
|
+
end
|
403
|
+
|
404
|
+
# When Blackstart.run calls a test proc, the self object should be a unique
|
405
|
+
# instance of Blackstart::Scratchpad.
|
406
|
+
class ::Object
|
407
|
+
sp1 = sp2 = nil
|
408
|
+
Blackstart.run [proc { sp1 = self }, proc { sp2 = self }], nil
|
409
|
+
fail "" unless Blackstart::Scratchpad.equal? sp1.class
|
410
|
+
fail "" unless Blackstart::Scratchpad.equal? sp2.class
|
411
|
+
fail "" if sp1.equal? sp2
|
412
|
+
end
|
413
|
+
|
414
|
+
# When Blackstart.run runs a test, the scratchpad should not be frozen.
|
415
|
+
class ::Object
|
416
|
+
frozen = true
|
417
|
+
Blackstart.run [proc { frozen = frozen? }], nil
|
418
|
+
fail "" if frozen
|
380
419
|
end
|
381
420
|
|
382
421
|
# Blackstart.run should raise an exception if too few or too many non-block
|
@@ -387,11 +426,34 @@ rescue ArgumentError
|
|
387
426
|
else
|
388
427
|
fail ""
|
389
428
|
end
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
429
|
+
class ::Object
|
430
|
+
source = []
|
431
|
+
begin
|
432
|
+
Blackstart.run source, nil, nil
|
433
|
+
rescue ArgumentError
|
434
|
+
else
|
435
|
+
fail ""
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
# The following section modifies the state of the library, so it must be run
|
440
|
+
# after all the ordinary tests.
|
441
|
+
|
442
|
+
# Blackstart.run should run each test by sending instance_exec to a scratchpad.
|
443
|
+
class Blackstart::Scratchpad
|
444
|
+
def foo
|
445
|
+
2
|
446
|
+
end
|
447
|
+
|
448
|
+
def instance_exec(*)
|
449
|
+
@ivar = 1
|
450
|
+
super
|
451
|
+
end
|
452
|
+
end
|
453
|
+
class ::Object
|
454
|
+
result = nil
|
455
|
+
Blackstart.run [proc { result = [@ivar, foo()] }], nil
|
456
|
+
fail "" unless [1, 2] == result
|
395
457
|
end
|
396
458
|
|
397
459
|
# 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: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 6
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.6.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:
|
18
|
+
date: 2025-05-16 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -30,7 +30,6 @@ extra_rdoc_files: []
|
|
30
30
|
files:
|
31
31
|
- LICENSE.txt
|
32
32
|
- README.txt
|
33
|
-
- blackstart.gemspec
|
34
33
|
- lib/blackstart.rb
|
35
34
|
- test/blackstart_test.rb
|
36
35
|
has_rdoc: true
|
@@ -69,5 +68,5 @@ rubygems_version: 1.6.2
|
|
69
68
|
signing_key:
|
70
69
|
specification_version: 3
|
71
70
|
summary: A small, subdued library for automated testing.
|
72
|
-
test_files:
|
73
|
-
|
71
|
+
test_files: []
|
72
|
+
|
data/blackstart.gemspec
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = "blackstart"
|
3
|
-
s.version = "0.2.0"
|
4
|
-
s.authors = ["Aaron Beckerman"]
|
5
|
-
s.summary = "A small, subdued library for automated testing."
|
6
|
-
s.licenses = ["MIT"]
|
7
|
-
s.required_ruby_version = ">= 1.8.7"
|
8
|
-
s.files = ["LICENSE.txt", "README.txt", "blackstart.gemspec",
|
9
|
-
"lib/blackstart.rb", "test/blackstart_test.rb"]
|
10
|
-
s.test_files = ["test/blackstart_test.rb"]
|
11
|
-
end
|