thread_safe 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,943 @@
1
+ Thread.abort_on_exception = true
2
+
3
+ module ThreadSafe
4
+ describe Cache do
5
+ before(:each) do
6
+ @cache = described_class.new
7
+ end
8
+
9
+ it 'concurrency' do
10
+ (1..THREADS).map do |i|
11
+ Thread.new do
12
+ 1000.times do |j|
13
+ key = i * 1000 + j
14
+ @cache[key] = i
15
+ @cache[key]
16
+ @cache.delete(key)
17
+ end
18
+ end
19
+ end.map(&:join)
20
+ end
21
+
22
+ it 'retrieval' do
23
+ expect_size_change(1) do
24
+ expect(nil).to eq @cache[:a]
25
+ expect(nil).to eq @cache.get(:a)
26
+ @cache[:a] = 1
27
+ expect(1).to eq @cache[:a]
28
+ expect(1).to eq @cache.get(:a)
29
+ end
30
+ end
31
+
32
+ it '#put_if_absent' do
33
+ with_or_without_default_proc do
34
+ expect_size_change(1) do
35
+ expect(nil).to eq @cache.put_if_absent(:a, 1)
36
+ expect(1).to eq @cache.put_if_absent(:a, 1)
37
+ expect(1).to eq @cache.put_if_absent(:a, 2)
38
+ expect(1).to eq @cache[:a]
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#compute_if_absent' do
44
+ it 'common' do
45
+ with_or_without_default_proc do
46
+ expect_size_change(3) do
47
+ expect(1).to eq @cache.compute_if_absent(:a) { 1 }
48
+ expect(1).to eq @cache.compute_if_absent(:a) { 2 }
49
+ expect(1).to eq @cache[:a]
50
+
51
+ @cache[:b] = nil
52
+ expect(nil).to eq @cache.compute_if_absent(:b) { 1 }
53
+ expect(nil).to eq @cache.compute_if_absent(:c) {}
54
+ expect(nil).to eq @cache[:c]
55
+ expect(true).to eq @cache.key?(:c)
56
+ end
57
+ end
58
+ end
59
+
60
+ it 'with return' do
61
+ with_or_without_default_proc do
62
+ expect_handles_return_lambda(:compute_if_absent, :a)
63
+ end
64
+ end
65
+
66
+ it 'exception' do
67
+ with_or_without_default_proc do
68
+ expect_handles_exception(:compute_if_absent, :a)
69
+ end
70
+ end
71
+
72
+ it 'atomicity' do
73
+ late_compute_threads_count = 10
74
+ late_put_if_absent_threads_count = 10
75
+ getter_threads_count = 5
76
+ compute_started = ThreadSafe::Test::Latch.new(1)
77
+ compute_proceed = ThreadSafe::Test::Latch.new(
78
+ late_compute_threads_count +
79
+ late_put_if_absent_threads_count +
80
+ getter_threads_count
81
+ )
82
+ block_until_compute_started = lambda do |name|
83
+ # what does it mean?
84
+ if (v = @cache[:a]) != nil
85
+ expect(nil).to v
86
+ end
87
+ compute_proceed.release
88
+ compute_started.await
89
+ end
90
+
91
+ expect_size_change 1 do
92
+ late_compute_threads = Array.new(late_compute_threads_count) do
93
+ Thread.new do
94
+ block_until_compute_started.call('compute_if_absent')
95
+ expect(1).to eq @cache.compute_if_absent(:a) { fail }
96
+ end
97
+ end
98
+
99
+ late_put_if_absent_threads = Array.new(late_put_if_absent_threads_count) do
100
+ Thread.new do
101
+ block_until_compute_started.call('put_if_absent')
102
+ expect(1).to eq @cache.put_if_absent(:a, 2)
103
+ end
104
+ end
105
+
106
+ getter_threads = Array.new(getter_threads_count) do
107
+ Thread.new do
108
+ block_until_compute_started.call('getter')
109
+ Thread.pass while @cache[:a].nil?
110
+ expect(1).to eq @cache[:a]
111
+ end
112
+ end
113
+
114
+ Thread.new do
115
+ @cache.compute_if_absent(:a) do
116
+ compute_started.release
117
+ compute_proceed.await
118
+ sleep(0.2)
119
+ 1
120
+ end
121
+ end.join
122
+ (late_compute_threads +
123
+ late_put_if_absent_threads +
124
+ getter_threads).each(&:join)
125
+ end
126
+ end
127
+ end
128
+
129
+ describe '#compute_if_present' do
130
+ it 'common' do
131
+ with_or_without_default_proc do
132
+ expect_no_size_change do
133
+ expect(nil).to eq @cache.compute_if_present(:a) {}
134
+ expect(nil).to eq @cache.compute_if_present(:a) { 1 }
135
+ expect(nil).to eq @cache.compute_if_present(:a) { fail }
136
+ expect(false).to eq @cache.key?(:a)
137
+ end
138
+
139
+ @cache[:a] = 1
140
+ expect_no_size_change do
141
+ expect(1).to eq @cache.compute_if_present(:a) { 1 }
142
+ expect(1).to eq @cache[:a]
143
+ expect(2).to eq @cache.compute_if_present(:a) { 2 }
144
+ expect(2).to eq @cache[:a]
145
+ expect(false).to eq @cache.compute_if_present(:a) { false }
146
+ expect(false).to eq @cache[:a]
147
+
148
+ @cache[:a] = 1
149
+ yielded = false
150
+ @cache.compute_if_present(:a) do |old_value|
151
+ yielded = true
152
+ expect(1).to eq old_value
153
+ 2
154
+ end
155
+ expect(true).to eq yielded
156
+ end
157
+
158
+ expect_size_change(-1) do
159
+ expect(nil).to eq @cache.compute_if_present(:a) {}
160
+ expect(false).to eq @cache.key?(:a)
161
+ expect(nil).to eq @cache.compute_if_present(:a) { 1 }
162
+ expect(false).to eq @cache.key?(:a)
163
+ end
164
+ end
165
+ end
166
+
167
+ it 'with return' do
168
+ with_or_without_default_proc do
169
+ @cache[:a] = 1
170
+ expect_handles_return_lambda(:compute_if_present, :a)
171
+ end
172
+ end
173
+
174
+ it 'exception' do
175
+ with_or_without_default_proc do
176
+ @cache[:a] = 1
177
+ expect_handles_exception(:compute_if_present, :a)
178
+ end
179
+ end
180
+ end
181
+
182
+ describe '#compute' do
183
+ it 'common' do
184
+ with_or_without_default_proc do
185
+ expect_no_size_change do
186
+ expect_compute(:a, nil, nil) {}
187
+ end
188
+
189
+ expect_size_change(1) do
190
+ expect_compute(:a, nil, 1) { 1 }
191
+ expect_compute(:a, 1, 2) { 2 }
192
+ expect_compute(:a, 2, false) { false }
193
+ expect(false).to eq @cache[:a]
194
+ end
195
+
196
+ expect_size_change(-1) do
197
+ expect_compute(:a, false, nil) {}
198
+ end
199
+ end
200
+ end
201
+
202
+ it 'with return' do
203
+ with_or_without_default_proc do
204
+ expect_handles_return_lambda(:compute, :a)
205
+ @cache[:a] = 1
206
+ expect_handles_return_lambda(:compute, :a)
207
+ end
208
+ end
209
+
210
+ it 'exception' do
211
+ with_or_without_default_proc do
212
+ expect_handles_exception(:compute, :a)
213
+ @cache[:a] = 2
214
+ expect_handles_exception(:compute, :a)
215
+ end
216
+ end
217
+ end
218
+
219
+ describe '#merge_pair' do
220
+ it 'common' do
221
+ with_or_without_default_proc do
222
+ expect_size_change(1) do
223
+ expect(nil).to eq @cache.merge_pair(:a, nil) { fail }
224
+ expect(true).to eq @cache.key?(:a)
225
+ expect(nil).to eq @cache[:a]
226
+ end
227
+
228
+ expect_no_size_change do
229
+ expect_merge_pair(:a, nil, nil, false) { false }
230
+ expect_merge_pair(:a, nil, false, 1) { 1 }
231
+ expect_merge_pair(:a, nil, 1, 2) { 2 }
232
+ end
233
+
234
+ expect_size_change(-1) do
235
+ expect_merge_pair(:a, nil, 2, nil) {}
236
+ expect(false).to eq @cache.key?(:a)
237
+ end
238
+ end
239
+ end
240
+
241
+ it 'with return' do
242
+ with_or_without_default_proc do
243
+ @cache[:a] = 1
244
+ expect_handles_return_lambda(:merge_pair, :a, 2)
245
+ end
246
+ end
247
+
248
+ it 'exception' do
249
+ with_or_without_default_proc do
250
+ @cache[:a] = 1
251
+ expect_handles_exception(:merge_pair, :a, 2)
252
+ end
253
+ end
254
+ end
255
+
256
+ it 'updates dont block reads' do
257
+ getters_count = 20
258
+ key_klass = ThreadSafe::Test::HashCollisionKey
259
+ keys = [key_klass.new(1, 100),
260
+ key_klass.new(2, 100),
261
+ key_klass.new(3, 100)] # hash colliding keys
262
+ inserted_keys = []
263
+
264
+ keys.each do |key, i|
265
+ compute_started = ThreadSafe::Test::Latch.new(1)
266
+ compute_finished = ThreadSafe::Test::Latch.new(1)
267
+ getters_started = ThreadSafe::Test::Latch.new(getters_count)
268
+ getters_finished = ThreadSafe::Test::Latch.new(getters_count)
269
+
270
+ computer_thread = Thread.new do
271
+ getters_started.await
272
+ @cache.compute_if_absent(key) do
273
+ compute_started.release
274
+ getters_finished.await
275
+ 1
276
+ end
277
+ compute_finished.release
278
+ end
279
+
280
+ getter_threads = (1..getters_count).map do
281
+ Thread.new do
282
+ getters_started.release
283
+ inserted_keys.each do |inserted_key|
284
+ expect(true).to eq @cache.key?(inserted_key)
285
+ expect(1).to eq @cache[inserted_key]
286
+ end
287
+ expect(false).to eq @cache.key?(key)
288
+ compute_started.await
289
+ inserted_keys.each do |inserted_key|
290
+ expect(true).to eq @cache.key?(inserted_key)
291
+ expect(1).to eq @cache[inserted_key]
292
+ end
293
+ expect(false).to eq @cache.key?(key)
294
+ expect(nil).to eq @cache[key]
295
+ getters_finished.release
296
+ compute_finished.await
297
+ expect(true).to eq @cache.key?(key)
298
+ expect(1).to eq @cache[key]
299
+ end
300
+ end
301
+
302
+ (getter_threads << computer_thread).map do |t|
303
+ expect(t.join(2)).to be_truthy
304
+ end # asserting no deadlocks
305
+ inserted_keys << key
306
+ end
307
+ end
308
+
309
+ specify 'collision resistance' do
310
+ expect_collision_resistance(
311
+ (0..1000).map { |i| ThreadSafe::Test::HashCollisionKey(i, 1) }
312
+ )
313
+ end
314
+
315
+ specify 'collision resistance with arrays' do
316
+ special_array_class = Class.new(Array) do
317
+ def key # assert_collision_resistance expects to be able to call .key to get the "real" key
318
+ first.key
319
+ end
320
+ end
321
+ # Test collision resistance with a keys that say they responds_to <=>, but then raise exceptions
322
+ # when actually called (ie: an Array filled with non-comparable keys).
323
+ # See https://github.com/headius/thread_safe/issues/19 for more info.
324
+ expect_collision_resistance(
325
+ (0..100).map do |i|
326
+ special_array_class.new(
327
+ [ThreadSafe::Test::HashCollisionKeyNonComparable.new(i, 1)]
328
+ )
329
+ end
330
+ )
331
+ end
332
+
333
+ it '#replace_pair' do
334
+ with_or_without_default_proc do
335
+ expect_no_size_change do
336
+ expect(false).to eq @cache.replace_pair(:a, 1, 2)
337
+ expect(false).to eq @cache.replace_pair(:a, nil, nil)
338
+ expect(false).to eq @cache.key?(:a)
339
+ end
340
+ end
341
+
342
+ @cache[:a] = 1
343
+ expect_no_size_change do
344
+ expect(true).to eq @cache.replace_pair(:a, 1, 2)
345
+ expect(false).to eq @cache.replace_pair(:a, 1, 2)
346
+ expect(2).to eq @cache[:a]
347
+ expect(true).to eq @cache.replace_pair(:a, 2, 2)
348
+ expect(2).to eq @cache[:a]
349
+ expect(true).to eq @cache.replace_pair(:a, 2, nil)
350
+ expect(false).to eq @cache.replace_pair(:a, 2, nil)
351
+ expect(nil).to eq @cache[:a]
352
+ expect(true).to eq @cache.key?(:a)
353
+ expect(true).to eq @cache.replace_pair(:a, nil, nil)
354
+ expect(true).to eq @cache.key?(:a)
355
+ expect(true).to eq @cache.replace_pair(:a, nil, 1)
356
+ expect(1).to eq @cache[:a]
357
+ end
358
+ end
359
+
360
+ it '#replace_if_exists' do
361
+ with_or_without_default_proc do
362
+ expect_no_size_change do
363
+ expect(nil).to eq @cache.replace_if_exists(:a, 1)
364
+ expect(false).to eq @cache.key?(:a)
365
+ end
366
+
367
+ @cache[:a] = 1
368
+ expect_no_size_change do
369
+ expect(1).to eq @cache.replace_if_exists(:a, 2)
370
+ expect(2).to eq @cache[:a]
371
+ expect(2).to eq @cache.replace_if_exists(:a, nil)
372
+ expect(nil).to eq @cache[:a]
373
+ expect(true).to eq @cache.key?(:a)
374
+ expect(nil).to eq @cache.replace_if_exists(:a, 1)
375
+ expect(1).to eq @cache[:a]
376
+ end
377
+ end
378
+ end
379
+
380
+ it '#get_and_set' do
381
+ with_or_without_default_proc do
382
+ expect(nil).to eq @cache.get_and_set(:a, 1)
383
+ expect(true).to eq @cache.key?(:a)
384
+ expect(1).to eq @cache[:a]
385
+ expect(1).to eq @cache.get_and_set(:a, 2)
386
+ expect(2).to eq @cache.get_and_set(:a, nil)
387
+ expect(nil).to eq @cache[:a]
388
+ expect(true).to eq @cache.key?(:a)
389
+ expect(nil).to eq @cache.get_and_set(:a, 1)
390
+ expect(1).to eq @cache[:a]
391
+ end
392
+ end
393
+
394
+ it '#key' do
395
+ with_or_without_default_proc do
396
+ expect(nil).to eq @cache.key(1)
397
+ @cache[:a] = 1
398
+ expect(:a).to eq @cache.key(1)
399
+ expect(nil).to eq @cache.key(0)
400
+ end
401
+ end
402
+
403
+ it '#key?' do
404
+ with_or_without_default_proc do
405
+ expect(false).to eq @cache.key?(:a)
406
+ @cache[:a] = 1
407
+ expect(true).to eq @cache.key?(:a)
408
+ end
409
+ end
410
+
411
+ it '#value?' do
412
+ with_or_without_default_proc do
413
+ expect(false).to eq @cache.value?(1)
414
+ @cache[:a] = 1
415
+ expect(true).to eq @cache.value?(1)
416
+ end
417
+ end
418
+
419
+ it '#delete' do
420
+ with_or_without_default_proc do |default_proc_set|
421
+ expect_no_size_change do
422
+ expect(nil).to eq @cache.delete(:a)
423
+ end
424
+ @cache[:a] = 1
425
+ expect_size_change -1 do
426
+ expect(1).to eq @cache.delete(:a)
427
+ end
428
+ expect_no_size_change do
429
+ expect(nil).to eq @cache[:a] unless default_proc_set
430
+
431
+ expect(false).to eq @cache.key?(:a)
432
+ expect(nil).to eq @cache.delete(:a)
433
+ end
434
+ end
435
+ end
436
+
437
+ it '#delete_pair' do
438
+ with_or_without_default_proc do
439
+ expect_no_size_change do
440
+ expect(false).to eq @cache.delete_pair(:a, 2)
441
+ expect(false).to eq @cache.delete_pair(:a, nil)
442
+ end
443
+ @cache[:a] = 1
444
+ expect_no_size_change do
445
+ expect(false).to eq @cache.delete_pair(:a, 2)
446
+ end
447
+ expect_size_change(-1) do
448
+ expect(1).to eq @cache[:a]
449
+ expect(true).to eq @cache.delete_pair(:a, 1)
450
+ expect(false).to eq @cache.delete_pair(:a, 1)
451
+ expect(false).to eq @cache.key?(:a)
452
+ end
453
+ end
454
+ end
455
+
456
+ specify 'default proc' do
457
+ @cache = cache_with_default_proc(1)
458
+ expect_no_size_change do
459
+ expect(false).to eq @cache.key?(:a)
460
+ end
461
+ expect_size_change(1) do
462
+ expect(1).to eq @cache[:a]
463
+ expect(true).to eq @cache.key?(:a)
464
+ end
465
+ end
466
+
467
+ specify 'falsy default proc' do
468
+ @cache = cache_with_default_proc(nil)
469
+ expect_no_size_change do
470
+ expect(false).to eq @cache.key?(:a)
471
+ end
472
+ expect_size_change(1) do
473
+ expect(nil).to eq @cache[:a]
474
+ expect(true).to eq @cache.key?(:a)
475
+ end
476
+ end
477
+
478
+ describe '#fetch' do
479
+ it 'common' do
480
+ with_or_without_default_proc do |default_proc_set|
481
+ expect_no_size_change do
482
+ expect(1).to eq @cache.fetch(:a, 1)
483
+ expect(1).to eq @cache.fetch(:a) { 1 }
484
+ expect(false).to eq @cache.key?(:a)
485
+
486
+ expect(nil).to eq @cache[:a] unless default_proc_set
487
+ end
488
+
489
+ @cache[:a] = 1
490
+ expect_no_size_change do
491
+ expect(1).to eq @cache.fetch(:a) { fail }
492
+ end
493
+
494
+ expect { @cache.fetch(:b) }.to raise_error(KeyError)
495
+
496
+ expect_no_size_change do
497
+ expect(1).to eq @cache.fetch(:b, :c) {1} # assert block supersedes default value argument
498
+ expect(false).to eq @cache.key?(:b)
499
+ end
500
+ end
501
+ end
502
+
503
+ it 'falsy' do
504
+ with_or_without_default_proc do
505
+ expect(false).to eq @cache.key?(:a)
506
+
507
+ expect_no_size_change do
508
+ expect(nil).to eq @cache.fetch(:a, nil)
509
+ expect(false).to eq @cache.fetch(:a, false)
510
+ expect(nil).to eq @cache.fetch(:a) {}
511
+ expect(false).to eq @cache.fetch(:a) { false }
512
+ end
513
+
514
+ @cache[:a] = nil
515
+ expect_no_size_change do
516
+ expect(true).to eq @cache.key?(:a)
517
+ expect(nil).to eq @cache.fetch(:a) { fail }
518
+ end
519
+ end
520
+ end
521
+
522
+ it 'with return' do
523
+ with_or_without_default_proc do
524
+ r = fetch_with_return
525
+ # r = lambda do
526
+ # @cache.fetch(:a) { return 10 }
527
+ # end.call
528
+
529
+ expect_no_size_change do
530
+ expect(10).to eq r
531
+ expect(false).to eq @cache.key?(:a)
532
+ end
533
+ end
534
+ end
535
+ end
536
+
537
+ describe '#fetch_or_store' do
538
+ it 'common' do
539
+ with_or_without_default_proc do |default_proc_set|
540
+ expect_size_change(1) do
541
+ expect(1).to eq @cache.fetch_or_store(:a, 1)
542
+ expect(1).to eq @cache[:a]
543
+ end
544
+
545
+ @cache.delete(:a)
546
+
547
+ expect_size_change 1 do
548
+ expect(1).to eq @cache.fetch_or_store(:a) { 1 }
549
+ expect(1).to eq @cache[:a]
550
+ end
551
+
552
+ expect_no_size_change do
553
+ expect(1).to eq @cache.fetch_or_store(:a) { fail }
554
+ end
555
+
556
+ expect { @cache.fetch_or_store(:b) }.
557
+ to raise_error(KeyError)
558
+
559
+ expect_size_change(1) do
560
+ expect(1).to eq @cache.fetch_or_store(:b, :c) { 1 } # assert block supersedes default value argument
561
+ expect(1).to eq @cache[:b]
562
+ end
563
+ end
564
+ end
565
+
566
+ it 'falsy' do
567
+ with_or_without_default_proc do
568
+ expect(false).to eq @cache.key?(:a)
569
+
570
+ expect_size_change(1) do
571
+ expect(nil).to eq @cache.fetch_or_store(:a, nil)
572
+ expect(nil).to eq @cache[:a]
573
+ expect(true).to eq @cache.key?(:a)
574
+ end
575
+ @cache.delete(:a)
576
+
577
+ expect_size_change(1) do
578
+ expect(false).to eq @cache.fetch_or_store(:a, false)
579
+ expect(false).to eq @cache[:a]
580
+ expect(true).to eq @cache.key?(:a)
581
+ end
582
+ @cache.delete(:a)
583
+
584
+ expect_size_change(1) do
585
+ expect(nil).to eq @cache.fetch_or_store(:a) {}
586
+ expect(nil).to eq @cache[:a]
587
+ expect(true).to eq @cache.key?(:a)
588
+ end
589
+ @cache.delete(:a)
590
+
591
+ expect_size_change(1) do
592
+ expect(false).to eq @cache.fetch_or_store(:a) { false }
593
+ expect(false).to eq @cache[:a]
594
+ expect(true).to eq @cache.key?(:a)
595
+ end
596
+
597
+ @cache[:a] = nil
598
+ expect_no_size_change do
599
+ expect(nil).to eq @cache.fetch_or_store(:a) { fail }
600
+ end
601
+ end
602
+ end
603
+
604
+ it 'with return' do
605
+ with_or_without_default_proc do
606
+ r = fetch_or_store_with_return
607
+
608
+ expect_no_size_change do
609
+ expect(10).to eq r
610
+ expect(false).to eq @cache.key?(:a)
611
+ end
612
+ end
613
+ end
614
+ end
615
+
616
+ it '#clear' do
617
+ @cache[:a] = 1
618
+ expect_size_change(-1) do
619
+ expect(@cache).to eq @cache.clear
620
+ expect(false).to eq @cache.key?(:a)
621
+ expect(nil).to eq @cache[:a]
622
+ end
623
+ end
624
+
625
+ describe '#each_pair' do
626
+ it 'common' do
627
+ @cache.each_pair { |k, v| fail }
628
+ expect(@cache).to eq @cache.each_pair {}
629
+ @cache[:a] = 1
630
+
631
+ h = {}
632
+ @cache.each_pair { |k, v| h[k] = v }
633
+ expect({:a => 1}).to eq h
634
+
635
+ @cache[:b] = 2
636
+ h = {}
637
+ @cache.each_pair { |k, v| h[k] = v }
638
+ expect({:a => 1, :b => 2}).to eq h
639
+ end
640
+
641
+ it 'pair iterator' do
642
+ @cache[:a] = 1
643
+ @cache[:b] = 2
644
+ i = 0
645
+ r = @cache.each_pair do |k, v|
646
+ if i == 0
647
+ i += 1
648
+ next
649
+ fail
650
+ elsif i == 1
651
+ break :breaked
652
+ end
653
+ end
654
+
655
+ expect(:breaked).to eq r
656
+ end
657
+
658
+ it 'allows modification' do
659
+ @cache[:a] = 1
660
+ @cache[:b] = 1
661
+ @cache[:c] = 1
662
+
663
+ expect_size_change(1) do
664
+ @cache.each_pair do |k, v|
665
+ @cache[:z] = 1
666
+ end
667
+ end
668
+ end
669
+ end
670
+
671
+ it '#keys' do
672
+ expect([]).to eq @cache.keys
673
+
674
+ @cache[1] = 1
675
+ expect([1]).to eq @cache.keys
676
+
677
+ @cache[2] = 2
678
+ expect([1, 2]).to eq @cache.keys.sort
679
+ end
680
+
681
+ it '#values' do
682
+ expect([]).to eq @cache.values
683
+
684
+ @cache[1] = 1
685
+ expect([1]).to eq @cache.values
686
+
687
+ @cache[2] = 2
688
+ expect([1, 2]).to eq @cache.values.sort
689
+ end
690
+
691
+ it '#each_key' do
692
+ expect(@cache).to eq @cache.each_key { fail }
693
+
694
+ @cache[1] = 1
695
+ arr = []
696
+ @cache.each_key { |k| arr << k }
697
+ expect([1]).to eq arr
698
+
699
+ @cache[2] = 2
700
+ arr = []
701
+ @cache.each_key { |k| arr << k }
702
+ expect([1, 2]).to eq arr.sort
703
+ end
704
+
705
+ it '#each_value' do
706
+ expect(@cache).to eq @cache.each_value { fail }
707
+
708
+ @cache[1] = 1
709
+ arr = []
710
+ @cache.each_value { |k| arr << k }
711
+ expect([1]).to eq arr
712
+
713
+ @cache[2] = 2
714
+ arr = []
715
+ @cache.each_value { |k| arr << k }
716
+ expect([1, 2]).to eq arr.sort
717
+ end
718
+
719
+ it '#empty' do
720
+ expect(true).to eq @cache.empty?
721
+ @cache[:a] = 1
722
+ expect(false).to eq @cache.empty?
723
+ end
724
+
725
+ it 'options validation' do
726
+ expect_valid_options(nil)
727
+ expect_valid_options({})
728
+ expect_valid_options(foo: :bar)
729
+ end
730
+
731
+ it 'initial capacity options validation' do
732
+ expect_valid_option(:initial_capacity, nil)
733
+ expect_valid_option(:initial_capacity, 1)
734
+ expect_invalid_option(:initial_capacity, '')
735
+ expect_invalid_option(:initial_capacity, 1.0)
736
+ expect_invalid_option(:initial_capacity, -1)
737
+ end
738
+
739
+ it 'load factor options validation' do
740
+ expect_valid_option(:load_factor, nil)
741
+ expect_valid_option(:load_factor, 0.01)
742
+ expect_valid_option(:load_factor, 0.75)
743
+ expect_valid_option(:load_factor, 1)
744
+ expect_invalid_option(:load_factor, '')
745
+ expect_invalid_option(:load_factor, 0)
746
+ expect_invalid_option(:load_factor, 1.1)
747
+ expect_invalid_option(:load_factor, 2)
748
+ expect_invalid_option(:load_factor, -1)
749
+ end
750
+
751
+ it '#size' do
752
+ expect(0).to eq @cache.size
753
+ @cache[:a] = 1
754
+ expect(1).to eq @cache.size
755
+ @cache[:b] = 1
756
+ expect(2).to eq @cache.size
757
+ @cache.delete(:a)
758
+ expect(1).to eq @cache.size
759
+ @cache.delete(:b)
760
+ expect(0).to eq @cache.size
761
+ end
762
+
763
+ it '#get_or_default' do
764
+ with_or_without_default_proc do
765
+ expect(1).to eq @cache.get_or_default(:a, 1)
766
+ expect(nil).to eq @cache.get_or_default(:a, nil)
767
+ expect(false).to eq @cache.get_or_default(:a, false)
768
+ expect(false).to eq @cache.key?(:a)
769
+
770
+ @cache[:a] = 1
771
+ expect(1).to eq @cache.get_or_default(:a, 2)
772
+ end
773
+ end
774
+
775
+ it '#dup,#clone' do
776
+ [:dup, :clone].each do |meth|
777
+ cache = cache_with_default_proc(:default_value)
778
+ cache[:a] = 1
779
+ dupped = cache.send(meth)
780
+ expect(1).to eq dupped[:a]
781
+ expect(1).to eq dupped.size
782
+ expect_size_change(1, cache) do
783
+ expect_no_size_change(dupped) do
784
+ cache[:b] = 1
785
+ end
786
+ end
787
+ expect(false).to eq dupped.key?(:b)
788
+ expect_no_size_change(cache) do
789
+ expect_size_change(-1, dupped) do
790
+ dupped.delete(:a)
791
+ end
792
+ end
793
+ expect(false).to eq dupped.key?(:a)
794
+ expect(true).to eq cache.key?(:a)
795
+ # test default proc
796
+ expect_size_change(1, cache) do
797
+ expect_no_size_change dupped do
798
+ expect(:default_value).to eq cache[:c]
799
+ expect(false).to eq dupped.key?(:c)
800
+ end
801
+ end
802
+ expect_no_size_change cache do
803
+ expect_size_change 1, dupped do
804
+ expect(:default_value).to eq dupped[:d]
805
+ expect(false).to eq cache.key?(:d)
806
+ end
807
+ end
808
+ end
809
+ end
810
+
811
+ it 'is unfreezable' do
812
+ expect { @cache.freeze }.to raise_error(NoMethodError)
813
+ end
814
+
815
+ it 'marshal dump load' do
816
+ new_cache = Marshal.load(Marshal.dump(@cache))
817
+ expect(new_cache).to be_an_instance_of ThreadSafe::Cache
818
+ expect(0).to eq new_cache.size
819
+ @cache[:a] = 1
820
+ new_cache = Marshal.load(Marshal.dump(@cache))
821
+ expect(1).to eq @cache[:a]
822
+ expect(1).to eq new_cache.size
823
+ end
824
+
825
+ it 'marshal dump doesnt work with default proc' do
826
+ expect { Marshal.dump(ThreadSafe::Cache.new {}) }.to raise_error(TypeError)
827
+ end
828
+
829
+ private
830
+
831
+ def with_or_without_default_proc(&block)
832
+ block.call(false)
833
+ @cache = ThreadSafe::Cache.new { |h, k| h[k] = :default_value }
834
+ block.call(true)
835
+ end
836
+
837
+ def cache_with_default_proc(default_value = 1)
838
+ ThreadSafe::Cache.new { |cache, k| cache[k] = default_value }
839
+ end
840
+
841
+ def expect_size_change(change, cache = @cache, &block)
842
+ start = cache.size
843
+ block.call
844
+ expect(change).to eq cache.size - start
845
+ end
846
+
847
+ def expect_valid_option(option_name, value)
848
+ expect_valid_options(option_name => value)
849
+ end
850
+
851
+ def expect_valid_options(options)
852
+ c = ThreadSafe::Cache.new(options)
853
+ expect(c).to be_an_instance_of ThreadSafe::Cache
854
+ end
855
+
856
+ def expect_invalid_option(option_name, value)
857
+ expect_invalid_options(option_name => value)
858
+ end
859
+
860
+ def expect_invalid_options(options)
861
+ expect { ThreadSafe::Cache.new(options) }.to raise_error(ArgumentError)
862
+ end
863
+
864
+ def expect_no_size_change(cache = @cache, &block)
865
+ expect_size_change(0, cache, &block)
866
+ end
867
+
868
+ def expect_handles_return_lambda(method, key, *args)
869
+ before_had_key = @cache.key?(key)
870
+ before_had_value = before_had_key ? @cache[key] : nil
871
+
872
+ returning_lambda = lambda do
873
+ @cache.send(method, key, *args) { return :direct_return }
874
+ end
875
+
876
+ expect_no_size_change do
877
+ expect(:direct_return).to eq returning_lambda.call
878
+ expect(before_had_key).to eq @cache.key?(key)
879
+ expect(before_had_value).to eq @cache[key] if before_had_value
880
+ end
881
+ end
882
+
883
+ class TestException < Exception; end
884
+ def expect_handles_exception(method, key, *args)
885
+ before_had_key = @cache.key?(key)
886
+ before_had_value = before_had_key ? @cache[key] : nil
887
+
888
+ expect_no_size_change do
889
+ expect { @cache.send(method, key, *args) { raise TestException, '' } }.
890
+ to raise_error(TestException)
891
+
892
+ expect(before_had_key).to eq @cache.key?(key)
893
+ expect(before_had_value).to eq @cache[key] if before_had_value
894
+ end
895
+ end
896
+
897
+ def expect_compute(key, expected_old_value, expected_result, &block)
898
+ result = @cache.compute(:a) do |old_value|
899
+ expect(expected_old_value).to eq old_value
900
+ block.call
901
+ end
902
+ expect(expected_result).to eq result
903
+ end
904
+
905
+ def expect_merge_pair(key, value, expected_old_value, expected_result, &block)
906
+ result = @cache.merge_pair(key, value) do |old_value|
907
+ expect(expected_old_value).to eq old_value
908
+ block.call
909
+ end
910
+ expect(expected_result).to eq result
911
+ end
912
+
913
+ def expect_collision_resistance(keys)
914
+ keys.each { |k| @cache[k] = k.key }
915
+ 10.times do |i|
916
+ size = keys.size
917
+ while i < size
918
+ k = keys[i]
919
+ expect(k.key == @cache.delete(k) &&
920
+ !@cache.key?(k) &&
921
+ (@cache[k] = k.key; @cache[k] == k.key)).to be_truthy
922
+ i += 10
923
+ end
924
+ end
925
+ expect(keys.all? { |k| @cache[k] == k.key }).to be_truthy
926
+ end
927
+
928
+ # Took out for compatibility with Rubinius, see https://github.com/rubinius/rubinius/issues/1312
929
+ def fetch_with_return
930
+ lambda do
931
+ @cache.fetch(:a) { return 10 }
932
+ end.call
933
+ end
934
+
935
+ # Took out for compatibility with Rubinius, see https://github.com/rubinius/rubinius/issues/1312
936
+ def fetch_or_store_with_return
937
+ lambda do
938
+ @cache.fetch_or_store(:a) { return 10 }
939
+ end.call
940
+ end
941
+
942
+ end
943
+ end