dfect 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/CREDITS +2 -2
  2. data/HISTORY +306 -0
  3. data/INSTALL +35 -0
  4. data/README +95 -0
  5. data/USAGE +393 -0
  6. data/doc/api/Dfect.html +3179 -0
  7. data/doc/api/Object.html +107 -0
  8. data/doc/api/_index.html +107 -0
  9. data/doc/api/class_list.html +36 -0
  10. data/doc/api/css/common.css +1 -0
  11. data/doc/api/css/full_list.css +50 -0
  12. data/doc/api/css/style.css +268 -0
  13. data/doc/api/file.LICENSE.html +73 -0
  14. data/doc/api/file_list.html +38 -0
  15. data/doc/api/frames.html +13 -0
  16. data/doc/api/index.html +72 -13
  17. data/doc/api/js/app.js +99 -0
  18. data/doc/api/js/full_list.js +106 -0
  19. data/doc/api/js/{jquery-1.3.2.min.js → jquery.js} +0 -0
  20. data/doc/api/method_list.html +339 -0
  21. data/doc/api/top-level-namespace.html +87 -0
  22. data/doc/index.erb +16 -9
  23. data/doc/index.html +1057 -726
  24. data/lib/dfect.rb +431 -284
  25. data/lib/dfect/auto.rb +2 -6
  26. data/lib/dfect/inochi.rb +48 -0
  27. data/lib/dfect/inochi.yaml +75 -0
  28. data/lib/dfect/mini.rb +1 -5
  29. data/lib/dfect/spec.rb +6 -13
  30. data/lib/dfect/unit.rb +21 -33
  31. data/test/dfect/inochi_test.rb +17 -0
  32. data/test/{dfect.rb → dfect_test.rb} +167 -7
  33. data/test/runner +25 -0
  34. data/test/test_helper.rb +1 -0
  35. metadata +43 -55
  36. data/doc/api/apple-touch-icon.png +0 -0
  37. data/doc/api/classes/Class.html +0 -73
  38. data/doc/api/classes/Dfect.html +0 -1245
  39. data/doc/api/classes/Kernel.html +0 -322
  40. data/doc/api/classes/Object.html +0 -72
  41. data/doc/api/created.rid +0 -1
  42. data/doc/api/css/main.css +0 -263
  43. data/doc/api/css/panel.css +0 -383
  44. data/doc/api/css/reset.css +0 -53
  45. data/doc/api/favicon.ico +0 -0
  46. data/doc/api/files/CREDITS.html +0 -65
  47. data/doc/api/files/LICENSE.html +0 -76
  48. data/doc/api/files/lib/dfect/auto_rb.html +0 -80
  49. data/doc/api/files/lib/dfect/mini_rb.html +0 -77
  50. data/doc/api/files/lib/dfect/spec_rb.html +0 -73
  51. data/doc/api/files/lib/dfect/unit_rb.html +0 -73
  52. data/doc/api/files/lib/dfect_rb.html +0 -74
  53. data/doc/api/i/arrows.png +0 -0
  54. data/doc/api/i/results_bg.png +0 -0
  55. data/doc/api/i/tree_bg.png +0 -0
  56. data/doc/api/js/jquery-effect.js +0 -593
  57. data/doc/api/js/main.js +0 -22
  58. data/doc/api/js/searchdoc.js +0 -628
  59. data/doc/api/panel/index.html +0 -71
  60. data/doc/api/panel/search_index.js +0 -1
  61. data/doc/api/panel/tree.js +0 -1
  62. data/doc/history.erb +0 -161
  63. data/doc/intro.erb +0 -104
  64. data/doc/setup.erb +0 -107
  65. data/doc/usage.erb +0 -310
  66. data/rakefile +0 -21
data/lib/dfect.rb CHANGED
@@ -1,8 +1,3 @@
1
- #--
2
- # Copyright protects this work.
3
- # See LICENSE file for details.
4
- #++
5
-
6
1
  require 'yaml'
7
2
  #
8
3
  # YAML raises this error when we try to serialize a class:
@@ -11,7 +6,7 @@ require 'yaml'
11
6
  #
12
7
  # Work around this by representing a class by its name.
13
8
  #
14
- class Class #:nodoc: all
9
+ class Class # @private
15
10
  alias __to_yaml__ to_yaml
16
11
  undef to_yaml
17
12
 
@@ -19,7 +14,6 @@ class Class #:nodoc: all
19
14
  begin
20
15
  __to_yaml__
21
16
  rescue TypeError => e
22
- warn e
23
17
  self.name.to_yaml opts
24
18
  end
25
19
  end
@@ -42,37 +36,40 @@ end
42
36
  module Dfect
43
37
  class << self
44
38
  ##
45
- # Hash of test results, assembled by #run.
39
+ # Hash of test results, assembled by {Dfect.run}.
46
40
  #
47
- # [:execution]
41
+ # [:trace]
48
42
  # Hierarchical trace of all tests executed, where each test is
49
43
  # represented by its description, is mapped to an Array of
50
44
  # nested tests, and may contain zero or more assertion failures.
51
45
  #
52
46
  # Assertion failures are represented as a Hash:
53
47
  #
54
- # ["fail"]
48
+ # [:fail]
55
49
  # Description of the assertion failure.
56
50
  #
57
- # ["code"]
51
+ # [:code]
58
52
  # Source code surrounding the point of failure.
59
53
  #
60
- # ["vars"]
54
+ # [:vars]
61
55
  # Local variables visible at the point of failure.
62
56
  #
63
- # ["call"]
57
+ # [:call]
64
58
  # Stack trace leading to the point of failure.
65
59
  #
66
- # [:statistics]
60
+ # [:stats]
67
61
  # Hash of counts of major events in test execution:
68
62
  #
