mongodb-mongo 0.1.3

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.
Files changed (48) hide show
  1. data/README.rdoc +216 -0
  2. data/Rakefile +54 -0
  3. data/bin/mongo_console +21 -0
  4. data/bin/validate +51 -0
  5. data/examples/benchmarks.rb +38 -0
  6. data/examples/blog.rb +76 -0
  7. data/examples/index_test.rb +128 -0
  8. data/examples/simple.rb +17 -0
  9. data/lib/mongo/admin.rb +86 -0
  10. data/lib/mongo/collection.rb +161 -0
  11. data/lib/mongo/cursor.rb +230 -0
  12. data/lib/mongo/db.rb +399 -0
  13. data/lib/mongo/message/get_more_message.rb +21 -0
  14. data/lib/mongo/message/insert_message.rb +19 -0
  15. data/lib/mongo/message/kill_cursors_message.rb +20 -0
  16. data/lib/mongo/message/message.rb +68 -0
  17. data/lib/mongo/message/message_header.rb +34 -0
  18. data/lib/mongo/message/msg_message.rb +17 -0
  19. data/lib/mongo/message/opcodes.rb +16 -0
  20. data/lib/mongo/message/query_message.rb +67 -0
  21. data/lib/mongo/message/remove_message.rb +20 -0
  22. data/lib/mongo/message/update_message.rb +21 -0
  23. data/lib/mongo/message.rb +4 -0
  24. data/lib/mongo/mongo.rb +98 -0
  25. data/lib/mongo/query.rb +110 -0
  26. data/lib/mongo/types/binary.rb +34 -0
  27. data/lib/mongo/types/dbref.rb +37 -0
  28. data/lib/mongo/types/objectid.rb +137 -0
  29. data/lib/mongo/types/regexp_of_holding.rb +44 -0
  30. data/lib/mongo/types/undefined.rb +31 -0
  31. data/lib/mongo/util/bson.rb +431 -0
  32. data/lib/mongo/util/byte_buffer.rb +163 -0
  33. data/lib/mongo/util/ordered_hash.rb +68 -0
  34. data/lib/mongo/util/xml_to_ruby.rb +102 -0
  35. data/lib/mongo.rb +12 -0
  36. data/mongo-ruby-driver.gemspec +62 -0
  37. data/tests/test_admin.rb +60 -0
  38. data/tests/test_bson.rb +135 -0
  39. data/tests/test_byte_buffer.rb +69 -0
  40. data/tests/test_cursor.rb +66 -0
  41. data/tests/test_db.rb +85 -0
  42. data/tests/test_db_api.rb +354 -0
  43. data/tests/test_db_connection.rb +17 -0
  44. data/tests/test_message.rb +35 -0
  45. data/tests/test_objectid.rb +98 -0
  46. data/tests/test_ordered_hash.rb +85 -0
  47. data/tests/test_round_trip.rb +116 -0
  48. metadata +100 -0
