thread_safe 0.3.5-java → 0.3.6-java

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.
@@ -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