mongo 1.0 → 1.1.5

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 (95) hide show
  1. data/LICENSE.txt +1 -13
  2. data/{README.rdoc → README.md} +129 -149
  3. data/Rakefile +94 -58
  4. data/bin/mongo_console +21 -0
  5. data/docs/1.0_UPGRADE.md +21 -0
  6. data/docs/CREDITS.md +123 -0
  7. data/docs/FAQ.md +112 -0
  8. data/docs/GridFS.md +158 -0
  9. data/docs/HISTORY.md +185 -0
  10. data/docs/REPLICA_SETS.md +75 -0
  11. data/docs/TUTORIAL.md +247 -0
  12. data/docs/WRITE_CONCERN.md +28 -0
  13. data/lib/mongo/collection.rb +225 -105
  14. data/lib/mongo/connection.rb +374 -315
  15. data/lib/mongo/cursor.rb +122 -77
  16. data/lib/mongo/db.rb +109 -85
  17. data/lib/mongo/exceptions.rb +6 -0
  18. data/lib/mongo/gridfs/grid.rb +19 -11
  19. data/lib/mongo/gridfs/grid_ext.rb +36 -9
  20. data/lib/mongo/gridfs/grid_file_system.rb +15 -9
  21. data/lib/mongo/gridfs/grid_io.rb +49 -16
  22. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  23. data/lib/mongo/repl_set_connection.rb +290 -0
  24. data/lib/mongo/util/conversions.rb +3 -1
  25. data/lib/mongo/util/core_ext.rb +17 -4
  26. data/lib/mongo/util/pool.rb +125 -0
  27. data/lib/mongo/util/server_version.rb +2 -0
  28. data/lib/mongo/util/support.rb +12 -0
  29. data/lib/mongo/util/uri_parser.rb +71 -0
  30. data/lib/mongo.rb +23 -7
  31. data/{mongo-ruby-driver.gemspec → mongo.gemspec} +9 -7
  32. data/test/auxillary/1.4_features.rb +2 -2
  33. data/test/auxillary/authentication_test.rb +1 -1
  34. data/test/auxillary/autoreconnect_test.rb +1 -1
  35. data/test/{slave_connection_test.rb → auxillary/slave_connection_test.rb} +6 -6
  36. data/test/bson/binary_test.rb +15 -0
  37. data/test/bson/bson_test.rb +537 -0
  38. data/test/bson/byte_buffer_test.rb +190 -0
  39. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  40. data/test/bson/json_test.rb +17 -0
  41. data/test/bson/object_id_test.rb +141 -0
  42. data/test/bson/ordered_hash_test.rb +197 -0
  43. data/test/collection_test.rb +195 -15
  44. data/test/connection_test.rb +93 -56
  45. data/test/conversions_test.rb +1 -1
  46. data/test/cursor_fail_test.rb +75 -0
  47. data/test/cursor_message_test.rb +43 -0
  48. data/test/cursor_test.rb +93 -32
  49. data/test/db_api_test.rb +28 -55
  50. data/test/db_connection_test.rb +2 -3
  51. data/test/db_test.rb +45 -40
  52. data/test/grid_file_system_test.rb +14 -6
  53. data/test/grid_io_test.rb +36 -7
  54. data/test/grid_test.rb +54 -10
  55. data/test/replica_sets/connect_test.rb +84 -0
  56. data/test/replica_sets/count_test.rb +35 -0
  57. data/test/{replica → replica_sets}/insert_test.rb +17 -14
  58. data/test/replica_sets/pooled_insert_test.rb +55 -0
  59. data/test/replica_sets/query_secondaries.rb +80 -0
  60. data/test/replica_sets/query_test.rb +41 -0
  61. data/test/replica_sets/replication_ack_test.rb +64 -0
  62. data/test/replica_sets/rs_test_helper.rb +29 -0
  63. data/test/safe_test.rb +68 -0
  64. data/test/support/hash_with_indifferent_access.rb +199 -0
  65. data/test/support/keys.rb +45 -0
  66. data/test/support_test.rb +19 -0
  67. data/test/test_helper.rb +53 -15
  68. data/test/threading/{test_threading_large_pool.rb → threading_with_large_pool_test.rb} +2 -2
  69. data/test/threading_test.rb +2 -2
  70. data/test/tools/repl_set_manager.rb +241 -0
  71. data/test/tools/test.rb +13 -0
  72. data/test/unit/collection_test.rb +70 -7
  73. data/test/unit/connection_test.rb +18 -39
  74. data/test/unit/cursor_test.rb +7 -8
  75. data/test/unit/db_test.rb +14 -17
  76. data/test/unit/grid_test.rb +49 -0
  77. data/test/unit/pool_test.rb +9 -0
  78. data/test/unit/repl_set_connection_test.rb +82 -0
  79. data/test/unit/safe_test.rb +125 -0
  80. metadata +132 -51
  81. data/bin/bson_benchmark.rb +0 -59
  82. data/bin/fail_if_no_c.rb +0 -11
  83. data/examples/admin.rb +0 -43
  84. data/examples/capped.rb +0 -22
  85. data/examples/cursor.rb +0 -48
  86. data/examples/gridfs.rb +0 -44
  87. data/examples/index_test.rb +0 -126
  88. data/examples/info.rb +0 -31
  89. data/examples/queries.rb +0 -70
  90. data/examples/simple.rb +0 -24
  91. data/examples/strict.rb +0 -35
  92. data/examples/types.rb +0 -36
  93. data/test/replica/count_test.rb +0 -34
  94. data/test/replica/pooled_insert_test.rb +0 -54
  95. data/test/replica/query_test.rb +0 -39
