em-dextras 0.2.0 → 0.3.0

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