blackstart 0.2.0 → 0.9.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.
@@ -1,198 +1,42 @@
1
1
  # Sections that create local variables are in class definitions so that later
2
2
  # sections cannot accidentally reference those local variables.
3
3
 
4
+ # Kernel#fail is an alias of Kernel#raise, so they do the same thing. However,
5
+ # they are used differently in this file: Kernel#fail is used only for
6
+ # assertions and Kernel#raise is used otherwise. Assertions always pass a
7
+ # string argument so that a new RuntimeError is raised. Kernel#fail with no
8
+ # arguments re-raises the current exception ($!) or, if there is none, raises a
9
+ # new RuntimeError. This would be fine (that is, it would raise a new
10
+ # RuntimeError) sometimes, but not always (not in a rescue clause, for
11
+ # example). Always using Kernel#fail with a string argument makes assertions
12
+ # consistent and avoids this subtle error.
13
+
14
+ # Constant references in blocks are fully qualified because this program must
15
+ # run correctly on a wide range of Ruby versions, including those in the 1.9.1
16
+ # and 1.9.2 branches. Ruby 1.9.1 and 1.9.2 used constant-lookup rules in
17
+ # certain situations that differed from those of earlier and later branches of
18
+ # Ruby.
19
+
4
20
  require "blackstart"
5
21
 
6
22
  # Blackstart should be a module.
7
23
  fail "" unless Module.equal? Blackstart.class
8
24
 
9
- # Blackstart should say it responds to vet, add, and run.
10
- fail "" unless Blackstart.respond_to? :vet
11
- fail "" unless Blackstart.respond_to? :add
25
+ # Blackstart should say it responds to run.
12
26
  fail "" unless Blackstart.respond_to? :run
13
27
 
14
- # Test Blackstart.vet:
15
-
16
- # Blackstart.vet should raise an exception if any non-block arguments are sent.
17
- begin
18
- Blackstart.vet nil
19
- rescue ArgumentError
20
- else
21
- fail ""
22
- end
23
- begin
24
- Blackstart.vet(nil) {}
25
- rescue ArgumentError
26
- else
27
- fail ""
28
- end
29
-
30
- # If sending call with no arguments to the block raises an error,
31
- # Blackstart.vet should return a new descriptive string.
32
- class ::Object
33
- fail "" unless /\ANoMethodError: / =~ Blackstart.vet
34
-
35
- e_class = Class.new StandardError
36
- def e_class.to_s
37
- "FakeError".freeze
38
- end
39
-
40
- e_nil = e_class.new "a".freeze
41
- def e_nil.backtrace
42
- nil
43
- end
44
- fail "" unless "FakeError: a" == Blackstart.vet { ::Kernel.raise e_nil }
45
-
46
- e_empty = e_class.new "b".freeze
47
- def e_empty.backtrace
48
- [].freeze
49
- end
50
- fail "" unless "FakeError: b" == Blackstart.vet { ::Kernel.raise e_empty }
51
-
52
- e_full = e_class.new "c".freeze
53
- def e_full.backtrace
54
- ["d".freeze, "e".freeze].freeze
55
- end
56
- fail "" unless "FakeError: c\nd\ne" ==
57
- Blackstart.vet { ::Kernel.raise e_full }
58
- end
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 {}
28
+ # Test Blackstart::Scratchpad:
110
29
 
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
125
- end
30
+ # Blackstart::Scratchpad.new should return an instance of
31
+ # Blackstart::Scratchpad, which implies that Blackstart::Scratchpad should be a
32
+ # class.
33
+ fail "" unless Blackstart::Scratchpad.equal? Blackstart::Scratchpad.new.class
126
34
 
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
- begin
132
- Blackstart.add(sink) { |adder| adder.call("invalid") {} }
133
- rescue ArgumentError
134
- else
135
- fail ""
136
- end
137
- begin
138
- Blackstart.add(sink) { |adder| adder.call "invalid" }
139
- rescue ArgumentError
140
- else
141
- fail ""
142
- end
143
- fail "" unless 0 == sink.length
144
- end
35
+ # Blackstart::Scratchpad should not be frozen.
36
+ fail "" if Blackstart::Scratchpad.frozen?
145
37
 
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
- 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
162
- begin
163
- Blackstart.add { ::Kernel.raise e }
164
- rescue
165
- fail "" unless e.equal? $!
166
- else
167
- fail ""
168
- end
169
- end
170
-
171
- # When there is a block that tries to add to the collection and the collection
172
- # responds to the << message by raising an exception, Blackstart.add should not
173
- # rescue it.
174
- begin
175
- Blackstart.add(nil) { |adder| adder.call {} }
176
- rescue NoMethodError
177
- else
178
- fail ""
179
- end
180
-
181
- # When no collection is specified and the block tries to add to the collection,
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
187
-
188
- # Test Blackstart::Context:
189
-
190
- # Blackstart::Context.new should return an instance of Blackstart::Context,
191
- # which implies that Blackstart::Context should be a class.
192
- fail "" unless Blackstart::Context.equal? Blackstart::Context.new.class
193
-
194
- # A new context should have no instance variables.
195
- fail "" unless 0 == Blackstart::Context.new.instance_variables.length
38
+ # A new scratchpad should have no instance variables.
39
+ fail "" unless 0 == Blackstart::Scratchpad.new.instance_variables.length
196
40
 