69
- # [:passed_assertions]
63
+ # [:time]
64
+ # Number of seconds elapsed for test execution.
65
+ #
66
+ # [:pass]
70
67
  # Number of assertions that held true.
71
68
  #
72
- # [:failed_assertions]
69
+ # [:fail]
73
70
  # Number of assertions that did not hold true.
74
71
  #
75
- # [:uncaught_exceptions]
72
+ # [:error]
76
73
  # Number of exceptions that were not rescued.
77
74
  #
78
75
  attr_reader :report
@@ -85,7 +82,7 @@ module Dfect
85
82
  # during assertion failures so
86
83
  # the user can investigate them.
87
84
  #
88
- # The default value is true.
85
+ # The default value is $DEBUG.
89
86
  #
90
87
  # [:quiet]
91
88
  # Do not print the report
@@ -96,17 +93,20 @@ module Dfect
96
93
  attr_accessor :options
97
94
 
98
95
  ##
99
- # Defines a new test, composed of the given
96
+ # Defines a new test composed of the given
100
97
  # description and the given block to execute.
101
98
  #
102
- # A test may contain nested tests.
99
+ # This test may contain nested tests.
100
+ #
101
+ # Tests at the outer-most level are automatically
102
+ # insulated from the top-level Ruby environment.
103
103
  #
104
- # ==== Parameters
104
+ # @param [Object, Array<Object>] description
105
105
  #
106
- # [description]
107
- # A short summary of the test being defined.
106
+ # A brief title or a series of objects
107
+ # that describe the test being defined.
108
108
  #
109
- # ==== Examples
109
+ # @example
110
110
  #
111
111
  # D "a new array" do
112
112
  # D .< { @array = [] }
@@ -124,18 +124,54 @@ module Dfect
124
124
  # end
125
125
  # end
126
126
  #
127
- def D description = caller.first, &block
128
- raise ArgumentError, 'block must be given' unless block
129
- @curr_suite.tests << Suite::Test.new(description.to_s, block)
127
+ def D *description, &block
128
+ create_test @tests.empty?, *description, &block
130
129
  end
131
130
 
132
131
  ##
133
- # :call-seq: <(&block)
132
+ # Defines a new test that is explicitly insulated from the tests
133
+ # that contain it and also from the top-level Ruby environment.
134
+ #
135
+ # This test may contain nested tests.
136
+ #
137
+ # @param description (see Dfect.D)
138
+ #
139
+ # @example
140
+ #
141
+ # D "a root-level test" do
142
+ # @outside = 1
143
+ # T { defined? @outside }
144
+ # T { @outside == 1 }
145
+ #
146
+ # D "an inner, non-insulated test" do
147
+ # T { defined? @outside }
148
+ # T { @outside == 1 }
149
+ # end
150
+ #
151
+ # D! "an inner, insulated test" do
152
+ # F { defined? @outside }
153
+ # F { @outside == 1 }
154
+ #
155
+ # @inside = 2
156
+ # T { defined? @inside }
157
+ # T { @inside == 2 }
158
+ # end
159
+ #
160
+ # F { defined? @inside }
161
+ # F { @inside == 2 }
162
+ # end
163
+ #
164
+ def D! *description, &block
165
+ create_test true, *description, &block
166
+ end
167
+
168
+ ##
169
+ # @overload def <(&block)
134
170
  #
135
171
  # Registers the given block to be executed
136
172
  # before each nested test inside this test.
137
173
  #
138
- # ==== Examples
174
+ # @example
139
175
  #
140
176
  # D .< { puts "before each nested test" }
141
177
  #
@@ -146,7 +182,7 @@ module Dfect
146
182
  def <(*args, &block)
147
183
  if args.empty?
148
184
  raise ArgumentError, 'block must be given' unless block
149
- @curr_suite.before_each << block
185
+ @suite.before_each << block
150
186
  else
151
187
  # the < method is being used as a check for inheritance
152
188
  super
@@ -157,7 +193,7 @@ module Dfect
157
193
  # Registers the given block to be executed
158
194
  # after each nested test inside this test.
159
195
  #
160
- # ==== Examples
196
+ # @example
161
197
  #
162
198
  # D .> { puts "after each nested test" }
163
199
  #
@@ -167,14 +203,14 @@ module Dfect
167
203
  #
168
204
  def > &block
169
205
  raise ArgumentError, 'block must be given' unless block
170
- @curr_suite.after_each << block
206
+ @suite.after_each << block
171
207
  end
172
208
 
173
209
  ##
174
210
  # Registers the given block to be executed
175
211
  # before all nested tests inside this test.
176
212
  #
177
- # ==== Examples
213
+ # @example
178
214
  #
179
215
  # D .<< { puts "before all nested tests" }
180
216
  #
@@ -184,14 +220,14 @@ module Dfect
184
220
  #
185
221
  def << &block
186
222
  raise ArgumentError, 'block must be given' unless block
187
- @curr_suite.before_all << block
223
+ @suite.before_all << block
188
224
  end
189
225
 
190
226
  ##
191
227
  # Registers the given block to be executed
192
228
  # after all nested tests inside this test.
193
229
  #
194
- # ==== Examples
230
+ # @example
195
231
  #
196
232
  # D .>> { puts "after all nested tests" }
197
233
  #
@@ -201,84 +237,84 @@ module Dfect
201
237
  #
202
238
  def >> &block
203
239
  raise ArgumentError, 'block must be given' unless block
204
- @curr_suite.after_all << block
240
+ @suite.after_all << block
205
241
  end
206
242
 
207
243
  ##
208
- # Asserts that the result of the given block is
209
- # neither nil nor false and returns that result.
244
+ # Asserts that the given condition or the
245
+ # result of the given block is neither
246
+ # nil nor false and returns that result.
247
+ #
248
+ # @param condition
249
+ #
250
+ # The condition to be asserted. A block
251
+ # may be given in place of this parameter.
210
252
  #
