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,17 @@
1
+ module ThreadSafe
2
+ describe Hash do
3
+ let!(:hsh) { described_class.new }
4
+
5
+ it 'concurrency' do
6
+ (1..THREADS).map do |i|
7
+ Thread.new do
8
+ 1000.times do |j|
9
+ hsh[i * 1000 + j] = i
10
+ hsh[i * 1000 + j]
11
+ hsh.delete(i * 1000 + j)
12
+ end
13
+ end
14
+ end.map(&:join)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ if defined?(JRUBY_VERSION) && ENV['TEST_NO_UNSAFE']
2
+ # to be used like this: rake test TEST_NO_UNSAFE=true
3
+ load 'test/package.jar'
4
+ java_import 'thread_safe.SecurityManager'
5
+ manager = SecurityManager.new
6
+
7
+ # Prevent accessing internal classes
8
+ manager.deny(java.lang.RuntimePermission.new('accessClassInPackage.sun.misc'))
9
+ java.lang.System.setSecurityManager(manager)
10
+
11
+ module ThreadSafe
12
+ describe 'no_unsafe' do
13
+ it 'security_manager_is_used' do
14
+ begin
15
+ java_import 'sun.misc.Unsafe'
16
+ fail
17
+ rescue SecurityError
18
+ end
19
+ end
20
+
21
+ it 'no_unsafe_version_of_chmv8_is_used' do
22
+ require 'thread_safe/jruby_cache_backend' # make sure the jar has been loaded
23
+ expect(!Java::OrgJrubyExtThread_safe::JRubyCacheBackendLibrary::JRubyCacheBackend::CAN_USE_UNSAFE_CHM).to be_truthy
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,85 @@
1
+ require 'thread_safe/synchronized_delegator.rb'
2
+
3
+ module ThreadSafe
4
+ describe SynchronizedDelegator do
5
+ it 'wraps array' do
6
+ array = ::Array.new
7
+ sync_array = described_class.new(array)
8
+
9
+ array << 1
10
+ expect(1).to eq sync_array[0]
11
+
12
+ sync_array << 2
13
+ expect(2).to eq array[1]
14
+ end
15
+
16
+ it 'synchronizes access' do
17
+ t1_continue, t2_continue = false, false
18
+
19
+ hash = ::Hash.new do |hash, key|
20
+ t2_continue = true
21
+ unless hash.find { |e| e[1] == key.to_s } # just to do something
22
+ hash[key] = key.to_s
23
+ Thread.pass until t1_continue
24
+ end
25
+ end
26
+ sync_hash = described_class.new(hash)
27
+ sync_hash[1] = 'egy'
28
+
29
+ t1 = Thread.new do
30
+ sync_hash[2] = 'dva'
31
+ sync_hash[3] # triggers t2_continue
32
+ end
33
+
34
+ t2 = Thread.new do
35
+ Thread.pass until t2_continue
36
+ sync_hash[4] = '42'
37
+ end
38
+
39
+ sleep(0.05) # sleep some to allow threads to boot
40
+
41
+ until t2.status == 'sleep' do
42
+ Thread.pass
43
+ end
44
+
45
+ expect(3).to eq hash.keys.size
46
+
47
+ t1_continue = true
48
+ t1.join; t2.join
49
+
50
+ expect(4).to eq sync_hash.size
51
+ end
52
+
53
+ it 'synchronizes access with block' do
54
+ t1_continue, t2_continue = false, false
55
+
56
+ array = ::Array.new
57
+ sync_array = described_class.new(array)
58
+
59
+ t1 = Thread.new do
60
+ sync_array << 1
61
+ sync_array.each do
62
+ t2_continue = true
63
+ Thread.pass until t1_continue
64
+ end
65
+ end
66
+
67
+ t2 = Thread.new do
68
+ # sleep(0.01)
69
+ Thread.pass until t2_continue
70
+ sync_array << 2
71
+ end
72
+
73
+ until t2.status == 'sleep' || t2.status == false
74
+ Thread.pass
75
+ end
76
+
77
+ expect(1).to eq array.size
78
+
79
+ t1_continue = true
80
+ t1.join; t2.join
81
+
82
+ expect([1, 2]).to eq array
83
+ end
84
+ end
85
+ end
@@ -5,8 +5,8 @@ require 'thread_safe/version'
5
5
  Gem::Specification.new do |gem|
6
6
  gem.authors = ["Charles Oliver Nutter", "thedarkone"]
7
7
  gem.email = ["headius@headius.com", "thedarkone2@gmail.com"]
8
- gem.description = %q{Thread-safe collections and utilities for Ruby}
9
- gem.summary = %q{A collection of data structures and utilities to make thread-safe programming in Ruby easier}
8
+ gem.summary = %q{Thread-safe collections and utilities for Ruby}
9
+ gem.description = %q{A collection of data structures and utilities to make thread-safe programming in Ruby easier}
10
10
  gem.homepage = "https://github.com/ruby-concurrency/thread_safe"
11
11
 
12
12
  gem.files = `git ls-files`.split($\)
@@ -21,6 +21,6 @@ Gem::Specification.new do |gem|
21
21
  gem.license = "Apache-2.0"
22
22
 
23
23
  gem.add_development_dependency 'atomic', '= 1.1.16'
24
- gem.add_development_dependency 'rake'
25
- gem.add_development_dependency 'minitest', '>= 4'
24
+ gem.add_development_dependency 'rake', '< 12.0'
25
+ gem.add_development_dependency 'rspec', '~> 3.2'
26
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thread_safe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  platform: java
6
6
  authors:
7
7
  - Charles Oliver Nutter
@@ -9,51 +9,51 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-03-11 00:00:00.000000000 Z
12
+ date: 2017-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: atomic
16
- version_requirements: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - '='
19
- - !ruby/object:Gem::Version
20
- version: 1.1.16
21
15
  requirement: !ruby/object:Gem::Requirement
22
16
  requirements:
23
17
  - - '='
24
18
  - !ruby/object:Gem::Version
25
19
  version: 1.1.16
20
+ name: atomic
26
21
  prerelease: false
27
22
  type: :development
28
- - !ruby/object:Gem::Dependency
29
- name: rake
30
23
  version_requirements: !ruby/object:Gem::Requirement
31
24
  requirements:
32
- - - '>='
25
+ - - '='
33
26
  - !ruby/object:Gem::Version
34
- version: '0'
27
+ version: 1.1.16
28
+ - !ruby/object:Gem::Dependency
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - '>='
31
+ - - "<"
38
32
  - !ruby/object:Gem::Version
39
- version: '0'
33
+ version: '12.0'
34
+ name: rake
40
35
  prerelease: false
41
36
  type: :development
42
- - !ruby/object:Gem::Dependency
43
- name: minitest
44
37
  version_requirements: !ruby/object:Gem::Requirement
45
38
  requirements:
46
- - - '>='
39
+ - - "<"
47
40
  - !ruby/object:Gem::Version
48
- version: '4'
41
+ version: '12.0'
42
+ - !ruby/object:Gem::Dependency
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
- - - '>='
45
+ - - "~>"
52
46
  - !ruby/object:Gem::Version
53
- version: '4'
47
+ version: '3.2'
48
+ name: rspec
54
49
  prerelease: false
55
50
  type: :development
56
- description: Thread-safe collections and utilities for Ruby
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.2'
56
+ description: A collection of data structures and utilities to make thread-safe programming in Ruby easier
57
57
  email:
58
58
  - headius@headius.com
59
59
  - thedarkone2@gmail.com
@@ -61,8 +61,9 @@ executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
63
63
  files:
64
- - .travis.yml
65
- - .yardopts
64
+ - ".rspec"
65
+ - ".travis.yml"
66
+ - ".yardopts"
66
67
  - Gemfile
67
68
  - LICENSE
68
69
  - README.md
@@ -96,14 +97,20 @@ files:
96
97
  - lib/thread_safe/util/volatile_tuple.rb
97
98
  - lib/thread_safe/util/xor_shift_random.rb
98
99
  - lib/thread_safe/version.rb
100
+ - spec/.gitignore
101
+ - spec/spec_helper.rb
102
+ - spec/src/thread_safe/SecurityManager.java
103
+ - spec/support/.gitignore
104
+ - spec/support/threads.rb
105
+ - spec/support/threadsafe_test.rb
106
+ - spec/thread_safe/.gitignore
107
+ - spec/thread_safe/array_spec.rb
108
+ - spec/thread_safe/cache_loops_spec.rb
109
+ - spec/thread_safe/cache_spec.rb
110
+ - spec/thread_safe/hash_spec.rb
111
+ - spec/thread_safe/no_unsafe_spec.rb
112
+ - spec/thread_safe/synchronized_delegator_spec.rb
99
113
  - tasks/update_doc.rake
100
- - test/src/thread_safe/SecurityManager.java
101
- - test/test_array.rb
102
- - test/test_cache.rb
103
- - test/test_cache_loops.rb
104
- - test/test_hash.rb
105
- - test/test_helper.rb
106
- - test/test_synchronized_delegator.rb
107
114
  - thread_safe.gemspec
108
115
  - yard-template/default/fulldoc/html/css/common.css
109
116
  - yard-template/default/layout/html/footer.erb
@@ -117,26 +124,32 @@ require_paths:
117
124
  - lib
118
125
  required_ruby_version: !ruby/object:Gem::Requirement
119
126
  requirements:
120
- - - '>='
127
+ - - ">="
121
128
  - !ruby/object:Gem::Version
122
129
  version: '0'
123
130
  required_rubygems_version: !ruby/object:Gem::Requirement
124
131
  requirements:
125
- - - '>='
132
+ - - ">="
126
133
  - !ruby/object:Gem::Version
127
134
  version: '0'
128
135
  requirements: []
129
136
  rubyforge_project:
130
- rubygems_version: 2.4.5
137
+ rubygems_version: 2.6.8
131
138
  signing_key:
132
139
  specification_version: 4
133
- summary: A collection of data structures and utilities to make thread-safe programming in Ruby easier
140
+ summary: Thread-safe collections and utilities for Ruby
134
141
  test_files:
135
- - test/src/thread_safe/SecurityManager.java
136
- - test/test_array.rb
137
- - test/test_cache.rb
138
- - test/test_cache_loops.rb
139
- - test/test_hash.rb
140
- - test/test_helper.rb
141
- - test/test_synchronized_delegator.rb
142
+ - spec/.gitignore
143
+ - spec/spec_helper.rb
144
+ - spec/src/thread_safe/SecurityManager.java
145
+ - spec/support/.gitignore
146
+ - spec/support/threads.rb
147
+ - spec/support/threadsafe_test.rb
148
+ - spec/thread_safe/.gitignore
149
+ - spec/thread_safe/array_spec.rb
150
+ - spec/thread_safe/cache_loops_spec.rb
151
+ - spec/thread_safe/cache_spec.rb
152
+ - spec/thread_safe/hash_spec.rb
153
+ - spec/thread_safe/no_unsafe_spec.rb
154
+ - spec/thread_safe/synchronized_delegator_spec.rb
142
155
  has_rdoc:
@@ -1,18 +0,0 @@
1
- require 'thread_safe'
2
- require File.join(File.dirname(__FILE__), "test_helper")
3
-
4
- class TestArray < Minitest::Test
5
- def test_concurrency
6
- ary = ThreadSafe::Array.new
7
- (1..THREADS).map do |i|
8
- Thread.new do
9
- 1000.times do
10
- ary << i
11
- ary.each {|x| x * 2}
12
- ary.shift
13
- ary.last
14
- end
15
- end
16
- end.map(&:join)
17
- end
18
- end
@@ -1,901 +0,0 @@
1
- require 'thread_safe'
2
- require 'thread'
3
- require File.join(File.dirname(__FILE__), "test_helper")
4
-
5
- Thread.abort_on_exception = true
6
-
7
- class TestCache < Minitest::Test
8
- def setup
9
- @cache = ThreadSafe::Cache.new
10
- end
11
-
12
- def test_concurrency
13
- cache = @cache
14
- (1..THREADS).map do |i|
15
- Thread.new do
16
- 1000.times do |j|
17
- key = i*1000+j
18
- cache[key] = i
19
- cache[key]
20
- cache.delete(key)
21
- end
22
- end
23
- end.map(&:join)
24
- end
25
-
26
- def test_retrieval
27
- assert_size_change 1 do
28
- assert_equal nil, @cache[:a]
29
- assert_equal nil, @cache.get(:a)
30
- @cache[:a] = 1
31
- assert_equal 1, @cache[:a]
32
- assert_equal 1, @cache.get(:a)
33
- end
34
- end
35
-
36
- def test_put_if_absent
37
- with_or_without_default_proc do
38
- assert_size_change 1 do
39
- assert_equal nil, @cache.put_if_absent(:a, 1)
40
- assert_equal 1, @cache.put_if_absent(:a, 1)
41
- assert_equal 1, @cache.put_if_absent(:a, 2)
42
- assert_equal 1, @cache[:a]
43
- end
44
- end
45
- end
46
-
47
- def test_compute_if_absent
48
- with_or_without_default_proc do
49
- assert_size_change 3 do
50
- assert_equal(1, (@cache.compute_if_absent(:a) {1}))
51
- assert_equal(1, (@cache.compute_if_absent(:a) {2}))
52
- assert_equal 1, @cache[:a]
53
- @cache[:b] = nil
54
- assert_equal(nil, (@cache.compute_if_absent(:b) {1}))
55
- assert_equal(nil, (@cache.compute_if_absent(:c) {}))
56
- assert_equal nil, @cache[:c]
57
- assert_equal true, @cache.key?(:c)
58
- end
59
- end
60
- end
61
-
62
- def test_compute_if_absent_with_return
63
- with_or_without_default_proc { assert_handles_return_lambda(:compute_if_absent, :a) }
64
- end
65
-
66
- def test_compute_if_absent_exception
67
- with_or_without_default_proc { assert_handles_exception(:compute_if_absent, :a) }
68
- end
69
-
70
- def test_compute_if_absent_atomicity
71
- late_compute_threads_count = 10
72
- late_put_if_absent_threads_count = 10
73
- getter_threads_count = 5
74
- compute_started = ThreadSafe::Test::Latch.new(1)
75
- compute_proceed = ThreadSafe::Test::Latch.new(late_compute_threads_count + late_put_if_absent_threads_count + getter_threads_count)
76
- block_until_compute_started = lambda do |name|
77
- if (v = @cache[:a]) != nil
78
- assert_equal nil, v
79
- end
80
- compute_proceed.release
81
- compute_started.await
82
- end
83
-
84
- assert_size_change 1 do
85
- late_compute_threads = Array.new(late_compute_threads_count) do
86
- Thread.new do
87
- block_until_compute_started.call('compute_if_absent')
88
- assert_equal(1, (@cache.compute_if_absent(:a) { flunk }))
89
- end
90
- end
91
-
92
- late_put_if_absent_threads = Array.new(late_put_if_absent_threads_count) do
93
- Thread.new do
94
- block_until_compute_started.call('put_if_absent')
95
- assert_equal(1, @cache.put_if_absent(:a, 2))
96
- end
97
- end
98
-
99
- getter_threads = Array.new(getter_threads_count) do
100
- Thread.new do
101
- block_until_compute_started.call('getter')
102
- Thread.pass while @cache[:a].nil?
103
- assert_equal 1, @cache[:a]
104
- end
105
- end
106
-
107
- Thread.new do
108
- @cache.compute_if_absent(:a) do
109
- compute_started.release
110
- compute_proceed.await
111
- sleep(0.2)
112
- 1
113
- end
114
- end.join
115
- (late_compute_threads + late_put_if_absent_threads + getter_threads).each(&:join)
116
- end
117
- end
118
-
119
- def test_compute_if_present
120
- with_or_without_default_proc do
121
- assert_no_size_change do
122
- assert_equal(nil, @cache.compute_if_present(:a) {})
123
- assert_equal(nil, @cache.compute_if_present(:a) {1})
124
- assert_equal(nil, @cache.compute_if_present(:a) {flunk})
125
- assert_equal false, @cache.key?(:a)
126
- end
127
-
128
- @cache[:a] = 1
129
- assert_no_size_change do
130
- assert_equal(1, @cache.compute_if_present(:a) {1})
131
- assert_equal(1, @cache[:a])
132
- assert_equal(2, @cache.compute_if_present(:a) {2})
133
- assert_equal(2, @cache[:a])
134
- assert_equal(false, @cache.compute_if_present(:a) {false})
135
- assert_equal(false, @cache[:a])
136
-
137
- @cache[:a] = 1
138
- yielded = false
139
- @cache.compute_if_present(:a) do |old_value|
140
- yielded = true
141
- assert_equal 1, old_value
142
- 2
143
- end
144
- assert yielded
145
- end
146
-
147
- assert_size_change -1 do
148
- assert_equal(nil, @cache.compute_if_present(:a) {})
149
- assert_equal(false, @cache.key?(:a))
150
- assert_equal(nil, @cache.compute_if_present(:a) {1})
151
- assert_equal(false, @cache.key?(:a))
152
- end
153
- end
154
- end
155
-
156
- def test_compute_if_present_with_return
157
- with_or_without_default_proc do
158
- @cache[:a] = 1
159
- assert_handles_return_lambda(:compute_if_present, :a)
160
- end
161
- end
162
-
163
- def test_compute_if_present_exception
164
- with_or_without_default_proc do
165
- @cache[:a] = 1
166
- assert_handles_exception(:compute_if_present, :a)
167
- end
168
- end
169
-
170
- def test_compute
171
- with_or_without_default_proc do
172
- assert_no_size_change do
173
- assert_compute(:a, nil, nil) {}
174
- end
175
-
176
- assert_size_change 1 do
177
- assert_compute(:a, nil, 1) {1}
178
- assert_compute(:a, 1, 2) {2}
179
- assert_compute(:a, 2, false) {false}
180
- assert_equal false, @cache[:a]
181
- end
182
-
183
- assert_size_change -1 do
184
- assert_compute(:a, false, nil) {}
185
- end
186
- end
187
- end
188
-
189
- def test_compute_with_return
190
- with_or_without_default_proc do
191
- assert_handles_return_lambda(:compute, :a)
192
- @cache[:a] = 1
193
- assert_handles_return_lambda(:compute, :a)
194
- end
195
- end
196
-
197
- def test_compute_exception
198
- with_or_without_default_proc do
199
- assert_handles_exception(:compute, :a)
200
- @cache[:a] = 1
201
- assert_handles_exception(:compute, :a)
202
- end
203
- end
204
-
205
- def test_merge_pair
206
- with_or_without_default_proc do
207
- assert_size_change 1 do
208
- assert_equal(nil, @cache.merge_pair(:a, nil) {flunk})
209
- assert_equal true, @cache.key?(:a)
210
- assert_equal nil, @cache[:a]
211
- end
212
-
213
- assert_no_size_change do
214
- assert_merge_pair(:a, nil, nil, false) {false}
215
- assert_merge_pair(:a, nil, false, 1) {1}
216
- assert_merge_pair(:a, nil, 1, 2) {2}
217
- end
218
-
219
- assert_size_change -1 do
220
- assert_merge_pair(:a, nil, 2, nil) {}
221
- assert_equal false, @cache.key?(:a)
222
- end
223
- end
224
- end
225
-
226
- def test_merge_pair_with_return
227
- with_or_without_default_proc do
228
- @cache[:a] = 1
229
- assert_handles_return_lambda(:merge_pair, :a, 2)
230
- end
231
- end
232
-
233
- def test_merge_pair_exception
234
- with_or_without_default_proc do
235
- @cache[:a] = 1
236
- assert_handles_exception(:merge_pair, :a, 2)
237
- end
238
- end
239
-
240
- def test_updates_dont_block_reads
241
- getters_count = 20
242
- key_klass = ThreadSafe::Test::HashCollisionKey
243
- keys = [key_klass.new(1, 100), key_klass.new(2, 100), key_klass.new(3, 100)] # hash colliding keys
244
- inserted_keys = []
245
-
246
- keys.each do |key, i|
247
- compute_started = ThreadSafe::Test::Latch.new(1)
248
- compute_finished = ThreadSafe::Test::Latch.new(1)
249
- getters_started = ThreadSafe::Test::Latch.new(getters_count)
250
- getters_finished = ThreadSafe::Test::Latch.new(getters_count)
251
-
252
- computer_thread = Thread.new do
253
- getters_started.await
254
- @cache.compute_if_absent(key) do
255
- compute_started.release
256
- getters_finished.await
257
- 1
258
- end
259
- compute_finished.release
260
- end
261
-
262
- getter_threads = (1..getters_count).map do
263
- Thread.new do
264
- getters_started.release
265
- inserted_keys.each do |inserted_key|
266
- assert_equal true, @cache.key?(inserted_key)
267
- assert_equal 1, @cache[inserted_key]
268
- end
269
- assert_equal false, @cache.key?(key)
270
- compute_started.await
271
- inserted_keys.each do |inserted_key|
272
- assert_equal true, @cache.key?(inserted_key)
273
- assert_equal 1, @cache[inserted_key]
274
- end
275
- assert_equal false, @cache.key?(key)
276
- assert_equal nil, @cache[key]
277
- getters_finished.release
278
- compute_finished.await
279
- assert_equal true, @cache.key?(key)
280
- assert_equal 1, @cache[key]
281
- end
282
- end
283
-
284
- (getter_threads << computer_thread).map {|t| assert(t.join(2))} # asserting no deadlocks
285
- inserted_keys << key
286
- end
287
- end
288
-
289
- def test_collision_resistance
290
- assert_collision_resistance((0..1000).map {|i| ThreadSafe::Test::HashCollisionKey(i, 1)})
291
- end
292
-
293
- def test_collision_resistance_with_arrays
294
- special_array_class = Class.new(Array) do
295
- def key # assert_collision_resistance expects to be able to call .key to get the "real" key
296
- first.key
297
- end
298
- end
299
- # Test collision resistance with a keys that say they responds_to <=>, but then raise exceptions
300
- # when actually called (ie: an Array filled with non-comparable keys).
301
- # See https://github.com/headius/thread_safe/issues/19 for more info.
302
- assert_collision_resistance((0..100).map do |i|
303
- special_array_class.new([ThreadSafe::Test::HashCollisionKeyNonComparable.new(i, 1)])
304
- end)
305
- end
306
-
307
- def test_replace_pair
308
- with_or_without_default_proc do
309
- assert_no_size_change do
310
- assert_equal false, @cache.replace_pair(:a, 1, 2)
311
- assert_equal false, @cache.replace_pair(:a, nil, nil)
312
- assert_equal false, @cache.key?(:a)
313
- end
314
-
315
- @cache[:a] = 1
316
- assert_no_size_change do
317
- assert_equal true, @cache.replace_pair(:a, 1, 2)
318
- assert_equal false, @cache.replace_pair(:a, 1, 2)
319
- assert_equal 2, @cache[:a]
320
- assert_equal true, @cache.replace_pair(:a, 2, 2)
321
- assert_equal 2, @cache[:a]
322
- assert_equal true, @cache.replace_pair(:a, 2, nil)
323
- assert_equal false, @cache.replace_pair(:a, 2, nil)
324
- assert_equal nil, @cache[:a]
325
- assert_equal true, @cache.key?(:a)
326
- assert_equal true, @cache.replace_pair(:a, nil, nil)
327
- assert_equal true, @cache.key?(:a)
328
- assert_equal true, @cache.replace_pair(:a, nil, 1)
329
- assert_equal 1, @cache[:a]
330
- end
331
- end
332
- end
333
-
334
- def test_replace_if_exists
335
- with_or_without_default_proc do
336
- assert_no_size_change do
337
- assert_equal nil, @cache.replace_if_exists(:a, 1)
338
- assert_equal false, @cache.key?(:a)
339
- end
340
-
341
- @cache[:a] = 1
342
- assert_no_size_change do
343
- assert_equal 1, @cache.replace_if_exists(:a, 2)
344
- assert_equal 2, @cache[:a]
345
- assert_equal 2, @cache.replace_if_exists(:a, nil)
346
- assert_equal nil, @cache[:a]
347
- assert_equal true, @cache.key?(:a)
348
- assert_equal nil, @cache.replace_if_exists(:a, 1)
349
- assert_equal 1, @cache[:a]
350
- end
351
- end
352
- end
353
-
354
- def test_get_and_set
355
- with_or_without_default_proc do
356
- assert_size_change 1 do
357
- assert_equal nil, @cache.get_and_set(:a, 1)
358
- assert_equal true, @cache.key?(:a)
359
- assert_equal 1, @cache[:a]
360
- assert_equal 1, @cache.get_and_set(:a, 2)
361
- assert_equal 2, @cache.get_and_set(:a, nil)
362
- assert_equal nil, @cache[:a]
363
- assert_equal true, @cache.key?(:a)
364
- assert_equal nil, @cache.get_and_set(:a, 1)
365
- assert_equal 1, @cache[:a]
366
- end
367
- end
368
- end
369
-
370
- def test_key
371
- with_or_without_default_proc do
372
- assert_equal nil, @cache.key(1)
373
- @cache[:a] = 1
374
- assert_equal :a, @cache.key(1)
375
- assert_equal nil, @cache.key(0)
376
- assert_equal :a, @cache.index(1) if RUBY_VERSION =~ /1\.8/
377
- end
378
- end
379
-
380
- def test_key?
381
- with_or_without_default_proc do
382
- assert_equal false, @cache.key?(:a)
383
- @cache[:a] = 1
384
- assert_equal true, @cache.key?(:a)
385
- end
386
- end
387
-
388
- def test_value?
389
- with_or_without_default_proc do
390
- assert_equal false, @cache.value?(1)
391
- @cache[:a] = 1
392
- assert_equal true, @cache.value?(1)
393
- end
394
- end
395
-
396
- def test_delete
397
- with_or_without_default_proc do |default_proc_set|
398
- assert_no_size_change do
399
- assert_equal nil, @cache.delete(:a)
400
- end
401
- @cache[:a] = 1
402
- assert_size_change -1 do
403
- assert_equal 1, @cache.delete(:a)
404
- end
405
- assert_no_size_change do
406
- assert_equal nil, @cache[:a] unless default_proc_set
407
-
408
- assert_equal false, @cache.key?(:a)
409
- assert_equal nil, @cache.delete(:a)
410
- end
411
- end
412
- end
413
-
414
- def test_delete_pair
415
- with_or_without_default_proc do
416
- assert_no_size_change do
417
- assert_equal false, @cache.delete_pair(:a, 2)
418
- assert_equal false, @cache.delete_pair(:a, nil)
419
- end
420
- @cache[:a] = 1
421
- assert_no_size_change do
422
- assert_equal false, @cache.delete_pair(:a, 2)
423
- end
424
- assert_size_change -1 do
425
- assert_equal 1, @cache[:a]
426
- assert_equal true, @cache.delete_pair(:a, 1)
427
- assert_equal false, @cache.delete_pair(:a, 1)
428
- assert_equal false, @cache.key?(:a)
429
- end
430
- end
431
- end
432
-
433
- def test_default_proc
434
- @cache = cache_with_default_proc(1)
435
- assert_no_size_change do
436
- assert_equal false, @cache.key?(:a)
437
- end
438
- assert_size_change 1 do
439
- assert_equal 1, @cache[:a]
440
- assert_equal true, @cache.key?(:a)
441
- end
442
- end
443
-
444
- def test_falsy_default_proc
445
- @cache = cache_with_default_proc(nil)
446
- assert_no_size_change do
447
- assert_equal false, @cache.key?(:a)
448
- end
449
- assert_size_change 1 do
450
- assert_equal nil, @cache[:a]
451
- assert_equal true, @cache.key?(:a)
452
- end
453
- end
454
-
455
- def test_fetch
456
- with_or_without_default_proc do |default_proc_set|
457
- assert_no_size_change do
458
- assert_equal 1, @cache.fetch(:a, 1)
459
- assert_equal(1, (@cache.fetch(:a) {1}))
460
- assert_equal false, @cache.key?(:a)
461
-
462
- assert_equal nil, @cache[:a] unless default_proc_set
463
- end
464
-
465
- @cache[:a] = 1
466
- assert_no_size_change do
467
- assert_equal(1, (@cache.fetch(:a) {flunk}))
468
- end
469
-
470
- assert_raises(ThreadSafe::Cache::KEY_ERROR) do
471
- @cache.fetch(:b)
472
- end
473
-
474
- assert_no_size_change do
475
- assert_equal 1, (@cache.fetch(:b, :c) {1}) # assert block supersedes default value argument
476
- assert_equal false, @cache.key?(:b)
477
- end
478
- end
479
- end
480
-
481
- def test_falsy_fetch
482
- with_or_without_default_proc do
483
- assert_equal false, @cache.key?(:a)
484
-
485
- assert_no_size_change do
486
- assert_equal(nil, @cache.fetch(:a, nil))
487
- assert_equal(false, @cache.fetch(:a, false))
488
- assert_equal(nil, (@cache.fetch(:a) {}))
489
- assert_equal(false, (@cache.fetch(:a) {false}))
490
- end
491
-
492
- @cache[:a] = nil
493
- assert_no_size_change do
494
- assert_equal true, @cache.key?(:a)
495
- assert_equal(nil, (@cache.fetch(:a) {flunk}))
496
- end
497
- end
498
- end
499
-
500
- def test_fetch_with_return
501
- with_or_without_default_proc do
502
- r = lambda do
503
- @cache.fetch(:a) { return 10 }
504
- end.call
505
-
506
- assert_no_size_change do
507
- assert_equal 10, r
508
- assert_equal false, @cache.key?(:a)
509
- end
510
- end
511
- end
512
-
513
- def test_fetch_or_store
514
- with_or_without_default_proc do |default_proc_set|
515
- assert_size_change 1 do
516
- assert_equal 1, @cache.fetch_or_store(:a, 1)
517
- assert_equal 1, @cache[:a]
518
- end
519
-
520
- @cache.delete(:a)
521
-
522
- assert_size_change 1 do
523
- assert_equal 1, (@cache.fetch_or_store(:a) {1})
524
- assert_equal 1, @cache[:a]
525
- end
526
-
527
- assert_no_size_change do
528
- assert_equal(1, (@cache.fetch_or_store(:a) {flunk}))
529
- end
530
-
531
- assert_raises(ThreadSafe::Cache::KEY_ERROR) do
532
- @cache.fetch_or_store(:b)
533
- end
534
-
535
- assert_size_change 1 do
536
- assert_equal 1, (@cache.fetch_or_store(:b, :c) {1}) # assert block supersedes default value argument
537
- assert_equal 1, @cache[:b]
538
- end
539
- end
540
- end
541
-
542
- def test_falsy_fetch_or_store
543
- with_or_without_default_proc do
544
- assert_equal false, @cache.key?(:a)
545
-
546
- assert_size_change 1 do
547
- assert_equal(nil, @cache.fetch_or_store(:a, nil))
548
- assert_equal nil, @cache[:a]
549
- assert_equal true, @cache.key?(:a)
550
- end
551
- @cache.delete(:a)
552
-
553
- assert_size_change 1 do
554
- assert_equal(false, @cache.fetch_or_store(:a, false))
555
- assert_equal false, @cache[:a]
556
- assert_equal true, @cache.key?(:a)
557
- end
558
- @cache.delete(:a)
559
-
560
- assert_size_change 1 do
561
- assert_equal(nil, (@cache.fetch_or_store(:a) {}))
562
- assert_equal nil, @cache[:a]
563
- assert_equal true, @cache.key?(:a)
564
- end
565
- @cache.delete(:a)
566
-
567
- assert_size_change 1 do
568
- assert_equal(false, (@cache.fetch_or_store(:a) {false}))
569
- assert_equal false, @cache[:a]
570
- assert_equal true, @cache.key?(:a)
571
- end
572
-
573
- @cache[:a] = nil
574
- assert_no_size_change do
575
- assert_equal(nil, (@cache.fetch_or_store(:a) {flunk}))
576
- end
577
- end
578
- end
579
-
580
- def test_fetch_or_store_with_return
581
- with_or_without_default_proc do
582
- r = lambda do
583
- @cache.fetch_or_store(:a) { return 10 }
584
- end.call
585
-
586
- assert_no_size_change do
587
- assert_equal 10, r
588
- assert_equal false, @cache.key?(:a)
589
- end
590
- end
591
- end
592
-
593
- def test_clear
594
- @cache[:a] = 1
595
- assert_size_change -1 do
596
- assert_equal @cache, @cache.clear
597
- assert_equal false, @cache.key?(:a)
598
- assert_equal nil, @cache[:a]
599
- end
600
- end
601
-
602
- def test_each_pair
603
- @cache.each_pair {|k, v| flunk}
604
- assert_equal(@cache, (@cache.each_pair {}))
605
- @cache[:a] = 1
606
-
607
- h = {}
608
- @cache.each_pair {|k, v| h[k] = v}
609
- assert_equal({:a => 1}, h)
610
-
611
- @cache[:b] = 2
612
- h = {}
613
- @cache.each_pair {|k, v| h[k] = v}
614
- assert_equal({:a => 1, :b => 2}, h)
615
- end
616
-
617
- def test_each_pair_iterator
618
- @cache[:a] = 1
619
- @cache[:b] = 2
620
- i = 0
621
- r = @cache.each_pair do |k, v|
622
- if i == 0
623
- i += 1
624
- next
625
- flunk
626
- elsif i == 1
627
- break :breaked
628
- end
629
- end
630
-
631
- assert_equal :breaked, r
632
- end
633
-
634
- def test_each_pair_allows_modification
635
- @cache[:a] = 1
636
- @cache[:b] = 1
637
- @cache[:c] = 1
638
-
639
- assert_size_change 1 do
640
- @cache.each_pair do |k, v|
641
- @cache[:z] = 1
642
- end
643
- end
644
- end
645
-
646
- def test_keys
647
- assert_equal [], @cache.keys
648
-
649
- @cache[1] = 1
650
- assert_equal [1], @cache.keys
651
-
652
- @cache[2] = 2
653
- assert_equal [1, 2], @cache.keys.sort
654
- end
655
-
656
- def test_values
657
- assert_equal [], @cache.values
658
-
659
- @cache[1] = 1
660
- assert_equal [1], @cache.values
661
-
662
- @cache[2] = 2
663
- assert_equal [1, 2], @cache.values.sort
664
- end
665
-
666
- def test_each_key
667
- assert_equal(@cache, (@cache.each_key {flunk}))
668
-
669
- @cache[1] = 1
670
- arr = []
671
- @cache.each_key {|k| arr << k}
672
- assert_equal [1], arr
673
-
674
- @cache[2] = 2
675
- arr = []
676
- @cache.each_key {|k| arr << k}
677
- assert_equal [1, 2], arr.sort
678
- end
679
-
680
- def test_each_value
681
- assert_equal(@cache, (@cache.each_value {flunk}))
682
-
683
- @cache[1] = 1
684
- arr = []
685
- @cache.each_value {|k| arr << k}
686
- assert_equal [1], arr
687
-
688
- @cache[2] = 2
689
- arr = []
690
- @cache.each_value {|k| arr << k}
691
- assert_equal [1, 2], arr.sort
692
- end
693
-
694
- def test_empty
695
- assert_equal true, @cache.empty?
696
- @cache[:a] = 1
697
- assert_equal false, @cache.empty?
698
- end
699
-
700
- def test_options_validation
701
- assert_valid_options(nil)
702
- assert_valid_options({})
703
- assert_valid_options(:foo => :bar)
704
- end
705
-
706
- def test_initial_capacity_options_validation
707
- assert_valid_option(:initial_capacity, nil)
708
- assert_valid_option(:initial_capacity, 1)
709
- assert_invalid_option(:initial_capacity, '')
710
- assert_invalid_option(:initial_capacity, 1.0)
711
- assert_invalid_option(:initial_capacity, -1)
712
- end
713
-
714
- def test_load_factor_options_validation
715
- assert_valid_option(:load_factor, nil)
716
- assert_valid_option(:load_factor, 0.01)
717
- assert_valid_option(:load_factor, 0.75)
718
- assert_valid_option(:load_factor, 1)
719
- assert_invalid_option(:load_factor, '')
720
- assert_invalid_option(:load_factor, 0)
721
- assert_invalid_option(:load_factor, 1.1)
722
- assert_invalid_option(:load_factor, 2)
723
- assert_invalid_option(:load_factor, -1)
724
- end
725
-
726
- def test_size
727
- assert_equal 0, @cache.size
728
- @cache[:a] = 1
729
- assert_equal 1, @cache.size
730
- @cache[:b] = 1
731
- assert_equal 2, @cache.size
732
- @cache.delete(:a)
733
- assert_equal 1, @cache.size
734
- @cache.delete(:b)
735
- assert_equal 0, @cache.size
736
- end
737
-
738
- def test_get_or_default
739
- with_or_without_default_proc do
740
- assert_equal 1, @cache.get_or_default(:a, 1)
741
- assert_equal nil, @cache.get_or_default(:a, nil)
742
- assert_equal false, @cache.get_or_default(:a, false)
743
- assert_equal false, @cache.key?(:a)
744
-
745
- @cache[:a] = 1
746
- assert_equal 1, @cache.get_or_default(:a, 2)
747
- end
748
- end
749
-
750
- def test_dup_clone
751
- [:dup, :clone].each do |meth|
752
- cache = cache_with_default_proc(:default_value)
753
- cache[:a] = 1
754
- dupped = cache.send(meth)
755
- assert_equal 1, dupped[:a]
756
- assert_equal 1, dupped.size
757
- assert_size_change 1, cache do
758
- assert_no_size_change dupped do
759
- cache[:b] = 1
760
- end
761
- end
762
- assert_equal false, dupped.key?(:b)
763
- assert_no_size_change cache do
764
- assert_size_change -1, dupped do
765
- dupped.delete(:a)
766
- end
767
- end
768
- assert_equal false, dupped.key?(:a)
769
- assert_equal true, cache.key?(:a)
770
- # test default proc
771
- assert_size_change 1, cache do
772
- assert_no_size_change dupped do
773
- assert_equal :default_value, cache[:c]
774
- assert_equal false, dupped.key?(:c)
775
- end
776
- end
777
- assert_no_size_change cache do
778
- assert_size_change 1, dupped do
779
- assert_equal :default_value, dupped[:d]
780
- assert_equal false, cache.key?(:d)
781
- end
782
- end
783
- end
784
- end
785
-
786
- def test_is_unfreezable
787
- assert_raises(NoMethodError) { @cache.freeze }
788
- end
789
-
790
- def test_marshal_dump_load
791
- new_cache = Marshal.load(Marshal.dump(@cache))
792
- assert_instance_of ThreadSafe::Cache, new_cache
793
- assert_equal 0, new_cache.size
794
- @cache[:a] = 1
795
- new_cache = Marshal.load(Marshal.dump(@cache))
796
- assert_equal 1, @cache[:a]
797
- assert_equal 1, new_cache.size
798
- end
799
-
800
- def test_marshal_dump_doesnt_work_with_default_proc
801
- assert_raises(TypeError) do
802
- Marshal.dump(ThreadSafe::Cache.new {})
803
- end
804
- end
805
-
806
- private
807
- def with_or_without_default_proc
808
- yield false
809
- @cache = ThreadSafe::Cache.new {|h, k| h[k] = :default_value}
810
- yield true
811
- end
812
-
813
- def cache_with_default_proc(default_value = 1)
814
- ThreadSafe::Cache.new {|cache, k| cache[k] = default_value}
815
- end
816
-
817
- def assert_valid_option(option_name, value)
818
- assert_valid_options(option_name => value)
819
- end
820
-
821
- def assert_valid_options(options)
822
- c = ThreadSafe::Cache.new(options)
823
- assert_instance_of ThreadSafe::Cache, c
824
- end
825
-
826
- def assert_invalid_option(option_name, value)
827
- assert_invalid_options(option_name => value)
828
- end
829
-
830
- def assert_invalid_options(options)
831
- assert_raises(ArgumentError) { ThreadSafe::Cache.new(options) }
832
- end
833
-
834
- def assert_size_change(change, cache = @cache)
835
- start = cache.size
836
- yield
837
- assert_equal change, cache.size - start
838
- end
839
-
840
- def assert_no_size_change(cache = @cache, &block)
841
- assert_size_change(0, cache, &block)
842
- end
843
-
844
- def assert_handles_return_lambda(method, key, *args)
845
- before_had_key = @cache.key?(key)
846
- before_had_value = before_had_key ? @cache[key] : nil
847
-
848
- returning_lambda = lambda do
849
- @cache.send(method, key, *args) { return :direct_return }
850
- end
851
-
852
- assert_no_size_change do
853
- assert_equal(:direct_return, returning_lambda.call)
854
- assert_equal before_had_key, @cache.key?(key)
855
- assert_equal before_had_value, @cache[key] if before_had_value
856
- end
857
- end
858
-
859
- class TestException < Exception; end
860
- def assert_handles_exception(method, key, *args)
861
- before_had_key = @cache.key?(key)
862
- before_had_value = before_had_key ? @cache[key] : nil
863
-
864
- assert_no_size_change do
865
- assert_raises(TestException) do
866
- @cache.send(method, key, *args) { raise TestException, '' }
867
- end
868
- assert_equal before_had_key, @cache.key?(key)
869
- assert_equal before_had_value, @cache[key] if before_had_value
870
- end
871
- end
872
-
873
- def assert_compute(key, expected_old_value, expected_result)
874
- result = @cache.compute(:a) do |old_value|
875
- assert_equal expected_old_value, old_value
876
- yield
877
- end
878
- assert_equal expected_result, result
879
- end
880
-
881
- def assert_merge_pair(key, value, expected_old_value, expected_result)
882
- result = @cache.merge_pair(key, value) do |old_value|
883
- assert_equal expected_old_value, old_value
884
- yield
885
- end
886
- assert_equal expected_result, result
887
- end
888
-
889
- def assert_collision_resistance(keys)
890
- keys.each {|k| @cache[k] = k.key}
891
- 10.times do |i|
892
- size = keys.size
893
- while i < size
894
- k = keys[i]
895
- assert(k.key == @cache.delete(k) && !@cache.key?(k) && (@cache[k] = k.key; @cache[k] == k.key))
896
- i += 10
897
- end
898
- end
899
- assert(keys.all? {|k| @cache[k] == k.key})
900
- end
901
- end