197
41
  # Test Blackstart.run:
198
42
 
@@ -200,7 +44,7 @@ fail "" unless 0 == Blackstart::Context.new.instance_variables.length
200
44
  # return the appropriate object based on the behavior of the tests.
201
45
  class ::Object
202
46
  begin
203
- # Set $stdout to a test spy and set it back in the ensure clause.
47
+ # Set $stdout to a test spy and restore it in the ensure clause.
204
48
  original_stdout = $stdout
205
49
  spy_stdout = $stdout.clone
206
50
  spy_stdout.instance_variable_set :@_test_calls, stdout_calls = []
@@ -210,155 +54,289 @@ class ::Object
210
54
  end
211
55
  $stdout = spy_stdout
212
56
 
213
- # No tests.
214
- source = [].freeze
215
- fail "" unless true.equal? Blackstart.run(source, Object.new)
57
+ invalid_ostream = Object.new
58
+ def invalid_ostream.puts(*)
59
+ ::Kernel.raise ::NoMethodError
60
+ end
61
+
62
+ # When there are no tests, Blackstart.run should not notify the output
63
+ # stream of any failures.
64
+ source = []
65
+ fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
216
66
  fail "" unless true.equal? Blackstart.run(source, spy_stdout)
217
67
  fail "" unless true.equal? Blackstart.run(source)
218
68
  fail "" unless 0 == stdout_calls.length
219
69
 
220
- # Tests that do not raise exceptions.
221
- source = [proc {}, proc { 42 }].freeze
222
- fail "" unless true.equal? Blackstart.run(source, Object.new)
70
+ # When there are tests but none raise exceptions, Blackstart.run should not
71
+ # notify the output stream of any failures. The objects returned by the
72
+ # test procs shouldn't affect Blackstart.run's behavior.
73
+ source = [lambda {}, lambda { 42 }]
74
+ fail "" unless true.equal? Blackstart.run(source, invalid_ostream)
223
75
  fail "" unless true.equal? Blackstart.run(source, spy_stdout)
224
76
  fail "" unless true.equal? Blackstart.run(source)
225
77
  fail "" unless 0 == stdout_calls.length
226
78
 
227
- # Define some example test objects.
228
- e_class = Class.new StandardError
79
+ # Define some test objects. Freeze them, the strings to which they convert,
80
+ # some exception details, and the strings to which the exception classes
81
+ # convert to check that they don't get modified.
82
+ e_class = Class.new StandardError do
83
+ def backtrace
84
+ ["line1".freeze, "line2".freeze].freeze
85
+ end
86
+ end
229
87
  def e_class.to_s
230
- "FakeError"
88
+ "FakeError".freeze
231
89
  end