211
- # ==== Parameters
253
+ # @param message
212
254
  #
213
- # [message]
214
255
  # Optional message to show in the
215
256
  # report if this assertion fails.
216
257
  #
217
- # ==== Examples
218
- #
219
- # # no message specified:
258
+ # @example no message given
220
259
  #
221
260
  # T { true } # passes
222
261
  # T { false } # fails
223
262
  # T { nil } # fails
224
263
  #
225
- # # message specified:
264
+ # @example message is given
226
265
  #
227
- # T( "computers do not doublethink" ) { 2 + 2 != 5 } # passes
266
+ # T("computers do not doublethink") { 2 + 2 != 5 } # passes
228
267
  #
229
- def T message = nil, &block
230
- assert_yield :assert, message, &block
268
+ def T condition = nil, message = nil, &block
269
+ assert_yield :assert, condition, message, &block
231
270
  end
232
271
 
233
272
  ##
234
- # Asserts that the result of the given block is
235
- # either nil or false and returns that result.
236
- #
237
- # ==== Parameters
273
+ # Asserts that the given condition or the
274
+ # result of the given block is either nil
275
+ # or false and returns that result.
238
276
  #
239
- # [message]
240
- # Optional message to show in the
241
- # report if this assertion fails.
277
+ # @param condition (see Dfect.T)
242
278
  #
243
- # ==== Examples
279
+ # @param message (see Dfect.T)
244
280
  #
245
- # # no message specified:
281
+ # @example no message given
246
282
  #
247
283
  # T! { true } # fails
248
284
  # T! { false } # passes
249
285
  # T! { nil } # passes
250
286
  #
251
- # # message specified:
287
+ # @example message is given
252
288
  #
253
- # T!( "computers do not doublethink" ) { 2 + 2 == 5 } # passes
289
+ # T!("computers do not doublethink") { 2 + 2 == 5 } # passes
254
290
  #
255
- def T! message = nil, &block
256
- assert_yield :negate, message, &block
291
+ def T! condition = nil, message = nil, &block
292
+ assert_yield :negate, condition, message, &block
257
293
  end
258
294
 
259
295
  ##
260
- # Returns true if the result of the given block is
261
- # neither nil nor false. Otherwise, returns false.
296
+ # Returns true if the given condition or
297
+ # the result of the given block is neither
298
+ # nil nor false. Otherwise, returns false.
262
299
  #
263
- # ==== Parameters
300
+ # @param condition (see Dfect.T)
264
301
  #
265
- # [message]
266
- # This parameter is optional and completely ignored.
302
+ # @param message
267
303
  #
268
- # ==== Examples
304
+ # This parameter is optional and completely ignored.
269
305
  #
270
- # # no message specified:
306
+ # @example no message given
271
307
  #
272
308
  # T? { true } # => true
273
309
  # T? { false } # => false
274
310
  # T? { nil } # => false
275
311
  #
276
- # # message specified:
312
+ # @example message is given
277
313
  #
278
- # T?( "computers do not doublethink" ) { 2 + 2 != 5 } # => true
314
+ # T?("computers do not doublethink") { 2 + 2 != 5 } # => true
279
315
  #
280
- def T? message = nil, &block
281
- assert_yield :sample, message, &block
316
+ def T? condition = nil, message = nil, &block
317
+ assert_yield :sample, condition, message, &block
282
318
  end
283
319
 
284
320
  alias F T!
@@ -289,20 +325,15 @@ module Dfect
289
325
  # Returns true if the result of the given block is
290
326
  # either nil or false. Otherwise, returns false.
291
327
  #
292
- # ==== Parameters
293
- #
294
- # [message]
295
- # This parameter is optional and completely ignored.
296
- #
297
- # ==== Examples
328
+ # @param message (see Dfect.T?)
298
329
  #
299
- # # no message specified:
330
+ # @example no message given
300
331
  #
301
332
  # F? { true } # => false
302
333
  # F? { false } # => true
303
334
  # F? { nil } # => true
304
335
  #
305
- # # message specified:
336
+ # @example message is given
306
337
  #
307
338
  # F?( "computers do not doublethink" ) { 2 + 2 == 5 } # => true
308
339
  #
@@ -315,84 +346,67 @@ module Dfect
315
346
  # kinds of exceptions is raised
316
347
  # when the given block is executed.
317
348
  #
318
- # If the block raises an exception,
319
- # then that exception is returned.
349
+ # @return
320
350
  #
321
- # Otherwise, nil is returned.
351
+ # If the block raises an exception,
352
+ # then that exception is returned.
322
353
  #
323
- # ==== Parameters
354
+ # Otherwise, nil is returned.
324
355
  #
325
- # [message]
326
- # Optional message to show in the
327
- # report if this assertion fails.
356
+ # @param [...] kinds_then_message
328
357
  #
329
- # [kinds]
330
- # Exception classes that must be raised by the given block.
358
+ # Exception classes that must be raised by the given block, optionally
359
+ # followed by a message to show in the report if this assertion fails.
331
360
  #
332
- # If none are given, then StandardError is assumed (similar to how a
333
- # plain 'rescue' statement without any arguments catches StandardError).
361
+ # If no exception classes are given, then
362
+ # StandardError is assumed (similar to
363
+ # how a plain 'rescue' statement without
364
+ # any arguments catches StandardError).
334
365
  #
335
- # ==== Examples
336
- #
337
- # # no exceptions specified:
366
+ # @example no exceptions given
338
367
  #
339
368
  # E { } # fails
340
369
  # E { raise } # passes
341
370
  #
342
- # # single exception specified:
371
+ # @example single exception given
343
372
  #
