dfect 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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