mongodb-mongo 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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