rx-rspec 0.3.1 → 0.4.1
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +11 -4
- data/lib/rx-rspec.rb +2 -0
- data/lib/rx-rspec/async_runner.rb +47 -0
- data/lib/rx-rspec/error.rb +38 -0
- data/lib/rx-rspec/exactly.rb +21 -29
- data/lib/rx-rspec/first.rb +21 -28
- data/lib/rx-rspec/include.rb +23 -30
- data/lib/rx-rspec/nothing.rb +17 -25
- data/lib/rx-rspec/shared.rb +28 -0
- data/rx-rspec.gemspec +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ec713b68381116f5cd0dd29928c999f8e831176
|
4
|
+
data.tar.gz: 41a63b49be631831293838c800c634fa5526a5d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ae42de39081fd07ec0cefeca36057110f8e61110e46e679337283e0167ff7e6a8a2e75dffc5df029506bf60939cb28bc1bcd476f6a682d4ed4af856d8e0874c
|
7
|
+
data.tar.gz: fab1f1821c010e82aa29f8a1b8f63729f75a6dae62c66e5f4ebcc4eda7e61f36246c68e2ee2131fb14b1fc4738f7a103d913ce861ef6a4bdbc5099e53cb6e1b7
|
data/Gemfile.lock
CHANGED
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
|
-
- **
|
41
|
-
- **emit_exactly()** matches
|
42
|
-
- **emit_first()** matches against the first
|
43
|
-
- **emit_include()** consumes
|
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.
|
data/lib/rx-rspec.rb
CHANGED
@@ -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
|
data/lib/rx-rspec/exactly.rb
CHANGED
@@ -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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
31
|
-
|
20
|
+
rescue Exception => err
|
21
|
+
@actual = [:error, err]
|
22
|
+
raise err
|
23
|
+
end
|
32
24
|
|
33
|
-
|
34
|
-
|
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
|
-
|
42
|
-
|
32
|
+
type, emitted = @actual
|
33
|
+
if type == :events
|
34
|
+
"expected #{emitted} to match #{expected}"
|
43
35
|
else
|
44
|
-
present_error(expected,
|
36
|
+
present_error(expected, emitted)
|
45
37
|
end
|
46
38
|
end
|
47
39
|
end
|
data/lib/rx-rspec/first.rb
CHANGED
@@ -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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
34
|
-
|
22
|
+
rescue Exception => err
|
23
|
+
@actual = [:error, err]
|
24
|
+
raise err
|
25
|
+
end
|
35
26
|
|
36
|
-
|
27
|
+
@actual = error || [:events, events]
|
28
|
+
error.nil? && values_match?(expected, events)
|
37
29
|
end
|
38
30
|
|
39
31
|
failure_message do
|
40
|
-
|
41
|
-
|
32
|
+
type, emitted = @actual
|
33
|
+
if type == :events
|
34
|
+
"expected #{emitted} to emit at least #{expected}"
|
42
35
|
else
|
43
|
-
present_error(expected,
|
36
|
+
present_error(expected, emitted)
|
44
37
|
end
|
45
38
|
end
|
46
39
|
end
|
data/lib/rx-rspec/include.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
35
|
-
|
23
|
+
rescue Exception => err
|
24
|
+
@actual = [:error, err]
|
25
|
+
raise err
|
26
|
+
end
|
36
27
|
|
37
|
-
|
28
|
+
@actual = error || [:events, events]
|
29
|
+
expected.empty? && error.nil?
|
38
30
|
end
|
39
31
|
|
40
32
|
failure_message do
|
41
|
-
|
42
|
-
|
33
|
+
type, emitted = @actual
|
34
|
+
if type == :events
|
35
|
+
"expected #{emitted} to include #{expected}"
|
43
36
|
else
|
44
|
-
present_error(expected,
|
37
|
+
present_error(expected, emitted)
|
45
38
|
end
|
46
39
|
end
|
47
40
|
end
|
data/lib/rx-rspec/nothing.rb
CHANGED
@@ -1,39 +1,31 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require 'rx-rspec/shared'
|
3
3
|
|
4
|
-
|
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
|
-
|
15
|
-
actual
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
return
|
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
|
-
|
34
|
-
|
24
|
+
type, emitted = @actual
|
25
|
+
if type == :events
|
26
|
+
"unexpected #{emitted} emitted"
|
35
27
|
else
|
36
|
-
present_error(expected,
|
28
|
+
present_error(expected, emitted)
|
37
29
|
end
|
38
30
|
end
|
39
31
|
end
|
data/lib/rx-rspec/shared.rb
CHANGED
@@ -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
|
data/rx-rspec.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = 'rx-rspec'
|
3
|
-
spec.version = '0.
|
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.
|
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:
|
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
|