@@ -0,0 +1,537 @@
1
+ # encoding:utf-8
2
+ require './test/test_helper'
3
+ require 'complex'
4
+ require 'bigdecimal'
5
+ require 'rational'
6
+
7
+ begin
8
+ require 'active_support/core_ext'
9
+ Time.zone = "Pacific Time (US & Canada)"
10
+ Zone = Time.zone.now
11
+ rescue LoadError
12
+ warn 'Mocking time with zone'
13
+ module ActiveSupport
14
+ class TimeWithZone
15
+ def initialize(utc_time, zone)
16
+ end
17
+ end
18
+ end
19
+ Zone = ActiveSupport::TimeWithZone.new(Time.now.utc, 'EST')
20
+ end
21
+
22
+ class BSONTest < Test::Unit::TestCase
23
+
24
+ include BSON
25
+
26
+ def setup
27
+ @encoder = BSON::BSON_CODER
28
+ end
29
+
30
+ def assert_doc_pass(doc, options={})
31
+ bson = @encoder.serialize(doc)
32
+ if options[:debug]
33
+ puts "DEBUGGING DOC:"
34
+ p bson.to_a
35
+ puts "DESERIALIZES TO:"
36
+ end
37
+ assert_equal @encoder.serialize(doc).to_a, bson.to_a
38
+ assert_equal doc, @encoder.deserialize(bson)
39
+ end
40
+
41
+ def test_require_hash
42
+ assert_raise_error InvalidDocument, "takes a Hash" do
43
+ BSON.serialize('foo')
44
+ end
45
+
46
+ assert_raise_error InvalidDocument, "takes a Hash" do
47
+ BSON.serialize(Object.new)
48
+ end
49
+
50
+ assert_raise_error InvalidDocument, "takes a Hash" do
51
+ BSON.serialize(Set.new)
52
+ end
53
+ end
54
+
55
+ def test_string
56
+ doc = {'doc' => 'hello, world'}
57
+ assert_doc_pass(doc)
58
+ end
59
+
60
+ def test_valid_utf8_string
61
+ doc = {'doc' => 'aé'}
62
+ assert_doc_pass(doc)
63
+ end
64
+
65
+ def test_valid_utf8_key
66
+ doc = {'aé' => 'hello'}
67
+ assert_doc_pass(doc)
68
+ end
69
+
70
+ def test_document_length
71
+ doc = {'name' => 'a' * 5 * 1024 * 1024}
72
+ assert_raise InvalidDocument do
73
+ assert @encoder.serialize(doc)
74
+ end
75
+ end
76
+
77
+ def test_round_trip
78
+ doc = {'doc' => 123}
79
+ @encoder.deserialize(@encoder.serialize(doc))
80
+ end
81
+
82
+ # In 1.8 we test that other string encodings raise an exception.
83
+ # In 1.9 we test that they get auto-converted.
84
+ if RUBY_VERSION < '1.9'
85
+ if ! RUBY_PLATFORM =~ /java/
86
+ require 'iconv'
87
+ def test_non_utf8_string
88
+ string = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
89
+ doc = {'doc' => string}
90
+ assert_doc_pass(doc)
91
+ assert_raise InvalidStringEncoding do
92
+ @encoder.serialize(doc)
93
+ end
94
+ end
95
+
96
+ def test_non_utf8_key
97
+ key = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
98
+ doc = {key => 'hello'}
99
+ assert_raise InvalidStringEncoding do
100
+ @encoder.serialize(doc)
101
+ end
102
+ end
103
+ end
104
+ else
105
+ def test_non_utf8_string
106
+ bson = BSON::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
107
+ result = BSON::BSON_CODER.deserialize(bson)['str']
108
+ assert_equal 'aé', result
109
+ assert_equal 'UTF-8', result.encoding.name
110
+ end
111
+
112
+ def test_non_utf8_key
113
+ bson = BSON::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
114
+ assert_equal 'hello', BSON::BSON_CODER.deserialize(bson)['aé']
115
+ end
116
+
117
+ # Based on a test from sqlite3-ruby
118
+ def test_default_internal_is_honored
119
+ before_enc = Encoding.default_internal
120
+
121
+ str = "壁に耳あり、障子に目あり"
122
+ bson = BSON::BSON_CODER.serialize("x" => str)
123
+
124
+ Encoding.default_internal = 'EUC-JP'
125
+ out = BSON::BSON_CODER.deserialize(bson)["x"]
126
+
127
+ assert_equal Encoding.default_internal, out.encoding
128
+ assert_equal str.encode('EUC-JP'), out
129
+ assert_equal str, out.encode(str.encoding)
130
+ ensure
131
+ Encoding.default_internal = before_enc
132
+ end
133
+ end
134
+
135
+ def test_code
136
+ doc = {'$where' => Code.new('this.a.b < this.b')}
137
+ assert_doc_pass(doc)
138
+ end
139
+
140
+ def test_code_with_scope
141
+ doc = {'$where' => Code.new('this.a.b < this.b', {'foo' => 1})}
142
+ assert_doc_pass(doc)
143
+ end
144
+
145
+ def test_double
146
+ doc = {'doc' => 41.25}
147
+ assert_doc_pass(doc)
148
+ end
149
+
150
+ def test_int
151
+ doc = {'doc' => 42}
152
+ assert_doc_pass(doc)
153
+
154
+ doc = {"doc" => -5600}
155
+ assert_doc_pass(doc)
156
+
157
+ doc = {"doc" => 2147483647}
158
+ assert_doc_pass(doc)
159
+
160
+ doc = {"doc" => -2147483648}
161
+ assert_doc_pass(doc)
162
+ end
163
+
164
+ def test_ordered_hash
165
+ doc = BSON::OrderedHash.new
166
+ doc["b"] = 1
167
+ doc["a"] = 2
168
+ doc["c"] = 3
169
+ doc["d"] = 4
170
+ assert_doc_pass(doc)
171
+ end
172
+
173
+ def test_object
174
+ doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
175
+ assert_doc_pass(doc)
176
+ end
177
+
178
+ def test_embedded_document_with_nil
179
+ doc = {'doc' => {'age' => 42, 'name' => nil, 'shoe_size' => 9.5}}
180
+ assert_doc_pass(doc)
181
+ end
182
+
183
+ def test_embedded_document_with_date
184
+ doc = {'doc' => {'age' => 42, 'date' => Time.now.utc, 'shoe_size' => 9.5}}
185
+ bson = @encoder.serialize(doc)
186
+ doc2 = @encoder.deserialize(bson)
187
+ assert doc['doc']
188
+ assert_equal 42, doc['doc']['age']
189
+ assert_equal 9.5, doc['doc']['shoe_size']
190
+ assert_in_delta Time.now, doc['doc']['date'], 1
191
+ end
192
+
193
+ def test_oid
194
+ doc = {'doc' => ObjectId.new}
195
+ assert_doc_pass(doc)
196
+ end
197
+
198
+ def test_array
199
+ doc = {'doc' => [1, 2, 'a', 'b']}
200
+ assert_doc_pass(doc)
201
+ end
202
+
203
+ def test_regex
204
+ doc = {'doc' => /foobar/i}
205
+ assert_doc_pass(doc)
206
+ end
207
+
208
+ def test_boolean
209
+ doc = {'doc' => true}
210
+ assert_doc_pass(doc)
211
+ end
212
+
213
+ def test_date
214
+ doc = {'date' => Time.now}
215
+ bson = @encoder.serialize(doc)
216
+ doc2 = @encoder.deserialize(bson)
217
+ # Mongo only stores up to the millisecond
218
+ assert_in_delta doc['date'], doc2['date'], 0.001
219
+ end
220
+
221
+ def test_date_returns_as_utc
222
+ doc = {'date' => Time.now}
223
+ bson = @encoder.serialize(doc)
224
+ doc2 = @encoder.deserialize(bson)
225
+ assert doc2['date'].utc?
226
+ end
227
+
228
+ def test_date_before_epoch
229
+ begin
230
+ doc = {'date' => Time.utc(1600)}
231
+ bson = @encoder.serialize(doc)
232
+ doc2 = @encoder.deserialize(bson)
233
+ # Mongo only stores up to the millisecond
234
+ assert_in_delta doc['date'], doc2['date'], 2
235
+ rescue ArgumentError
236
+ # some versions of Ruby won't let you create pre-epoch Time instances
237
+ #
238
+ # TODO figure out how that will work if somebady has saved data
239
+ # w/ early dates already and is just querying for it.
240
+ end
241
+ end
242
+
243
+ def test_exeption_on_using_unsupported_date_class
244
+ [DateTime.now, Date.today, Zone].each do |invalid_date|
245
+ doc = {:date => invalid_date}
246
+ begin
247
+ bson = BSON::BSON_CODER.serialize(doc)
248
+ rescue => e
249
+ ensure
250
+ if !invalid_date.is_a? Time
251
+ assert_equal InvalidDocument, e.class
252
+ assert_match /UTC Time/, e.message
253
+ end
254
+ end
255
+ end
256
+ end
257
+
258
+ def test_dbref
259
+ oid = ObjectId.new
260
+ doc = {}
261
+ doc['dbref'] = DBRef.new('namespace', oid)
262
+ bson = @encoder.serialize(doc)
263
+ doc2 = @encoder.deserialize(bson)
264
+
265
+ # Java doesn't deserialize to DBRefs
266
+ if RUBY_PLATFORM =~ /java/
267
+ assert_equal 'namespace', doc2['dbref']['$ns']
268
+ assert_equal oid, doc2['dbref']['$id']
269
+ else
270
+ assert_equal 'namespace', doc2['dbref'].namespace
271
+ assert_equal oid, doc2['dbref'].object_id
272
+ end
273
+ end
274
+
275
+ def test_symbol
276
+ doc = {'sym' => :foo}
277
+ bson = @encoder.serialize(doc)
278
+ doc2 = @encoder.deserialize(bson)
279
+ assert_equal :foo, doc2['sym']
280
+ end
281
+
282
+ def test_binary
283
+ bin = Binary.new
284
+ 'binstring'.each_byte { |b| bin.put(b) }
285
+
286
+ doc = {'bin' => bin}
287
+ bson = @encoder.serialize(doc)
288
+ doc2 = @encoder.deserialize(bson)
289
+ bin2 = doc2['bin']
290
+ assert_kind_of Binary, bin2
291
+ assert_equal 'binstring', bin2.to_s
292
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
293
+ end
294
+
295
+ def test_binary_with_string
296
+ b = Binary.new('somebinarystring')
297
+ doc = {'bin' => b}
298
+ bson = @encoder.serialize(doc)
299
+ doc2 = @encoder.deserialize(bson)
300
+ bin2 = doc2['bin']
301
+ assert_kind_of Binary, bin2
302
+ assert_equal 'somebinarystring', bin2.to_s
303
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
304
+ end
305
+
306
+ def test_binary_type
307
+ bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED)
308
+
309
+ doc = {'bin' => bin}
310
+ bson = @encoder.serialize(doc)
311
+ doc2 = @encoder.deserialize(bson)
312
+ bin2 = doc2['bin']
313
+ assert_kind_of Binary, bin2
314
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
315
+ assert_equal Binary::SUBTYPE_USER_DEFINED, bin2.subtype
316
+ end
317
+
318
+ # Java doesn't support binary subtype 0 yet
319
+ if !(RUBY_PLATFORM =~ /java/)
320
+ def test_binary_subtype_0
321
+ bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_SIMPLE)
322
+
323
+ doc = {'bin' => bin}
324
+ bson = @encoder.serialize(doc)
325
+ doc2 = @encoder.deserialize(bson)
326
+ bin2 = doc2['bin']
327
+ assert_kind_of Binary, bin2
328
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
329
+ assert_equal Binary::SUBTYPE_SIMPLE, bin2.subtype
330
+ end
331
+ end
332
+
333
+ def test_binary_byte_buffer
334
+ bb = Binary.new
335
+ 5.times { |i| bb.put(i + 1) }
336
+
337
+ doc = {'bin' => bb}
338
+ bson = @encoder.serialize(doc)
339
+ doc2 = @encoder.deserialize(bson)
340
+ bin2 = doc2['bin']
341
+ assert_kind_of Binary, bin2
342
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
343
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
344
+ end
345
+
346
+ def test_put_id_first
347
+ val = BSON::OrderedHash.new
348
+ val['not_id'] = 1
349
+ val['_id'] = 2
350
+ roundtrip = @encoder.deserialize(@encoder.serialize(val, false, true).to_s)
351
+ assert_kind_of BSON::OrderedHash, roundtrip
352
+ assert_equal '_id', roundtrip.keys.first
353
+
354
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
355
+ roundtrip = @encoder.deserialize(@encoder.serialize(val, false, true).to_s)
356
+ assert_kind_of BSON::OrderedHash, roundtrip
357
+ assert_equal '_id', roundtrip.keys.first
358
+ end
359
+
360
+ def test_nil_id
361
+ doc = {"_id" => nil}
362
+ assert_doc_pass(doc)
363
+ end
364
+
365
+ if !(RUBY_PLATFORM =~ /java/)
366
+ def test_timestamp
367
+ val = {"test" => [4, 20]}
368
+ assert_equal val, @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
369
+ 0x11, 0x74, 0x65, 0x73,
370
+ 0x74, 0x00, 0x04, 0x00,
371
+ 0x00, 0x00, 0x14, 0x00,
372
+ 0x00, 0x00, 0x00])
373
+
374
+ end
375
+ end
376
+
377
+ def test_overflow
378
+ doc = {"x" => 2**75}
379
+ assert_raise RangeError do
380
+ bson = @encoder.serialize(doc)
381
+ end
382
+
383
+ doc = {"x" => 9223372036854775}
384
+ assert_doc_pass(doc)
385
+
386
+ doc = {"x" => 9223372036854775807}
387
+ assert_doc_pass(doc)
388
+
389
+ doc["x"] = doc["x"] + 1
390
+ assert_raise RangeError do
391
+ bson = @encoder.serialize(doc)
392
+ end
393
+
394
+ doc = {"x" => -9223372036854775}
395
+ assert_doc_pass(doc)
396
+
397
+ doc = {"x" => -9223372036854775808}
398
+ assert_doc_pass(doc)
399
+
400
+ doc["x"] = doc["x"] - 1
401
+ assert_raise RangeError do
402
+ bson = BSON::BSON_CODER.serialize(doc)
403
+ end
404
+ end
405
+
406
+ def test_invalid_numeric_types
407
+ [BigDecimal.new("1.0"), Complex(0, 1), Rational(2, 3)].each do |type|
408
+ doc = {"x" => type}
409
+ begin
410
+ @encoder.serialize(doc)
411
+ rescue => e
412
+ ensure
413
+ assert_equal InvalidDocument, e.class
414
+ assert_match /Cannot serialize/, e.message
415
+ end
416
+ end
417
+ end
418
+
419
+ def test_do_not_change_original_object
420
+ val = BSON::OrderedHash.new
421
+ val['not_id'] = 1
422
+ val['_id'] = 2
423
+ assert val.keys.include?('_id')
424
+ @encoder.serialize(val)
425
+ assert val.keys.include?('_id')
426
+
427
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
428
+ assert val.keys.include?(:_id)
429
+ @encoder.serialize(val)
430
+ assert val.keys.include?(:_id)
431
+ end
432
+
433
+ # note we only test for _id here because in the general case we will
434
+ # write duplicates for :key and "key". _id is a special case because
435
+ # we call has_key? to check for it's existence rather than just iterating
436
+ # over it like we do for the rest of the keys. thus, things like
437
+ # HashWithIndifferentAccess can cause problems for _id but not for other
438
+ # keys. rather than require rails to test with HWIA directly, we do this
439
+ # somewhat hacky test.
440
+ #
441
+ # Note that the driver only eliminates duplicate ids when move_id is true.
442
+ def test_no_duplicate_id
443
+ dup = {"_id" => "foo", :_id => "foo"}
444
+ one = {"_id" => "foo"}
445
+
446
+ assert_equal @encoder.serialize(one, false, true).to_a, @encoder.serialize(dup, false, true).to_a
447
+ end
448
+
449
+ def test_duplicate_keys
450
+ #dup = {"_foo" => "foo", :_foo => "foo"}
451
+ #one = {"_foo" => "foo"}
452
+
453
+ #assert_equal @encoder.serialize(one).to_a, @encoder.serialize(dup).to_a
454
+ warn "Pending test for duplicate keys"
455
+ end
456
+
457
+ def test_no_duplicate_id_when_moving_id
458
+ dup = {"_id" => "foo", :_id => "foo"}
459
+ one = {:_id => "foo"}
460
+
461
+ assert_equal @encoder.serialize(one, false, true).to_s, @encoder.serialize(dup, false, true).to_s
462
+ end
463
+
464
+ def test_null_character
465
+ doc = {"a" => "\x00"}
466
+
467
+ assert_doc_pass(doc)
468
+
469
+ assert_raise InvalidDocument do
470
+ @encoder.serialize({"\x00" => "a"})
471
+ end
472
+
473
+ assert_raise InvalidDocument do
474
+ @encoder.serialize({"a" => (Regexp.compile "ab\x00c")})
475
+ end
476
+ end
477
+
478
+ def test_max_key
479
+ doc = {"a" => MaxKey.new}
480
+ assert_doc_pass(doc)
481
+ end
482
+
483
+ def test_min_key
484
+ doc = {"a" => MinKey.new}
485
+ assert_doc_pass(doc)
486
+ end
487
+
488
+ def test_invalid_object
489
+ o = Object.new
490
+ assert_raise InvalidDocument do
491
+ @encoder.serialize({:foo => o})
492
+ end
493
+
494
+ assert_raise InvalidDocument do
495
+ @encoder.serialize({:foo => Date.today})
496
+ end
497
+ end
498
+
499
+ def test_move_id
500
+ a = BSON::OrderedHash.new
501
+ a['text'] = 'abc'
502
+ a['key'] = 'abc'
503
+ a['_id'] = 1
504
+
505
+
506
+ assert_equal ")\000\000\000\020_id\000\001\000\000\000\002text" +
507
+ "\000\004\000\000\000abc\000\002key\000\004\000\000\000abc\000\000",
508
+ @encoder.serialize(a, false, true).to_s
509
+
510
+ assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" +
511
+ "\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000",
512
+ @encoder.serialize(a, false, false).to_s
513
+ end
514
+
515
+ def test_move_id_with_nested_doc
516
+ b = BSON::OrderedHash.new
517
+ b['text'] = 'abc'
518
+ b['_id'] = 2
519
+ c = BSON::OrderedHash.new
520
+ c['text'] = 'abc'
521
+ c['hash'] = b
522
+ c['_id'] = 3
523
+ assert_equal ">\000\000\000\020_id\000\003\000\000\000\002text" +
524
+ "\000\004\000\000\000abc\000\003hash\000\034\000\000" +
525
+ "\000\002text\000\004\000\000\000abc\000\020_id\000\002\000\000\000\000\000",
526
+ @encoder.serialize(c, false, true).to_s
527
+
528
+ # Java doesn't support this. Isn't actually necessary.
529
+ if !(RUBY_PLATFORM =~ /java/)
530
+ assert_equal ">\000\000\000\002text\000\004\000\000\000abc\000\003hash" +
531
+ "\000\034\000\000\000\002text\000\004\000\000\000abc\000\020_id" +
532
+ "\000\002\000\000\000\000\020_id\000\003\000\000\000\000",
533
+ @encoder.serialize(c, false, false).to_s
534
+ end
535
+ end
536
+
537
+ end
@@ -0,0 +1,190 @@
1
+ # encoding: binary
2
+ require './test/test_helper'
3
+
4
+ class ByteBufferTest < Test::Unit::TestCase
5
+ include BSON
6
+
7
+ def setup
8
+ @buf = ByteBuffer.new
9
+ end
10
+
11
+ def test_initial_state
12
+ assert_equal 0, @buf.position
13
+ assert_equal [], @buf.to_a
14
+ assert_equal "", @buf.to_s
15
+ assert_equal 0, @buf.length
16
+ end
17
+
18
+ def test_nil_get_returns_one_byte
19
+ @buf.put_array([1, 2, 3, 4])
20
+ @buf.rewind
21
+ assert_equal 1, @buf.get
22
+ end
23
+
24
+ def test_one_get_returns_array_length_one
25
+ @buf.put_array([1, 2, 3, 4])
26
+ @buf.rewind
27
+ assert_equal [1], @buf.get(1)
28
+ end
29
+
30
+ def test_zero_get_returns_empty_array
31
+ @buf.put_array([1, 2, 3, 4])
32
+ @buf.rewind
33
+ assert_equal [], @buf.get(0)
34
+ end
35
+
36
+ def test_length
37
+ @buf.put_int 3
38
+ assert_equal 4, @buf.length
39
+ end
40
+
41
+ def test_default_order
42
+ assert_equal :little_endian, @buf.order
43
+ end
44
+
45
+ def test_long_length
46
+ @buf.put_long 1027
47
+ assert_equal 8, @buf.length
48
+ end
49
+
50
+ def test_get_long
51
+ @buf.put_long 1027
52
+ @buf.rewind
53
+ assert_equal 1027, @buf.get_long
54
+ end
55
+
56
+ def test_get_double
57
+ @buf.put_double 41.2
58
+ @buf.rewind
59
+ assert_equal 41.2, @buf.get_double
60
+ end
61
+
62
+ if defined?(Encoding)
63
+ def test_serialize_cstr_converts_encoding_to_utf8
64
+ theta = "hello \xC8".force_encoding("ISO-8859-7")
65
+ ByteBuffer.serialize_cstr(@buf, theta)
66
+ assert_equal "hello \xCE\x98\0", @buf.to_s
67
+ assert_equal Encoding.find('binary'), @buf.to_s.encoding
68
+ end
69
+
70
+ def test_serialize_cstr_validates_data_as_utf8
71
+ assert_raises(Encoding::UndefinedConversionError) do
72
+ ByteBuffer.serialize_cstr(@buf, "hello \xFF")
73
+ end
74
+ end
75
+ else
76
+ def test_serialize_cstr_forces_encoding_to_utf8
77
+ # Unicode snowman (\u2603)
78
+ ByteBuffer.serialize_cstr(@buf, "hello \342\230\203")
79
+ assert_equal "hello \342\230\203\0", @buf.to_s
80
+ end
81
+
82
+ def test_serialize_cstr_validates_data_as_utf8
83
+ assert_raises(BSON::InvalidStringEncoding) do
84
+ ByteBuffer.serialize_cstr(@buf, "hello \xFF")
85
+ end
86
+ end
87
+ end
88
+
89
+ def test_put_negative_byte
90
+ @buf.put(-1)
91
+ @buf.rewind
92
+ assert_equal 255, @buf.get
93
+ assert_equal "\xFF", @buf.to_s
94
+ end
95
+
96
+ def test_put_with_offset
97
+ @buf.put(1)
98
+ @buf.put(2, 0)
99
+ @buf.put(3, 3)
100
+ assert_equal "\x02\x00\x00\x03", @buf.to_s
101
+ end
102
+
103
+ def test_put_array_with_offset
104
+ @buf.put(1)
105
+ @buf.put_array([2, 3], 0)
106
+ @buf.put_array([4, 5], 4)
107
+ assert_equal "\x02\x03\x00\x00\x04\x05", @buf.to_s
108
+ end
109
+
110
+ def test_put_int_with_offset
111
+ @buf.put(1)
112
+ @buf.put_int(2, 0)
113
+ @buf.put_int(3, 5)
114
+ assert_equal "\x02\x00\x00\x00\x00\x03\x00\x00\x00", @buf.to_s
115
+ end
116
+
117
+ def test_put_long_with_offset
118
+ @buf.put(1)
119
+ @buf.put_long(2, 0)
120
+ @buf.put_long(3, 9)
121
+ assert_equal(
122
+ "\x02\x00\x00\x00\x00\x00\x00\x00" +
123
+ "\x00" +
124
+ "\x03\x00\x00\x00\x00\x00\x00\x00",
125
+ @buf.to_s)
126
+ end
127
+
128
+ def test_put_binary
129
+ @buf.put(1)
130
+ @buf.put_binary("\x02\x03", 0)
131
+ @buf.put_binary("\x04\x05", 4)
132
+ assert_equal "\x02\x03\x00\x00\x04\x05", @buf.to_s
133
+ end
134
+
135
+ def test_rewrite
136
+ @buf.put_int(0)
137
+ @buf.rewind
138
+ @buf.put_int(1027)
139
+ assert_equal 4, @buf.length
140
+ @buf.rewind
141
+ assert_equal 1027, @buf.get_int
142
+ assert_equal 4, @buf.position
143
+ end
144
+
145
+ def test_prepend_byte_buffer
146
+ @buf.put_int(4)
147
+ new_buf = ByteBuffer.new([5, 0, 0, 0])
148
+ @buf.prepend!(new_buf)
149
+ assert_equal [5, 0, 0, 0, 4, 0, 0, 0], @buf.to_a
150
+ end
151
+
152
+ def test_append_byte_buffer
153
+ @buf.put_int(4)
154
+ new_buf = ByteBuffer.new([5, 0, 0, 0])
155
+ @buf.append!(new_buf)
156
+ assert_equal [4, 0, 0, 0, 5, 0, 0, 0], @buf.to_a
157
+ end
158
+
159
+ def test_array_as_initial_input
160
+ @buf = ByteBuffer.new([5, 0, 0, 0])
161
+ assert_equal 4, @buf.size
162
+ assert_equal "\x05\x00\x00\x00", @buf.to_s
163
+ assert_equal [5, 0, 0, 0], @buf.to_a
164
+ @buf.put_int(32)
165
+ @buf.rewind
166
+ assert_equal 5, @buf.get_int
167
+ assert_equal 32, @buf.get_int
168
+ end
169
+
170
+ def test_binary_string_as_initial_input
171
+ str = "abcd"
172
+ str.force_encoding('binary') if str.respond_to?(:force_encoding)
173
+ @buf = ByteBuffer.new(str)
174
+ assert_equal "abcd", @buf.to_s
175
+ assert_equal [97, 98, 99, 100], @buf.to_a
176
+ @buf.put_int(0)
177
+ assert_equal [97, 98, 99, 100, 0, 0, 0, 0], @buf.to_a
178
+ end
179
+
180
+ def test_more
181
+ assert !@buf.more?
182
+ @buf.put_int(5)
183
+ assert !@buf.more?
184
+ @buf.rewind
185
+ assert @buf.more?
186
+ @buf.get_int
187
+ assert !@buf.more?
188
+ end
189
+
190
+ end