oklahoma_mixer 0.1.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.
@@ -0,0 +1,218 @@
1
+ require "test_helper"
2
+
3
+ class TestGettingAndSettingKeys < Test::Unit::TestCase
4
+ def setup
5
+ @db = hdb
6
+ end
7
+
8
+ def teardown
9
+ @db.close
10
+ remove_db_files
11
+ end
12
+
13
+ def test_a_key_value_pair_can_be_stored_and_fetched_from_the_database
14
+ assert_equal("value", @db.store("key", "value"))
15
+ assert_equal("value", @db.fetch("key")) # later
16
+ end
17
+
18
+ def test_both_keys_and_values_are_converted_to_strings
19
+ assert_equal(42, @db.store(:num, 42))
20
+ assert_equal("42", @db.fetch("num")) # effectively the same key
21
+ end
22
+
23
+ def test_fetching_a_missing_value_fails_with_an_index_error
24
+ assert_raise(IndexError) do
25
+ @db.fetch(:missing)
26
+ end
27
+ end
28
+
29
+ def test_fetch_can_return_a_default_for_a_missing_value
30
+ assert_equal(42, @db.fetch(:missing, 42))
31
+ end
32
+
33
+ def test_fetch_can_run_a_block_returning_its_result_for_a_missing_value
34
+ assert_equal(:missing, @db.fetch(:missing) { |key| key })
35
+ end
36
+
37
+ def test_fetching_with_a_block_overrides_a_default_and_triggers_a_warning
38
+ warning = capture_stderr do
39
+ assert_equal(42, @db.fetch(:missing, 13) { 42 })
40
+ end
41
+ assert(!warning.empty?, "A warning was not issued for a default and block")
42
+ end
43
+
44
+ def test_storing_with_keep_mode_adds_a_value_only_if_it_didnt_already_exist
45
+ assert(@db.store(:key, :new, :keep), "Failed to store a new key")
46
+ assert_equal("new", @db.fetch(:key))
47
+ assert(!@db.store(:key, :replace, :keep), "Replaced an existing key")
48
+ assert_equal("new", @db.fetch(:key))
49
+ end
50
+
51
+ def test_storing_with_cat_mode_concatenates_content_onto_an_existing_value
52
+ assert_equal("One", @db.store(:list, "One", :cat))
53
+ assert_equal("One", @db.fetch(:list))
54
+ assert_equal(", Two", @db.store(:list, ", Two", :cat))
55
+ assert_equal("One, Two", @db.fetch(:list))
56
+ end
57
+
58
+ def test_storing_with_async_mode_is_buffered_writing
59
+ args = capture_args(OKMixer::HashDatabase::C, :putasync) do
60
+ assert_equal(:buffered, @db.store(:key, :buffered, :async))
61
+ end
62
+ assert_instance_of(FFI::Pointer, args[0])
63
+ assert_equal(["key", 3, "buffered", 8], args[1..-1])
64
+ end
65
+
66
+ def test_storing_with_add_mode_adds_to_an_existing_value
67
+ assert_equal(0, @db.store(:i, 0, :add))
68
+ assert_equal(1, @db.store(:i, 1, :add))
69
+ assert_equal(2, @db.store(:i, 1, :add))
70
+ assert_equal(1, @db.store(:i, -1, :add))
71
+
72
+ assert_in_delta(1.5, @db.store(:f, 1.5, :add), 2 ** -20)
73
+ assert_in_delta(3.5, @db.store(:f, 2.0, :add), 2 ** -20)
74
+ assert_in_delta(2.5, @db.store(:f, -1.0, :add), 2 ** -20)
75
+ end
76
+
77
+ def test_storing_with_a_block_allows_duplicate_resolution
78
+ @db[:key] = :old
79
+ assert_equal( :new, @db.store(:key, :new) { |key, old, new|
80
+ "#{key}=#{old}&#{new}" } )
81
+ assert_equal("key=old&new", @db[:key])
82
+ end
83
+
84
+ def test_storing_with_a_block_overrides_a_mode_and_triggers_a_warning
85
+ warning = capture_stderr do
86
+ assert_equal(:new, @db.store(:key, :new, :async) { |key, old, new| })
87
+ end
88
+ assert(!warning.empty?, "A warning was not issued for a mode and block")
89
+ end
90
+
91
+ def test_store_and_fetch_can_also_be_used_through_the_indexing_brackets
92
+ assert_equal(42, @db[:num] = 42)
93
+ assert_equal("42", @db[:num])
94
+ end
95
+
96
+ def test_indexing_returns_nil_instead_of_failing_with_index_error
97
+ assert_nil(@db[:missing])
98
+ end
99
+
100
+ def test_a_default_can_be_set_for_indexing_to_return
101
+ @db.default = 42
102
+ assert_equal(42, @db[:missing])
103
+ end
104
+
105
+ def test_the_indexing_default_will_be_run_if_it_is_a_proc
106
+ @db.default = lambda { |key| "is #{key}" }
107
+ assert_equal("is missing", @db[:missing])
108
+ end
109
+
110
+ def test_the_indexing_default_for_a_given_key_can_be_retrieved
111
+ @db.default = lambda { |key| "is #{key}" }
112
+ assert_equal("is ", @db.default)
113
+ assert_equal("is missing", @db.default(:missing))
114
+ end
115
+
116
+ def test_the_indexing_default_can_be_changed
117
+ assert_nil(@db[:missing])
118
+ assert_equal(42, @db.default = 42)
119
+ assert_equal(42, @db[:missing])
120
+ proc = lambda { |key| fail RuntimeError, "%p not found" % [key]}
121
+ assert_equal(proc, @db.default = proc)
122
+ error = assert_raise(RuntimeError) { @db[:missing] }
123
+ assert_equal(":missing not found", error.message)
124
+ end
125
+
126
+ def test_include_and_aliases_can_be_used_to_check_for_the_existance_of_a_key
127
+ @db[:exist] = true
128
+ %w[include? has_key? key? member?].each do |query|
129
+ assert(@db.send(query, :exist), "Failed to detect an existing key")
130
+ assert(!@db.send(query, :missing), "Failed to detect an missing key")
131
+ end
132
+ end
133
+
134
+ def test_update_sets_multiple_values_at_once_overwriting_old_values
135
+ @db[:b] = "old_b"
136
+ assert_equal(@db, @db.update(:a => "new_a", :b => "new_b", :c => "new_c"))
137
+ assert_equal(%w[new_a new_b new_c], @db.values_at(:a, :b, :c))
138
+ end
139
+
140
+ def test_update_can_be_passed_a_block_for_handling_duplicates
141
+ @db[:b] = "old"
142
+ assert_equal( @db, @db.update( :a => "new",
143
+ :b => "new",
144
+ :c => "new") { |key, old, new|
145
+ "#{key}=#{old}&#{new}" } )
146
+ assert_equal(%w[new b=old&new new], @db.values_at(:a, :b, :c))
147
+ end
148
+
149
+ def test_values_at_can_be_used_to_retrieve_multiple_values_at_once
150
+ @db[:a] = 1
151
+ @db[:c] = 2
152
+ assert_equal([ ], @db.values_at)
153
+ assert_equal(["1", nil, "2"], @db.values_at(:a, :b, :c))
154
+ end
155
+
156
+ def test_values_at_supports_defaults
157
+ @db.default = 42
158
+ @db[:a] = 1
159
+ @db[:c] = 2
160
+ assert_equal(["1", 42, "2"], @db.values_at(:a, :b, :c))
161
+ end
162
+
163
+ def test_keys_returns_all_keys_in_the_database
164
+ @db.update(:a => 1, :b => 2, :c => 3)
165
+ assert_equal(%w[a b c], @db.keys.sort)
166
+ end
167
+
168
+ def test_keys_can_take_a_prefix_which_all_returned_keys_must_start_with
169
+ @db.update( "names:1:first" => "James",
170
+ "names:1:last" => "Gray",
171
+ "names:2:first" => "Other",
172
+ "names:2:last" => "Guy" )
173
+ assert_equal( %w[names:1:first names:1:last],
174
+ @db.keys(:prefix => "names:1:").sort )
175
+ end
176
+
177
+ def test_keys_can_take_a_limit_of_keys_to_return
178
+ @db.update(:a => 1, :b => 2, :c => 3)
179
+ keys = @db.keys(:limit => 2)
180
+ assert_equal(2, keys.size)
181
+ keys.each do |key|
182
+ assert(%w[a b c].include?(key), "Key wasn't in known keys")
183
+ end
184
+ end
185
+
186
+ def test_values_returns_all_values_in_the_database
187
+ @db.update(:a => 1, :b => 2, :c => 3)
188
+ assert_equal(%w[1 2 3], @db.values.sort)
189
+ end
190
+
191
+ def test_delete_removes_a_key_from_the_database
192
+ @db[:key] = :value
193
+ assert_equal("value", @db.delete(:key))
194
+ assert_nil(@db[:key])
195
+ end
196
+
197
+ def test_delete_returns_nil_for_a_missing_key
198
+ assert_nil(@db.delete(:missing))
199
+ end
200
+
201
+ def test_delete_can_be_passed_a_block_to_handle_missing_keys
202
+ assert_equal(42, @db.delete(:missing) { 42 })
203
+ end
204
+
205
+ def test_clear_removes_all_keys_from_the_database
206
+ @db.update(:a => 1, :b => 2, :c => 3)
207
+ assert_equal(@db, @db.clear)
208
+ assert_equal([nil, nil, nil], @db.values_at(:a, :b, :c))
209
+ end
210
+
211
+ def test_size_and_length_return_the_count_of_key_value_pairs_in_the_database
212
+ assert_equal(0, @db.size)
213
+ assert_equal(0, @db.length)
214
+ @db.update(:a => 1, :b => 2, :c => 3)
215
+ assert_equal(3, @db.size)
216
+ assert_equal(3, @db.length)
217
+ end
218
+ end
@@ -0,0 +1,94 @@
1
+ require "test_helper"
2
+
3
+ class TestIteration < Test::Unit::TestCase
4
+ def setup
5
+ @db = hdb
6
+ @keys = %w[a b c]
7
+ @values = @keys.map { |key| key * 2 }
8
+ @keys.zip(@values) do |key, value|
9
+ @db[key] = value
10
+ end
11
+ end
12
+
13
+ def teardown
14
+ @db.close
15
+ remove_db_files
16
+ end
17
+
18
+ def test_each_key_iterates_over_all_keys_in_the_database
19
+ @db.each_key do |key|
20
+ @keys.delete(key)
21
+ end
22
+ assert(@keys.empty?, "All keys were not iterated over")
23
+ end
24
+
25
+ def test_each_iterates_over_all_key_value_pairs_in_arrays
26
+ @db.each do |key_value_array|
27
+ assert_instance_of(Array, key_value_array)
28
+ assert_equal(2, key_value_array.size)
29
+ key, value = key_value_array
30
+ assert_equal(key * 2, value)
31
+ @keys.delete(key)
32
+ @values.delete(value)
33
+ end
34
+ assert( @keys.empty? && @values.empty?,
35
+ "All key/value pairs were not iterated over" )
36
+ end
37
+
38
+ def test_the_arrays_passed_to_each_can_be_split
39
+ @db.each do |key, value|
40
+ @keys.delete(key)
41
+ @values.delete(value)
42
+ end
43
+ assert( @keys.empty? && @values.empty?,
44
+ "All key/value pairs were not iterated over" )
45
+ end
46
+
47
+ def test_each_pair_is_an_alias_for_each
48
+ each_arrays = [ ]
49
+ @db.each do |array|
50
+ each_arrays << array
51
+ end
52
+ @db.each_pair do |array|
53
+ each_arrays.delete(array)
54
+ end
55
+ each_keys_and_values = [ ]
56
+ @db.each do |key, value|
57
+ each_keys_and_values << [key, value]
58
+ end
59
+ @db.each_pair do |key, value|
60
+ each_keys_and_values.delete([key, value])
61
+ end
62
+ assert( each_arrays.empty? && each_keys_and_values.empty?,
63
+ "The iterations did not match" )
64
+ end
65
+
66
+ def test_each_value_iterates_over_all_values_in_the_database
67
+ @db.each_value do |value|
68
+ @values.delete(value)
69
+ end
70
+ assert(@values.empty?, "All values were not iterated over")
71
+ end
72
+
73
+ def test_the_standard_iterators_are_supported
74
+ assert_kind_of(Enumerable, @db)
75
+
76
+ # examples
77
+ assert_equal(%w[b bb], @db.find { |_, value| value == "bb" })
78
+ assert_nil(@db.find { |_, value| value == "dd" })
79
+ assert_equal(%w[aaa bbb ccc], @db.map { |key, value| key + value }.sort)
80
+ assert( @db.any? { |_, value| value.include? "c" },
81
+ "A value was not found during iteration" )
82
+ end
83
+
84
+ def test_delete_if_removes_all_keys_for_which_the_block_returns_true
85
+ @db.delete_if { |key, _| key != "a" }
86
+ assert_equal([%w[a aa]], @db.to_a)
87
+ end
88
+
89
+ def test_iterators_return_self_to_match_hash_interface
90
+ %w[each_key each each_pair each_value delete_if].each do |iterator|
91
+ assert_equal(@db, @db.send(iterator) { })
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,51 @@
1
+ require "stringio"
2
+ require "test/unit"
3
+
4
+ require "rubygems" # for FFI
5
+
6
+ require "oklahoma_mixer"
7
+
8
+ module TestHelper
9
+ def capture_stderr
10
+ $stderr = StringIO.new
11
+ yield
12
+ $stderr.string
13
+ ensure
14
+ $stderr = STDERR
15
+ end
16
+
17
+ def capture_args(receiver, method)
18
+ called_with = nil
19
+ singleton = class << receiver; self end
20
+ singleton.send(:define_method, method) do |*args|
21
+ called_with = args
22
+ end
23
+ yield
24
+ called_with
25
+ end
26
+
27
+ def db_path(ext)
28
+ File.join( File.dirname(__FILE__),
29
+ "#{self.class.to_s.sub(/\ATest/, '').downcase}.#{ext}" )
30
+ end
31
+
32
+ def hdb(*args)
33
+ db = OKMixer::HashDatabase.new(db_path("tch"), *args)
34
+ if block_given?
35
+ begin
36
+ yield db
37
+ ensure
38
+ db.close
39
+ end
40
+ else
41
+ db
42
+ end
43
+ end
44
+
45
+ def remove_db_files
46
+ Dir.glob(db_path("*")) do |file|
47
+ File.unlink(file)
48
+ end
49
+ end
50
+ end
51
+ Test::Unit::TestCase.send(:include, TestHelper)
@@ -0,0 +1,47 @@
1
+ require "test_helper"
2
+
3
+ class TestTopLevelInterface < Test::Unit::TestCase
4
+ def teardown
5
+ remove_db_files
6
+ end
7
+
8
+ def test_the_version_constant_contains_the_current_version_number
9
+ assert_match(/\A\d\.\d\.\d\z/, OKMixer::VERSION)
10
+ end
11
+
12
+ def test_open_of_a_tch_extension_file_creates_a_hash_database
13
+ OKMixer.open(db_path("tch")) do |db|
14
+ assert_instance_of(OKMixer::HashDatabase, db)
15
+ end
16
+ end
17
+
18
+ def test_open_of_an_unrecognized_extension_fails_with_an_error
19
+ assert_raise(ArgumentError) do
20
+ OKMixer.open("unrecognized")
21
+ end
22
+ end
23
+
24
+ def test_open_with_a_block_returns_the_value_of_the_last_expression
25
+ assert_equal(42, OKMixer.open(db_path("tch")) { 42 })
26
+ end
27
+
28
+ def test_open_with_a_block_automatically_closes_the_database
29
+ OKMixer.open(db_path("tch")) do |db|
30
+ db[:data] = :saved
31
+ end
32
+ OKMixer.open(db_path("tch")) do |db| # we can reopen since the lock is gone
33
+ assert_equal("saved", db[:data])
34
+ end
35
+ end
36
+
37
+ def test_open_without_a_block_returns_the_still_open_database
38
+ db = OKMixer.open(db_path("tch"))
39
+ assert_nil(db[:unset]) # we can fetch a value since it's still open
40
+ ensure
41
+ db.close if db
42
+ end
43
+
44
+ def test_ok_mixer_is_a_shortcut_for_oklahoma_mixer
45
+ assert_same(OklahomaMixer, OKMixer)
46
+ end
47
+ end
@@ -0,0 +1,66 @@
1
+ require "test_helper"
2
+
3
+ class TestTransactions < Test::Unit::TestCase
4
+ def setup
5
+ @db = hdb
6
+ @db[:data] = "before transaction"
7
+ end
8
+
9
+ def teardown
10
+ @db.close
11
+ remove_db_files
12
+ end
13
+
14
+ def test_a_transaction_returns_the_value_of_the_last_expression
15
+ assert_equal(42, @db.transaction { 42 })
16
+ end
17
+
18
+ def test_a_transaction_is_automatically_committed_if_the_block_finishes
19
+ @db.transaction do
20
+ @db[:data] = "after transaction"
21
+ end
22
+ assert_equal("after transaction", @db[:data])
23
+ end
24
+
25
+ def test_failing_with_an_error_aborts_the_transaction
26
+ assert_raise(RuntimeError) do
27
+ @db.transaction do
28
+ @db[:data] = "after transaction"
29
+ fail "Testing abort"
30
+ end
31
+ end
32
+ assert_equal("before transaction", @db[:data])
33
+ end
34
+
35
+ def test_a_transaction_can_be_committed_before_the_end_of_the_block
36
+ @db.transaction do
37
+ @db[:data] = "after transaction"
38
+ @db.commit
39
+ @db[:other] = "set" # not executed
40
+ end
41
+ assert_equal("after transaction", @db[:data])
42
+ assert_nil(@db[:other])
43
+ end
44
+
45
+ def test_a_transaction_can_be_aborted_before_the_end_of_the_block
46
+ @db.transaction do
47
+ @db[:data] = "after transaction"
48
+ @db.abort
49
+ @db[:other] = "set" # not executed
50
+ end
51
+ assert_equal("before transaction", @db[:data])
52
+ assert_nil(@db[:other])
53
+ end
54
+
55
+ def test_commit_fails_with_an_error_if_called_outside_a_transaction
56
+ assert_raise(OKMixer::Error::TransactionError) do
57
+ @db.commit
58
+ end
59
+ end
60
+
61
+ def test_abort_fails_with_an_error_if_called_outside_a_transaction
62
+ assert_raise(OKMixer::Error::TransactionError) do
63
+ @db.abort
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,173 @@
1
+ require "test_helper"
2
+
3
+ class TestTuning < Test::Unit::TestCase
4
+ def teardown
5
+ remove_db_files
6
+ end
7
+
8
+ def test_a_mutex_can_be_activated_as_the_database_is_created
9
+ assert_option_calls([:setmutex], :mutex => true)
10
+ end
11
+
12
+ def test_a_bucket_array_size_can_be_set_with_other_tuning_defaults
13
+ size = rand(1_000) + 1
14
+ assert_option_calls([:tune, size, -1, -1, 0xFF], :bnum => size)
15
+ end
16
+
17
+ def test_bucket_array_size_is_converted_to_an_int
18
+ assert_option_calls([:tune, 42, -1, -1, 0xFF], :bnum => "42")
19
+ end
20
+
21
+ def test_a_record_alignment_power_can_be_set_with_other_tuning_defaults
22
+ pow = rand(10) + 1
23
+ assert_option_calls([:tune, 0, pow, -1, 0xFF], :apow => pow)
24
+ end
25
+
26
+ def test_record_alignment_power_is_converted_to_an_int
27
+ assert_option_calls([:tune, 0, 42, -1, 0xFF], :apow => "42")
28
+ end
29
+
30
+ def test_a_max_free_block_power_can_be_set_with_other_tuning_defaults
31
+ pow = rand(10) + 1
32
+ assert_option_calls([:tune, 0, -1, pow, 0xFF], :fpow => pow)
33
+ end
34
+
35
+ def test_max_free_block_power_is_converted_to_an_int
36
+ assert_option_calls([:tune, 0, -1, 42, 0xFF], :fpow => "42")
37
+ end
38
+
39
+ def test_options_can_be_set_with_other_tuning_defaults
40
+ assert_option_calls([:tune, 0, -1, -1, 1 | 2], :opts => "ld")
41
+ end
42
+
43
+ def test_options_is_a_string_of_characters_mapped_to_enums_and_ored_together
44
+ opts = { "l" => OKMixer::HashDatabase::C::OPTS[:HDBTLARGE],
45
+ "b" => OKMixer::HashDatabase::C::OPTS[:HDBTBZIP] }
46
+ assert_option_calls( [ :tune, 0, -1, -1,
47
+ opts.values.inject(0) { |o, v| o | v } ],
48
+ :opts => opts.keys.join )
49
+ end
50
+
51
+ def test_the_options_string_is_not_case_sensative
52
+ assert_option_calls([:tune, 0, -1, -1, 1 | 2], :opts => "ld")
53
+ assert_option_calls([:tune, 0, -1, -1, 1 | 2], :opts => "LD")
54
+ end
55
+
56
+ def test_unknown_options_are_ignored_with_a_warning
57
+ warning = capture_stderr do
58
+ assert_option_calls([:tune, 0, -1, -1, 1 | 8], :opts => "ltu")
59
+ end
60
+ assert(!warning.empty?, "A warning was not issued for an unknown option")
61
+ end
62
+
63
+ def test_multiple_tuning_parameters_can_be_set_at_the_same_time
64
+ size = rand(1_000) + 1
65
+ assert_option_calls( [:tune, size, -1, -1, 1 | 2],
66
+ :bnum => size, :opts => "ld" )
67
+ end
68
+
69
+ def test_optimize_allows_the_adjustment_of_tune_options_for_an_open_database
70
+ hdb do |db|
71
+ args = capture_args(OKMixer::HashDatabase::C, :optimize) do
72
+ db.optimize(:apow => "42", :opts => "ld")
73
+ end
74
+ assert_instance_of(FFI::Pointer, args[0])
75
+ assert_equal([0, 42, -1, 1 | 2], args[1..-1])
76
+ end
77
+ end
78
+
79
+ def test_limit_for_cached_records_can_be_set
80
+ limit = rand(1_000) + 1
81
+ assert_option_calls([:setcache, limit], :rcnum => limit)
82
+ end
83
+
84
+ def test_limit_for_cached_records_is_converted_to_an_int
85
+ assert_option_calls([:setcache, 42], :rcnum => "42")
86
+ end
87
+
88
+ def test_a_size_can_be_set_for_extra_mapped_memory
89
+ size = rand(1_000) + 1
90
+ assert_option_calls([:setxmsiz, size], :xmsiz => size)
91
+ end
92
+
93
+ def test_extra_mapped_memory_size_is_converted_to_an_int
94
+ assert_option_calls([:setxmsiz, 42], :xmsiz => "42")
95
+ end
96
+
97
+ def test_a_step_unit_can_be_set_for_auto_defragmentation
98
+ unit = rand(1_000) + 1
99
+ assert_option_calls([:setdfunit, unit], :dfunit => unit)
100
+ end
101
+
102
+ def test_auto_defragmentation_step_unit_is_converted_to_an_int
103
+ assert_option_calls([:setdfunit, 42], :dfunit => "42")
104
+ end
105
+
106
+ def test_nested_transactions_can_be_ignored
107
+ hdb(:nested_transactions => :ignore) do |db|
108
+ result = db.transaction {
109
+ db.transaction { # ignored
110
+ 41
111
+ } + 1 # ignored
112
+ }
113
+ assert_equal(42, result)
114
+ end
115
+ end
116
+
117
+ def test_nested_transactions_can_be_set_to_fail_with_an_error
118
+ [:fail, :raise].each do |setting|
119
+ hdb(:nested_transactions => setting) do |db|
120
+ db.transaction do
121
+ assert_raise(OKMixer::Error::TransactionError) do
122
+ db.transaction { } # nested fails with error
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def test_a_mode_string_can_be_passed
130
+ assert_raise(OKMixer::Error::CabinetError) do # file not found
131
+ hdb("r")
132
+ end
133
+ end
134
+
135
+ def test_the_mode_can_be_passed_as_as_option
136
+ assert_raise(OKMixer::Error::CabinetError) do # file not found
137
+ hdb(:mode => "r")
138
+ end
139
+ end
140
+
141
+ def test_an_option_mode_overrides_the_mode_argument_and_triggers_a_warning
142
+ warning = capture_stderr do
143
+ hdb("r", :mode => "wc") do
144
+ # just open and close
145
+ end
146
+ end
147
+ assert( !warning.empty?,
148
+ "A warning was not issued for an option mode with a mode argument" )
149
+ end
150
+
151
+ def test_an_unknown_mode_triggers_a_warning
152
+ warning = capture_stderr do
153
+ hdb("wcu") do
154
+ # just open and close
155
+ end
156
+ end
157
+ assert(!warning.empty?, "A warning was not issued for an unknown mode")
158
+ end
159
+
160
+ #######
161
+ private
162
+ #######
163
+
164
+ def assert_option_calls(c_call, options)
165
+ args = capture_args(OKMixer::HashDatabase::C, c_call[0]) do
166
+ hdb(options) do
167
+ # just open and close
168
+ end
169
+ end
170
+ assert_instance_of(FFI::Pointer, args[0])
171
+ assert_equal(c_call[1..-1], args[1..-1])
172
+ end
173
+ end