data_objects 0.2.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Reader do
4
+
5
+ it "should define a standard API" do
6
+ connection = DataObjects::Connection.new('mock://localhost')
7
+
8
+ command = connection.create_command("SELECT * FROM example")
9
+
10
+ reader = command.execute_reader
11
+
12
+ reader.should respond_to(:close)
13
+ reader.should respond_to(:next!)
14
+ reader.should respond_to(:values)
15
+ reader.should respond_to(:fields)
16
+ end
17
+
18
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Result do
4
+
5
+ it "should define a standard API" do
6
+ connection = DataObjects::Connection.new('mock://localhost')
7
+
8
+ command = connection.create_command("SELECT * FROM example")
9
+
10
+ result = command.execute_non_query
11
+
12
+ # In case the driver needs to access the command or connection to load additional data.
13
+ result.instance_variables.should include('@command')
14
+
15
+ # Affected Rows:
16
+ result.should respond_to(:to_i)
17
+ result.to_i.should == 0
18
+
19
+ # The id of the inserted row.
20
+ result.should respond_to(:insert_id)
21
+ end
22
+
23
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'data_objects'))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), 'do_mock'))
@@ -0,0 +1,374 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'data_objects', 'support', 'pooling')
3
+ require 'timeout'
4
+
5
+ # This implements dispose
6
+ # and works perfectly with
7
+ # pooling.
8
+ class DisposableResource
9
+ include Object::Pooling
10
+ attr_reader :name
11
+
12
+ def initialize(name = "")
13
+ @name = name
14
+ end
15
+
16
+ def dispose
17
+ @name = nil
18
+ end
19
+ end
20
+
21
+ # This baby causes exceptions
22
+ # to be raised when you use
23
+ # it with pooling.
24
+ class UndisposableResource
25
+ end
26
+
27
+ describe Object::Pooling::ResourcePool do
28
+ before :each do
29
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, :expiration_period => 50)
30
+ end
31
+
32
+ it "responds to flush!" do
33
+ @pool.should respond_to(:flush!)
34
+ end
35
+
36
+ it "responds to aquire" do
37
+ @pool.should respond_to(:aquire)
38
+ end
39
+
40
+ it "responds to release" do
41
+ @pool.should respond_to(:release)
42
+ end
43
+
44
+ it "responds to :available?" do
45
+ @pool.should respond_to(:available?)
46
+ end
47
+
48
+ it "has a size limit" do
49
+ @pool.size_limit.should == 7
50
+ end
51
+
52
+ it "has initial size of zero" do
53
+ @pool.size.should == 0
54
+ end
55
+
56
+ it "has a set of reserved resources" do
57
+ @pool.instance_variable_get("@reserved").should be_empty
58
+ end
59
+
60
+ it "has a set of available resources" do
61
+ @pool.instance_variable_get("@available").should be_empty
62
+ end
63
+
64
+ it "knows class of resources (objects) it works with" do
65
+ @pool.class_of_resources.should == DisposableResource
66
+ end
67
+
68
+ it "raises exception when given anything but class for resources class" do
69
+ lambda {
70
+ @pool = Object::Pooling::ResourcePool.new(7, "Hooray!", {})
71
+ }.should raise_error(ArgumentError, /class/)
72
+ end
73
+
74
+ it "requires class of resources (objects) it works with to have a dispose instance method" do
75
+ lambda {
76
+ @pool = Object::Pooling::ResourcePool.new(3, UndisposableResource, {})
77
+ }.should raise_error(ArgumentError, /dispose/)
78
+ end
79
+
80
+ it "may take initialization arguments" do
81
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, { :initialization_args => ["paper"] })
82
+ @pool.instance_variable_get("@initialization_args").should == ["paper"]
83
+ end
84
+
85
+ it "may take expiration period option" do
86
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, { :expiration_period => 100 })
87
+ @pool.expiration_period.should == 100
88
+ end
89
+
90
+ it "has default expiration period of one minute" do
91
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, {})
92
+ @pool.expiration_period.should == 60
93
+ end
94
+
95
+ it "spawns a thread to dispose objects haven't been used for a while" do
96
+ @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, {})
97
+ @pool.instance_variable_get("@pool_expiration_thread").should be_an_instance_of(Thread)
98
+ end
99
+ end
100
+
101
+
102
+
103
+ describe "Aquire from contant size pool" do
104
+ before :each do
105
+ DisposableResource.initialize_pool(2)
106
+ end
107
+
108
+ after :each do
109
+ DisposableResource.instance_variable_set("@__pool", nil)
110
+ end
111
+
112
+ it "increased size of the pool" do
113
+ @time = DisposableResource.pool.aquire
114
+ DisposableResource.pool.size.should == 1
115
+ end
116
+
117
+ it "places initialized instance in the reserved set" do
118
+ @time = DisposableResource.pool.aquire
119
+ DisposableResource.pool.instance_variable_get("@reserved").size.should == 1
120
+ end
121
+
122
+ it "raises an exception when pool size limit is hit" do
123
+ @t1 = DisposableResource.pool.aquire
124
+ @t2 = DisposableResource.pool.aquire
125
+
126
+ lambda { DisposableResource.pool.aquire }.should raise_error(RuntimeError)
127
+ end
128
+
129
+ it "returns last released resource" do
130
+ @t1 = DisposableResource.pool.aquire
131
+ @t2 = DisposableResource.pool.aquire
132
+ DisposableResource.pool.release(@t1)
133
+
134
+ DisposableResource.pool.aquire.should == @t1
135
+ end
136
+
137
+ it "really truly returns last released resource" do
138
+ @t1 = DisposableResource.pool.aquire
139
+ DisposableResource.pool.release(@t1)
140
+
141
+ @t2 = DisposableResource.pool.aquire
142
+ DisposableResource.pool.release(@t2)
143
+
144
+ @t3 = DisposableResource.pool.aquire
145
+ DisposableResource.pool.release(@t3)
146
+
147
+ DisposableResource.pool.aquire.should == @t1
148
+ @t1.should == @t3
149
+ end
150
+
151
+ it "sets allocation timestamp on resource instance" do
152
+ @t1 = DisposableResource.new
153
+ @t1.instance_variable_get("@__pool_aquire_timestamp").should be_close(Time.now, 2)
154
+ end
155
+ end
156
+
157
+
158
+
159
+ describe "Releasing from contant size pool" do
160
+ before :each do
161
+ DisposableResource.initialize_pool(2)
162
+ end
163
+
164
+ after :each do
165
+ DisposableResource.instance_variable_set("@__pool", nil)
166
+ end
167
+
168
+ it "decreases size of the pool" do
169
+ @t1 = DisposableResource.pool.aquire
170
+ @t2 = DisposableResource.pool.aquire
171
+ DisposableResource.pool.release(@t1)
172
+
173
+ DisposableResource.pool.size.should == 1
174
+ end
175
+
176
+ it "raises an exception on attempt to releases object not in pool" do
177
+ @t1 = DisposableResource.new
178
+ @t2 = Set.new
179
+
180
+ DisposableResource.pool.release(@t1)
181
+ lambda { DisposableResource.pool.release(@t2) }.should raise_error(RuntimeError)
182
+ end
183
+
184
+ it "disposes released object" do
185
+ @t1 = DisposableResource.pool.aquire
186
+
187
+ @t1.should_receive(:dispose)
188
+ DisposableResource.pool.release(@t1)
189
+ end
190
+
191
+ it "removes released object from reserved set" do
192
+ @t1 = DisposableResource.pool.aquire
193
+
194
+ lambda {
195
+ DisposableResource.pool.release(@t1)
196
+ }.should change(DisposableResource.pool.instance_variable_get("@reserved"), :size).by(-1)
197
+ end
198
+
199
+ it "returns released object back to available set" do
200
+ @t1 = DisposableResource.pool.aquire
201
+
202
+ lambda {
203
+ DisposableResource.pool.release(@t1)
204
+ }.should change(DisposableResource.pool.instance_variable_get("@available"), :size).by(1)
205
+ end
206
+
207
+ it "updates aquire timestamp on already allocated resource instance" do
208
+ # aquire it once
209
+ @t1 = DisposableResource.new
210
+ # wait a bit
211
+ sleep 3
212
+
213
+ # check old timestamp
214
+ @t1.instance_variable_get("@__pool_aquire_timestamp").should be_close(Time.now, 4)
215
+
216
+ # re-aquire
217
+ DisposableResource.pool.release(@t1)
218
+ @t1 = DisposableResource.new
219
+ # see timestamp is updated
220
+ @t1.instance_variable_get("@__pool_aquire_timestamp").should be_close(Time.now, 2)
221
+ end
222
+ end
223
+
224
+
225
+
226
+ describe Object::Pooling::ResourcePool, "#available?" do
227
+ before :each do
228
+ DisposableResource.initialize_pool(2)
229
+ DisposableResource.new
230
+ end
231
+
232
+ after :each do
233
+ DisposableResource.instance_variable_set("@__pool", nil)
234
+ end
235
+
236
+ it "returns true when pool has available instances" do
237
+ DisposableResource.pool.should be_available
238
+ end
239
+
240
+ it "returns false when pool is exhausted" do
241
+ # aquires the last available resource
242
+ DisposableResource.new
243
+ DisposableResource.pool.should_not be_available
244
+ end
245
+ end
246
+
247
+
248
+
249
+ describe "Flushing of contant size pool" do
250
+ before :each do
251
+ DisposableResource.initialize_pool(2)
252
+
253
+ @t1 = DisposableResource.new
254
+ @t2 = DisposableResource.new
255
+
256
+ # sanity check
257
+ DisposableResource.pool.instance_variable_get("@reserved").should_not be_empty
258
+ end
259
+
260
+ after :each do
261
+ DisposableResource.instance_variable_set("@__pool", nil)
262
+ end
263
+
264
+ it "disposes all pooled objects" do
265
+ [@t1, @t2].each { |instance| instance.should_receive(:dispose) }
266
+
267
+ DisposableResource.pool.flush!
268
+ end
269
+
270
+ it "empties reserved set" do
271
+ DisposableResource.pool.flush!
272
+
273
+ DisposableResource.pool.instance_variable_get("@reserved").should be_empty
274
+ end
275
+
276
+ it "returns all instances to available set" do
277
+ DisposableResource.pool.flush!
278
+
279
+ DisposableResource.pool.instance_variable_get("@available").size.should == 2
280
+ end
281
+ end
282
+
283
+
284
+
285
+ describe "Poolable resource class" do
286
+ before :each do
287
+ DisposableResource.initialize_pool(3, :initialization_args => ["paper"])
288
+ end
289
+
290
+ after :each do
291
+ DisposableResource.instance_variable_set("@__pool", nil)
292
+ end
293
+
294
+ it "aquires new instances from pool" do
295
+ @instance_one = DisposableResource.new
296
+
297
+ DisposableResource.pool.aquired?(@instance_one).should be(true)
298
+ end
299
+
300
+ it "flushed existing pool on re-initialization" do
301
+ DisposableResource.pool.should_receive(:flush!)
302
+ DisposableResource.initialize_pool(5)
303
+ end
304
+
305
+ it "replaces pool on re-initialization" do
306
+ DisposableResource.initialize_pool(5)
307
+ DisposableResource.pool.size_limit.should == 5
308
+ end
309
+
310
+ it "passes initialization parameters to newly created resource instances" do
311
+ DisposableResource.new.name.should == "paper"
312
+ end
313
+ end
314
+
315
+
316
+
317
+ describe "Pooled object", "on initialization" do
318
+ after :each do
319
+ DisposableResource.instance_variable_set("@__pool", nil)
320
+ end
321
+
322
+ it "does not flush pool" do
323
+ # using pool here initializes the pool first
324
+ # so we use instance variable directly
325
+ DisposableResource.instance_variable_get("@__pool").should_not_receive(:flush!)
326
+ DisposableResource.initialize_pool(23)
327
+ end
328
+
329
+ it "flushes pool first when re-initialized" do
330
+ DisposableResource.initialize_pool(5)
331
+ DisposableResource.pool.should_receive(:flush!)
332
+ DisposableResource.initialize_pool(23)
333
+ end
334
+ end
335
+
336
+
337
+
338
+ describe Object::Pooling::ResourcePool, "#time_to_dispose?" do
339
+ before :each do
340
+ DisposableResource.initialize_pool(7, :expiration_period => 2)
341
+ end
342
+
343
+ after :each do
344
+ DisposableResource.instance_variable_set("@__pool", nil)
345
+ end
346
+
347
+ it "returns true when object's last aquisition time is greater than limit" do
348
+ @t1 = DisposableResource.new
349
+ DisposableResource.pool.time_to_release?(@t1).should be(false)
350
+
351
+ sleep 3
352
+ DisposableResource.pool.time_to_release?(@t1).should be(true)
353
+ end
354
+ end
355
+
356
+
357
+
358
+ describe Object::Pooling::ResourcePool, "#dispose_outdated" do
359
+ before :each do
360
+ DisposableResource.initialize_pool(7, :expiration_period => 2)
361
+ end
362
+
363
+ after :each do
364
+ DisposableResource.instance_variable_set("@__pool", nil)
365
+ end
366
+
367
+ it "releases and thus disposes outdated instances" do
368
+ @t1 = DisposableResource.new
369
+ DisposableResource.pool.should_receive(:time_to_release?).with(@t1).and_return(true)
370
+ DisposableResource.pool.should_receive(:release).with(@t1)
371
+
372
+ DisposableResource.pool.release_outdated
373
+ end
374
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Transaction do
4
+
5
+ before :each do
6
+ @connection = mock("connection")
7
+ DataObjects::Connection.should_receive(:new).with("mock://mock/mock").once.and_return(@connection)
8
+ @transaction = DataObjects::Transaction.new("mock://mock/mock")
9
+ end
10
+
11
+ it "should have a HOST constant" do
12
+ DataObjects::Transaction::HOST.should_not == nil?
13
+ end
14
+
15
+ describe "#initialize" do
16
+ it "should provide a connection" do
17
+ @transaction.connection.should == @connection
18
+ end
19
+ it "should provide an id" do
20
+ @transaction.id.should_not == nil
21
+ end
22
+ it "should provide a unique id" do
23
+ DataObjects::Connection.should_receive(:new).with("mock://mock/mock2").once.and_return(@connection)
24
+ @transaction.id.should_not == DataObjects::Transaction.new("mock://mock/mock2").id
25
+ end
26
+ end
27
+ describe "#close" do
28
+ it "should close its connection" do
29
+ @connection.should_receive(:close).once
30
+ @transaction.close
31
+ end
32
+ end
33
+ [:begin, :commit, :rollback, :rollback_prepared, :prepare].each do |meth|
34
+ it "should raise NotImplementedError on #{meth}" do
35
+ lambda do @transaction.send(meth) end.should raise_error(NotImplementedError)
36
+ end
37
+ end
38
+
39
+ end