232
- fail_prc1 = proc { ::Kernel.raise e_class, "fake message 1" }
90
+ fail_prc1 = lambda { ::Kernel.raise e_class, "fake message 1".freeze }
233
91
  def fail_prc1.to_s
234
- "to_s 1"
92
+ "to_s 1".freeze
235
93
  end
94
+ fail_prc1.freeze
95
+ pass_prc = lambda {}.freeze
236
96
  fail_prc2 = Object.new # Not a Proc instance, but converts to one.
237
97
  fail_prc2.instance_variable_set :@_test_e_class, e_class
238
98
  def fail_prc2.to_proc
239
99
  e_class = @_test_e_class
240
- ::Proc.new { ::Kernel.raise e_class, "fake message 2" }
100
+ ::Kernel.lambda do
101
+ ::Kernel.raise e_class, "fake message 2".freeze
102
+ end.freeze
241
103
  end
242
104
  def fail_prc2.to_s
243
- "to_s 2"
105
+ "to_s 2".freeze
244
106
  end
245
- pass_prc = proc {}
246
-
247
- # A mix of tests that raise errors and tests that return.
248
- source = [fail_prc1, fail_prc2, pass_prc].freeze
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 ""
107
+ fail_prc2.freeze
108
+ non_prc = Object.new # Does not convert to a proc.
109
+ def non_prc.to_proc
110
+ ::Kernel.raise ::NoMethodError
258
111
  end
259
- fail "" unless 0 == stdout_calls.length
112
+ def non_prc.to_s
113
+ "to_s 3".freeze
114
+ end
115
+ non_prc.freeze
116
+ bad_prc = Object.new # Pretends to convert to a proc but doesn't.
117
+ def bad_prc.to_proc
118
+ 0
119
+ end
120
+ def bad_prc.to_s
121
+ "to_s 4".freeze
122
+ end
123
+ bad_prc.freeze
124
+
125
+ # A mix of tests that raise errors and tests that return. Freeze the array
126
+ # to check that it's not being modified.
127
+ source = [fail_prc1, pass_prc, fail_prc2, non_prc, bad_prc].freeze
260
128
 
261
- # Output stream that conforms to the expected interface.
129
+ # An output stream that conforms to the expected interface and does not
130
+ # raise an exception should receive all failure information.
262
131
  spy_ostream = Object.new
263
132
  spy_ostream.instance_variable_set :@_test_calls, calls = []
264
133
  def spy_ostream.puts *args
265
134
  @_test_calls << args
266
- nil
135
+ # A real standard output stream would return nil here (and spy_stdout
136
+ # does that), but Blackstart.run's behavior should not be affected by the
137
+ # returned object. Returning this unusual object tests that.
138
+ ::Object.new
267
139
  end
140
+ # Freeze the output stream to check that it's not being modified.
141
+ spy_ostream.freeze
268
142
  fail "" unless false.equal? Blackstart.run(source, spy_ostream)
269
143
  fail "" unless 0 == stdout_calls.length
270
- fail "" unless 2 == calls.length
271
- fail "" unless 5 == calls[0].length
144
+ fail "" unless 4 == calls.length
145
+ fail "" unless 7 == calls[0].length
272
146
  fail "" unless "FAILED TEST:" == calls[0][0]
273
147
  fail "" unless "to_s 1" == calls[0][1]
274
148
  fail "" unless "...ERROR:" == calls[0][2]
275
- fail "" unless /\AFakeError: fake message 1$/ =~ calls[0][3]
276
- fail "" unless "" == calls[0][4]
277
- fail "" unless 5 == calls[1].length
149
+ fail "" unless "FakeError" == calls[0][3]
150
+ fail "" unless "fake message 1" == calls[0][4]
151
+ fail "" unless ["line1", "line2"] == calls[0][5]
152
+ fail "" unless "" == calls[0][6]
153
+ fail "" unless 7 == calls[1].length
278
154
  fail "" unless "FAILED TEST:" == calls[1][0]
279
155
  fail "" unless "to_s 2" == calls[1][1]
