em-dextras 0.2.0 → 0.3.0

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.
@@ -1,6 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe EMDextras::Chains do
3
+ describe EMDextras::Chains do
4
+ class EchoStage
5
+ def todo(input)
6
+ EMDextras::Chains::Deferrables.succeeded(input)
7
+ end
8
+ end
9
+
4
10
  class ProduceStage
5
11
  def initialize(result)
6
12
  @result = result
@@ -24,7 +30,13 @@ describe EMDextras::Chains do
24
30
 
25
31
  class ErrorStage
26
32
  def todo(input)
27
- EMDextras::Chains::Deferrables.failed "Failed with #{input}"
33
+ EMDextras::Chains::Deferrables.failed input
34
+ end
35
+ end
36
+
37
+ class InvalidStage
38
+ def todo(input)
39
+ "not-a-deferrable"
28
40
  end
29
41
  end
30
42
 
@@ -43,52 +55,200 @@ describe EMDextras::Chains do
43
55
  end
44
56
  end
45
57
 
46
- let(:monitoring) { mock.as_null_object }
47
-
48
- it "should chain todo stages" do
49
- EM.run do
50
- inputs = []
58
+ class InterruptChainStage
59
+ def todo(ignored)
60
+ deferrable = EventMachine::DefaultDeferrable.new
61
+ deferrable.succeed()
62
+ deferrable
63
+ end
64
+ end
65
+
66
+ let(:monitoring) { EMDextras::Spec::Spy.new }
67
+
68
+ context " - when all stages succeed -" do
69
+ it "should chain todo stages" do
70
+ EM.run do
71
+ inputs = []
72
+
73
+ EMDextras::Chains.pipe("input", monitoring, [
74
+ SpyStage.new(inputs),
75
+ SpyStage.new(inputs),
76
+ StopStage.new
77
+ ])
78
+
79
+ inputs.should == ["input", "input"]
80
+ end
81
+ end
51
82
 
52
- EMDextras::Chains.pipe("input", monitoring, [
53
- SpyStage.new(inputs),
54
- SpyStage.new(inputs),
55
- StopStage.new
56
- ])
83
+ it "should return a deferrable with the result of the last step" do
84
+ EM.run do
85
+ result = EMDextras::Chains.pipe("ignored", monitoring, [
86
+ ProduceStage.new("out")
87
+ ])
57
88
 
58
- inputs.should == ["input", "input"]
89
+ result.should succeed_with("out")
90
+ end
59
91
  end
60
92
  end
61
93
 
62
- it "should notify monitoring of any exceptions" do
63
- EM.run do
64
- monitoring.should_receive(:inform_exception!) do
65
- EM.stop
94
+ context " - when a stage fails - " do
95
+ it "should fail the resulting deferrable" do
96
+ EM.run do
97
+ result = EMDextras::Chains.pipe("error", monitoring, [
98
+ EchoStage.new,
99
+ ErrorStage.new,
100
+ ProduceStage.new(42)
101
+ ])
102
+
103
+ result.should fail_with("error")
104
+ end
105
+ end
106
+
107
+ it "should not proceed with the chain" do
108
+ EM.run do
109
+ produced = []
110
+
111
+ result = EMDextras::Chains.pipe("in", monitoring, [
112
+ SpyStage.new(produced),
113
+ ErrorStage.new,
114
+ SpyStage.new(produced)
115
+ ])
116
+
117
+ probe_event_machine check: (Proc.new do
118
+ produced.should == ["in"]
119
+ end)
66
120
  end
121
+ end
122
+ end
123
+
124
+ context "- interruption -" do
125
+ it "should interrupt the chain when a stage returns an empty succeeded deferrable" do
126
+ EM.run do
127
+ input = []
67
128
 
68
- EM.add_timer(2) do
69
- fail("timeout")
129
+ EMDextras::Chains.pipe("input", monitoring, [
130
+ ProduceStage.new("x"),
131
+ InterruptChainStage.new,
132
+ SpyStage.new(input)
133
+ ])
134
+
135
+ probe_event_machine check: (Proc.new do
136
+ input.should == []
137
+ end)
70
138
  end
