jonbell-mongo 1.3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/LICENSE.txt +190 -0
  2. data/README.md +333 -0
  3. data/Rakefile +215 -0
  4. data/bin/mongo_console +21 -0
  5. data/docs/CREDITS.md +123 -0
  6. data/docs/FAQ.md +116 -0
  7. data/docs/GridFS.md +158 -0
  8. data/docs/HISTORY.md +263 -0
  9. data/docs/RELEASES.md +33 -0
  10. data/docs/REPLICA_SETS.md +72 -0
  11. data/docs/TUTORIAL.md +247 -0
  12. data/docs/WRITE_CONCERN.md +28 -0
  13. data/lib/mongo.rb +97 -0
  14. data/lib/mongo/collection.rb +895 -0
  15. data/lib/mongo/connection.rb +926 -0
  16. data/lib/mongo/cursor.rb +474 -0
  17. data/lib/mongo/db.rb +617 -0
  18. data/lib/mongo/exceptions.rb +71 -0
  19. data/lib/mongo/gridfs/grid.rb +107 -0
  20. data/lib/mongo/gridfs/grid_ext.rb +57 -0
  21. data/lib/mongo/gridfs/grid_file_system.rb +146 -0
  22. data/lib/mongo/gridfs/grid_io.rb +485 -0
  23. data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
  24. data/lib/mongo/repl_set_connection.rb +356 -0
  25. data/lib/mongo/util/conversions.rb +89 -0
  26. data/lib/mongo/util/core_ext.rb +60 -0
  27. data/lib/mongo/util/pool.rb +177 -0
  28. data/lib/mongo/util/server_version.rb +71 -0
  29. data/lib/mongo/util/support.rb +82 -0
  30. data/lib/mongo/util/uri_parser.rb +185 -0
  31. data/mongo.gemspec +34 -0
  32. data/test/auxillary/1.4_features.rb +166 -0
  33. data/test/auxillary/authentication_test.rb +68 -0
  34. data/test/auxillary/autoreconnect_test.rb +41 -0
  35. data/test/auxillary/fork_test.rb +30 -0
  36. data/test/auxillary/repl_set_auth_test.rb +58 -0
  37. data/test/auxillary/slave_connection_test.rb +36 -0
  38. data/test/auxillary/threaded_authentication_test.rb +101 -0
  39. data/test/bson/binary_test.rb +15 -0
  40. data/test/bson/bson_test.rb +654 -0
  41. data/test/bson/byte_buffer_test.rb +208 -0
  42. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  43. data/test/bson/json_test.rb +17 -0
  44. data/test/bson/object_id_test.rb +154 -0
  45. data/test/bson/ordered_hash_test.rb +210 -0
  46. data/test/bson/timestamp_test.rb +24 -0
  47. data/test/collection_test.rb +910 -0
  48. data/test/connection_test.rb +324 -0
  49. data/test/conversions_test.rb +119 -0
  50. data/test/cursor_fail_test.rb +75 -0
  51. data/test/cursor_message_test.rb +43 -0
  52. data/test/cursor_test.rb +483 -0
  53. data/test/db_api_test.rb +738 -0
  54. data/test/db_connection_test.rb +15 -0
  55. data/test/db_test.rb +315 -0
  56. data/test/grid_file_system_test.rb +259 -0
  57. data/test/grid_io_test.rb +209 -0
  58. data/test/grid_test.rb +258 -0
  59. data/test/load/thin/load.rb +24 -0
  60. data/test/load/unicorn/load.rb +23 -0
  61. data/test/replica_sets/connect_test.rb +112 -0
  62. data/test/replica_sets/connection_string_test.rb +32 -0
  63. data/test/replica_sets/count_test.rb +35 -0
  64. data/test/replica_sets/insert_test.rb +53 -0
  65. data/test/replica_sets/pooled_insert_test.rb +55 -0
  66. data/test/replica_sets/query_secondaries.rb +108 -0
  67. data/test/replica_sets/query_test.rb +51 -0
  68. data/test/replica_sets/replication_ack_test.rb +66 -0
  69. data/test/replica_sets/rs_test_helper.rb +27 -0
  70. data/test/safe_test.rb +68 -0
  71. data/test/support/hash_with_indifferent_access.rb +186 -0
  72. data/test/support/keys.rb +45 -0
  73. data/test/support_test.rb +18 -0
  74. data/test/test_helper.rb +102 -0
  75. data/test/threading/threading_with_large_pool_test.rb +90 -0
  76. data/test/threading_test.rb +87 -0
  77. data/test/tools/auth_repl_set_manager.rb +14 -0
  78. data/test/tools/repl_set_manager.rb +266 -0
  79. data/test/unit/collection_test.rb +130 -0
  80. data/test/unit/connection_test.rb +85 -0
  81. data/test/unit/cursor_test.rb +109 -0
  82. data/test/unit/db_test.rb +94 -0
  83. data/test/unit/grid_test.rb +49 -0
  84. data/test/unit/pool_test.rb +9 -0
  85. data/test/unit/repl_set_connection_test.rb +59 -0
  86. data/test/unit/safe_test.rb +125 -0
  87. data/test/uri_test.rb +91 -0
  88. metadata +224 -0
