must_be 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,675 @@
1
+ require 'spec_helper'
2
+
3
+ describe MustBe do
4
+ include MustBeExampleHelper
5
+
6
+ ### Short Inspect ###
7
+
8
+ QUOTATION_MARKS = 2
9
+
10
+ describe ".short_inspect" do
11
+ it "should not shorten strings of length"\
12
+ " MustBe::SHORT_INSPECT_CUTOFF_LENGTH" do
13
+ s = "x" * (MustBe::SHORT_INSPECT_CUTOFF_LENGTH - QUOTATION_MARKS)
14
+ s.inspect.length.should == MustBe::SHORT_INSPECT_CUTOFF_LENGTH
15
+ si = MustBe.short_inspect(s)
16
+ si.should == s.inspect
17
+ end
18
+
19
+ it "should shorten strings longer than"\
20
+ " MustBe::SHORT_INSPECT_CUTOFF_LENGTH" do
21
+ s = "x" * MustBe::SHORT_INSPECT_CUTOFF_LENGTH
22
+ s.inspect.length.should == MustBe::SHORT_INSPECT_CUTOFF_LENGTH +
23
+ QUOTATION_MARKS
24
+ si = MustBe.short_inspect(s)
25
+ si.length.should == MustBe::SHORT_INSPECT_CUTOFF_LENGTH
26
+ end
27
+
28
+ it "should break at word boundries if possible" do
29
+ side_length = MustBe::SHORT_INSPECT_CUTOFF_LENGTH / 2
30
+ padding = "x" * (side_length - 7)
31
+ s = "#{padding} helloXXXXXworld #{padding}"
32
+ is = MustBe.short_inspect(s)
33
+ is.should match("xx ... xx")
34
+ end
35
+
36
+ it "should be used by MustBe" do
37
+ s = "x" * MustBe::SHORT_INSPECT_CUTOFF_LENGTH
38
+ s.must_be(Symbol)
39
+ should notify(/"x*\.\.\.x*"\.must_be\(Symbol\), but matches String/)
40
+ end
41
+ end
42
+
43
+ ### Enable ###
44
+
45
+ describe ".disable" do
46
+ before_disable_after_enable
47
+
48
+ it "should be disabled" do
49
+ MustBe.should_not be_enabled
50
+ end
51
+
52
+ example "#must_be should not notify" do
53
+ 5799.must_be(:lolly).should == 5799
54
+ should_not notify
55
+ end
56
+
57
+ example "#must_notify should return receiver (not a note)" do
58
+ 5799.must_notify("ignored message").should == 5799
59
+ should_not notify
60
+ end
61
+
62
+ example "#must_check should not yield to its block" do
63
+ yielded = false
64
+ must_check { yielded = true }
65
+ yielded.should be_false
66
+ end
67
+
68
+ example "#must should return receiver (not a proxy)" do
69
+ :delegate.must.object_id.should == :delegate.object_id
70
+ end
71
+
72
+ it "should be idempotent" do
73
+ MustBe.disable
74
+ MustBe.should_not be_enabled
75
+ end
76
+ end
77
+
78
+ describe ".enable" do
79
+ it "should start off enabled" do
80
+ MustBe.should be_enabled
81
+ end
82
+
83
+ context "after disabling" do
84
+ before_disable_and_reenable
85
+
86
+ it "should re-enable" do
87
+ MustBe.should be_enabled
88
+ end
89
+
90
+ example "#must_be should notify" do
91
+ 5799.must_be(:lolly).should == 5799
92
+ should notify("5799.must_be(:lolly), but matches Fixnum")
93
+ end
94
+
95
+ example "#must_notify should return a note" do
96
+ 5799.must_notify("noted message").should be_a(Note)
97
+ should notify("noted message")
98
+ end
99
+
100
+ example "#must_check should yield to its block" do
101
+ yielded = false
102
+ must_check { yielded = true }
103
+ yielded.should be_true
104
+ end
105
+
106
+ example "#must should return a proxy" do
107
+ :delegate.must.object_id.should_not == :delegate.object_id
108
+ end
109
+
110
+ it "should be idempotent" do
111
+ MustBe.enable
112
+ MustBe.should be_enabled
113
+ end
114
+ end
115
+ end
116
+
117
+ describe ".register_disabled_method" do
118
+ before :all do
119
+ module ::MustBe
120
+ def must_try_register_disabled_method
121
+ :enabled
122
+ end
123
+
124
+ def must_try_register_disabled_method__disabled
125
+ :disabled
126
+ end
127
+
128
+ register_disabled_method(
129
+ :must_try_register_disabled_method__disabled)
130
+
131
+ register_disabled_method(:must_try_register_disabled_method,
132
+ :must_try_register_disabled_method__disabled)
133
+ end
134
+ end
135
+
136
+ context "after disabling" do
137
+ before_disable_after_enable
138
+
139
+ example "#must_try_register_disabled_method should be disabled" do
140
+ must_try_register_disabled_method.should == :disabled
141
+ end
142
+ end
143
+
144
+ context "after re-enabling" do
145
+ before_disable_and_reenable
146
+
147
+ example "#must_try_register_disabled_method should return :enabled" do
148
+ must_try_register_disabled_method.should == :enabled
149
+ end
150
+ end
151
+ end
152
+
153
+ describe ".register_disabled_handler" do
154
+ before do
155
+ @original_disabled_handlers =
156
+ MustBe.send(:class_variable_get, :@@disabled_handlers).clone
157
+
158
+ @handler_called = nil
159
+ @handler = lambda do |enabled|
160
+ @handler_called = enabled
161
+ end
162
+ end
163
+
164
+ after do
165
+ MustBe.send(:class_variable_set, :@@disabled_handlers,
166
+ @original_disabled_handlers)
167
+ end
168
+
169
+ context "when initially enabled" do
170
+ before do
171
+ MustBe.register_disabled_handler(&@handler)
172
+ end
173
+
174
+ example "handler should not be called immediately" do
175
+ @handler_called.should be_nil
176
+ end
177
+
178
+ context "when disabled" do
179
+ before_disable_after_enable
180
+
181
+ example "handler should be called" do
182
+ @handler_called.should be_false
183
+ end
184
+ end
185
+ end
186
+
187
+ context "when initially disabled" do
188
+ before_disable_after_enable
189
+
190
+ before do
191
+ MustBe.register_disabled_handler(&@handler)
192
+ end
193
+
194
+ example "handler should be called immediately" do
195
+ @handler_called.should be_false
196
+ end
197
+
198
+ context "when enabled" do
199
+ before do
200
+ MustBe.enable
201
+ end
202
+
203
+ example "handler should be called" do
204
+ @handler_called.should be_true
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ describe '#must_just_return' do
211
+ it "should return the receiver" do
212
+ :gnarly.must_just_return(:args, :ignored).should == :gnarly
213
+ should_not notify
214
+ end
215
+ end
216
+
217
+ describe '#must_just_yield' do
218
+ it "should yield" do
219
+ did_yield = false
220
+ must_just_yield { did_yield = true }
221
+ did_yield.should be_true
222
+ should_not notify
223
+ end
224
+ end
225
+
226
+ ### Notifiers ###
227
+
228
+ describe "default notifier" do
229
+ it "should be RaiseNotifier" do
230
+ $default_must_be_notifier.should == MustBe::RaiseNotifier
231
+ end
232
+ end
233
+
234
+ describe "other built-in notifiers" do
235
+ before do
236
+ @original_notifier = MustBe.notifier
237
+ MustBe.notifier = notifier
238
+
239
+ @original_stdout = $stdout
240
+ $stdout = StringIO.new
241
+ end
242
+
243
+ after do
244
+ MustBe.notifier = @original_notifier
245
+ $stdout = @original_stdout
246
+ end
247
+
248
+ describe 'LogNotifier' do
249
+ let(:notifier) { LogNotifier }
250
+
251
+ it "should puts message and backtrace" do
252
+ 3.must_be(4)
253
+
254
+ $stdout.string.should match(
255
+ /3.must_be\(4\), but matches Fixnum\n\t.*core_spec.rb:\d+.*\n\t/)
256
+ end
257
+ end
258
+
259
+ describe 'DebugNotifier' do
260
+ let(:notifier) { DebugNotifier }
261
+
262
+ before do
263
+ $" << 'ruby-debug' # to keep ruby-debug from being loaded.
264
+
265
+ $must_be__did_call_debugger = false
266
+ def MustBe.debugger
267
+ $must_be__did_call_debugger = true
268
+ end
269
+ end
270
+
271
+ after do
272
+ class <<MustBe
273
+ remove_method :debugger
274
+ end
275
+ end
276
+
277
+ it "should puts a message, store the note in $must_be__note,"\
278
+ " and call the debugger" do
279
+ 3.must_be(4)
280
+
281
+ $stdout.string.should == "3.must_be(4), but matches Fixnum\n"\
282
+ "Starting debugger ($must_be__note stores the note)...\n"
283
+ $must_be__note.message.should == "3.must_be(4), but matches Fixnum"
284
+ $must_be__did_call_debugger.should be_true
285
+ end
286
+ end
287
+ end
288
+
289
+ describe ".set_notifier_from_env" do
290
+ before do
291
+ $notifier = MustBe.notifier
292
+ end
293
+
294
+ after do
295
+ MustBe.notifier = $notifier
296
+ end
297
+
298
+ it "should use 'log' to set the LogNotifier" do
299
+ MustBe.set_notifier_from_env('log')
300
+ MustBe.notifier.should == MustBe::LogNotifier
301
+ end
302
+
303
+ it "should use ENV['MUST_BE__NOTIFIER'] when no argument provided" do
304
+ ENV['MUST_BE__NOTIFIER'] = 'debug'
305
+ MustBe.set_notifier_from_env
306
+ MustBe.notifier.should == MustBe::DebugNotifier
307
+ end
308
+
309
+ it "should raise NoMethodError when argument does not respond to :to_sym" do
310
+ expect do
311
+ MustBe.set_notifier_from_env(nil)
312
+ end.should raise_error(NoMethodError)
313
+ end
314
+
315
+ it "should raise ArgumentError when unknown notifier name provided" do
316
+ expect do
317
+ MustBe.set_notifier_from_env(:unknown)
318
+ end.should raise_error(ArgumentError)
319
+ end
320
+
321
+ it "should treat 'disable' as a special case" do
322
+ MustBe.set_notifier_from_env('disable')
323
+ MustBe.should_not be_enabled
324
+ MustBe.notifier.should == $notifier
325
+ MustBe.enable
326
+ MustBe.should be_enabled
327
+ end
328
+ end
329
+
330
+ describe ".def_notifier" do
331
+ context "when called with a key" do
332
+ before :all do
333
+ MustBe.def_notifier(:Spec__ExampleNotifier, :spec__example) {}
334
+ end
335
+
336
+ it "should set a constant" do
337
+ MustBe::Spec__ExampleNotifier.should be_a(Proc)
338
+ end
339
+
340
+ it "should add the key to NOTIFIERS" do
341
+ MustBe::NOTIFIERS[:spec__example].should == :Spec__ExampleNotifier
342
+ end
343
+
344
+ it "should extend .set_notifier_from_env" do
345
+ MustBe.set_notifier_from_env(:spec__example)
346
+ MustBe.notifier.should == MustBe::Spec__ExampleNotifier
347
+ end
348
+ end
349
+
350
+ context "when called with no key" do
351
+ before :all do
352
+ @original_notifiers = MustBe::NOTIFIERS.clone
353
+
354
+ MustBe.def_notifier(:Spec__SecondExampleNotifier) {}
355
+ end
356
+
357
+ it "should set a constant" do
358
+ MustBe::Spec__SecondExampleNotifier.should be_a(Proc)
359
+ end
360
+
361
+ it "should leave NOTIFIERS unchanged" do
362
+ MustBe::NOTIFIERS.should == @original_notifiers
363
+ end
364
+ end
365
+ end
366
+
367
+ describe "RaiseNotifier" do
368
+ before do
369
+ MustBe.notifier = RaiseNotifier
370
+ end
371
+
372
+ it "should raise Note" do
373
+ expect do
374
+ must_notify("funny bunny")
375
+ end.should(raise_error(Note, "funny bunny") do |note|
376
+ note.backtrace[0].should_not match(/`must_notify'/)
377
+ end)
378
+ end
379
+ end
380
+
381
+ ### Note ###
382
+
383
+ describe "Note" do
384
+ # #must_notify provides examples covering #initialize and #to_s.
385
+
386
+ describe "difference between #backtrace and #complete_backtrace" do
387
+ example "#backtrace should drop lines containing %r{lib/must_be.*\\.rb:}"\
388
+ " from #complete_backtrace" do
389
+ backtrace = [
390
+ "first line kept",
391
+ "other lib/must_be.rb: kept as well"]
392
+
393
+ complete_backtrace = [
394
+ "lib/must_be.rb: at start",
395
+ "in middle lib/must_be_elsewhere.rb: is okay",
396
+ "at end too lib/must_be_yet_again.rb:",
397
+ *backtrace]
398
+
399
+ note = Note.new("sample")
400
+ note.set_backtrace(complete_backtrace)
401
+
402
+ note.complete_backtrace.should == complete_backtrace
403
+ note.backtrace.should == backtrace
404
+ end
405
+ end
406
+ end
407
+
408
+ describe '#must_notify' do
409
+ class <<self
410
+ def it_should_notify(message)
411
+ its(:message) { should == message }
412
+ it("should notify") { should notify(message) }
413
+ end
414
+
415
+ def its_assertion_properties_should_be_nil
416
+ its(:receiver) { should be_nil }
417
+ its(:assertion) { should be_nil }
418
+ its(:args) { should be_nil }
419
+ its(:block) { should be_nil }
420
+ its(:additional_message) { should be_nil }
421
+ end
422
+ end
423
+
424
+ block = lambda { nil }
425
+
426
+ context "when called with no arguments" do
427
+ subject { must_notify }
428
+ it_should_notify("MustBe::Note")
429
+ its_assertion_properties_should_be_nil
430
+ end
431
+
432
+ context "when called with single (message) argument" do
433
+ subject { must_notify("message for note") }
434
+ it_should_notify("message for note")
435
+ its_assertion_properties_should_be_nil
436
+ end
437
+
438
+ context "when called with existing note" do
439
+ note = Note.new("existing note")
440
+
441
+ subject { must_notify(note) }
442
+ it_should_notify("existing note")
443
+ it { should == note }
444
+ end
445
+
446
+ context "when called with receiver and assertion" do
447
+ subject { must_notify(4890, :must_be_silly) }
448
+ it_should_notify("4890.must_be_silly")
449
+ its(:receiver) { should == 4890 }
450
+ its(:assertion) { should == :must_be_silly }
451
+ its(:args) { should be_nil }
452
+ its(:block) { should be_nil }
453
+ end
454
+
455
+ context "when called with receiver, assertion, and an argument" do
456
+ subject { must_notify(4890, :must_be_silly, [57]) }
457
+ it_should_notify("4890.must_be_silly(57)")
458
+ its(:receiver) { should == 4890 }
459
+ its(:assertion) { should == :must_be_silly }
460
+ its(:args) { should == [57] }
461
+ its(:block) { should be_nil }
462
+ end
463
+
464
+ context "when called with receiver, assertion, and arguments" do
465
+ subject { must_notify(4890, :must_be_silly, [57, 71]) }
466
+ it_should_notify("4890.must_be_silly(57, 71)")
467
+ its(:receiver) { should == 4890 }
468
+ its(:assertion) { should == :must_be_silly }
469
+ its(:args) { should == [57, 71] }
470
+ its(:block) { should be_nil }
471
+ end
472
+
473
+ context "when called with receiver, assertion, and block" do
474
+ block = lambda { nil }
475
+
476
+ subject { must_notify(4890, :must_be_silly, nil, block) }
477
+ it_should_notify("4890.must_be_silly {}")
478
+ its(:receiver) { should == 4890 }
479
+ its(:assertion) { should == :must_be_silly }
480
+ its(:args) { should == nil }
481
+ its(:block) { should == block }
482
+ end
483
+
484
+ context "when called with receiver, assertion, arguments, and block" do
485
+ subject { must_notify(4890, :must_be_silly, [57, 71], block) }
486
+ it_should_notify("4890.must_be_silly(57, 71) {}")
487
+ its(:receiver) { should == 4890 }
488
+ its(:assertion) { should == :must_be_silly }
489
+ its(:args) { should == [57, 71] }
490
+ its(:block) { should == block }
491
+ end
492
+
493
+ context "when called with #additional_message" do
494
+ subject do
495
+ must_notify(5, :must_be, [String], nil, ", but matches Fixnum")
496
+ end
497
+
498
+ it_should_notify("5.must_be(String), but matches Fixnum")
499
+ its(:receiver) { should == 5 }
500
+ its(:assertion) { should == :must_be }
501
+ its(:args) { should == [String] }
502
+ its(:block) { should be_nil }
503
+ its(:additional_message) { should == ", but matches Fixnum"}
504
+ end
505
+ end
506
+
507
+ describe '#must_check' do
508
+ context "when its block attempts to notify" do
509
+ it "should return the note without notifying" do
510
+ note = must_check do
511
+ must_notify("ignored note")
512
+ must_notify("returned note")
513
+ "extra stuff"
514
+ end
515
+ should_not notify
516
+ note.should be_a(Note)
517
+ note.message.should == "returned note"
518
+ end
519
+ end
520
+
521
+ context "when its block does not notify" do
522
+ it "should return nil" do
523
+ did_yield = false
524
+ note = must_check { did_yield = true }
525
+ did_yield.should be_true
526
+ should_not notify
527
+ note.should == nil
528
+ end
529
+ end
530
+
531
+ context "when called with a proc" do
532
+ it "should not call its block if the proc does not notify" do
533
+ did_call_block = false
534
+ must_check(lambda {}) do
535
+ did_call_block = true
536
+ end
537
+ did_call_block.should be_false
538
+ end
539
+
540
+ it "should call its block and notify if the proc notifies" do
541
+ did_call_block = false
542
+ must_check(lambda { must_notify("check") }) do |note|
543
+ did_call_block = true
544
+ note.message.should == "check"
545
+ Note.new("mate")
546
+ end
547
+ did_call_block.should be_true
548
+ should notify("mate")
549
+ end
550
+
551
+ it "should use the result of the proc to generate a new note" do
552
+ did_call_block = false
553
+ must_check(lambda { must_notify("check") }) do |note|
554
+ did_call_block = true
555
+ note.message.should == "check"
556
+ "mate"
557
+ end
558
+ did_call_block.should be_true
559
+ should notify("mate")
560
+ end
561
+ end
562
+
563
+ context "when nesting" do
564
+ it "should be safe" do
565
+ note = must_check do
566
+ inner_note = must_check { must_notify("inner") }
567
+ should_not notify
568
+ inner_note.message.should == "inner"
569
+
570
+ must_notify("outer")
571
+ end
572
+ should_not notify
573
+ note.message.should == "outer"
574
+ end
575
+
576
+ it "should ignore inner checked notes" do
577
+ note = must_check do
578
+ must_notify("outer")
579
+ must_check { must_notify("inner") }
580
+ end
581
+ should_not notify
582
+ note.message.should == "outer"
583
+ end
584
+
585
+ it "should return nil if all inner notes are also checked" do
586
+ note = must_check do
587
+ must_check { must_notify("inner") }
588
+ end
589
+ should_not notify
590
+ note.should == nil
591
+ end
592
+ end
593
+
594
+ describe "safety" do
595
+ it "should be able to be called multiple times" do
596
+ note = must_check { must_notify("once") }
597
+ should_not notify
598
+ note.message.should == "once"
599
+
600
+ note = must_check { must_notify("again") }
601
+ should_not notify
602
+ note.message.should == "again"
603
+ end
604
+
605
+ it "should be error safe" do
606
+ expect do
607
+ must_check do
608
+ raise
609
+ end
610
+ end.should raise_error
611
+
612
+ must_notify
613
+ should notify
614
+ end
615
+
616
+ it "should be thread safe" do
617
+ mutex = Mutex.new
618
+ cv = ConditionVariable.new
619
+
620
+ cv_yield = lambda do
621
+ cv.signal
622
+ cv.wait(mutex)
623
+ end
624
+
625
+ thread = nil
626
+ thread_note = nil
627
+ note = nil
628
+
629
+ thread_block = lambda do
630
+ mutex.synchronize do
631
+ thread_note = must_check do
632
+ must_notify("thread")
633
+ cv_yield[]
634
+ end
635
+ end
636
+ end
637
+
638
+ mutex.synchronize do
639
+ note = must_check do
640
+ must_notify("main")
641
+ thread = Thread.new &thread_block
642
+ cv_yield[]
643
+ end
644
+ cv.signal
645
+ end
646
+ thread.join
647
+
648
+ note.message.should == "main"
649
+ thread_note.message.should == "thread"
650
+ end
651
+
652
+ if RUBY_VERSION > "1.9"
653
+ it "should be fiber safe" do
654
+ fiber_note = nil
655
+
656
+ fiber = Fiber.new do
657
+ fiber_note = must_check do
658
+ must_notify("fiber")
659
+ Fiber.yield
660
+ end
661
+ end
662
+
663
+ note = must_check do
664
+ must_notify("main")
665
+ fiber.resume
666
+ end
667
+ fiber.resume
668
+
669
+ note.message.should == "main"
670
+ fiber_note.message.should == "fiber"
671
+ end
672
+ end
673
+ end
674
+ end
675
+ end