280
156
  fail "" unless "...ERROR:" == calls[1][2]
281
- fail "" unless /\AFakeError: fake message 2$/ =~ calls[1][3]
282
- fail "" unless "" == calls[1][4]
283
- spy_ostream = calls = nil
284
-
285
- # Default output stream.
157
+ fail "" unless "FakeError" == calls[1][3]
158
+ fail "" unless "fake message 2" == calls[1][4]
159
+ fail "" unless ["line1", "line2"] == calls[1][5]
160
+ fail "" unless "" == calls[1][6]
161
+ fail "" unless 7 == calls[2].length
162
+ fail "" unless "FAILED TEST:" == calls[2][0]
163
+ fail "" unless "to_s 3" == calls[2][1]
164
+ fail "" unless "...ERROR:" == calls[2][2]
165
+ # (No need to check the error details.)
166
+ fail "" unless "" == calls[2][6]
167
+ fail "" unless 7 == calls[3].length
168
+ fail "" unless "FAILED TEST:" == calls[3][0]
169
+ fail "" unless "to_s 4" == calls[3][1]
170
+ fail "" unless "...ERROR:" == calls[3][2]
171
+ # (No need to check the error details.)
172
+ fail "" unless "" == calls[3][6]
173
+
174
+ # The faked standard output stream.
286
175
  stdout_calls.clear
287
176
  fail "" unless false.equal? Blackstart.run(source)
288
- fail "" unless 2 == stdout_calls.length
289
- fail "" unless 5 == stdout_calls[0].length
177
+ fail "" unless 4 == stdout_calls.length
178
+ fail "" unless 7 == stdout_calls[0].length
290
179
  fail "" unless "FAILED TEST:" == stdout_calls[0][0]
291
180
  fail "" unless "to_s 1" == stdout_calls[0][1]
292
181
  fail "" unless "...ERROR:" == stdout_calls[0][2]
293
- fail "" unless /\AFakeError: fake message 1$/ =~ stdout_calls[0][3]
294
- fail "" unless "" == stdout_calls[0][4]
295
- fail "" unless 5 == stdout_calls[1].length
182
+ fail "" unless "FakeError" == stdout_calls[0][3]
183
+ fail "" unless "fake message 1" == stdout_calls[0][4]
184
+ fail "" unless ["line1", "line2"] == stdout_calls[0][5]
185
+ fail "" unless "" == stdout_calls[0][6]
186
+ fail "" unless 7 == stdout_calls[1].length
296
187
  fail "" unless "FAILED TEST:" == stdout_calls[1][0]
297
188
  fail "" unless "to_s 2" == stdout_calls[1][1]
298
189
  fail "" unless "...ERROR:" == stdout_calls[1][2]
299
- fail "" unless /\AFakeError: fake message 2$/ =~ stdout_calls[1][3]
300
- fail "" unless "" == stdout_calls[1][4]
190
+ fail "" unless "FakeError" == stdout_calls[1][3]
191
+ fail "" unless "fake message 2" == stdout_calls[1][4]
192
+ fail "" unless ["line1", "line2"] == stdout_calls[1][5]
193
+ fail "" unless "" == stdout_calls[1][6]
194
+ fail "" unless 7 == stdout_calls[2].length
195
+ fail "" unless "FAILED TEST:" == stdout_calls[2][0]
196
+ fail "" unless "to_s 3" == stdout_calls[2][1]
197
+ fail "" unless "...ERROR:" == stdout_calls[2][2]
198
+ # (No need to check the error details.)
199
+ fail "" unless "" == stdout_calls[2][6]
200
+ fail "" unless 7 == stdout_calls[3].length
201
+ fail "" unless "FAILED TEST:" == stdout_calls[3][0]
202
+ fail "" unless "to_s 4" == stdout_calls[3][1]
203
+ fail "" unless "...ERROR:" == stdout_calls[3][2]
204
+ # (No need to check the error details.)
205
+ fail "" unless "" == stdout_calls[3][6]
301
206
  stdout_calls.clear
302
207
 