139
+ end
71
140
 
72
- EMDextras::Chains.pipe("anything", monitoring, [ErrorStage.new]);
141
+ it "should notify the monitor that the chain ended (with nil value)" do
142
+ EM.run do
143
+ input = []
144
+
145
+ monitoring = EMDextras::Spec::Spy.new
146
+
147
+ EMDextras::Chains.pipe("input", monitoring, [
148
+ ProduceStage.new("x"),
149
+ InterruptChainStage.new,
150
+ SpyStage.new(input)
151
+ ])
152
+
153
+ monitoring.received_call!(:end_of_chain!, nil)
154
+ end
73
155
  end
74
156
  end
75
157
 
76
- it "should pass a 'context' object if given and the stage takes one" do
77
- contextual_stage = ContextualStage.new
78
-
79
- EM.run do
80
- EMDextras::Chains.pipe("anything", monitoring, [
81
- contextual_stage,
82
- StopStage.new
83
- ], :context => "the context")
158
+ context "- monitoring -" do
159
+ it "should notify monitoring of any exceptions" do
160
+ EM.run do
161
+ EMDextras::Chains.pipe("anything", monitoring, [ErrorStage.new]);
162
+
163
+ monitoring.received_call!(:inform_exception!, any_args)
164
+ end
165
+ end
166
+
167
+ it "should pass the context to monitoring.inform_exception if given" do
168
+ EM.run do
169
+ error_stage = ErrorStage.new
170
+
171
+ EMDextras::Chains.pipe("input", monitoring, [
172
+ error_stage
173
+ ], context: "the context")
174
+
175
+ monitoring.received_call!(:inform_exception!, "input", error_stage, "the context")
176
+ end
177
+ end
178
+
179
+ it "should notify monitoring of the end of the pipeline" do
180
+ EM.run do
181
+ monitoring = EMDextras::Spec::Spy.new
182
+ EMDextras::Chains.pipe("x", monitoring, [
183
+ ProduceStage.new("y"),
184
+ SpyStage.new([]),
185
+ ProduceStage.new("z")
186
+ ])
187
+
188
+ monitoring.received_call!(:end_of_chain!, "z")
189
+ end
190
+ end
191
+
192
+ it "should notify monitoring of the end of the pipeline even when a stage fails" do
193
+ EM.run do
194
+ monitoring = EMDextras::Spec::Spy.new
195
+
196
+ EMDextras::Chains.pipe("x", monitoring, [
197
+ ProduceStage.new("y"),
198
+ ErrorStage.new,
199
+ ProduceStage.new("z")
200
+ ])
201
+
202
+ monitoring.received_call!(:end_of_chain!, "y")
203
+ end
204
+ end
205
+
206
+ context 'when monitoring does not respond to end_of_chain' do
207
+ it 'does not to try to call that method' do
208
+ EM.run do
209
+ monitoring = EMDextras::Spec::Spy.new only_respond_to: [:this_method]
210
+
211
+ EMDextras::Chains.pipe('x', monitoring, [
212
+ ProduceStage.new('y'),
213
+ SpyStage.new([]),
214
+ ProduceStage.new('z')
215
+ ])
216
+
217
+ monitoring.not_received_call!(:end_of_chain!, 'z')
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ context " - context - " do
224
+ it "should pass a 'context' object if given and the stage takes one" do
225
+ contextual_stage = ContextualStage.new
226
+
227
+ EM.run do
228
+ EMDextras::Chains.pipe("anything", monitoring, [
229
+ contextual_stage,
230
+ StopStage.new
231
+ ], :context => "the context")
232
+
233
+ probe_event_machine :check => lambda {|x|
234
+ contextual_stage.context.should == "the context"
235
+ }
236
+ end
237
+ end
238
+
239
+ it "should pass the contect object to monitoring if given" do
240
+ EM.run do
241
+ monitoring = EMDextras::Spec::Spy.new
242
+ EMDextras::Chains.pipe("x", monitoring, [
243
+ ProduceStage.new("y"),
244
+ ], context: "the context")
84
245
 
