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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.travis.yml +23 -20
- data/Gemfile +5 -2
- data/README.md +5 -1
- data/Rakefile +10 -9
- data/lib/thread_safe/cache.rb +3 -5
- data/lib/thread_safe/jruby_cache_backend.jar +0 -0
- data/lib/thread_safe/mri_cache_backend.rb +0 -9
- data/lib/thread_safe/synchronized_delegator.rb +0 -17
- data/lib/thread_safe/util/atomic_reference.rb +0 -1
- data/lib/thread_safe/version.rb +1 -1
- data/spec/.gitignore +0 -0
- data/spec/spec_helper.rb +31 -0
- data/{test → spec}/src/thread_safe/SecurityManager.java +0 -0
- data/spec/support/.gitignore +0 -0
- data/spec/support/threads.rb +1 -0
- data/{test/test_helper.rb → spec/support/threadsafe_test.rb} +0 -59
- data/spec/thread_safe/.gitignore +0 -0
- data/spec/thread_safe/array_spec.rb +18 -0
- data/spec/thread_safe/cache_loops_spec.rb +507 -0
- data/spec/thread_safe/cache_spec.rb +943 -0
- data/spec/thread_safe/hash_spec.rb +17 -0
- data/spec/thread_safe/no_unsafe_spec.rb +27 -0
- data/spec/thread_safe/synchronized_delegator_spec.rb +85 -0
- data/thread_safe.gemspec +4 -4
- metadata +54 -41
- data/test/test_array.rb +0 -18
- data/test/test_cache.rb +0 -901
- data/test/test_cache_loops.rb +0 -449
- data/test/test_hash.rb +0 -17
- data/test/test_synchronized_delegator.rb +0 -84
@@ -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
|
data/thread_safe.gemspec
CHANGED
@@ -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.
|
9
|
-
gem.
|
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 '
|
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.
|
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:
|
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:
|
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: '
|
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: '
|
47
|
+
version: '3.2'
|
48
|
+
name: rspec
|
54
49
|
prerelease: false
|
55
50
|
type: :development
|
56
|
-
|
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
|
-
- .
|
65
|
-
- .
|
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.
|
137
|
+
rubygems_version: 2.6.8
|
131
138
|
signing_key:
|
132
139
|
specification_version: 4
|
133
|
-
summary:
|
140
|
+
summary: Thread-safe collections and utilities for Ruby
|
134
141
|
test_files:
|
135
|
-
-
|
136
|
-
-
|
137
|
-
-
|
138
|
-
-
|
139
|
-
-
|
140
|
-
-
|
141
|
-
-
|
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:
|
data/test/test_array.rb
DELETED
@@ -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
|
data/test/test_cache.rb
DELETED
@@ -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
|