303
- # Test that raises a non-StandardError exception. (And in general, the only
304
- # exceptions Blackstart.run should rescue are StandardError exceptions
305
- # raised by tests.)
306
- non_standard_error = Class.new Exception
307
- source = [proc { ::Kernel.raise non_standard_error }]
208
+ # An output stream that raises an exception when notified of a failure
209
+ # should cause Blackstart.run to raise that exception and stop processing
210
+ # tests. (In general, the only exceptions Blackstart.run should rescue are
211
+ # StandardError exceptions raised by tests.)
212
+ second_test_run = false
213
+ source = [lambda { ::Kernel.raise "" }, lambda { second_test_run = true }]
308
214
  begin
309
- Blackstart.run source, spy_stdout
310
- rescue non_standard_error
215
+ Blackstart.run source, invalid_ostream
216
+ rescue NoMethodError
311
217
  else
312
218
  fail ""
313
219
  end
220
+ fail "" if second_test_run
314
221
  fail "" unless 0 == stdout_calls.length
315
222
 
316
- # Test sequence that does not conform to the interface. (And in general,
317
- # the only exceptions Blackstart.run should rescue are StandardError
318
- # exceptions raised by tests.)
223
+ # When a test raises a non-StandardError exception, Blackstart.run should
224
+ # raise that exception and stop processing tests. (In general, the only
225
+ # exceptions Blackstart.run should rescue are StandardError exceptions
226
+ # raised by tests.)
227
+ second_test_run = false
228
+ non_standard_error = Class.new Exception
229
+ source = [lambda { ::Kernel.raise non_standard_error },
230
+ lambda { second_test_run = true }]
319
231
  begin
320
- Blackstart.run nil, spy_stdout
321
- rescue NoMethodError
232
+ Blackstart.run source, spy_stdout
233
+ rescue non_standard_error
322
234
  else
323
235
  fail ""
324
236
  end
237
+ fail "" if second_test_run
325
238
  fail "" unless 0 == stdout_calls.length
326
239
 
327
- # Test sequences containing objects that do not convert to procs (and do
328
- # not pretend to) and objects that pretend to convert to procs but do not.
329
- # (And in general, the only exceptions Blackstart.run should rescue are
240
+ # When the test sequence does not conform to the interface and raises an
241
+ # exception during iteration, Blackstart.run should raise that exception.
242
+ # (In general, the only exceptions Blackstart.run should rescue are
330
243
  # StandardError exceptions raised by tests.)
331
- source = [nil]
332
- begin
333
- Blackstart.run source, spy_stdout
334
- rescue ArgumentError
335
- else
336
- fail ""
244
+ source = Object.new
245
+ def source.each(*)
246
+ ::Kernel.raise ::NoMethodError
337
247
  end
338
- source = [0]
339
248
  begin
340
249
  Blackstart.run source, spy_stdout
341
- rescue TypeError
250
+ rescue NoMethodError
342
251
  else
343
252
  fail ""
344
253
  end
345
- bad_to_proc = Object.new
346
- def bad_to_proc.to_proc
347
- nil
254
+ fail "" unless 0 == stdout_calls.length
255
+
256
+ # When a test fails and converting the test object to a string raises an
257
+ # exception, Blackstart.run should raise that exception and stop processing
258
+ # tests. (In general, the only exceptions Blackstart.run should rescue are
259
+ # StandardError exceptions raised by tests.)
260
+ second_test_run = false
261
+ no_desc = lambda { ::Kernel.raise "" }
262
+ def no_desc.to_s
263
+ ::Kernel.raise ::NoMethodError
348
264
  end
349
- source = [bad_to_proc]
265
+ source = [no_desc, lambda { second_test_run = true }]
350
266
  begin
351
267
  Blackstart.run source, spy_stdout
352
- rescue TypeError
268
+ rescue NoMethodError
353
269
  else
354
270
  fail ""
355
271
  end
272
+ fail "" if second_test_run
356
273
  fail "" unless 0 == stdout_calls.length
357
274
  ensure
358
275
  $stdout = original_stdout
359
276
  end