85
- probe_event_machine :check => lambda {|x|
86
- contextual_stage.context.should == "the context"
87
- }
246
+ monitoring.received_call!(:end_of_chain!, "y", "the context")
247
+ end
88
248
  end
89
249
  end
90
250
 
91
- context "when given a :split stage" do
251
+ context "when given a :split stage" do
92
252
  context "and the input is enumberable" do
93
253
  it "should invoke the next step the given number of times" do
94
254
  EM.run do
@@ -105,6 +265,26 @@ describe EMDextras::Chains do
105
265
  end
106
266
  end
107
267
 
268
+ it "successive splits should recursively divide the pipeline" do
269
+ EM.run do
270
+ final_inputs = []
271
+ intermediate_inputs = []
272
+
273
+ EMDextras::Chains.pipe("anything", monitoring, [
274
+ ProduceStage.new([1,2]),
275
+ :split,
276
+ SpyStage.new(intermediate_inputs),
277
+ ProduceStage.new([3,4]),
278
+ :split,
279
+ SpyStage.new(final_inputs),
280
+ StopStage.new
281
+ ])
282
+
283
+ intermediate_inputs.should =~ [1,2]
284
+ final_inputs.should =~ [3,4,3,4]
285
+ end
286
+ end
287
+
108
288
  it "should split the given context" do
109
289
  before = ContextualStage.new
110
290
  after = ContextualStage.new
@@ -114,7 +294,7 @@ describe EMDextras::Chains do
114
294
 
115
295
  first_context.stub(:split).and_return second_context
116
296
 