@@ -0,0 +1,15 @@
1
+ require './test/test_helper'
2
+
3
+ class DBConnectionTest < Test::Unit::TestCase
4
+
5
+ include Mongo
6
+
7
+ def test_no_exceptions
8
+ host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
9
+ port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
10
+ db = Connection.new(host, port).db(MONGO_TEST_DB)
11
+ coll = db.collection('test')
12
+ coll.remove
13
+ db.get_last_error
14
+ end
15
+ end
data/test/db_test.rb ADDED
@@ -0,0 +1,315 @@
1
+ require './test/test_helper'
2
+ require 'digest/md5'
3
+ require 'stringio'
4
+ require 'logger'
5
+
6
+ class TestPKFactory
7
+ def create_pk(row)
8
+ row['_id'] ||= BSON::ObjectId.new
9
+ row
10
+ end
11
+ end
12
+
13
+ class DBTest < Test::Unit::TestCase
14
+
15
+ include Mongo
16
+
17
+ @@conn = standard_connection
18
+ @@db = @@conn.db(MONGO_TEST_DB)
19
+ @@users = @@db.collection('system.users')
20
+ @@version = @@conn.server_version
21
+
22
+ def test_close
23
+ @@conn.close
24
+ assert !@@conn.connected?
25
+ begin
26
+ @@db.collection('test').insert('a' => 1)
27
+ fail "expected 'NilClass' exception"
28
+ rescue => ex
29
+ assert_match(/NilClass/, ex.to_s)
30
+ ensure
31
+ @@db = standard_connection.db(MONGO_TEST_DB)
32
+ @@users = @@db.collection('system.users')
33
+ end
34
+ end
35
+
36
+ def test_create_collection
37
+ col = @@db.create_collection('foo')
38
+ assert_equal @@db['foo'].name, col.name
39
+
40
+ col = @@db.create_collection(:foo)
41
+ assert_equal @@db['foo'].name, col.name
42
+
43
+ @@db.drop_collection('foo')
44
+ end
45
+
46
+ def test_get_and_drop_collection
47
+ db = @@conn.db(MONGO_TEST_DB, :strict => true)
48
+ db.create_collection('foo')
49
+ assert db.collection('foo')
50
+ assert db.drop_collection('foo')
51
+
52
+ db.create_collection(:foo)
53
+ assert db.collection(:foo)
54
+ assert db.drop_collection(:foo)
55
+ end
56
+
57
+ def test_logger
58
+ output = StringIO.new
59
+ logger = Logger.new(output)
60
+ logger.level = Logger::DEBUG
61
+ conn = standard_connection(:logger => logger)
62
+ assert_equal logger, conn.logger
63
+
64
+ conn.logger.debug 'testing'
65
+ assert output.string.include?('testing')
66
+ end
67
+
68
+ def test_full_coll_name
69
+ coll = @@db.collection('test')
70
+ assert_equal "#{MONGO_TEST_DB}.test", @@db.full_collection_name(coll.name)
71
+ end
72
+
73
+ def test_collection_names
74
+ @@db.collection("test").insert("foo" => 5)
75
+ @@db.collection("test.mike").insert("bar" => 0)
76
+
77
+ colls = @@db.collection_names()
78
+ assert colls.include?("test")
79
+ assert colls.include?("test.mike")
80
+ colls.each { |name|
81
+ assert !name.include?("$")
82
+ }
83
+ end
84
+
85
+ def test_collections
86
+ @@db.collection("test.durran").insert("foo" => 5)
87
+ @@db.collection("test.les").insert("bar" => 0)
88
+
89
+ colls = @@db.collections()
90
+ assert_not_nil colls.select { |coll| coll.name == "test.durran" }
91
+ assert_not_nil colls.select { |coll| coll.name == "test.les" }
92
+ assert_equal [], colls.select { |coll| coll.name == "does_not_exist" }
93
+
94
+ assert_kind_of Collection, colls[0]
95
+ end
96
+
97
+ def test_pk_factory
98
+ db = standard_connection.db(MONGO_TEST_DB, :pk => TestPKFactory.new)
99
+ coll = db.collection('test')
100
+ coll.remove
101
+
102
+ insert_id = coll.insert('name' => 'Fred', 'age' => 42)
103
+ # new id gets added to returned object
104
+ row = coll.find_one({'name' => 'Fred'})
105
+ oid = row['_id']
106
+ assert_not_nil oid
107
+ assert_equal insert_id, oid
108
+
109
+ oid = BSON::ObjectId.new
110
+ data = {'_id' => oid, 'name' => 'Barney', 'age' => 41}
111
+ coll.insert(data)
112
+ row = coll.find_one({'name' => data['name']})
113
+ db_oid = row['_id']
114
+ assert_equal oid, db_oid
115
+ assert_equal data, row
116
+
117
+ coll.remove
118
+ end
119
+
120
+ def test_pk_factory_reset
121
+ conn = standard_connection
122
+ db = conn.db(MONGO_TEST_DB)
123
+ db.pk_factory = Object.new # first time
124
+ begin
125
+ db.pk_factory = Object.new
126
+ fail "error: expected exception"
127
+ rescue => ex
128
+ assert_match(/Cannot change/, ex.to_s)
129
+ ensure
130
+ conn.close
131
+ end
132
+ end
133
+
134
+ def test_authenticate
135
+ @@db.add_user('spongebob', 'squarepants')
136
+ assert_raise Mongo::AuthenticationError do
137
+ assert !@@db.authenticate('nobody', 'nopassword')
138
+ end
139
+ assert_raise Mongo::AuthenticationError do
140
+ assert !@@db.authenticate('spongebob' , 'squareliederhosen')
141
+ end
142
+ assert @@db.authenticate('spongebob', 'squarepants')
143
+ @@db.logout
144
+ @@db.remove_user('spongebob')
145
+ end
146
+
147
+ def test_authenticate_with_special_characters
148
+ assert @@db.add_user('foo:bar', '@foo')
149
+ assert @@db.authenticate('foo:bar', '@foo')
150
+ @@db.logout
151
+ @@db.remove_user('foo:bar')
152
+ end
153
+
154
+ def test_authenticate_with_connection_uri
155
+ @@db.add_user('spongebob', 'squarepants')
156
+ assert Mongo::Connection.from_uri("mongodb://spongebob:squarepants@#{host_port}/#{@@db.name}")
157
+
158
+ assert_raise Mongo::AuthenticationError do
159
+ Mongo::Connection.from_uri("mongodb://wrong:info@#{host_port}/#{@@db.name}")
160
+ end
161
+ end
162
+
163
+ def test_logout
164
+ assert @@db.logout
165
+ end
166
+
167
+ def test_command
168
+ assert_raise OperationFailure do
169
+ @@db.command({:non_command => 1}, :check_response => true)
170
+ end
171
+
172
+ result = @@db.command({:non_command => 1}, :check_response => false)
173
+ assert !Mongo::Support.ok?(result)
174
+ end
175
+
176
+ def test_error
177
+ @@db.reset_error_history
178
+ assert_nil @@db.get_last_error['err']
179
+ assert !@@db.error?
180
+ assert_nil @@db.previous_error
181
+
182
+ @@db.command({:forceerror => 1}, :check_response => false)
183
+ assert @@db.error?
184
+ assert_not_nil @@db.get_last_error['err']
185
+ assert_not_nil @@db.previous_error
186
+
187
+ @@db.command({:forceerror => 1}, :check_response => false)
188
+ assert @@db.error?
189
+ assert @@db.get_last_error['err']
190
+ prev_error = @@db.previous_error
191
+ assert_equal 1, prev_error['nPrev']
192
+ assert_equal prev_error["err"], @@db.get_last_error['err']
193
+
194
+ @@db.collection('test').find_one
195
+ assert_nil @@db.get_last_error['err']
196
+ assert !@@db.error?
197
+ assert @@db.previous_error
198
+ assert_equal 2, @@db.previous_error['nPrev']
199
+
200
+ @@db.reset_error_history
201
+ assert_nil @@db.get_last_error['err']
202
+ assert !@@db.error?
203
+ assert_nil @@db.previous_error
204
+ end
205
+
206
+ def test_check_command_response
207
+ command = {:forceerror => 1}
208
+ assert_raise OperationFailure do
209
+ @@db.command(command)
210
+ end
211
+ end
212
+
213
+ def test_last_status
214
+ @@db['test'].remove
215
+ @@db['test'].save("i" => 1)
216
+
217
+ @@db['test'].update({"i" => 1}, {"$set" => {"i" => 2}})
218
+ assert @@db.get_last_error()["updatedExisting"]
219
+
220
+ @@db['test'].update({"i" => 1}, {"$set" => {"i" => 500}})
221
+ assert !@@db.get_last_error()["updatedExisting"]
222
+ end
223
+
224
+ def test_text_port_number_raises_no_errors
225
+ conn = standard_connection
226
+ db = conn[MONGO_TEST_DB]
227
+ db.collection('users').remove
228
+ end
229
+
230
+ def test_user_management
231
+ @@db.add_user("bob", "secret")
232
+ assert @@db.authenticate("bob", "secret")
233
+ @@db.logout
234
+ assert @@db.remove_user("bob")
235
+ assert_raise Mongo::AuthenticationError do
236
+ @@db.authenticate("bob", "secret")
237
+ end
238
+ end
239
+
240
+ def test_remove_non_existant_user
241
+ assert !@@db.remove_user("joe")
242
+ end
243
+
244
+ def test_stored_function_management
245
+ @@db.add_stored_function("sum", "function (x, y) { return x + y; }")
246
+ assert_equal @@db.eval("return sum(2,3);"), 5
247
+ assert @@db.remove_stored_function("sum")
248
+ assert_raise OperationFailure do
249
+ @@db.eval("return sum(2,3);")
250
+ end
251
+ end
252
+
253
+ def test_eval
254
+ @@db.eval("db.system.save({_id:'hello', value: function() { print('hello'); } })")
255
+ assert_equal 'hello', @@db['system'].find_one['_id']
256
+ end
257
+
258
+ if @@version >= "1.3.5"
259
+ def test_db_stats
260
+ stats = @@db.stats
261
+ assert stats.has_key?('collections')
262
+ assert stats.has_key?('dataSize')
263
+ end
264
+ end
265
+
266
+ context "database profiling" do
267
+ setup do
268
+ @db = @@conn[MONGO_TEST_DB]
269
+ @coll = @db['test']
270
+ @coll.remove
271
+ @r1 = @coll.insert('a' => 1) # collection not created until it's used
272
+ end
273
+
274
+ should "set default profiling level" do
275
+ assert_equal :off, @db.profiling_level
276
+ end
277
+
278
+ should "change profiling level" do
279
+ @db.profiling_level = :slow_only
280
+ assert_equal :slow_only, @db.profiling_level
281
+ @db.profiling_level = :off
282
+ assert_equal :off, @db.profiling_level
283
+ @db.profiling_level = :all
284
+ assert_equal :all, @db.profiling_level
285
+ begin
286
+ @db.profiling_level = :medium
287
+ fail "shouldn't be able to do this"
288
+ rescue
289
+ end
290
+ end
291
+
292
+ should "return profiling info" do
293
+ @db.profiling_level = :all
294
+ @coll.find()
295
+ @db.profiling_level = :off
296
+
297
+ info = @db.profiling_info
298
+ assert_kind_of Array, info
299
+ assert info.length >= 1
300
+ first = info.first
301
+ assert_kind_of Time, first['ts']
302
+ assert_kind_of Numeric, first['millis']
303
+ end
304
+
305
+ should "validate collection" do
306
+ doc = @db.validate_collection(@coll.name)
307
+ if @@version >= "1.9.1"
308
+ assert doc['valid']
309
+ else
310
+ assert doc['result']
311
+ end
312
+ end
313
+
314
+ end
315
+ end
@@ -0,0 +1,259 @@
1
+ require './test/test_helper'
2
+
3
+ class GridFileSystemTest < Test::Unit::TestCase
4
+ context "GridFileSystem:" do
5
+ setup do
6
+ @con = standard_connection
7
+ @db = @con.db(MONGO_TEST_DB)
8
+ end
9
+
10
+ teardown do
11
+ @db.drop_collection('fs.files')
12
+ @db.drop_collection('fs.chunks')
13
+ end
14
+
15
+ context "Initialization" do
16
+ setup do
17
+ @chunks_data = "CHUNKS" * 50000
18
+ @grid = GridFileSystem.new(@db)
19
+ @opts = {:safe => true}
20
+ @original_opts = @opts.dup
21
+ @grid.open('sample.file', 'w', @opts) do |f|
22
+ f.write @chunks_data
23
+ end
24
+ end
25
+
26
+ should "not modify original opts" do
27
+ assert_equal @original_opts, @opts
28
+ end
29
+ end
30
+
31
+ context "When reading:" do
32
+ setup do
33
+ @chunks_data = "CHUNKS" * 50000
34
+ @grid = GridFileSystem.new(@db)
35
+ @grid.open('sample.file', 'w') do |f|
36
+ f.write @chunks_data
37
+ end
38
+
39
+ @grid = GridFileSystem.new(@db)
40
+ end
41
+
42
+ should "return existence of the file" do
43
+ file = @grid.exist?(:filename => 'sample.file')
44
+ assert_equal 'sample.file', file['filename']
45
+ end
46
+
47
+ should "return nil if the file doesn't exist" do
48
+ assert_nil @grid.exist?(:filename => 'foo.file')
49
+ end
50
+
51
+ should "read sample data" do
52
+ data = @grid.open('sample.file', 'r') { |f| f.read }
53
+ assert_equal data.length, @chunks_data.length
54
+ end
55
+
56
+ should "have a unique index on chunks" do
57
+ assert @db['fs.chunks'].index_information['files_id_1_n_1']['unique']
58
+ end
59
+
60
+ should "have an index on filename" do
61
+ assert @db['fs.files'].index_information['filename_1_uploadDate_-1']
62
+ end
63
+
64
+ should "return an empty string if length is zero" do
65
+ data = @grid.open('sample.file', 'r') { |f| f.read(0) }
66
+ assert_equal '', data
67
+ end
68
+
69
+ should "return the first n bytes" do
70
+ data = @grid.open('sample.file', 'r') {|f| f.read(288888) }
71
+ assert_equal 288888, data.length
72
+ assert_equal @chunks_data[0...288888], data
73
+ end
74
+
75
+ should "return the first n bytes even with an offset" do
76
+ data = @grid.open('sample.file', 'r') do |f|
77
+ f.seek(1000)
78
+ f.read(288888)
79
+ end
80
+ assert_equal 288888, data.length
81
+ assert_equal @chunks_data[1000...289888], data
82
+ end
83
+ end
84
+
85
+ context "When writing:" do
86
+ setup do
87
+ @data = "BYTES" * 50
88
+ @grid = GridFileSystem.new(@db)
89
+ @grid.open('sample', 'w') do |f|
90
+ f.write @data
91
+ end
92
+ end
93
+
94
+ should "read sample data" do
95
+ data = @grid.open('sample', 'r') { |f| f.read }
96
+ assert_equal data.length, @data.length
97
+ end
98
+
99
+ should "return the total number of bytes written" do
100
+ data = 'a' * 300000
101
+ assert_equal 300000, @grid.open('sample', 'w') {|f| f.write(data) }
102
+ end
103
+
104
+ should "more read sample data" do
105
+ data = @grid.open('sample', 'r') { |f| f.read }
106
+ assert_equal data.length, @data.length
107
+ end
108
+
109
+ should "raise exception if file not found" do
110
+ assert_raise GridFileNotFound do
111
+ @grid.open('io', 'r') { |f| f.write('hello') }
112
+ end
113
+ end
114
+
115
+ should "raise exception if not opened for write" do
116
+ assert_raise GridError do
117
+ @grid.open('sample', 'r') { |f| f.write('hello') }
118
+ end
119
+ end
120
+
121
+ context "and when overwriting the file" do
122
+ setup do
123
+ @old = @grid.open('sample', 'r')
124
+
125
+ @new_data = "DATA" * 10
126
+ sleep(2)
127
+ @grid.open('sample', 'w') do |f|
128
+ f.write @new_data
129
+ end
130
+
131
+ @new = @grid.open('sample', 'r')
132
+ end
133
+
134
+ should "have a newer upload date" do
135
+ assert @new.upload_date > @old.upload_date, "New data is not greater than old date."
136
+ end
137
+
138
+ should "have a different files_id" do
139
+ assert_not_equal @new.files_id, @old.files_id
140
+ end
141
+
142
+ should "contain the new data" do
143
+ assert_equal @new_data, @new.read, "Expected DATA"
144
+ end
145
+
146
+ context "and on a second overwrite" do
147
+ setup do
148
+ sleep(2)
149
+ @new_data = "NEW" * 1000
150
+ @grid.open('sample', 'w') do |f|
151
+ f.write @new_data
152
+ end
153
+
154
+ @ids = @db['fs.files'].find({'filename' => 'sample'}).map {|file| file['_id']}
155
+ end
156
+
157
+ should "write a third version of the file" do
158
+ assert_equal 3, @db['fs.files'].find({'filename' => 'sample'}).count
159
+ assert_equal 3, @db['fs.chunks'].find({'files_id' => {'$in' => @ids}}).count
160
+ end
161
+
162
+ should "remove all versions and their data on delete" do
163
+ @grid.delete('sample')
164
+ assert_equal 0, @db['fs.files'].find({'filename' => 'sample'}).count
165
+ assert_equal 0, @db['fs.chunks'].find({'files_id' => {'$in' => @ids}}).count
166
+ end
167
+
168
+ should "delete old versions on write with :delete_old is passed in" do
169
+ @grid.open('sample', 'w', :delete_old => true) do |f|
170
+ f.write @new_data
171
+ end
172
+ @new_ids = @db['fs.files'].find({'filename' => 'sample'}).map {|file| file['_id']}
173
+ assert_equal 1, @new_ids.length
174
+ id = @new_ids.first
175
+ assert !@ids.include?(id)
176
+ assert_equal 1, @db['fs.files'].find({'filename' => 'sample'}).count
177
+ assert_equal 1, @db['fs.chunks'].find({'files_id' => id}).count
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ context "When writing chunks:" do
184
+ setup do
185
+ data = "B" * 50000
186
+ @grid = GridFileSystem.new(@db)
187
+ @grid.open('sample', 'w', :chunk_size => 1000) do |f|
188
+ f.write data
189
+ end
190
+ end
191
+
192
+ should "write the correct number of chunks" do
193
+ file = @db['fs.files'].find_one({:filename => 'sample'})
194
+ chunks = @db['fs.chunks'].find({'files_id' => file['_id']}).to_a
195
+ assert_equal 50, chunks.length
196
+ end
197
+ end
198
+
199
+ context "Positioning:" do
200
+ setup do
201
+ data = 'hello, world' + '1' * 5000 + 'goodbye!' + '2' * 1000 + '!'
202
+ @grid = GridFileSystem.new(@db)
203
+ @grid.open('hello', 'w', :chunk_size => 1000) do |f|
204
+ f.write data
205
+ end
206
+ end
207
+
208
+ should "seek within chunks" do
209
+ @grid.open('hello', 'r') do |f|
210
+ f.seek(0)
211
+ assert_equal 'h', f.read(1)
212
+ f.seek(7)
213
+ assert_equal 'w', f.read(1)
214
+ f.seek(4)
215
+ assert_equal 'o', f.read(1)
216
+ f.seek(0)
217
+ f.seek(7, IO::SEEK_CUR)
218
+ assert_equal 'w', f.read(1)
219
+ f.seek(-2, IO::SEEK_CUR)
220
+ assert_equal ' ', f.read(1)
221
+ f.seek(-4, IO::SEEK_CUR)
222
+ assert_equal 'l', f.read(1)
223
+ f.seek(3, IO::SEEK_CUR)
224
+ assert_equal 'w', f.read(1)
225
+ end
226
+ end
227
+
228
+ should "seek between chunks" do
229
+ @grid.open('hello', 'r') do |f|
230
+ f.seek(1000)
231
+ assert_equal '11111', f.read(5)
232
+
233
+ f.seek(5009)
234
+ assert_equal '111goodbye!222', f.read(14)
235
+
236
+ f.seek(-1, IO::SEEK_END)
237
+ assert_equal '!', f.read(1)
238
+ f.seek(-6, IO::SEEK_END)
239
+ assert_equal '2', f.read(1)
240
+ end
241
+ end
242
+
243
+ should "tell the current position" do
244
+ @grid.open('hello', 'r') do |f|
245
+ assert_equal 0, f.tell
246
+
247
+ f.seek(999)
248
+ assert_equal 999, f.tell
249
+ end
250
+ end
251
+
252
+ should "seek only in read mode" do
253
+ assert_raise GridError do
254
+ @grid.open('hello', 'w') {|f| f.seek(0) }
255
+ end
256
+ end
257
+ end
258
+ end
259
+ end