oklahoma_mixer 0.1.0 → 0.2.0
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.
- data/CHANGELOG.rdoc +10 -0
- data/TODO.rdoc +3 -3
- data/lib/oklahoma_mixer.rb +14 -5
- data/lib/oklahoma_mixer/b_tree_database.rb +185 -0
- data/lib/oklahoma_mixer/b_tree_database/c.rb +45 -0
- data/lib/oklahoma_mixer/cursor.rb +43 -0
- data/lib/oklahoma_mixer/cursor/c.rb +45 -0
- data/lib/oklahoma_mixer/fixed_length_database.rb +95 -0
- data/lib/oklahoma_mixer/fixed_length_database/c.rb +62 -0
- data/lib/oklahoma_mixer/hash_database.rb +84 -54
- data/lib/oklahoma_mixer/hash_database/c.rb +3 -96
- data/lib/oklahoma_mixer/utilities.rb +110 -5
- data/test/b_tree_binary_data_test.rb +45 -0
- data/test/b_tree_tuning_test.rb +132 -0
- data/test/binary_data_test.rb +3 -13
- data/test/cursor_based_iteration_test.rb +151 -0
- data/test/duplicate_storage_test.rb +107 -0
- data/test/fixed_length_tuning_test.rb +77 -0
- data/test/getting_and_setting_by_id_test.rb +288 -0
- data/test/getting_and_setting_keys_test.rb +37 -3
- data/test/iteration_test.rb +2 -76
- data/test/key_range_test.rb +161 -0
- data/test/order_test.rb +122 -0
- data/test/shared_binary_data.rb +14 -0
- data/test/shared_iteration.rb +99 -0
- data/test/shared_tuning.rb +91 -0
- data/test/test_helper.rb +10 -11
- data/test/top_level_interface_test.rb +12 -0
- data/test/tuning_test.rb +11 -87
- metadata +27 -2
@@ -0,0 +1,45 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "shared_binary_data"
|
3
|
+
|
4
|
+
class TestBTreeBinaryData < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@db = bdb
|
7
|
+
@key = "Binary\0Name"
|
8
|
+
@value = "James\0Edward\0Gray\0II"
|
9
|
+
@db[@key] = @value
|
10
|
+
@closed = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@db.close unless @closed
|
15
|
+
remove_db_files
|
16
|
+
end
|
17
|
+
|
18
|
+
include SharedBinaryData
|
19
|
+
|
20
|
+
def test_null_bytes_are_preserved_during_value_iteration
|
21
|
+
@db.each_value do |value|
|
22
|
+
assert_equal(@value, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_null_bytes_are_preserved_by_key_ranges
|
27
|
+
assert_equal([@key], @db.keys(:range => "A".."Z"))
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_null_bytes_are_preserved_in_comparison_functions
|
31
|
+
@db.close
|
32
|
+
@closed = true
|
33
|
+
remove_db_files
|
34
|
+
|
35
|
+
callback = lambda { |a, b|
|
36
|
+
assert_equal(@key, a) unless a.empty?
|
37
|
+
assert_equal(@key, b) unless b.empty?
|
38
|
+
a <=> b
|
39
|
+
}
|
40
|
+
bdb(:cmpfunc => callback) do |db|
|
41
|
+
db[@key] = @value
|
42
|
+
assert_equal([@key], db.keys) # forces a comparison with ""
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "shared_tuning"
|
3
|
+
|
4
|
+
class TestBTreeTuning < Test::Unit::TestCase
|
5
|
+
def teardown
|
6
|
+
remove_db_files
|
7
|
+
end
|
8
|
+
|
9
|
+
include SharedTuning
|
10
|
+
|
11
|
+
def test_leaf_members_can_be_set_with_other_tuning_defaults
|
12
|
+
members = rand(1_000) + 1
|
13
|
+
assert_option_calls([:tune, members, 0, 0, -1, -1, 0xFF], :lmemb => members)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_leaf_members_is_converted_to_an_int
|
17
|
+
assert_option_calls([:tune, 42, 0, 0, -1, -1, 0xFF], :lmemb => "42")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_non_leaf_members_can_be_set_with_other_tuning_defaults
|
21
|
+
members = rand(1_000) + 1
|
22
|
+
assert_option_calls([:tune, 0, members, 0, -1, -1, 0xFF], :nmemb => members)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_non_leaf_members_is_converted_to_an_int
|
26
|
+
assert_option_calls([:tune, 0, 42, 0, -1, -1, 0xFF], :nmemb => "42")
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_a_bucket_array_size_can_be_set_with_other_tuning_defaults
|
30
|
+
size = rand(1_000) + 1
|
31
|
+
assert_option_calls([:tune, 0, 0, size, -1, -1, 0xFF], :bnum => size)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_bucket_array_size_is_converted_to_an_int
|
35
|
+
assert_option_calls([:tune, 0, 0, 42, -1, -1, 0xFF], :bnum => "42")
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_a_record_alignment_power_can_be_set_with_other_tuning_defaults
|
39
|
+
pow = rand(10) + 1
|
40
|
+
assert_option_calls([:tune, 0, 0, 0, pow, -1, 0xFF], :apow => pow)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_record_alignment_power_is_converted_to_an_int
|
44
|
+
assert_option_calls([:tune, 0, 0, 0, 42, -1, 0xFF], :apow => "42")
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_a_max_free_block_power_can_be_set_with_other_tuning_defaults
|
48
|
+
pow = rand(10) + 1
|
49
|
+
assert_option_calls([:tune, 0, 0, 0, -1, pow, 0xFF], :fpow => pow)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_max_free_block_power_is_converted_to_an_int
|
53
|
+
assert_option_calls([:tune, 0, 0, 0, -1, 42, 0xFF], :fpow => "42")
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_options_can_be_set_with_other_tuning_defaults
|
57
|
+
assert_option_calls([:tune, 0, 0, 0, -1, -1, 1 | 2], :opts => "ld")
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_options_is_a_string_of_characters_mapped_to_enums_and_ored_together
|
61
|
+
opts = {"l" => lib::OPTS[:BDBTLARGE], "b" => lib::OPTS[:BDBTBZIP]}
|
62
|
+
assert_option_calls( [ :tune, 0, 0, 0, -1, -1,
|
63
|
+
opts.values.inject(0) { |o, v| o | v } ],
|
64
|
+
:opts => opts.keys.join )
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_the_options_string_is_not_case_sensative
|
68
|
+
assert_option_calls([:tune, 0, 0, 0, -1, -1, 1 | 2], :opts => "ld")
|
69
|
+
assert_option_calls([:tune, 0, 0, 0, -1, -1, 1 | 2], :opts => "LD")
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_unknown_options_are_ignored_with_a_warning
|
73
|
+
warning = capture_stderr do
|
74
|
+
assert_option_calls([:tune, 0, 0, 0, -1, -1, 1 | 8], :opts => "ltu")
|
75
|
+
end
|
76
|
+
assert(!warning.empty?, "A warning was not issued for an unknown option")
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_multiple_tuning_parameters_can_be_set_at_the_same_time
|
80
|
+
members = rand(1_000) + 1
|
81
|
+
size = rand(1_000) + 1
|
82
|
+
assert_option_calls( [:tune, members, 0, size, -1, -1, 1 | 2],
|
83
|
+
:lmemb => members, :bnum => size, :opts => "ld" )
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_optimize_allows_the_adjustment_of_tune_options_for_an_open_database
|
87
|
+
db do |db|
|
88
|
+
args = capture_args(lib, :optimize) do
|
89
|
+
db.optimize(:apow => "42", :opts => "ld")
|
90
|
+
end
|
91
|
+
assert_instance_of(FFI::Pointer, args[0])
|
92
|
+
assert_equal([0, 0, 0, 42, -1, 1 | 2], args[1..-1])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_leaf_nodes_cached_can_be_set_with_other_cache_defaults
|
97
|
+
nodes = rand(1_000) + 1
|
98
|
+
assert_option_calls([:setcache, nodes, 0], :lcnum => nodes)
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_leaf_nodes_cached_is_converted_to_an_int
|
102
|
+
assert_option_calls([:setcache, 42, 0], :lcnum => "42")
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_non_leaf_nodes_cached_can_be_set_with_other_cache_defaults
|
106
|
+
nodes = rand(1_000) + 1
|
107
|
+
assert_option_calls([:setcache, 0, nodes], :ncnum => nodes)
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_non_leaf_nodes_cached_is_converted_to_an_int
|
111
|
+
assert_option_calls([:setcache, 0, 42], :ncnum => "42")
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_multiple_cache_parameters_can_be_set_at_the_same_time
|
115
|
+
l_nodes = rand(1_000) + 1
|
116
|
+
n_nodes = rand(1_000) + 1
|
117
|
+
assert_option_calls( [:setcache, l_nodes, n_nodes],
|
118
|
+
:lcnum => l_nodes, :ncnum => n_nodes )
|
119
|
+
end
|
120
|
+
|
121
|
+
#######
|
122
|
+
private
|
123
|
+
#######
|
124
|
+
|
125
|
+
def lib
|
126
|
+
OKMixer::BTreeDatabase::C
|
127
|
+
end
|
128
|
+
|
129
|
+
def db(*args, &block)
|
130
|
+
bdb(*args, &block)
|
131
|
+
end
|
132
|
+
end
|
data/test/binary_data_test.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "test_helper"
|
2
|
+
require "shared_binary_data"
|
2
3
|
|
3
4
|
class TestBinaryData < Test::Unit::TestCase
|
4
5
|
def setup
|
@@ -13,6 +14,8 @@ class TestBinaryData < Test::Unit::TestCase
|
|
13
14
|
remove_db_files
|
14
15
|
end
|
15
16
|
|
17
|
+
include SharedBinaryData
|
18
|
+
|
16
19
|
def test_keys_and_values_can_be_read_with_null_bytes
|
17
20
|
assert_equal(@value, @db[@key])
|
18
21
|
end
|
@@ -22,19 +25,6 @@ class TestBinaryData < Test::Unit::TestCase
|
|
22
25
|
assert_equal("new\0value", @db[@key])
|
23
26
|
end
|
24
27
|
|
25
|
-
def test_null_bytes_are_preserved_during_key_iteration
|
26
|
-
@db.each_key do |key|
|
27
|
-
assert_equal(@key, key)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_null_bytes_are_preserved_during_iteration
|
32
|
-
@db.each do |key, value|
|
33
|
-
assert_equal(@key, key)
|
34
|
-
assert_equal(@value, value)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
28
|
def test_null_bytes_are_preserved_by_key_listing
|
39
29
|
assert_equal([@key], @db.keys)
|
40
30
|
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "shared_iteration"
|
3
|
+
|
4
|
+
class TestCursorBasedIteration < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@db = bdb
|
7
|
+
@keys = %w[a b c]
|
8
|
+
@values = @keys.map { |key| key * 2 }
|
9
|
+
@keys.zip(@values) do |key, value|
|
10
|
+
@db[key] = value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@db.close
|
16
|
+
remove_db_files
|
17
|
+
end
|
18
|
+
|
19
|
+
include SharedIteration
|
20
|
+
|
21
|
+
def test_each_key_can_begin_iteration_at_a_passed_key
|
22
|
+
@db.each_key("b") do |key|
|
23
|
+
@keys.delete(key)
|
24
|
+
end
|
25
|
+
assert_equal(%w[a], @keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_each_key_can_begin_iteration_between_keys
|
29
|
+
@db.each_key("ab") do |key| # after "a", but before "b"
|
30
|
+
@keys.delete(key)
|
31
|
+
end
|
32
|
+
assert_equal(%w[a], @keys)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_iteration_can_be_broken_with_each_key
|
36
|
+
@db.each_key("b") do |key|
|
37
|
+
@keys.delete(key)
|
38
|
+
break
|
39
|
+
end
|
40
|
+
assert_equal(%w[a c], @keys)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_each_can_begin_iteration_at_a_passed_key
|
44
|
+
@db.each("b") do |key, value|
|
45
|
+
@keys.delete(key)
|
46
|
+
@values.delete(value)
|
47
|
+
end
|
48
|
+
assert_equal(%w[a], @keys)
|
49
|
+
assert_equal(%w[aa], @values)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_each_can_begin_iteration_between_keys
|
53
|
+
@db.each("ab") do |key, value| # after "a", but before "b"
|
54
|
+
@keys.delete(key)
|
55
|
+
@values.delete(value)
|
56
|
+
end
|
57
|
+
assert_equal(%w[a], @keys)
|
58
|
+
assert_equal(%w[aa], @values)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_iteration_can_be_broken_with_each
|
62
|
+
@db.each("b") do |key, value|
|
63
|
+
@keys.delete(key)
|
64
|
+
@values.delete(value)
|
65
|
+
break
|
66
|
+
end
|
67
|
+
assert_equal(%w[a c], @keys)
|
68
|
+
assert_equal(%w[aa cc], @values)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_reverse_each_can_begin_iteration_at_a_passed_key
|
72
|
+
@db.reverse_each("b") do |key, value|
|
73
|
+
@keys.delete(key)
|
74
|
+
@values.delete(value)
|
75
|
+
end
|
76
|
+
assert_equal(%w[c], @keys)
|
77
|
+
assert_equal(%w[cc], @values)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_reverse_each_can_begin_iteration_between_keys
|
81
|
+
@db.reverse_each("ab") do |key, value| # after "a", but before "b"
|
82
|
+
@keys.delete(key)
|
83
|
+
@values.delete(value)
|
84
|
+
end
|
85
|
+
assert_equal(%w[c], @keys)
|
86
|
+
assert_equal(%w[cc], @values)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_iteration_can_be_broken_with_reverse_each
|
90
|
+
@db.reverse_each("b") do |key, value|
|
91
|
+
@keys.delete(key)
|
92
|
+
@values.delete(value)
|
93
|
+
break
|
94
|
+
end
|
95
|
+
assert_equal(%w[a c], @keys)
|
96
|
+
assert_equal(%w[aa cc], @values)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_each_value_can_begin_iteration_at_a_passed_key
|
100
|
+
@db.each_value("b") do |value|
|
101
|
+
@values.delete(value)
|
102
|
+
end
|
103
|
+
assert_equal(%w[aa], @values)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_each_value_can_begin_iteration_between_keys
|
107
|
+
@db.each_value("ab") do |value| # after "a", but before "b"
|
108
|
+
@values.delete(value)
|
109
|
+
end
|
110
|
+
assert_equal(%w[aa], @values)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_iteration_can_be_broken_with_each_value
|
114
|
+
@db.each_value("b") do |value|
|
115
|
+
@values.delete(value)
|
116
|
+
break
|
117
|
+
end
|
118
|
+
assert_equal(%w[aa cc], @values)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_delete_if_can_begin_iteration_at_a_passed_key
|
122
|
+
@db.delete_if("b") do |key, value|
|
123
|
+
@keys.delete(key)
|
124
|
+
@values.delete(value)
|
125
|
+
true
|
126
|
+
end
|
127
|
+
assert_equal(%w[a], @keys)
|
128
|
+
assert_equal(%w[aa], @values)
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_delete_if_can_begin_iteration_between_keys
|
132
|
+
@db.delete_if("ab") do |key, value| # after "a", but before "b"
|
133
|
+
@keys.delete(key)
|
134
|
+
@values.delete(value)
|
135
|
+
true
|
136
|
+
end
|
137
|
+
assert_equal(%w[a], @keys)
|
138
|
+
assert_equal(%w[aa], @values)
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_iteration_can_be_broken_with_each_delete_if
|
142
|
+
@db.delete_if("b") do |key, value|
|
143
|
+
break if key == "c"
|
144
|
+
@keys.delete(key)
|
145
|
+
@values.delete(value)
|
146
|
+
true
|
147
|
+
end
|
148
|
+
assert_equal(%w[a c], @keys)
|
149
|
+
assert_equal(%w[aa cc], @values)
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestDuplicateStorage < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@db = bdb
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
@db.close
|
10
|
+
remove_db_files
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_duplicates_can_be_stored
|
14
|
+
assert(@db.store("Gray", "Dana", :dup), "Failed to store initial value")
|
15
|
+
assert(@db.store("Gray", "James", :dup), "Failed to store duplicate value")
|
16
|
+
assert_equal("Dana", @db["Gray"]) # always returns the first
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_keys_are_a_unique_listing_not_showing_duplicates
|
20
|
+
@db.update("Gray" => "Dana", "Matsumoto" => "Yukihiro")
|
21
|
+
@db.store("Gray", "James", :dup)
|
22
|
+
assert_equal(%w[Gray Matsumoto], @db.keys)
|
23
|
+
assert_equal(%w[Gray], @db.keys(:range => "G"..."H"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_values_can_be_scoped_to_a_key_to_retrieve_all_duplicates
|
27
|
+
@db[:other] = "one record"
|
28
|
+
assert_equal(["one record"], @db.values)
|
29
|
+
assert_equal([ ], @db.values("Gray"))
|
30
|
+
assert(@db.store("Gray", "Dana", :dup), "Failed to store initial value")
|
31
|
+
assert_equal(%w[Dana], @db.values("Gray"))
|
32
|
+
assert(@db.store("Gray", "James", :dup), "Failed to store duplicate value")
|
33
|
+
assert_equal(%w[Dana James], @db.values("Gray"))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_delete_removes_the_first_value_by_default
|
37
|
+
@db.update("Gray" => "Dana", "Matsumoto" => "Yukihiro")
|
38
|
+
@db.store("Gray", "James", :dup)
|
39
|
+
assert_equal(%w[Dana James], @db.values("Gray"))
|
40
|
+
assert_equal("Dana", @db.delete("Gray"))
|
41
|
+
assert_equal(%w[James], @db.values("Gray"))
|
42
|
+
assert_equal("James", @db.delete("Gray"))
|
43
|
+
assert_nil(@db.delete("Gray"))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_delete_with_dup_mode_deletes_all_values
|
47
|
+
@db.update("Gray" => "Dana", "Matsumoto" => "Yukihiro")
|
48
|
+
@db.store("Gray", "James", :dup)
|
49
|
+
assert_equal(%w[Dana James], @db.values("Gray"))
|
50
|
+
assert_equal(%w[Dana James], @db.delete("Gray", :dup))
|
51
|
+
assert_equal([ ], @db.delete("Gray", :dup))
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_delete_with_dup_mode_supports_the_missing_handler
|
55
|
+
assert_nil(@db.delete(:missing, :dup) { nil })
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_size_can_be_scoped_to_a_key_to_retrieve_all_duplicates
|
59
|
+
@db[:other] = "one record"
|
60
|
+
assert_equal(1, @db.size)
|
61
|
+
assert_equal(0, @db.size("Gray"))
|
62
|
+
assert(@db.store("Gray", "Dana", :dup), "Failed to store initial value")
|
63
|
+
assert_equal(1, @db.size("Gray"))
|
64
|
+
assert(@db.store("Gray", "James", :dup), "Failed to store duplicate value")
|
65
|
+
assert_equal(2, @db.size("Gray"))
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_duplicates_show_up_in_cursor_based_iteration_of_keys
|
69
|
+
@db.update("Gray" => "Dana", "Matsumoto" => "Yukihiro")
|
70
|
+
@db.store("Gray", "James", :dup)
|
71
|
+
keys = [ ]
|
72
|
+
@db.each_key do |key|
|
73
|
+
keys << key
|
74
|
+
end
|
75
|
+
assert_equal(%w[Gray Gray Matsumoto], keys)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_duplicates_show_up_in_cursor_based_iteration
|
79
|
+
@db.update("Gray" => "Dana", "Matsumoto" => "Yukihiro")
|
80
|
+
@db.store("Gray", "James", :dup)
|
81
|
+
pairs = [ ]
|
82
|
+
@db.each do |pair|
|
83
|
+
pairs << pair
|
84
|
+
end
|
85
|
+
assert_equal([%w[Gray Dana], %w[Gray James], %w[Matsumoto Yukihiro]], pairs)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_duplicates_show_up_in_reverse_cursor_based_iteration
|
89
|
+
@db.update("Gray" => "Dana", "Matsumoto" => "Yukihiro")
|
90
|
+
@db.store("Gray", "James", :dup)
|
91
|
+
pairs = [ ]
|
92
|
+
@db.reverse_each do |pair|
|
93
|
+
pairs << pair
|
94
|
+
end
|
95
|
+
assert_equal([%w[Matsumoto Yukihiro], %w[Gray James], %w[Gray Dana]], pairs)
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_duplicates_show_up_in_cursor_based_iteration_of_values
|
99
|
+
@db.update("Gray" => "Dana", "Matsumoto" => "Yukihiro")
|
100
|
+
@db.store("Gray", "James", :dup)
|
101
|
+
values = [ ]
|
102
|
+
@db.each_value do |value|
|
103
|
+
values << value
|
104
|
+
end
|
105
|
+
assert_equal(%w[Dana James Yukihiro], values)
|
106
|
+
end
|
107
|
+
end
|