344
- # E( ArgumentError ) { raise ArgumentError }
345
- # E( "argument must be invalid", ArgumentError ) { raise ArgumentError }
373
+ # E(ArgumentError) { raise ArgumentError }
374
+ # E(ArgumentError, "argument must be invalid") { raise ArgumentError }
346
375
  #
347
- # # multiple exceptions specified:
376
+ # @example multiple exceptions given
348
377
  #
349
- # E( SyntaxError, NameError ) { eval "..." }
350
- # E( "string must compile", SyntaxError, NameError ) { eval "..." }
378
+ # E(SyntaxError, NameError) { eval "..." }
379
+ # E(SyntaxError, NameError, "string must compile") { eval "..." }
351
380
  #
352
- def E message = nil, *kinds, &block
353
- assert_raise :assert, message, *kinds, &block
381
+ def E *kinds_then_message, &block
382
+ assert_raise :assert, *kinds_then_message, &block
354
383
  end
355
384
 
356
385
  ##
357
386
  # Asserts that one of the given kinds of exceptions
358
387
  # is not raised when the given block is executed.
359
388
  #
360
- # If the block raises an exception,
361
- # then that exception is returned.
362
- #
363
- # Otherwise, nil is returned.
364
- #
365
- # ==== Parameters
366
- #
367
- # [message]
368
- # Optional message to show in the
369
- # report if this assertion fails.
370
- #
371
- # [kinds]
372
- # Exception classes that must not be raised by the given block.
373
- #
374
- # If none are given, then StandardError is assumed (similar to how a
375
- # plain 'rescue' statement without any arguments catches StandardError).
389
+ # @return (see Dfect.E)
376
390
  #
377
- # ==== Examples
391
+ # @param kinds_then_message (see Dfect.E)
378
392
  #
379
- # # no exceptions specified:
393
+ # @example no exceptions given
380
394
  #
381
395
  # E! { } # passes
382
396
  # E! { raise } # fails
383
397
  #
384
- # # single exception specified:
398
+ # @example single exception given
385
399
  #
386
- # E!( ArgumentError ) { raise ArgumentError } # fails
387
- # E!( "argument must be invalid", ArgumentError ) { raise ArgumentError }
400
+ # E!(ArgumentError) { raise ArgumentError } # fails
401
+ # E!(ArgumentError, "argument must be invalid") { raise ArgumentError }
388
402
  #
389
- # # multiple exceptions specified:
403
+ # @example multiple exceptions given
390
404
  #
391
- # E!( SyntaxError, NameError ) { eval "..." }
392
- # E!( "string must compile", SyntaxError, NameError ) { eval "..." }
405
+ # E!(SyntaxError, NameError) { eval "..." }
406
+ # E!(SyntaxError, NameError, "string must compile") { eval "..." }
393
407
  #
394
- def E! message = nil, *kinds, &block
395
- assert_raise :negate, message, *kinds, &block
408
+ def E! *kinds_then_message, &block
409
+ assert_raise :negate, *kinds_then_message, &block
396
410
  end
397
411
 
398
412
  ##
@@ -400,203 +414,306 @@ module Dfect
400
414
  # exceptions is raised when the given block
401
415
  # is executed. Otherwise, returns false.
402
416
  #
403
- # ==== Parameters
417
+ # @param [...] kinds_then_message
404
418
  #
405
- # [message]
406
- # This parameter is optional and completely ignored.
407
- #
408
- # [kinds]
409
- # Exception classes that must be raised by the given block.
419
+ # Exception classes that must be raised by
420
+ # the given block, optionally followed by
421
+ # a message that is completely ignored.
410
422
  #
411
- # If none are given, then StandardError is assumed (similar to how a
412
- # plain 'rescue' statement without any arguments catches StandardError).
423
+ # If no exception classes are given, then
424
+ # StandardError is assumed (similar to
425
+ # how a plain 'rescue' statement without
426
+ # any arguments catches StandardError).
413
427
  #
414
- # ==== Examples
415
- #
416
- # # no exceptions specified:
428
+ # @example no exceptions given
417
429
  #
418
430
  # E? { } # => false
419
431
  # E? { raise } # => true
420
432
  #
421
- # # single exception specified:
433
+ # @example single exception given
422
434
  #
423
- # E?( ArgumentError ) { raise ArgumentError } # => true
435
+ # E?(ArgumentError) { raise ArgumentError } # => true
424
436
  #
425
- # # multiple exceptions specified:
437
+ # @example multiple exceptions given
426
438
  #
427
- # E?( SyntaxError, NameError ) { eval "..." } # => true
439
+ # E?(SyntaxError, NameError) { eval "..." } # => true
440
+ # E!(SyntaxError, NameError, "string must compile") { eval "..." }
428
441
  #
429
- def E? message = nil, *kinds, &block
430
- assert_raise :sample, message, *kinds, &block
442
+ def E? *kinds_then_message, &block
443
+ assert_raise :sample, *kinds_then_message, &block
431
444
  end
432
445
 
433
446
  ##
434
447
  # Asserts that the given symbol is thrown
435
448
  # when the given block is executed.
436
449
  #
437
- # If a value is thrown along
438
- # with the expected symbol,
439
- # then that value is returned.
450
+ # @return
440
451
  #
441
- # Otherwise, nil is returned.
452
+ # If a value is thrown along
453
+ # with the expected symbol,
454
+ # then that value is returned.
442
455
  #
443
- # ==== Parameters
456
+ # Otherwise, nil is returned.
444
457
  #
445
- # [message]
446
- # Optional message to show in the
447
- # report if this assertion fails.
458
+ # @param [Symbol] symbol
448
459
  #
449
- # [symbol]
450
460
  # Symbol that must be thrown by the given block.
451
461
  #