360
277
  end
361
278
 
279
+ # When a test fails with an error whose backtrace message returns nil,
280
+ # Blackstart.run should send an empty array to the output stream in place of
281
+ # the backtrace.
282
+ class ::Object
283
+ e_class = Class.new StandardError
284
+ def e_class.to_s
285
+ "FakeError".freeze
286
+ end
287
+ e_no_bt = e_class.new "no backtrace".freeze
288
+ def e_no_bt.backtrace
289
+ nil
290
+ end
291
+
292
+ ostream = Object.new
293
+ ostream.instance_variable_set :@_test_calls, calls = []
294
+ def ostream.puts *args
295
+ @_test_calls << args
296
+ nil
297
+ end
298
+
299
+ result = Blackstart.run [lambda { ::Kernel.raise e_no_bt }], ostream
300
+ fail "" unless false.equal? result
301
+ fail "" unless 1 == calls.length
302
+ fail "" unless 7 == calls[0].length
303
+ fail "" unless "FAILED TEST:" == calls[0][0]
304
+ fail "" unless "...ERROR:" == calls[0][2]
305
+ fail "" unless "FakeError" == calls[0][3]
306
+ fail "" unless "no backtrace" == calls[0][4]
307
+ fail "" unless [] == calls[0][5]
308
+ fail "" unless "" == calls[0][6]
309
+ end
310
+
311
+ # When a test fails with an error that raises an exception when queried,
312
+ # Blackstart.run should raise that exception and stop processing tests. (In
313
+ # general, the only exceptions Blackstart.run should rescue are StandardError
314
+ # exceptions raised by tests.)
315
+ class ::Object
316
+ second_test_run = false
317
+ e2_class = Class.new StandardError
318
+ e1 = StandardError.new
319
+ e1.instance_variable_set :@next_exception_class, e2_class
320
+ def e1.to_s
321
+ ::Kernel.raise @next_exception_class
322
+ end
323
+ source = [lambda { ::Kernel.raise e1 }, lambda { second_test_run = true }]
324
+ begin
325
+ Blackstart.run source, nil
326
+ rescue e2_class
327
+ else
328
+ fail ""
329
+ end
330
+ fail "" if second_test_run
331
+ end
332
+
333
+ # Blackstart.run should run tests in order.
334
+ class ::Object
335
+ data = []
336
+ Blackstart.run [lambda { data << 1 }, lambda { data << 2 }], nil
337
+ fail "" unless [1, 2] == data
338
+ end
339
+
362
340
  # Blackstart.run should call each test proc with no arguments.
363
341
  class ::Object
364
342
  args = nil
@@ -369,14 +347,74 @@ class ::Object
369
347
  fail "" unless [] == args
370
348
  end
371
349
 
372
- # Blackstart.run should call each test proc in the context of a unique instance
373
- # of Blackstart::Context.
350
+ # Blackstart.run should work with any source that responds to each. The object
351
+ # returned in response to the each message should not affect Blackstart.run's
352
+ # behavior.
374
353
  class ::Object