117
- EM.run do
297
+ EM.run do
118
298
  EMDextras::Chains.pipe("anything", monitoring, [
119
299
  before,
120
300
  ProduceStage.new([1,2]),
@@ -128,27 +308,127 @@ describe EMDextras::Chains do
128
308
  }
129
309
  end
130
310
  end
131
- end
311
+
312
+ it "should return a deferrable acccumulating the results of the last step" do
313
+ EM.run do
314
+ result = EMDextras::Chains.pipe("anything", monitoring, [
315
+ ProduceStage.new([1,2]),
316
+ :split,
317
+ SpyStage.new([])
318
+ ])
132
319
 
133
- context "and the input is not enumberable" do
134
- it "will terminate the chain and report the error as an exception" do
135
- EM.run do
136
- monitoring.should_receive(:inform_exception!) do
137
- EM.stop
320
+ result.should succeed_with([1,2])
321
+ end
322
+ end
323
+
324
+ it "should return a deferrable with the result of the last step, accumulating results for multiple splits" do
325
+ EM.run do
326
+ result = EMDextras::Chains.pipe("anything", monitoring, [
327
+ ProduceStage.new([1,2]),
328
+ :split,
329
+ ProduceStage.new([3,4]),
330
+ :split,
331
+ SpyStage.new([])
332
+ ])
333
+
334
+ result.should succeed_with([[3,4],[3,4]])
335
+ end
336
+ end
337
+
338
+ context " - corner cases -" do
339
+ it "should handle a split as first chain element" do
340
+ EM.run do
341
+ results = []
342
+ EMDextras::Chains.pipe([1,2,3], monitoring, [
343
+ :split,
344
+ SpyStage.new(results)
345
+ ])
346
+ probe_event_machine :check => (lambda {|x|
347
+ results.should == [1,2,3]
348
+ })
349
+ end
350
+ end
351
+
352
+ it "should return the deferrable result even if split is the first argument" do
353
+ EM.run do
354
+ res = EMDextras::Chains.pipe([1,2,3], monitoring, [
355
+ :split,
356
+ EchoStage.new
357
+ ])
358
+
359
+ res.should succeed_with([1,2,3])
138
360
  end
361
+ end
139
362
 
140
- EM.add_timer(2) do
141
- fail("timeout")
363
+ it "should handle a split as last chain element" do
364
+ EM.run do
365
+ result = EMDextras::Chains.pipe('ignored', monitoring, [
366
+ ProduceStage.new([1,2,3]),
367
+ :split
368
+ ])
369
+
370
+ result.should succeed_with([1,2,3])
142
371
  end
372
+ end
373
+ end
374
+
375
+ context " - splits and monitoring - " do
376
+ it "should inform monitoring that the pipeline ended only once" do
377
+ EM.run do
378
+ monitoring = EMDextras::Spec::Spy.new
143
379
 
380
+ EMDextras::Chains.pipe("anything", monitoring, [
381
+ ProduceStage.new([1,2]),
382
+ :split,
383
+ ProduceStage.new([3,4]),
384
+ :split,
385
+ SpyStage.new([])
386
+ ])
387
+
388
+ monitoring.received_n_calls!(1, :end_of_chain!, [[3,4],[3,4]])
389
+ end
390
+ end
391
+
392
+ it "should inform monitoring that the pipeline ended with context if given" do
393
+ EM.run do
394
+ monitoring = EMDextras::Spec::Spy.new
395
+
396
+ EMDextras::Chains.pipe("anything", monitoring, [
397
+ ProduceStage.new([1,2]),
398
+ :split,
399
+ ProduceStage.new([3,4]),
400
+ :split,
401
+ SpyStage.new([])
402
+ ], context: :the_context)
403
+
404
+ monitoring.received_n_calls!(1, :end_of_chain!, [[3,4],[3,4]], :the_context)
405
+ end
406
+ end
407
+ end
408
+ end
409
+
410
+ context "and the input is not enumberable" do
411
+ it "will terminate the chain and report the error as an exception" do
412
+ EM.run do
144
413
  EMDextras::Chains.pipe("anything", monitoring, [
145
414
  ProduceStage.new(:not_enumberable_input),
146
415
  :split,
147
416
  SpyStage.new([]),
148
417
  StopStage.new
149
418
  ])
419
+ monitoring.received_call!(:inform_exception!, any_args)
150
420
  end
151
421
  end
152
422
  end
153
423
  end
424
+
425
+ context " - input validation - " do
426
+ it "should raise an exception when a stage doesn't return a deferrable" do
427
+ expect {EM.run do
428
+ EMDextras::Chains.pipe("the input", monitoring, [
429
+ InvalidStage.new
430
+ ])
431
+ end}.to raise_error(EMDextras::Chains::InvalidStage, /.*'InvalidStage'.*'the input'.*'not-a-deferrable'.*/)
432
+ end
433
+ end
154
434
  end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'em-dextras/extension/object/deferrable'
3
+
4
+ describe "EventMachine::Deferrable extensions" do
5
+ context "when the deferrable is succeeded" do
6
+ it 'returns a new one deferrable with a yielded value' do
7
+ EM.run do
8
+ deferrable = EventMachine::DefaultDeferrable.new
9
+
10
+ result_deferrable = deferrable.map do |parameter|
11
+ "transformed-#{parameter}"
12
+ end
13
+
14
+ deferrable.succeed('from-succeed')
15
+
16
+ result_deferrable.should succeed_with('transformed-from-succeed')
17
+ end
18
+ end
19
+ end
20
+
21
+ context "when the deferrable fails" do
22
+ it 'returns a new one failed deferrable' do
23
+ EM.run do
24
+ deferrable = EventMachine::DefaultDeferrable.new
25
+
26
+ result_deferrable = deferrable.map do |parameter|
27
+ "transformed-#{parameter}"
28
+ end
29
+
30
+ deferrable.fail('from-fail')
31
+
32
+ result_deferrable.should fail_with('from-fail')
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Spec Matchers' do
4
+ describe :succeed_with do
5
+ it "should accept a successful deferrable with the given value" do
6
+ EM.run do
7
+ success = EMDextras::Chains::Deferrables.succeeded("ok")
8
+ success.should succeed_with("ok")
9
+ end
10
+ end
11
+
12
+ it "should accept a deferrable if it eventually succeeds" do
13
+ EM.run do
14
+ deferrable = EventMachine::DefaultDeferrable.new
15
+ EM.next_tick do
16
+ deferrable.succeed("ok")
17
+ end
18
+ deferrable.should succeed_with("ok")
19
+ end
20
+ end
21
+
22
+ it "should reject a successful deferrable with a different value" do
23
+ expect {EM.run do
24
+ success = EMDextras::Chains::Deferrables.succeeded("not ok")
25
+ success.should succeed_with("ok")
26
+ end}.to raise_error
27
+ end
28
+
29
+ it "should reject a failed deferrable" do
30
+ expect {EM.run do
31
+ success = EMDextras::Chains::Deferrables.failed("any")
32
+ success.should succeed_with("ok")
33
+ end}.to raise_error
34
+ end
35
+ end
36
+
37
+ describe :fail_with do
38
+ it "should accept a failure with the expected value" do
39
+ EM.run do
40
+ failure = EMDextras::Chains::Deferrables.failed("expected")
41
+ failure.should fail_with("expected")
42
+ end
43
+ end
44
+
45
+ it "should reject a success" do
46
+ expect {EM.run do
47
+ presumed_failure = EMDextras::Chains::Deferrables.succeeded("expected")
48
+ presumed_failure.should fail_with("expected")
49
+ end}.to raise_error
50
+ end
51
+
52
+ it "should timeout if nothing happens" do
53
+ expect {EM.run do
54
+ presumed_failure = EventMachine::DefaultDeferrable.new
55
+ presumed_failure.should fail_with("expected")
56
+ end}.to raise_error
57
+ end
58
+
59
+ end
60
+
61
+ describe :succeed_according_to do
62
+ it "should accept if the block eventually yields without raising" do
63
+ EM.run do
64
+ deferrable = EventMachine::DefaultDeferrable.new
65
+ EM.next_tick { deferrable.succeed("ok") }
66
+ deferrable.should succeed_according_to(lambda do |value|
67
+ value.should == "ok"
68
+ end)
69
+ end
70
+ end
71
+
72
+ it "should reject if the block never yields without raising while probing" do
73
+ expect {EM.run do
74
+ deferrable = EventMachine::DefaultDeferrable.new
75
+ deferrable.should succeed_according_to(lambda do |value|
76
+ value.should == "ok"
77
+ end)
78
+ end}.to raise_error
79
+ end
80
+ end
81
+ end
@@ -2,19 +2,20 @@ require 'spec_helper'
2
2
 
3
3
  describe EMDextras::Spec::Spy do
4
4
  subject { described_class.new }
5
+
5
6
  describe :"called?" do
6
7
  it "should record calls" do
7
8
  subject.foo(1, :a => "b")
8
-
9
+
9
10
  subject.called?(:foo, 1, :a => "b").should be_true
10
11
  subject.called?(:bar, 1, :a => "b").should be_false
11
12
  subject.called?(:foo, 1, :a => "c").should be_false
12
- end
13
+ end
13
14
  end
14
15
 
15
16
  describe "default return value" do
16
17
  it "returns nil if no default return value is defined" do
17
- spy = EMDextras::Spec::Spy.new
18
+ spy = EMDextras::Spec::Spy.new
18
19
  spy.some_method.should == nil
19
20
  end
20
21
 
@@ -23,9 +24,36 @@ describe EMDextras::Spec::Spy do
23
24
  spy.some_method.should == "default"
24
25
  end
25
26
  end
27
+
28
+ describe :respond_to? do
29
+ context 'without respond parameter' do
30
+ let(:spy) { described_class.new }
31
+
32
+ it 'always returns true' do
33
+ spy.respond_to?(:method_name).should be_true
34
+ end
35
+ end
36
+
37
+ context 'with respond parameter on initialize' do
38
+ let(:spy) { described_class.new only_respond_to: [:foo] }
39
+
40
+ context 'and asking if it respond for the same method of initialize' do
41
+ it 'returns true' do
42
+ spy.respond_to?(:foo).should be_true
43
+ end
44
+ end
45
+
46
+ context 'and asking if it respond for the a method different of the initialize' do
47
+ it 'returns false' do
48
+ spy.respond_to?(:bar).should be_false
49
+ end
50
+ end
51
+ end
52
+ end
53
+
26
54
  describe :received_call! do
27
55
  it "should do nothing if the call was really received" do
28
- EM.run do
56
+ EM.run do
29
57
  subject.foo(1, :a => "b")
30
58
 
31
59
  subject.received_call!(:foo, 1, :a => "b")
@@ -34,25 +62,96 @@ describe EMDextras::Spec::Spy do
34
62
 
35
63
  it "should raise an exception if the call was not received" do
36
64
  expect {
37
- EM.run do
65
+ EM.run do
38
66
  subject.foo(1, :a => "b")
39
-
67
+
40
68
  subject.received_call!(:bar, 1, :a => "b")
41
69
  end
42
70
  }.to raise_error(/bar.*foo/)
43
71
  end
44
72
 
73
+ context " - rspec argument matchers - " do
74
+ it "should accept rspec specific argument matchers" do
75
+ subject.foo(1, "banana")
76
+
77
+ EM.run { subject.received_call!(:foo, 1, /ba(na)*/) } #doesn't raise
78
+ EM.run { subject.received_call!(:foo, 1, instance_of(String)) } #doesn't raise
79
+ expect {EM.run { subject.received_call!(:foo, 1, /apple/) } }.to raise_error
80
+ end
81
+
82
+ it "should accept rspec general matchers" do
83
+ subject.foo(1, "banana")
84
+
85
+ EM.run { subject.received_call!(:foo, any_args) } #doesn't raise
86
+ end
87
+ end
88
+
89
+ it "should be able to assert that a method will receive nil" do
90
+ EM.run do
91
+ subject.foobar(nil)
92
+
93
+ subject.received_call!(:foobar, nil)
94
+ end
95
+ end
96
+
45
97
  context "when the method is triggered asynchronously" do
46
98
  it "should should probe until the call is received" do
47
99
  EM.run do
48
- EM.next_tick do
100
+ EM.next_tick do
49
101
  subject.foo(1,2,3)
50
102
  end
51
-
103
+
52
104
  subject.received_call!(:foo, 1,2,3)
53
105
  end
54
106
  end
55
107
  end
56
108
  end
57
109
 
110
+ describe :received_n_calls! do
111
+ it "should do nothing if the call was received the given number of tiems" do
112
+ EM.run do
113
+ subject.foo(42, :a => "b")
114
+ subject.foo(42, :a => "b")
115
+
116
+ subject.received_n_calls!(2, :foo, 42, :a => "b")
117
+ end
118
+ end
119
+
120
+ it "should raise an exception if the call was not received" do
121
+ expect {
122
+ EM.run do
123
+ subject.received_n_calls!(1, :bar, :a => "b")
124
+ end
125
+ }.to raise_error(/1.*0/)
126
+ end
127
+
128
+ it "should raise an exception if the call was received a number of times less than what was expected" do
129
+ expect {
130
+ EM.run do
131
+ subject.foo(:a => "b")
132
+
133
+ subject.received_n_calls!(2, :foo, :a => "b")
134
+ end
135
+ }.to raise_error(/2.*1/)
136
+ end
137
+
138
+ end
139
+
140
+ describe :not_received_call! do
141
+ it 'does not be called' do
142
+ EM.run do
143
+ subject.not_received_call!(:foo, a: 'b')
144
+ end
145
+ end
146
+
147
+ it 'raises an exception if the call was received' do
148
+ expect {
149
+ EM.run do
150
+ subject.foo(a: 'b')
151
+
152
+ subject.not_received_call!(:foo, a: 'b')
153
+ end
154
+ }.to raise_error
155
+ end
156
+ end
58
157
  end