@@ -0,0 +1,354 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'test/unit'
4
+
5
+ # NOTE: assumes Mongo is running
6
+ class DBAPITest < Test::Unit::TestCase
7
+
8
+ include XGen::Mongo::Driver
9
+
10
+ def setup
11
+ host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
12
+ port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
13
+ @db = Mongo.new(host, port).db('ruby-mongo-test')
14
+ @coll = @db.collection('test')
15
+ @coll.clear
16
+ @r1 = @coll.insert('a' => 1) # collection not created until it's used
17
+ @coll_full_name = 'ruby-mongo-test.test'
18
+ end
19
+
20
+ def teardown
21
+ if @db.connected?
22
+ @coll.clear unless @coll == nil
23
+ @db.close
24
+ end
25
+ end
26
+
27
+ def test_clear
28
+ assert_equal 1, @coll.count
29
+ @coll.clear
30
+ assert_equal 0, @coll.count
31
+ end
32
+
33
+ def test_insert
34
+ @coll.insert('a' => 2)
35
+ @coll.insert('b' => 3)
36
+
37
+ assert_equal 3, @coll.count
38
+ docs = @coll.find().to_a
39
+ assert_equal 3, docs.length
40
+ assert docs.detect { |row| row['a'] == 1 }
41
+ assert docs.detect { |row| row['a'] == 2 }
42
+ assert docs.detect { |row| row['b'] == 3 }
43
+
44
+ @coll << {'b' => 4}
45
+ docs = @coll.find().to_a
46
+ assert_equal 4, docs.length
47
+ assert docs.detect { |row| row['b'] == 4 }
48
+ end
49
+
50
+ def test_find_simple
51
+ @r2 = @coll.insert('a' => 2)
52
+ @r3 = @coll.insert('b' => 3)
53
+ # Check sizes
54
+ docs = @coll.find().to_a
55
+ assert_equal 3, docs.size
56
+ assert_equal 3, @coll.count
57
+
58
+ # Find by other value
59
+ docs = @coll.find('a' => @r1['a']).to_a
60
+ assert_equal 1, docs.size
61
+ doc = docs.first
62
+ assert_equal doc['_id'], @r1['_id']
63
+ assert_equal doc['a'], @r1['a']
64
+ end
65
+
66
+ def test_find_advanced
67
+ @coll.insert('a' => 2)
68
+ @coll.insert('b' => 3)
69
+
70
+ # Find by advanced query (less than)
71
+ docs = @coll.find('a' => { '$lt' => 10 }).to_a
72
+ assert_equal 2, docs.size
73
+ assert docs.detect { |row| row['a'] == 1 }
74
+ assert docs.detect { |row| row['a'] == 2 }
75
+
76
+ # Find by advanced query (greater than)
77
+ docs = @coll.find('a' => { '$gt' => 1 }).to_a
78
+ assert_equal 1, docs.size
79
+ assert docs.detect { |row| row['a'] == 2 }
80
+
81
+ # Find by advanced query (less than or equal to)
82
+ docs = @coll.find('a' => { '$lte' => 1 }).to_a
83
+ assert_equal 1, docs.size
84
+ assert docs.detect { |row| row['a'] == 1 }
85
+
86
+ # Find by advanced query (greater than or equal to)
87
+ docs = @coll.find('a' => { '$gte' => 1 }).to_a
88
+ assert_equal 2, docs.size
89
+ assert docs.detect { |row| row['a'] == 1 }
90
+ assert docs.detect { |row| row['a'] == 2 }
91
+
92
+ # Find by advanced query (between)
93
+ docs = @coll.find('a' => { '$gt' => 1, '$lt' => 3 }).to_a
94
+ assert_equal 1, docs.size
95
+ assert docs.detect { |row| row['a'] == 2 }
96
+
97
+ # Find by advanced query (in clause)
98
+ docs = @coll.find('a' => {'$in' => [1,2]}).to_a
99
+ assert_equal 2, docs.size
100
+ assert docs.detect { |row| row['a'] == 1 }
101
+ assert docs.detect { |row| row['a'] == 2 }
102
+
103
+ # Find by advanced query (regexp)
104
+ docs = @coll.find('a' => /[1|2]/).to_a
105
+ assert_equal 2, docs.size
106
+ assert docs.detect { |row| row['a'] == 1 }
107
+ assert docs.detect { |row| row['a'] == 2 }
108
+ end
109
+
110
+ def test_find_sorting
111
+ @coll.clear
112
+ @coll.insert('a' => 1, 'b' => 2)
113
+ @coll.insert('a' => 2, 'b' => 1)
114
+ @coll.insert('a' => 3, 'b' => 2)
115
+ @coll.insert('a' => 4, 'b' => 1)
116
+
117
+ # Sorting (ascending)
118
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => {'a' => 1}).to_a
119
+ assert_equal 4, docs.size
120
+ assert_equal 1, docs[0]['a']
121
+ assert_equal 2, docs[1]['a']
122
+ assert_equal 3, docs[2]['a']
123
+ assert_equal 4, docs[3]['a']
124
+
125
+ # Sorting (descending)
126
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => {'a' => -1}).to_a
127
+ assert_equal 4, docs.size
128
+ assert_equal 4, docs[0]['a']
129
+ assert_equal 3, docs[1]['a']
130
+ assert_equal 2, docs[2]['a']
131
+ assert_equal 1, docs[3]['a']
132
+
133
+ # Sorting using array of names; assumes ascending order.
134
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => ['a']).to_a
135
+ assert_equal 4, docs.size
136
+ assert_equal 1, docs[0]['a']
137
+ assert_equal 2, docs[1]['a']
138
+ assert_equal 3, docs[2]['a']
139
+ assert_equal 4, docs[3]['a']
140
+
141
+ # Sorting using single name; assumes ascending order.
142
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => 'a').to_a
143
+ assert_equal 4, docs.size
144
+ assert_equal 1, docs[0]['a']
145
+ assert_equal 2, docs[1]['a']
146
+ assert_equal 3, docs[2]['a']
147
+ assert_equal 4, docs[3]['a']
148
+
149
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => ['b', 'a']).to_a
150
+ assert_equal 4, docs.size
151
+ assert_equal 2, docs[0]['a']
152
+ assert_equal 4, docs[1]['a']
153
+ assert_equal 1, docs[2]['a']
154
+ assert_equal 3, docs[3]['a']
155
+
156
+ # Sorting using empty array; no order guarantee (Mongo bug #898) but
157
+ # should not blow up.
158
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => []).to_a
159
+ assert_equal 4, docs.size
160
+
161
+ # Sorting using array of hashes; no order guarantee (Mongo bug #898) but
162
+ # should not blow up.
163
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => [{'b' => 1}, {'a' => -1}]).to_a
164
+ assert_equal 4, docs.size
165
+
166
+ # Sorting using ordered hash. You can use an unordered one, but then the
167
+ # order of the keys won't be guaranteed thus your sort won't make sense.
168
+ oh = OrderedHash.new
169
+ oh['a'] = -1
170
+ docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => oh).to_a
171
+ assert_equal 4, docs.size
172
+ assert_equal 4, docs[0]['a']
173
+ assert_equal 3, docs[1]['a']
174
+ assert_equal 2, docs[2]['a']
175
+ assert_equal 1, docs[3]['a']
176
+
177
+ # TODO this will not pass due to known Mongo bug #898
178
+ # oh = OrderedHash.new
179
+ # oh['b'] = -1
180
+ # oh['a'] = 1
181
+ # docs = @coll.find({'a' => { '$lt' => 10 }}, :sort => oh).to_a
182
+ # assert_equal 4, docs.size
183
+ # assert_equal 1, docs[0]['a']
184
+ # assert_equal 3, docs[1]['a']
185
+ # assert_equal 2, docs[2]['a']
186
+ # assert_equal 4, docs[3]['a']
187
+ end
188
+
189
+ def test_find_limits
190
+ @coll.insert('b' => 2)
191
+ @coll.insert('c' => 3)
192
+ @coll.insert('d' => 4)
193
+
194
+ docs = @coll.find({}, :limit => 1).to_a
195
+ assert_equal 1, docs.size
196
+ docs = @coll.find({}, :limit => 2).to_a
197
+ assert_equal 2, docs.size
198
+ docs = @coll.find({}, :limit => 3).to_a
199
+ assert_equal 3, docs.size
200
+ docs = @coll.find({}, :limit => 4).to_a
201
+ assert_equal 4, docs.size
202
+ docs = @coll.find({}).to_a
203
+ assert_equal 4, docs.size
204
+ docs = @coll.find({}, :limit => 99).to_a
205
+ assert_equal 4, docs.size
206
+ end
207
+
208
+ def test_drop_collection
209
+ assert @db.drop_collection(@coll.name), "drop of collection #{@coll.name} failed"
210
+ assert !@db.collection_names.include?(@coll_full_name)
211
+ @coll = nil
212
+ end
213
+
214
+ def test_collection_names
215
+ names = @db.collection_names
216
+ assert names.length >= 1
217
+ assert names.include?(@coll_full_name)
218
+
219
+ coll2 = @db.collection('test2')
220
+ coll2.insert('a' => 1) # collection not created until it's used
221
+ names = @db.collection_names
222
+ assert names.length >= 2
223
+ assert names.include?(@coll_full_name)
224
+ assert names.include?('ruby-mongo-test.test2')
225
+ ensure
226
+ @db.drop_collection('test2')
227
+ end
228
+
229
+ def test_collections_info
230
+ cursor = @db.collections_info
231
+ rows = cursor.to_a
232
+ assert rows.length >= 1
233
+ row = rows.detect { |r| r['name'] == @coll_full_name }
234
+ assert_not_nil row
235
+ end
236
+
237
+ def test_collection_options
238
+ @db.drop_collection('foobar')
239
+ @db.strict = true
240
+
241
+ begin
242
+ coll = @db.create_collection('foobar', :capped => true, :size => 1024)
243
+ options = coll.options()
244
+ assert_equal 'foobar', options['create']
245
+ assert_equal true, options['capped']
246
+ assert_equal 1024, options['size']
247
+ rescue => ex
248
+ @db.drop_collection('foobar')
249
+ fail "did not expect exception \"#{ex}\""
250
+ end
251
+ end
252
+
253
+ def test_index_information
254
+ @db.create_index(@coll.name, 'index_name', ['a'])
255
+ list = @db.index_information(@coll.name)
256
+ assert_equal 1, list.length
257
+
258
+ info = list[0]
259
+ assert_equal 'index_name', info[:name]
260
+ assert_equal 1, info[:keys]['a']
261
+ end
262
+
263
+ def test_array
264
+ @coll << {'b' => [1, 2, 3]}
265
+ rows = @coll.find({}, {:fields => ['b']}).to_a
266
+ assert_equal 1, rows.length
267
+ assert_equal [1, 2, 3], rows[0]['b']
268
+ end
269
+
270
+ def test_regex
271
+ regex = /foobar/i
272
+ @coll << {'b' => regex}
273
+ rows = @coll.find({}, {:fields => ['b']}).to_a
274
+ assert_equal 1, rows.length
275
+ assert_equal regex, rows[0]['b']
276
+ end
277
+
278
+ def test_strict
279
+ assert !@db.strict?
280
+ @db.strict = true
281
+ assert @db.strict?
282
+ end
283
+
284
+ def test_strict_access_collection
285
+ @db.strict = true
286
+ begin
287
+ @db.collection('does-not-exist')
288
+ fail "expected exception"
289
+ rescue => ex
290
+ assert_equal "Collection does-not-exist doesn't exist. Currently in strict mode.", ex.to_s
291
+ ensure
292
+ @db.strict = false
293
+ @db.drop_collection('does-not-exist')
294
+ end
295
+ end
296
+
297
+ def test_strict_create_collection
298
+ @db.drop_collection('foobar')
299
+ @db.strict = true
300
+
301
+ begin
302
+ @db.create_collection('foobar')
303
+ assert true
304
+ rescue => ex
305
+ fail "did not expect exception \"#{ex}\""
306
+ end
307
+
308
+ # Now the collection exists. This time we should see an exception.
309
+ begin
310
+ @db.create_collection('foobar')
311
+ fail "expected exception"
312
+ rescue => ex
313
+ assert_equal "Collection foobar already exists. Currently in strict mode.", ex.to_s
314
+ ensure
315
+ @db.strict = false
316
+ @db.drop_collection('foobar')
317
+ end
318
+ end
319
+
320
+ def test_to_a
321
+ cursor = @coll.find()
322
+ rows = cursor.to_a
323
+
324
+ # Make sure we get back exactly the same array the next time we ask
325
+ rows2 = cursor.to_a
326
+ assert_same rows, rows2
327
+
328
+ # Make sure we can still iterate after calling to_a
329
+ rows_with_each = cursor.collect{|row| row}
330
+ assert_equal rows, rows_with_each
331
+
332
+ # Make sure we can iterate more than once after calling to_a
333
+ end
334
+
335
+ def test_to_a_after_each
336
+ cursor = @coll.find
337
+ cursor.each { |row| row }
338
+ begin
339
+ cursor.to_a
340
+ fail "expected \"can't call\" error"
341
+ rescue => ex
342
+ assert_equal "can't call Cursor#to_a after calling Cursor#each", ex.to_s
343
+ end
344
+ end
345
+
346
+ def test_ismaster
347
+ assert @db.master?
348
+ end
349
+
350
+ def test_master
351
+ assert_equal "#{@db.host}:#{@db.port}", @db.master
352
+ end
353
+
354
+ end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'test/unit'
4
+
5
+ # NOTE: assumes Mongo is running
6
+ class DBConnectionTest < Test::Unit::TestCase
7
+
8
+ include XGen::Mongo::Driver
9
+
10
+ def test_no_exceptions
11
+ host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
12
+ port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
13
+ db = Mongo.new(host, port).db('ruby-mongo-demo')
14
+ coll = db.collection('test')
15
+ coll.clear
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'test/unit'
4
+
5
+ class MessageTest < Test::Unit::TestCase
6
+
7
+ include XGen::Mongo::Driver
8
+
9
+ def setup
10
+ @msg = Message.new(42)
11
+ end
12
+
13
+ def test_initial_info
14
+ assert_equal Message::HEADER_SIZE, @msg.buf.length
15
+ @msg.write_long(1029)
16
+ @msg.buf.rewind
17
+ assert_equal Message::HEADER_SIZE + 8, @msg.buf.get_int
18
+ @msg.buf.get_int # skip message id
19
+ assert_equal 0, @msg.buf.get_int
20
+ assert_equal 42, @msg.buf.get_int
21
+ assert_equal 1029, @msg.buf.get_long
22
+ end
23
+
24
+ def test_update_length
25
+ @msg.update_message_length
26
+ @msg.buf.rewind
27
+ assert_equal Message::HEADER_SIZE, @msg.buf.get_int
28
+ end
29
+
30
+ def test_long_length
31
+ @msg.write_long(1027)
32
+ assert_equal Message::HEADER_SIZE + 8, @msg.buf.length
33
+ end
34
+
35
+ end
@@ -0,0 +1,98 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'test/unit'
4
+
5
+ class ObjectIDTest < Test::Unit::TestCase
6
+
7
+ include XGen::Mongo::Driver
8
+
9
+ def setup
10
+ @t = 42
11
+ @o = ObjectID.new(nil, @t)
12
+ end
13
+
14
+ def test_index_for_time
15
+ t = 99
16
+ assert_equal 0, @o.index_for_time(t)
17
+ assert_equal 1, @o.index_for_time(t)
18
+ assert_equal 2, @o.index_for_time(t)
19
+ t = 100
20
+ assert_equal 0, @o.index_for_time(t)
21
+ end
22
+
23
+ def test_time_bytes
24
+ a = @o.to_a
25
+ assert_equal @t, a[0]
26
+ 3.times { |i| assert_equal 0, a[i+1] }
27
+
28
+ t = 43
29
+ o = ObjectID.new(nil, t)
30
+ a = o.to_a
31
+ assert_equal t, a[0]
32
+ 3.times { |i| assert_equal 0, a[i+1] }
33
+ assert_equal 1, o.index_for_time(t) # 0 was used for o
34
+ end
35
+
36
+ def test_different
37
+ o2 = ObjectID.new(nil, @t)
38
+ assert @o.to_a != o2.to_a
39
+ end
40
+
41
+ def test_eql?
42
+ o2 = ObjectID.new(@o.to_a)
43
+ assert @o.eql?(o2)
44
+ assert @o == o2
45
+ end
46
+
47
+ def test_to_s
48
+ s = @o.to_s
49
+ assert_equal 24, s.length
50
+ s =~ /^([0-9a-f]+)$/
51
+ assert_equal 24, $1.length
52
+ end
53
+
54
+ def test_save_and_restore
55
+ host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
56
+ port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
57
+ db = Mongo.new(host, port).db('ruby-mongo-test')
58
+ coll = db.collection('test')
59
+
60
+ coll.clear
61
+ coll << {'a' => 1, '_id' => @o}
62
+
63
+ row = coll.find().collect.first
64
+ assert_equal 1, row['a']
65
+ assert_equal @o, row['_id']
66
+ end
67
+
68
+ def test_from_string
69
+ hex_str = @o.to_s
70
+ o2 = ObjectID.from_string(hex_str)
71
+ assert_equal hex_str, o2.to_s
72
+ assert_equal @o, o2
73
+ assert_equal @o.to_s, o2.to_s
74
+ end
75
+
76
+ def test_legal_oid_string
77
+ assert !ObjectID.legal_oid_string(nil)
78
+ assert !ObjectID.legal_oid_string("fred")
79
+ assert !ObjectID.legal_oid_string("0000")
80
+ assert !ObjectID.legal_oid_string('000102030405060708090A0')
81
+ assert ObjectID.legal_oid_string('000102030405060708090A0B')
82
+ assert ObjectID.legal_oid_string('abcdefABCDEF123456789012')
83
+ assert !ObjectID.legal_oid_string('abcdefABCDEF12345678901x')
84
+ end
85
+
86
+ def test_from_string_leading_zeroes
87
+ hex_str = '000000000000000000abcdef'
88
+ o = ObjectID.from_string(hex_str)
89
+ assert_equal hex_str, o.to_s
90
+ end
91
+
92
+ def test_byte_order
93
+ hex_str = '000102030405060708090A0B'
94
+ o = ObjectID.from_string(hex_str)
95
+ assert_equal [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08], o.to_a
96
+ end
97
+
98
+ end
@@ -0,0 +1,85 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo/util/ordered_hash'
3
+ require 'test/unit'
4
+
5
+ class OrderedHashTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @oh = OrderedHash.new
9
+ @oh['c'] = 1
10
+ @oh['a'] = 2
11
+ @oh['z'] = 3
12
+ @ordered_keys = %w(c a z)
13
+ end
14
+
15
+ def test_empty
16
+ assert_equal [], OrderedHash.new.keys
17
+ end
18
+
19
+ def test_order_preserved
20
+ assert_equal @ordered_keys, @oh.keys
21
+ end
22
+
23
+ def test_order_preserved_after_replace
24
+ @oh['a'] = 42
25
+ assert_equal @ordered_keys, @oh.keys
26
+ @oh['c'] = 'foobar'
27
+ assert_equal @ordered_keys, @oh.keys
28
+ @oh['z'] = /huh?/
29
+ assert_equal @ordered_keys, @oh.keys
30
+ end
31
+
32
+ def test_each
33
+ keys = []
34
+ @oh.each { |k, v| keys << k }
35
+ assert_equal keys, @oh.keys
36
+
37
+ @oh['z'] = 42
38
+ assert_equal keys, @oh.keys
39
+ end
40
+
41
+ def test_values
42
+ assert_equal [1, 2, 3], @oh.values
43
+ end
44
+
45
+ def test_merge
46
+ other = OrderedHash.new
47
+ other['f'] = 'foo'
48
+ noob = @oh.merge(other)
49
+ assert_equal @ordered_keys + ['f'], noob.keys
50
+ assert_equal [1, 2, 3, 'foo'], noob.values
51
+ end
52
+
53
+ def test_merge_bang
54
+ other = OrderedHash.new
55
+ other['f'] = 'foo'
56
+ @oh.merge!(other)
57
+ assert_equal @ordered_keys + ['f'], @oh.keys
58
+ assert_equal [1, 2, 3, 'foo'], @oh.values
59
+ end
60
+
61
+ def test_merge_bang_with_overlap
62
+ other = OrderedHash.new
63
+ other['a'] = 'apple'
64
+ other['c'] = 'crab'
65
+ other['f'] = 'foo'
66
+ @oh.merge!(other)
67
+ assert_equal @ordered_keys + ['f'], @oh.keys
68
+ assert_equal ['crab', 'apple', 3, 'foo'], @oh.values
69
+ end
70
+
71
+ def test_merge_bang_with_hash_with_overlap
72
+ other = Hash.new
73
+ other['a'] = 'apple'
74
+ other['c'] = 'crab'
75
+ other['f'] = 'foo'
76
+ @oh.merge!(other)
77
+ assert_equal @ordered_keys + ['f'], @oh.keys
78
+ assert_equal ['crab', 'apple', 3, 'foo'], @oh.values
79
+ end
80
+
81
+ def test_inspect_retains_order
82
+ assert_equal '{"c"=>1, "a"=>2, "z"=>3}', @oh.inspect
83
+ end
84
+
85
+ end
@@ -0,0 +1,116 @@
1
+ HERE = File.dirname(__FILE__)
2
+ $LOAD_PATH[0,0] = File.join(HERE, '..', 'lib')
3
+ require 'mongo'
4
+ require 'mongo/util/xml_to_ruby'
5
+ require 'test/unit'
6
+
7
+ # For each xml/bson file in the data subdirectory, we turn the XML into an
8
+ # OrderedHash and then test both Ruby-to-BSON and BSON-to-Ruby translations.
9
+ #
10
+ # There is a whole other project that includes similar tests
11
+ # (http://github.com/mongodb/mongo-qa). If the directory ../mongo-qa exists,
12
+ # then we find the BSON test files there and use those, too. Use the Rake task
13
+ # "mongo_qa" to obtain those tests.
14
+ class RoundTripTest < Test::Unit::TestCase
15
+
16
+ include XGen::Mongo::Driver
17
+
18
+ @@ruby = nil
19
+
20
+ def setup
21
+ unless @@ruby
22
+ names = Dir[File.join(HERE, 'data', '*.xml')].collect {|f| File.basename(f).sub(/\.xml$/, '') }
23
+ @@ruby = {}
24
+ names.each { |name|
25
+ File.open(File.join(HERE, 'data', "#{name}.xml")) { |f|
26
+ @@ruby[name] = XMLToRuby.new.xml_to_ruby(f)
27
+ }
28
+ }
29
+ end
30
+ end
31
+
32
+ def test_dummy
33
+ assert true
34
+ end
35
+
36
+ def self.create_test_for_round_trip_files_in_dir(dir)
37
+ names = Dir[File.join(dir, '*.xson')].collect {|f| File.basename(f).sub(/\.xson$/, '') }
38
+ names.each { |name|
39
+ eval <<EOS
40
+ def test_#{name}_#{dir.gsub(/[^a-zA-Z0-9_]/, '_')}
41
+ one_round_trip("#{dir}", "#{name}")
42
+ end
43
+ EOS
44
+ }
45
+ end
46
+
47
+ # Dynamically generate one test for each test file. This way, if one test
48
+ # fails the others will still run.
49
+ create_test_for_round_trip_files_in_dir(File.join(HERE, 'data'))
50
+ mongo_qa_dir = File.join(HERE, '..', 'mongo-qa/modules/bson_tests/tests')
51
+ if File.exist?(mongo_qa_dir)
52
+ %w(basic_types complex single_types).each { |subdir_name|
53
+ create_test_for_round_trip_files_in_dir(File.join(mongo_qa_dir, subdir_name))
54
+ }
55
+ end
56
+
57
+ # Round-trip comparisons of Ruby-to-BSON and back.
58
+ # * Take the objects that were read from XML
59
+ # * Turn them into BSON bytes
60
+ # * Compare that with the BSON files we have
61
+ # * Turn those BSON bytes back in to Ruby objects
62
+ # * Turn them back into BSON bytes
63
+ # * Compare that with the BSON files we have (or the bytes that were already
64
+ # generated)
65
+ def one_round_trip(dir, name)
66
+ obj = File.open(File.join(dir, "#{name}.xson")) { |f|
67
+ XMLToRuby.new.xml_to_ruby(f)
68
+ }
69
+
70
+ File.open(File.join(dir, "#{name}.bson"), 'r') { |f|
71
+ # Read the BSON from the file
72
+ bson = f.read
73
+ bson = if RUBY_VERSION >= '1.9'
74
+ bson.bytes.to_a
75
+ else
76
+ bson.split(//).collect { |c| c[0] }
77
+ end
78
+
79
+ # Turn the Ruby object into BSON bytes and compare with the BSON bytes
80
+ # from the file.
81
+ bson_from_ruby = BSON.new.serialize(obj).to_a
82
+
83
+ begin
84
+ assert_equal bson.length, bson_from_ruby.length
85
+ assert_equal bson, bson_from_ruby
86
+ rescue => ex
87
+ # File.open(File.join(dir, "#{name}_out_a.bson"), 'wb') { |f| # DEBUG
88
+ # bson_from_ruby.each { |b| f.putc(b) }
89
+ # }
90
+ raise ex
91
+ end
92
+
93
+ # Turn those BSON bytes back into a Ruby object.
94
+ #
95
+ # We're passing a nil db to the contructor here, but that's OK because
96
+ # the BSON DBFef bytes don't contain the db object in any case, and we
97
+ # don't care what the database is.
98
+ obj_from_bson = BSON.new(nil).deserialize(ByteBuffer.new(bson_from_ruby))
99
+ assert_kind_of OrderedHash, obj_from_bson
100
+
101
+ # Turn that Ruby object into BSON and compare it to the original BSON
102
+ # bytes.
103
+ bson_from_ruby = BSON.new.serialize(obj_from_bson).to_a
104
+ begin
105
+ assert_equal bson.length, bson_from_ruby.length
106
+ assert_equal bson, bson_from_ruby
107
+ rescue => ex
108
+ # File.open(File.join(dir, "#{name}_out_b.bson"), 'wb') { |f| # DEBUG
109
+ # bson_from_ruby.each { |b| f.putc(b) }
110
+ # }
111
+ raise ex
112
+ end
113
+ }
114
+ end
115
+
116
+ end