452
- # ==== Examples
462
+ # @param message (see Dfect.T)
453
463
  #
454
- # # no message specified:
464
+ # @example no message given
455
465
  #
456
466
  # C(:foo) { throw :foo, 123 } # passes, => 123
457
467
  # C(:foo) { throw :bar, 456 } # fails, => 456
458
468
  # C(:foo) { } # fails, => nil
459
469
  #
460
- # # message specified:
470
+ # @example message is given
461
471
  #
462
- # C( ":foo must be thrown", :foo ) { throw :bar, 789 } # fails, => nil
472
+ # C(:foo, ":foo must be thrown") { throw :bar, 789 } # fails, => nil
463
473
  #
464
- def C message = nil, symbol = nil, &block
465
- assert_catch :assert, message, symbol, &block
474
+ def C symbol, message = nil, &block
475
+ assert_catch :assert, symbol, message, &block
466
476
  end
467
477
 
468
478
  ##
469
479
  # Asserts that the given symbol is not
470
480
  # thrown when the given block is executed.
471
481
  #
472
- # Returns nil, always.
473
- #
474
- # ==== Parameters
482
+ # @return nil, always.
475
483
  #
476
- # [message]
477
- # Optional message to show in the
478
- # report if this assertion fails.
484
+ # @param [Symbol] symbol
479
485
  #
480
- # [symbol]
481
486
  # Symbol that must not be thrown by the given block.
482
487
  #
483
- # ==== Examples
488
+ # @param message (see Dfect.T)
484
489
  #
485
- # # no message specified:
490
+ # @example no message given
486
491
  #
487
492
  # C!(:foo) { throw :foo, 123 } # fails, => nil
488
493
  # C!(:foo) { throw :bar, 456 } # passes, => nil
489
494
  # C!(:foo) { } # passes, => nil
490
495
  #
491
- # # message specified:
496
+ # @example message is given
492
497
  #
493
- # C!( ":foo must be thrown", :foo ) { throw :bar, 789 } # passes, => nil
498
+ # C!(:foo, ":foo must be thrown") { throw :bar, 789 } # passes, => nil
494
499
  #
495
- def C! message = nil, symbol = nil, &block
496
- assert_catch :negate, message, symbol, &block
500
+ def C! symbol, message = nil, &block
501
+ assert_catch :negate, symbol, message, &block
497
502
  end
498
503
 
499
504
  ##
500
505
  # Returns true if the given symbol is thrown when the
501
506
  # given block is executed. Otherwise, returns false.
502
507
  #
503
- # ==== Parameters
508
+ # @param symbol (see Dfect.C)
504
509
  #
505
- # [message]
506
- # This parameter is optional and completely ignored.
507
- #
508
- # [symbol]
509
- # Symbol that must be thrown by the given block.
510
- #
511
- # ==== Examples
510
+ # @param message (see Dfect.T?)
512
511
  #
513
- # # no message specified:
512
+ # @example no message given
514
513
  #
515
514
  # C?(:foo) { throw :foo, 123 } # => true
516
515
  # C?(:foo) { throw :bar, 456 } # => false
517
516
  # C?(:foo) { } # => false
518
517
  #
519
- # # message specified:
518
+ # @example message is given
520
519
  #
521
- # C?( ":foo must be thrown", :foo ) { throw :bar, 789 } # => false
520
+ # C?(:foo, ":foo must be thrown") { throw :bar, 789 } # => false
522
521
  #
523
- def C? message = nil, symbol = nil, &block
524
- assert_catch :sample, message, symbol, &block
522
+ def C? symbol, message = nil, &block
523
+ assert_catch :sample, symbol, message, &block
525
524
  end
526
525
 
527
526
  ##
528
- # Adds the given message to the report inside
527
+ # Adds the given messages to the report inside
529
528
  # the section of the currently running test.
530
529
  #
531
- # You can think of "S" as "say" or "status".
530
+ # You can think of "L" as "to log something".
532
531
  #
533
- # ==== Parameters
532
+ # @param messages
534
533
  #
535
- # [message]
536
534
  # Objects to be added to the report.
537
535
  #
538
- # ==== Examples
536
+ # @example single message given
537
+ #
538
+ # L "establishing connection..."
539
+ #
540
+ # @example multiple messages given
541
+ #
542
+ # L "beginning calculation...", Math::PI, [1, 2, 3, ['a', 'b', 'c']]
543
+ #
544
+ def L *messages
545
+ @trace.concat messages
546
+ end
547
+
548
+ ##
549
+ # Mechanism for sharing code between tests.
550
+ #
551
+ # If a block is given, it is shared under
552
+ # the given identifier. Otherwise, the
553
+ # code block that was previously shared
554
+ # under the given identifier is injected
555
+ # into the closest insulated Dfect test
556
+ # that contains the call to this method.
557
+ #
558
+ # @param [Symbol, Object] identifier
559
+ #
560
+ # An object that identifies shared code. This must be common
561
+ # knowledge to all parties that want to partake in the sharing.
562
+ #
563
+ # @example
564
+ #
565
+ # S :knowledge do
566
+ # #...
567
+ # end
539
568
  #
540
- # S "establishing connection..."
569
+ # D "some test" do
570
+ # S :knowledge
571
+ # end
541
572
  #
542
- # S "beginning calculation...", Math::PI, [1, 2, 3, ['a', 'b', 'c']]
573
+ # D "another test" do
574
+ # S :knowledge
575
+ # end
543
576
  #
