rx-rspec 0.3.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c09e839a091c586847715b943b738538766dbd9
4
- data.tar.gz: 24371ceefd48af50620a622f1677d99145453f2d
3
+ metadata.gz: 5ec713b68381116f5cd0dd29928c999f8e831176
4
+ data.tar.gz: 41a63b49be631831293838c800c634fa5526a5d0
5
5
  SHA512:
6
- metadata.gz: a1d837c502f93a25eb91a31a0464a894b3ef404a1b9ea006f668a15394e351278b3404631fcf1d58a6283bc6ab85531f5b3813e79dc24614ba23e4dae88a989f
7
- data.tar.gz: 7b22bc0e6606ac700b44ac10e22f325e550d98cbe9c1ae53012a7473abdc896bff221f035aaa20e049cb3de7b2325863710481c7090d3b46701e1d2fe5c91fd3
6
+ metadata.gz: 9ae42de39081fd07ec0cefeca36057110f8e61110e46e679337283e0167ff7e6a8a2e75dffc5df029506bf60939cb28bc1bcd476f6a682d4ed4af856d8e0874c
7
+ data.tar.gz: fab1f1821c010e82aa29f8a1b8f63729f75a6dae62c66e5f4ebcc4eda7e61f36246c68e2ee2131fb14b1fc4738f7a103d913ce861ef6a4bdbc5099e53cb6e1b7
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rx-rspec (0.3.1)
4
+ rx-rspec (0.4.1)
5
5
  rx
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -32,12 +32,19 @@ describe 'awesome' do
32
32
  it { should emit_exactly(1, 2, 3) }
33
33
  end