375
- context1 = context2 = nil
376
- Blackstart.run [proc { context1 = self }, proc { context2 = self }], nil
377
- fail "" unless Blackstart::Context.equal? context1.class
378
- fail "" unless Blackstart::Context.equal? context2.class
379
- fail "" if context1.equal? context2
354
+ source = Object.new
355
+ def source.each
356
+ ran = false
357
+ yield lambda { ran = true }
358
+ @ran = ran
359
+ self
360
+ end
361
+ fail "" unless true.equal? Blackstart.run(source, nil)
362
+ fail "" unless source.instance_variable_get :@ran
363
+
364
+ source = Object.new
365
+ def source.each
366
+ yield lambda {}
367
+ nil
368
+ end
369
+ fail "" unless true.equal? Blackstart.run(source, nil)
370
+ end
371
+
372
+ # Blackstart.run should send exactly one each message to the source.
373
+ class ::Object
374
+ source = Object.new
375
+ source.instance_variable_set :@count, 0
376
+ def source.each
377
+ @count += 1
378
+ self
379
+ end
380
+ Blackstart.run source, nil
381
+ fail "" unless 1 == source.instance_variable_get(:@count)
382
+ end
383
+
384
+ # The block that Blackstart.run sends with the each message should return nil
385
+ # whether the test fails or succeeds, regardless of the objects returned by the
386
+ # test and the output stream.
387
+ class ::Object
388
+ ostream = Object.new
389
+ def ostream.puts(*)
390
+ ::Object.new
391
+ end
392
+ source = Object.new
393
+ def source.each
394
+ pass_result = yield lambda { 42 }
395
+ fail_result = yield lambda { ::Kernel.raise "" }
396
+ @results = [pass_result, fail_result]
397
+ self
398
+ end
399
+ fail "" unless false.equal? Blackstart.run(source, ostream)
400
+ fail "" unless [nil, nil] == source.instance_variable_get(:@results)
401
+ end
402
+
403
+ # When Blackstart.run calls a test proc, the self object should be a unique
404
+ # instance of Blackstart::Scratchpad.
405
+ class ::Object
406
+ sp1 = sp2 = nil
407
+ Blackstart.run [lambda { sp1 = self }, lambda { sp2 = self }], nil
408
+ fail "" unless Blackstart::Scratchpad.equal? sp1.class
409
+ fail "" unless Blackstart::Scratchpad.equal? sp2.class
410
+ fail "" if sp1.equal? sp2
411
+ end
412
+
413
+ # When Blackstart.run runs a test, the scratchpad should not be frozen.
414
+ class ::Object
415
+ frozen = true
416
+ Blackstart.run [lambda { frozen = frozen? }], nil
417
+ fail "" if frozen
380
418
  end
381
419
 
382
420
  # Blackstart.run should raise an exception if too few or too many non-block
@@ -387,11 +425,59 @@ rescue ArgumentError
387
425
  else
388
426
  fail ""
389
427
  end
390
- begin
391
- Blackstart.run [], nil, "invalid"
392
- rescue ArgumentError
393
- else
394
- fail ""
428
+ class ::Object
429
+ source = []
430
+ begin
431
+ Blackstart.run source, nil, nil
432
+ rescue ArgumentError
433
+ else
434
+ fail ""
435
+ end
436
+ end
437
+
438
+ # The following sections modify the state of the library, so they must be run
439
+ # after all the ordinary tests.
440
+
441
+ # Blackstart.run should run each test by sending instance_exec to a scratchpad.
442
+ class Blackstart::Scratchpad
443
+ def foo
444
+ 2
445
+ end
446
+
447
+ def instance_exec(*)
448
+ @ivar = 1
449
+ super
450
+ end
451
+ end
452
+ class ::Object
453
+ result = nil
454
+ Blackstart.run [lambda { result = [@ivar, foo()] }], nil
455
+ fail "" unless [1, 2] == result
456
+ end
457
+
458
+ # When creating a scratchpad raises an exception, Blackstart.run should raise
459
+ # that exception and stop processing tests instead of treating it as a test
460
+ # failure, even when the exception is a StandardError. (In general, the only
461
+ # exceptions Blackstart.run should rescue are StandardError exceptions raised
462
+ # by tests; this one is raised outside of a test.) This section makes creating
463
+ # a scratchpad always raise an exception, so it must be the last section that
464
+ # runs tests.
465
+ class ::Object
466
+ second_test_run = false
467
+ e_class = Class.new StandardError
468
+ scratchpad_class = Blackstart::Scratchpad
469
+ scratchpad_class.instance_variable_set :@_test_e_class, e_class
470
+ def scratchpad_class.new(*)
471
+ ::Kernel.raise @_test_e_class
472
+ end
473
+ source = [lambda {}, lambda { second_test_run = true }]
474
+ begin
475
+ Blackstart.run source, nil
476
+ rescue e_class
477
+ else
478
+ fail ""
479
+ end
480
+ fail "" if second_test_run
395
481
  end
396
482
 
397
483
  # If this message doesn't get written, there was a problem.