544
- def S *message
545
- @exec_trace.concat message
577
+ def S identifier, &block
578
+ if block_given?
579
+ if already_shared = @share[identifier]
580
+ raise ArgumentError, "A code block #{already_shared.inspect} has already been shared under the identifier #{identifier.inspect}."
581
+ end
582
+
583
+ @share[identifier] = block
584
+
585
+ elsif block = @share[identifier]
586
+ if @tests.empty?
587
+ raise "Cannot inject code block #{block.inspect} shared under identifier #{identifier.inspect} outside of a Dfect test."
588
+ else
589
+ # find the closest insulated parent test; this should always
590
+ # succeed because root-level tests are insulated by default
591
+ test = @tests.reverse.find {|t| t.sandbox }
592
+ test.sandbox.instance_eval(&block)
593
+ end
594
+
595
+ else
596
+ raise ArgumentError, "No code block is shared under identifier #{identifier.inspect}."
597
+ end
546
598
  end
547
599
 
548
600
  ##
549
- # Executes all tests defined thus far and stores the results in #report.
601
+ # Shares the given code block under the given
602
+ # identifier and then immediately injects that
603
+ # code block into the closest insulated Dfect
604
+ # test that contains the call to this method.
605
+ #
606
+ # @param identifier (see Dfect.S)
607
+ #
608
+ # @example
550
609
  #
551
- # ==== Parameters
610
+ # D "some test" do
611
+ # S! :knowledge do
612
+ # #...
613
+ # end
614
+ # end
615
+ #
616
+ # D "another test" do
617
+ # S :knowledge
618
+ # end
619
+ #
620
+ def S! identifier, &block
621
+ raise 'block must be given' unless block_given?
622
+ S identifier, &block
623
+ S identifier
624
+ end
625
+
626
+ ##
627
+ # Checks whether any code has been shared under the given identifier.
628
+ #
629
+ def S? identifier
630
+ @share.key? identifier
631
+ end
632
+
633
+ ##
634
+ # Executes all tests defined thus far and
635
+ # stores the results in {Dfect.report}.
636
+ #
637
+ # @param [Boolean] continue
552
638
  #
553
- # [continue]
554
639
  # If true, results from previous executions will not be cleared.
555
640
  #
556
641
  def run continue = true
557
642
  # clear previous results
558
643
  unless continue
559
- @exec_stats.clear
560
- @exec_trace.clear
561
- @test_stack.clear
644
+ @stats.clear
645
+ @trace.clear
646
+ @tests.clear
562
647
  end
563
648
 
564
649
  # make new results
650
+ start = Time.now
565
651
  catch(:stop_dfect_execution) { execute }
652
+ finish = Time.now
653
+ @stats[:time] = finish - start
566
654
 
567
655
  # print new results
568
- puts @report.to_yaml unless @options[:quiet]
656
+ unless @stats.key? :fail or @stats.key? :error
657
+ #
658
+ # show execution trace only if all tests passed.
659
+ # otherwise, we will be repeating already printed
660
+ # failure details and obstructing the developer!
661
+ #
662
+ display @trace
663
+ end
664
+
665
+ display @stats
569
666
  end
570
667
 
571
668
  ##
572
- # Stops the execution of the #run method or raises an
573
- # exception if that method is not currently executing.
669
+ # Stops the execution of the {Dfect.run} method or raises
670
+ # an exception if that method is not currently executing.
574
671
  #
575
672
  def stop
576
673
  throw :stop_dfect_execution
577
674
  end
578
675
 
676
+ ##
677
+ # Returns the details of the failure that
678
+ # is currently being debugged by the user.
679
+ #
680
+ def info
681
+ @trace.last
682
+ end
683
+
579
684
  private
580
685
 
581
- def assert_yield mode, message = nil, &block
686
+ def create_test insulate, *description, &block
582
687
  raise ArgumentError, 'block must be given' unless block
583
688
 
584
- message ||=
689
+ description = description.join(' ')
690
+ sandbox = Object.new if insulate
691
+
692
+ @suite.tests << Suite::Test.new(description, block, sandbox)
693
+ end
694
+
695
+ def assert_yield mode, condition = nil, message = nil, &block
696
+ # first parameter is actually the message when block is given
697
+ message = condition if block
698
+
699
+ message ||= (
700
+ prefix = block ? 'block must yield' : 'condition must be'
585
701
  case mode
586
- when :assert then 'block must yield true (!nil && !false)'
587
- when :negate then 'block must yield false (nil || false)'
702
+ when :assert then "#{prefix} true (!nil && !false)"
703
+ when :negate then "#{prefix} false (nil || false)"
588
704
  end
705
+ )
589
706
 
590
707
  passed = lambda do
591
- @exec_stats[:passed_assertions] += 1
708
+ @stats[:pass] += 1
592
709
  end
593
710
 
594
711
  failed = lambda do
595
- @exec_stats[:failed_assertions] += 1
712
+ @stats[:fail] += 1
596
713
  debug block, message
597
714
  end
598
715
 
599
- result = call(block)
716
+ result = block ? call(block) : condition
600
717
 
601
718
  case mode
602
719
  when :sample then return result ? true : false
@@ -607,11 +724,14 @@ module Dfect
607
724
  result
608
725
  end
609
726
 
610
- def assert_raise mode, message = nil, *kinds, &block
727
+ def assert_raise mode, *kinds_then_message, &block
611
728
  raise ArgumentError, 'block must be given' unless block
612
729
 
613
- if message.is_a? Class
614
- kinds.unshift message
730
+ message = kinds_then_message.pop
731
+ kinds = kinds_then_message
732
+
733
+ if message.kind_of? Class
734
+ kinds << message
615
735
  message = nil
616
736
  end
617
737
 
@@ -624,11 +744,11 @@ module Dfect
624
744
  end
625
745
 
626
746
  passed = lambda do
627
- @exec_stats[:passed_assertions] += 1
747
+ @stats[:pass] += 1
628
748
  end
629
749
 
630
750
  failed = lambda do |exception|
631
- @exec_stats[:failed_assertions] += 1
751
+ @stats[:fail] += 1
632
752
 