34
34
  ```
35
+ You can also set a timeout in seconds so your specs don't block forever in case an observable never emits/completes/errors.
36
+ ```
37
+ it 'should be fast' do
38
+ expect(observable).to emit_exactly(42).within(0.1)
39
+ end
40
+ ```
35
41
 
36
42
  ## Matchers
37
43
 
38
44
  rx-spec include the following matchers:
39
45
 
40
- - **emit_nothing()** matches an observable that completes without emitting events or erroring.
41
- - **emit_exactly()** matches against all items produced by the observable and requires the observable to be completed.
42
- - **emit_first()** matches against the first elements of the observable, but does not require it to complete
43
- - **emit_include()** consumes elements until the expected elements have occurred
46
+ - **emit_error(class, message)** matches an observable that errors without emitting any values.
47
+ - **emit_exactly(\*events)** matches an observable that emits exactly the given values and requires the observable to be completed.
48
+ - **emit_first(\*events)** matches against the first values emitted by the observable, but does not require it to complete.
49
+ - **emit_include(\*events)** consumes values until the expected values have been seen.
50
+ - **emit_nothing()** matches an observable that completes without emitting values or erroring.
@@ -1,3 +1,5 @@
1
+ require 'rx-rspec/error'
1
2
  require 'rx-rspec/exactly'
2
3
  require 'rx-rspec/first'
3
4
  require 'rx-rspec/include'
5
+ require 'rx-rspec/nothing'
@@ -0,0 +1,47 @@
1
+ require 'rspec'
2
+
3
+ module RxRspec
4
+ class TimeoutError < RuntimeError ; end
5
+
6
+ class AsyncRunner
7
+ def initialize(timeout)
8
+ @timeout = timeout
9
+ end
10
+
11
+ def await_done(&block)
12
+ condition = ConditionVariable.new
13
+ deadline = Time.now + @timeout
14
+ done_args = nil
15
+ error = nil
16
+ Thread.new do
17
+ done = Proc.new do |*args|
18
+ done_args = args unless done_args
19
+ condition.signal
20
+ end
21
+ begin
22
+ block.call(done)
23
+ rescue => e
24
+ error = e
25
+ done.call
26
+ end
27
+ end.join(@timeout)
28
+
29
+ gate = Mutex.new
30
+ while Time.now < deadline && done_args.nil?
31
+ gate.synchronize { condition.wait(gate, deadline - Time.now) }
32
+ end
33
+
34
+ if error
35
+ raise error
36
+ elsif done_args.nil?
37
+ RSpec::Expectations.fail_with("Timeout after #{@timeout} seconds")
38
+ end
39
+
40
+ if done_args.nil? || done_args.size == 0
41
+ nil
42
+ else
43
+ done_args
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,38 @@
1
+ require 'rspec'
2
+ require 'rx-rspec/shared'
3
+
4
+ RSpec::Matchers.define :emit_error do
5
+ include RxRspec::Shared
6
+
7
+ match do |actual|
8
+ error_class, message = expected
9
+ begin
10
+ @actual = await_done do |done|
11
+ actual.subscribe(
12
+ lambda { |event| done.call(:events, event) },
13
+ lambda { |err| done.call(:error, err) },
14
+ lambda { done.call(:complete, nil) }
15
+ )
16
+ end
17
+ rescue Exception => err
18
+ @actual = [:error, err]
19
+ raise err
20
+ end
21
+ type, emitted = @actual
22
+ return type == :error &&
23
+ values_match?(error_class, emitted) &&
24
+ values_match?(message, emitted.message)
25
+ end
26
+
27
+ failure_message do
28
+ type, emitted = @actual
29
+ if type == :events
30
+ "unexpected #{emitted} emitted"
31
+ elsif type == :complete
32
+ "completed without error"
33
+ else
34
+ error_class, message = expected
35
+ present_error("#{error_class} with #{message}", emitted)
36
+ end
37
+ end
38
+ end
@@ -1,47 +1,39 @@
1
1
  require 'rspec'
2
2
  require 'rx-rspec/shared'
3
3
 
4
- # TODO:
5
- # - should have specific timeout message 'x seconds waiting for y'
6
-
7
4
  RSpec::Matchers.define :emit_exactly do |*expected|
8
5
  include RxRspec::Shared
9
6
 
10
- events = []
11
- errors = []
12
7
  match do |actual|
13
- Thread.new do
14
- spinlock = expected.size
15
- actual.subscribe(
16
- lambda do |event|
17
- events << event
18
- spinlock -= 1
19
- end,
20
- lambda do |err|
21
- errors << err
22
- spinlock = 0
23
- end,
24
- lambda { spinlock = 0 }
25
- )
26
- for n in 1..10
27
- break if spinlock == 0
28
- sleep(0.05)
8
+ events = []
9
+ begin
10
+ error = await_done do |done|
11
+ actual.subscribe(
12
+ lambda do |event|
13
+ events << event
14
+ done.call if events.size > expected.size
15
+ end,
16
+ lambda { |err| done.call(:error, err) },
17
+ lambda { done.call }
18
+ )
29
19
  end
30
- raise 'timeout' if spinlock > 0
31
- end.join
20
+ rescue Exception => err
21
+ @actual = [:error, err]
22
+ raise err
23
+ end
32
24
 
33
- return false unless errors.empty?
34
- @actual = events
35
- values_match? expected, events
25
+ @actual = error || [:events, events]
26
+ error.nil? && values_match?(expected, events)
36
27
  end
37
28
 
38
29
  diffable
39
30
 
40
31
  failure_message do
41
- if errors.empty?
42
- "expected #{events} to match #{expected}"
32
+ type, emitted = @actual
33
+ if type == :events
34
+ "expected #{emitted} to match #{expected}"
43
35
  else
44
- present_error(expected, errors[0])
36
+ present_error(expected, emitted)
45
37
  end
46
38
  end
47
39
  end
@@ -1,46 +1,39 @@
1
1
  require 'rspec'
2
2
  require 'rx-rspec/shared'
3
3
 
4
- # TODO:
5
- # - should have specific timeout message 'x seconds waiting for y'
6
-
7
4
  RSpec::Matchers.define :emit_first do |*expected|
8
5
  include RxRspec::Shared
9
6
 
10
- events = []
11
- errors = []
12
- completed = []
13
-
14
7
  match do |actual|
15
8
  raise 'Please supply at least one expectation' if expected.size == 0
16
9
  expect_clone = expected.dup
17
- Thread.new do
18
- deadline = Time.now + 0.5
19
- actual.take_while do |event|
20
- events << event
21
- values_match?(expect_clone.shift, event) && expect_clone.size > 0
22
- end.subscribe(
23
- lambda { |event| },
24
- lambda { |err| errors << err },
25
- lambda { completed << :complete }
26
- )
27
-
28
- until Time.now > deadline
29
- break if expect_clone.empty?
30
- break unless completed.empty? && errors.empty?
31
- sleep(0.05)
10
+ events = []
11
+ begin
12
+ error = await_done do |done|
13
+ actual.take_while do |event|
14
+ events << event
15
+ values_match?(expect_clone.shift, event) && expect_clone.size > 0
16
+ end.subscribe(
17
+ lambda { |event| },
18
+ lambda { |err| done.call(:error, err) },
19
+ lambda { done.call }
20
+ )
32
21
  end
33
- raise 'timeout' if Time.now > deadline
34
- end.join
22
+ rescue Exception => err
23
+ @actual = [:error, err]
24
+ raise err
25
+ end
35
26
 
36
- values_match? expected, events
27
+ @actual = error || [:events, events]
28
+ error.nil? && values_match?(expected, events)
37
29
  end
38
30
 
39
31
  failure_message do
40
- if errors.empty?
41
- "expected #{events} to emit at least #{expected}"
32
+ type, emitted = @actual
33
+ if type == :events
34
+ "expected #{emitted} to emit at least #{expected}"
42
35
  else
43
- present_error(expected, errors[0])
36
+ present_error(expected, emitted)
44
37
  end
45
38
  end
46
39
  end
@@ -1,47 +1,40 @@
1
1
  require 'rspec'
2
2
  require 'rx-rspec/shared'
3
3
 
4
- # TODO:
5
- # - should have specific timeout message 'x seconds waiting for y'
6
-
7
4
  RSpec::Matchers.define :emit_include do |*expected|
8
5
  include RxRspec::Shared
9
6
 
10
- events = []
11
- errors = []
12
- completed = []
13
-
14
7
  match do |actual|
15
8
  expected = expected.dup
16
- Thread.new do
17
- deadline = Time.now + 0.5
18
- actual.take_while do |event|
19
- events << event
20
- idx = expected.index { |exp| values_match?(exp, event) }
21
- expected.delete_at(idx) unless idx.nil?
22
- expected.size > 0
23
- end.subscribe(
24
- lambda { |event| },
25
- lambda { |err| errors << err },
26
- lambda { completed << :complete }
27
- )
28
-
29
- until Time.now > deadline
30
- break if expected.empty?
31
- break unless completed.empty? && errors.empty?
32
- sleep(0.05)
9
+ events = []
10
+ begin
11
+ error = await_done do |done|
12
+ actual.take_while do |event|
13
+ events << event
14
+ idx = expected.index { |exp| values_match?(exp, event) }
15
+ expected.delete_at(idx) unless idx.nil?
16
+ expected.size > 0
17
+ end.subscribe(
18
+ lambda { |event| },
19
+ lambda { |err| done.call(:error, err) },
20
+ lambda { done.call }
21
+ )
33
22
  end
34
- raise 'timeout' if Time.now > deadline
35
- end.join
23
+ rescue Exception => err
24
+ @actual = [:error, err]
25
+ raise err
26
+ end
36
27
 
37
- expected.empty?
28
+ @actual = error || [:events, events]
29
+ expected.empty? && error.nil?
38
30
  end
39
31
 
40
32
  failure_message do
41
- if errors.empty?
42
- "expected #{events} to include #{expected}"
33
+ type, emitted = @actual
34
+ if type == :events
35
+ "expected #{emitted} to include #{expected}"
43
36
  else
44
- present_error(expected, errors[0])
37
+ present_error(expected, emitted)
45
38
  end
46
39
  end
47
40
  end
@@ -1,39 +1,31 @@
1
1
  require 'rspec'
2
2
  require 'rx-rspec/shared'
3
3
 
4
- # TODO:
5
- # - should have specific timeout message 'x seconds waiting for y'
6
-
7
- RSpec::Matchers.define :emit_nothing do |*expected|
4
+ RSpec::Matchers.define :emit_nothing do
8
5
  include RxRspec::Shared
9
6
 
10
- events = []
11
- errors = []
12
- completed = []
13
7
  match do |actual|
14
- Thread.new do
15
- actual.subscribe(
16
- lambda { |event| events << event },
17
- lambda { |err| errors << err },
18
- lambda { completed << :complete }
19
- )
20
- for n in 1..10
21
- break unless completed.empty?
22
- break unless errors.empty? && events.empty?
23
- sleep(0.05)
8
+ begin
9
+ @actual = await_done do |done|
10
+ actual.subscribe(
11
+ lambda { |event| done.call(:events, event) },
12
+ lambda { |err| done.call(:error, err) },
13
+ lambda { done.call }
14
+ )
24
15
  end
25
- raise 'timeout' if errors.empty? && events.empty? && completed.empty?
26
- end.join
27
-
28
- return false unless errors.empty?
29
- return events.empty?
16
+ rescue Exception => err
17
+ @actual = [:error, err]
18
+ raise err
19
+ end
20
+ return @actual.nil?
30
21
  end
31
22
 
32
23
  failure_message do
33
- if errors.empty?
34
- "unexpected #{events[0]} emitted"
24
+ type, emitted = @actual
25
+ if type == :events
26
+ "unexpected #{emitted} emitted"
35
27
  else
36
- present_error(expected, errors[0])
28
+ present_error(expected, emitted)
37
29
  end
38
30
  end
39
31
  end
@@ -1,7 +1,35 @@
1
+ require 'rspec'
2
+ require 'rx-rspec/async_runner'
3
+
1
4
  module RxRspec
2
5
  module Shared
6
+ DEFAULT_TIMEOUT = 0.5
7
+
8
+ def self.included(mod)
9
+ mod.chain :within do |seconds|
10
+ @timeout = seconds
11
+ end
12
+
13
+ mod.description do
14
+ if @timeout
15
+ super() + " within #{@timeout} seconds"
16
+ else
17
+ super()
18
+ end
19
+ end
20
+ end
21
+
22
+ def timeout
23
+ @timeout || DEFAULT_TIMEOUT
24
+ end
25
+
26
+ def await_done(&block)
27
+ AsyncRunner.new(timeout).await_done(&block)
28
+ end
29
+
3
30
  def present_error(expected, error)
4
31
  backtrace = error.backtrace || []
32
+ return error.message if /^Timeout/.match(error.message)
5
33
  present_error = "#{error.inspect}:#{$/}#{backtrace.join($/)}"
6
34
  "expected #{expected} but received error #{present_error}"
7
35
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = 'rx-rspec'
3
- spec.version = '0.3.1'
3
+ spec.version = '0.4.1'
4
4
  spec.summary = 'rspec testing support for RxRuby'
5
5
  spec.description = 'Writing specs for reactive streams is tricky both because of their asynchronous nature and because their next/error/completed semantics. The goal of rx-rspec is to provide powerful matchers that lets you express your expectations in a traditional rspec-like synchronous manner.'
6
6
  spec.authors = ['Anders Qvist']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rx-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anders Qvist
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-14 00:00:00.000000000 Z
11
+ date: 2018-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rx
@@ -110,6 +110,8 @@ files:
110
110
  - LICENSE.md
111
111
  - README.md
112
112
  - lib/rx-rspec.rb
113
+ - lib/rx-rspec/async_runner.rb
114
+ - lib/rx-rspec/error.rb
113
115
  - lib/rx-rspec/exactly.rb
114
116
  - lib/rx-rspec/first.rb
115
117
  - lib/rx-rspec/include.rb