633
753
  if exception
634
754
  # debug the uncaught exception...
@@ -665,25 +785,18 @@ module Dfect
665
785
  exception
666
786
  end
667
787
 
668
- def assert_catch mode, message = nil, symbol = nil, &block
788
+ def assert_catch mode, symbol, message = nil, &block
669
789
  raise ArgumentError, 'block must be given' unless block
670
790
 
671
- if message.is_a? Symbol and not symbol
672
- symbol = message
673
- message = nil
674
- end
675
-
676
- raise ArgumentError, 'symbol must be given' unless symbol
677
-
678
791
  symbol = symbol.to_sym
679
792
  message ||= "block must throw #{symbol.inspect}"
680
793
 
681
794
  passed = lambda do
682
- @exec_stats[:passed_assertions] += 1
795
+ @stats[:pass] += 1
683
796
  end
684
797
 
685
798
  failed = lambda do
686
- @exec_stats[:failed_assertions] += 1
799
+ @stats[:fail] += 1
687
800
  debug block, message
688
801
  end
689
802
 
@@ -718,40 +831,50 @@ module Dfect
718
831
  result
719
832
  end
720
833
 
834
+ ##
835
+ # Prints the given object in YAML format.
836
+ #
837
+ def display object
838
+ unless @options[:quiet]
839
+ # stringify symbols in YAML output for better readability
840
+ puts object.to_yaml.gsub(/^([[:blank:]]*(- )?):(?=@?\w+: )/, '\1')
841
+ end
842
+ end
843
+
721
844
  ##
722
845
  # Executes the current test suite recursively.
723
846
  #
724
847
  def execute
725
- suite = @curr_suite
726
- trace = @exec_trace
848
+ suite = @suite
849
+ trace = @trace
727
850
 
728
851
  suite.before_all.each {|b| call b }
729
852
 
730
853
  suite.tests.each do |test|
731
854
  suite.before_each.each {|b| call b }
732
855
 
733
- @test_stack.push test
856
+ @tests.push test
734
857
 
735
858
  begin
736
859
  # create nested suite
737
- @curr_suite = Suite.new
738
- @exec_trace = []
860
+ @suite = Suite.new
861
+ @trace = []
739
862
 
740
863
  # populate nested suite
741
- call test.block
864
+ call test.block, test.sandbox
742
865
 
743
866
  # execute nested suite
744
867
  execute
745
868
 
746
869
  ensure
747
870
  # restore outer values
748
- @curr_suite = suite
871
+ @suite = suite
749
872
 
750
- trace << build_trace(@exec_trace)
751
- @exec_trace = trace
873
+ trace << build_exec_trace(@trace)
874
+ @trace = trace
752
875
  end
753
876
 
754
- @test_stack.pop
877
+ @tests.pop
755
878
 
756
879
  suite.after_each.each {|b| call b }
757
880
  end
@@ -763,36 +886,51 @@ module Dfect
763
886
  # Invokes the given block and debugs any
764
887
  # exceptions that may arise as a result.
765
888
  #
766
- def call block
889
+ def call block, sandbox = nil
767
890
  begin
768
- block.call
891
+ @calls.push block
892
+
893
+ if sandbox
894
+ sandbox.instance_eval(&block)
895
+ else
896
+ block.call
897
+ end
898
+
769
899
  rescue Exception => e
770
900
  debug_uncaught_exception block, e
901
+
902
+ ensure
903
+ @calls.pop
771
904
  end
772
905
  end
773
906
 
774
- INTERNALS = File.dirname(__FILE__) #:nodoc:
907
+ INTERNALS = File.dirname(__FILE__) # @private
775
908
 
776
909
  ##
777
910
  # Adds debugging information to the report.
778
911
  #
779
- # ==== Parameters
912
+ # @param [Binding, Proc, #binding] context
913
+ #
914
+ # Binding of code being debugged. This can be either a Binding or
915
+ # Proc object, or nil if no binding is available---in which case,
916
+ # the binding of the inner-most enclosing test or hook will be used.
780
917
  #
781
- # [context]
782
- # Binding of code being debugged. This
783
- # can be either a Binding or Proc object.
918
+ # @param message
784
919
  #
785
- # [message]
786
920
  # Message describing the failure
787
921
  # in the code being debugged.
788
922
  #
789
- # [backtrace]
923
+ # @param [Array<String>] backtrace
924
+ #
790
925
  # Stack trace corresponding to point of
791
926
  # failure in the code being debugged.
792
927
  #
793
928
  def debug context, message = nil, backtrace = caller
929
+ # inherit binding of enclosing test or hook
930
+ context ||= @calls.last
931
+
794
932
  # allow a Proc to be passed instead of a binding
795
- if context.respond_to? :binding
933
+ if context and context.respond_to? :binding
796
934
  context = context.binding
797
935
  end
798
936
 
@@ -800,21 +938,16 @@ module Dfect
800
938
  backtrace = backtrace.reject {|s| s.include? INTERNALS }
801
939
 
802
940
  # record failure details in the report
803
- #
804
- # NOTE: using string keys here instead
805
- # of symbols because they make
806
- # the YAML output easier to read
807
- #
808
941
  details = {
809
942
  # user message
810
- 'fail' => message,
943
+ :fail => message,
811
944
 
812
945
  # code snippet
813
- 'code' => (
946
+ :code => (
814
947
  if frame = backtrace.first
815
948
  file, line = frame.scan(/(.+?):(\d+(?=:|\z))/).first
816
949
 
817
- if source = @file_cache[file]
950
+ if source = @files[file]
818
951
  line = line.to_i
819
952
 
820
953
  radius = 5 # number of surrounding lines to show
@@ -838,31 +971,35 @@ module Dfect
838
971
  ),
839
972
 
840
973
  # variable values
841
- 'vars' => (
842
- names = eval('::Kernel.local_variables', context, __FILE__, __LINE__)
974
+ :vars => if context
975
+ names = eval('::Kernel.local_variables + self.instance_variables', context, __FILE__, __LINE__)
843
976
 
844
977
  pairs = names.inject([]) do |pair, name|
845
978
  variable = name.to_s
846
979
  value = eval(variable, context, __FILE__, __LINE__)
847
980
 
848
- pair.push variable, value
981
+ pair.push variable.to_sym, value
849
982
  end
850
983
 
851
984
  Hash[*pairs]
852
- ),
985
+ end,
853
986
 
854
987
  # stack trace
855
- 'call' => backtrace,
988
+ :call => backtrace,
856
989
  }
857
990
 
858
- @exec_trace << details
991
+ @trace << details
859
992
 
860
993
  # allow user to investigate the failure
861
- if @options[:debug]
862
- # show the failure to the user
863
- puts build_trace(details).to_yaml
994
+ if @options[:debug] and context
995
+ # show only the most helpful subset of the
996
+ # failure details, because the rest can be
997
+ # queried (on demand) inside the debugger
998
+ overview = details.dup
999
+ overview.delete :vars
1000
+ overview.delete :call
1001
+ display build_fail_trace(overview)
864
1002
 
865
- # start the investigation
866
1003
  if Kernel.respond_to? :debugger
867
1004
  eval '::Kernel.debugger', context, __FILE__, __LINE__
868
1005
  else
@@ -876,6 +1013,9 @@ module Dfect
876
1013
  irb.eval_input
877
1014
  end
878
1015
  end
1016
+ else
1017
+ # show all failure details to the user
1018
+ display build_fail_trace(details)
879
1019
  end
880
1020
 
881
1021
  nil
@@ -885,7 +1025,7 @@ module Dfect
885
1025
  # Debugs the given uncaught exception inside the given context.
886
1026
  #
887
1027
  def debug_uncaught_exception context, exception
888
- @exec_stats[:uncaught_exceptions] += 1
1028
+ @stats[:error] += 1
889
1029
  debug context, exception, exception.backtrace
890
1030
  end
891
1031
 
@@ -893,17 +1033,25 @@ module Dfect
893
1033
  # Returns a report that associates the given
894
1034
  # failure details with the currently running test.
895
1035
  #
896
- def build_trace details
897
- if @test_stack.empty?
1036
+ def build_exec_trace details
1037
+ if @tests.empty?
898
1038
  details
899
1039
  else
900
- { @test_stack.last.desc => details }
1040
+ { @tests.last.desc => details }
901
1041
  end
902
1042
  end
903
1043
 
904
- #:stopdoc:
1044
+ ##
1045
+ # Returns a report that qualifies the given
1046
+ # failure details with the current test stack.
1047
+ #
1048
+ def build_fail_trace details
1049
+ @tests.reverse.inject(details) do |inner, outer|
1050
+ { outer.desc => inner }
1051
+ end
1052
+ end
905
1053
 
906
- class Suite
1054
+ class Suite # @private
907
1055
  attr_reader :tests, :before_each, :after_each, :before_all, :after_all
908
1056
 
909
1057
  def initialize
@@ -914,26 +1062,25 @@ module Dfect
914
1062
  @after_all = []
915
1063
  end
916
1064
 
917
- Test = Struct.new :desc, :block
1065
+ Test = Struct.new(:desc, :block, :sandbox) # @private
918
1066
  end
919
-
920
- #:startdoc:
921
1067
  end
922
1068
 
923
- @options = {:debug => true, :quiet => false}
924
-
925
- @exec_stats = Hash.new {|h,k| h[k] = 0 }
926
- @exec_trace = []
927
- @report = {:execution => @exec_trace, :statistics => @exec_stats}.freeze
1069
+ @options = {:debug => $DEBUG, :quiet => false}
928
1070
 
929
- @curr_suite = class << self; Suite.new; end
1071
+ @stats = Hash.new {|h,k| h[k] = 0 }
1072
+ @trace = []
1073
+ @report = {:trace => @trace, :stats => @stats}.freeze
930
1074
 
931
- @test_stack = []
932
- @file_cache = Hash.new {|h,k| h[k] = File.readlines(k) rescue nil }
1075
+ @suite = class << self; Suite.new; end
1076
+ @share = {}
1077
+ @tests = []
1078
+ @calls = []
1079
+ @files = Hash.new {|h,k| h[k] = File.readlines(k) rescue nil }
933
1080
 
934
1081
  ##
935
- # Allows before and after hooks to be specified via
936
- # the D() method syntax when this module is mixed-in:
1082
+ # Allows before and after hooks to be specified via the
1083
+ # following method syntax when this module is mixed-in:
937
1084
  #
938
1085
  # D .<< { puts "before all nested tests" }
939
1086
  # D .< { puts "before each nested test" }
@@ -943,11 +1090,11 @@ module Dfect
943
1090
  D = self
944
1091
 
945
1092
  # provide mixin-able assertion methods
946
- methods(false).grep(/^[[:upper:]][[:punct:]]?$/).each do |name|
1093
+ methods(false).grep(/^[[:upper:]]?[[:punct:]]*$/).each do |name|
947
1094
  #
948
1095
  # XXX: using eval() on a string because Ruby 1.8's
949
1096
  # define_method() cannot take a block parameter
950
1097
  #
951
- eval "def #{name}(*a, &b) ::#{self}.#{name}(*a, &b) end", binding, __FILE__, __LINE__
1098
+ module_eval "def #{name}(*a, &b) ::#{self.name}.#{name}(*a, &b) end", __FILE__, __LINE__
952
1099
  end
953
1100
  end