rx_ruby 0.0.2
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 +7 -0
- data/.gitattributes +22 -0
- data/.gitignore +173 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/Rakefile +11 -0
- data/examples/aggregate.rb +39 -0
- data/examples/amb.rb +25 -0
- data/examples/ambproto.rb +24 -0
- data/examples/and.rb +26 -0
- data/examples/as_observable.rb +25 -0
- data/examples/average.rb +43 -0
- data/examples/buffer_with_count.rb +44 -0
- data/examples/buffer_with_time.rb +51 -0
- data/examples/case.rb +29 -0
- data/examples/catch.rb +20 -0
- data/examples/catchproto.rb +39 -0
- data/examples/combine_latest.rb +35 -0
- data/examples/combine_latestproto.rb +33 -0
- data/examples/concat.rb +22 -0
- data/examples/concat_all.rb +27 -0
- data/examples/concat_map.rb +61 -0
- data/examples/concat_map_observer.rb +29 -0
- data/examples/concatproto.rb +25 -0
- data/examples/connect.rb +41 -0
- data/examples/contains.rb +37 -0
- data/examples/count.rb +36 -0
- data/examples/create.rb +55 -0
- data/examples/debounce.rb +35 -0
- data/examples/default_if_empty.rb +35 -0
- data/examples/defer.rb +20 -0
- data/examples/delay.rb +49 -0
- data/examples/delay_with_selector.rb +63 -0
- data/examples/dematerialize.rb +22 -0
- data/examples/disposable.rb +12 -0
- data/examples/distinct.rb +43 -0
- data/examples/distinct_until_changed.rb +43 -0
- data/examples/do.rb +59 -0
- data/examples/empty.rb +16 -0
- data/examples/for.rb +26 -0
- data/examples/fork_join.rb +23 -0
- data/examples/from.rb +106 -0
- data/examples/from_array.rb +21 -0
- data/examples/from_callback.rb +21 -0
- data/examples/generate.rb +24 -0
- data/examples/group_join.rb +39 -0
- data/examples/if.rb +46 -0
- data/examples/intervals.rb +26 -0
- data/examples/merge.rb +36 -0
- data/examples/merge_all.rb +27 -0
- data/examples/multicast.rb +32 -0
- data/examples/never.rb +15 -0
- data/examples/of.rb +19 -0
- data/examples/on_error_resume_next.rb +21 -0
- data/examples/pairs.rb +26 -0
- data/examples/publish.rb +79 -0
- data/examples/range.rb +19 -0
- data/examples/reduce.rb +18 -0
- data/examples/repeat.rb +19 -0
- data/examples/return.rb +17 -0
- data/examples/scan.rb +41 -0
- data/examples/start.rb +29 -0
- data/examples/throw.rb +17 -0
- data/examples/time_intervals.rb +28 -0
- data/examples/timer.rb +26 -0
- data/examples/timestamp.rb +28 -0
- data/examples/to_a.rb +23 -0
- data/examples/to_async.rb +26 -0
- data/examples/using.rb +52 -0
- data/examples/when.rb +26 -0
- data/examples/while.rb +25 -0
- data/examples/window_with_time.rb +78 -0
- data/examples/zip.rb +27 -0
- data/examples/zip_array.rb +25 -0
- data/lib/core_ext/enumerable.rb +22 -0
- data/lib/rx_ruby.rb +27 -0
- data/lib/rx_ruby/concurrency/async_lock.rb +57 -0
- data/lib/rx_ruby/concurrency/current_thread_scheduler.rb +75 -0
- data/lib/rx_ruby/concurrency/default_scheduler.rb +51 -0
- data/lib/rx_ruby/concurrency/historical_scheduler.rb +16 -0
- data/lib/rx_ruby/concurrency/immediate_scheduler.rb +68 -0
- data/lib/rx_ruby/concurrency/local_scheduler.rb +39 -0
- data/lib/rx_ruby/concurrency/periodic_scheduler.rb +74 -0
- data/lib/rx_ruby/concurrency/scheduled_item.rb +42 -0
- data/lib/rx_ruby/concurrency/scheduler.rb +150 -0
- data/lib/rx_ruby/concurrency/virtual_time_scheduler.rb +170 -0
- data/lib/rx_ruby/core/async_lock_observer.rb +46 -0
- data/lib/rx_ruby/core/auto_detach_observer.rb +59 -0
- data/lib/rx_ruby/core/checked_observer.rb +66 -0
- data/lib/rx_ruby/core/notification.rb +161 -0
- data/lib/rx_ruby/core/observable.rb +104 -0
- data/lib/rx_ruby/core/observe_on_observer.rb +50 -0
- data/lib/rx_ruby/core/observer.rb +119 -0
- data/lib/rx_ruby/core/scheduled_observer.rb +83 -0
- data/lib/rx_ruby/core/synchronized_observer.rb +47 -0
- data/lib/rx_ruby/core/time_interval.rb +17 -0
- data/lib/rx_ruby/internal/priority_queue.rb +122 -0
- data/lib/rx_ruby/internal/util.rb +9 -0
- data/lib/rx_ruby/joins/active_plan.rb +45 -0
- data/lib/rx_ruby/joins/join_observer.rb +51 -0
- data/lib/rx_ruby/joins/pattern.rb +14 -0
- data/lib/rx_ruby/joins/plan.rb +44 -0
- data/lib/rx_ruby/linq/connectable_observable.rb +34 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_date_and_period.rb +22 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_time_span.rb +14 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_time_span_and_period.rb +20 -0
- data/lib/rx_ruby/linq/observable/aggregate.rb +7 -0
- data/lib/rx_ruby/linq/observable/and.rb +7 -0
- data/lib/rx_ruby/linq/observable/case.rb +15 -0
- data/lib/rx_ruby/linq/observable/concat_all.rb +7 -0
- data/lib/rx_ruby/linq/observable/concat_map.rb +35 -0
- data/lib/rx_ruby/linq/observable/concat_map_observer.rb +43 -0
- data/lib/rx_ruby/linq/observable/contains.rb +28 -0
- data/lib/rx_ruby/linq/observable/debounce.rb +41 -0
- data/lib/rx_ruby/linq/observable/delay.rb +81 -0
- data/lib/rx_ruby/linq/observable/delay_with_selector.rb +64 -0
- data/lib/rx_ruby/linq/observable/do.rb +42 -0
- data/lib/rx_ruby/linq/observable/for.rb +13 -0
- data/lib/rx_ruby/linq/observable/fork_join.rb +55 -0
- data/lib/rx_ruby/linq/observable/from.rb +34 -0
- data/lib/rx_ruby/linq/observable/group_join.rb +108 -0
- data/lib/rx_ruby/linq/observable/if.rb +17 -0
- data/lib/rx_ruby/linq/observable/interval.rb +5 -0
- data/lib/rx_ruby/linq/observable/multicast.rb +14 -0
- data/lib/rx_ruby/linq/observable/of.rb +11 -0
- data/lib/rx_ruby/linq/observable/pairs.rb +7 -0
- data/lib/rx_ruby/linq/observable/pluck.rb +7 -0
- data/lib/rx_ruby/linq/observable/publish.rb +11 -0
- data/lib/rx_ruby/linq/observable/start.rb +7 -0
- data/lib/rx_ruby/linq/observable/time_interval.rb +15 -0
- data/lib/rx_ruby/linq/observable/timer.rb +26 -0
- data/lib/rx_ruby/linq/observable/timestamp.rb +9 -0
- data/lib/rx_ruby/linq/observable/to_async.rb +40 -0
- data/lib/rx_ruby/linq/observable/when.rb +36 -0
- data/lib/rx_ruby/linq/observable/while.rb +41 -0
- data/lib/rx_ruby/operators/aggregates.rb +611 -0
- data/lib/rx_ruby/operators/creation.rb +220 -0
- data/lib/rx_ruby/operators/multiple.rb +735 -0
- data/lib/rx_ruby/operators/single.rb +399 -0
- data/lib/rx_ruby/operators/standard_query_operators.rb +279 -0
- data/lib/rx_ruby/operators/synchronization.rb +47 -0
- data/lib/rx_ruby/operators/time.rb +120 -0
- data/lib/rx_ruby/subjects/async_subject.rb +161 -0
- data/lib/rx_ruby/subjects/behavior_subject.rb +149 -0
- data/lib/rx_ruby/subjects/replay_subject.rb +39 -0
- data/lib/rx_ruby/subjects/subject.rb +131 -0
- data/lib/rx_ruby/subjects/subject_extensions.rb +45 -0
- data/lib/rx_ruby/subscriptions/composite_subscription.rb +91 -0
- data/lib/rx_ruby/subscriptions/ref_count_subscription.rb +88 -0
- data/lib/rx_ruby/subscriptions/scheduled_subscription.rb +32 -0
- data/lib/rx_ruby/subscriptions/serial_subscription.rb +60 -0
- data/lib/rx_ruby/subscriptions/single_assignment_subscription.rb +64 -0
- data/lib/rx_ruby/subscriptions/subscription.rb +56 -0
- data/lib/rx_ruby/testing/cold_observable.rb +45 -0
- data/lib/rx_ruby/testing/hot_observable.rb +47 -0
- data/lib/rx_ruby/testing/mock_observer.rb +33 -0
- data/lib/rx_ruby/testing/reactive_test.rb +94 -0
- data/lib/rx_ruby/testing/recorded.rb +17 -0
- data/lib/rx_ruby/testing/test_scheduler.rb +96 -0
- data/lib/rx_ruby/testing/test_subscription.rb +22 -0
- data/lib/rx_ruby/version.rb +3 -0
- data/license.txt +13 -0
- data/readme.md +152 -0
- data/rx_ruby.gemspec +22 -0
- data/test/rx_ruby/concurrency/helpers/historical_virtual_scheduler_helper.rb +135 -0
- data/test/rx_ruby/concurrency/helpers/immediate_local_scheduler_helper.rb +51 -0
- data/test/rx_ruby/concurrency/test_async_lock.rb +56 -0
- data/test/rx_ruby/concurrency/test_current_thread_scheduler.rb +44 -0
- data/test/rx_ruby/concurrency/test_default_scheduler.rb +44 -0
- data/test/rx_ruby/concurrency/test_historical_scheduler.rb +18 -0
- data/test/rx_ruby/concurrency/test_immediate_scheduler.rb +53 -0
- data/test/rx_ruby/concurrency/test_local_scheduler.rb +12 -0
- data/test/rx_ruby/concurrency/test_periodic_scheduler.rb +53 -0
- data/test/rx_ruby/concurrency/test_scheduled_item.rb +50 -0
- data/test/rx_ruby/concurrency/test_scheduler.rb +128 -0
- data/test/rx_ruby/concurrency/test_virtual_time_scheduler.rb +14 -0
- data/test/rx_ruby/core/test_notification.rb +129 -0
- data/test/rx_ruby/core/test_observable_creation.rb +483 -0
- data/test/rx_ruby/core/test_observer.rb +634 -0
- data/test/rx_ruby/internal/test_priority_queue.rb +71 -0
- data/test/rx_ruby/subscriptions/test_composite_subscription.rb +116 -0
- data/test/rx_ruby/subscriptions/test_serial_subscription.rb +62 -0
- data/test/rx_ruby/subscriptions/test_singleassignment_subscription.rb +61 -0
- data/test/rx_ruby/subscriptions/test_subscription.rb +27 -0
- data/test/test_helper.rb +11 -0
- metadata +291 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module RxRuby
|
6
|
+
|
7
|
+
# Represents a subscription resource which only allows a single assignment of its underlying subscription resource.
|
8
|
+
# If an underlying subscription resource has already been set, future attempts to set the underlying subscription resource will throw an error
|
9
|
+
class SingleAssignmentSubscription
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@gate = Mutex.new()
|
13
|
+
@current = nil
|
14
|
+
@unsubscribed = false
|
15
|
+
@set = false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Gets a value that indicates whether the object is unsubscribed.
|
19
|
+
def unsubscribed?
|
20
|
+
@gate.synchronize do
|
21
|
+
return @unsubscribed
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Gets the underlying subscription. After unsubscribing, the result of getting this property is undefined.
|
26
|
+
def subscription
|
27
|
+
@current
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the underlying disposable. If this has already been set, then an error is raised.
|
31
|
+
def subscription=(new_subscription)
|
32
|
+
raise 'Subscription already set' if @set
|
33
|
+
|
34
|
+
@set = true
|
35
|
+
should_unsubscribe = false
|
36
|
+
old = nil
|
37
|
+
@gate.synchronize do
|
38
|
+
should_unsubscribe = @unsubscribed
|
39
|
+
unless should_unsubscribe
|
40
|
+
old = @current
|
41
|
+
@current = new_subscription
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
old.unsubscribe if old
|
46
|
+
new_subscription.unsubscribe if should_unsubscribe && !new_subscription.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
# Unsubscribes the underlying subscription
|
50
|
+
def unsubscribe
|
51
|
+
old = nil
|
52
|
+
@gate.synchronize do
|
53
|
+
unless @unsubscribed
|
54
|
+
@unsubscribed = true
|
55
|
+
old = @current
|
56
|
+
@current = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
old.unsubscribe if old
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
module Subscription
|
8
|
+
def dispose
|
9
|
+
unsubscribe
|
10
|
+
end
|
11
|
+
end
|
12
|
+
Disposable = Subscription
|
13
|
+
|
14
|
+
class EmptySubscription
|
15
|
+
|
16
|
+
include Subscription
|
17
|
+
include Singleton
|
18
|
+
|
19
|
+
def unsubscribe
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class AnonymousSubscription
|
25
|
+
include Subscription
|
26
|
+
|
27
|
+
def initialize(&unsubscribe_action)
|
28
|
+
@unsubscribe_action = unsubscribe_action
|
29
|
+
@gate = Mutex.new
|
30
|
+
@unsubscribed = false
|
31
|
+
end
|
32
|
+
|
33
|
+
def unsubscribe
|
34
|
+
should_unsubscribe = false
|
35
|
+
@gate.synchronize do
|
36
|
+
should_unsubscribe = !@unsubscribed
|
37
|
+
end
|
38
|
+
|
39
|
+
@unsubscribe_action.call if should_unsubscribe
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Provides a set of class methods for creating Disposables.
|
44
|
+
module Subscription
|
45
|
+
|
46
|
+
# Creates a subscription object that invokes the specified action when unsubscribed.
|
47
|
+
def self.create(&unsubscribe_action)
|
48
|
+
AnonymousSubscription.new(&unsubscribe_action)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Gets the subscription that does nothing when unsubscribed.
|
52
|
+
def self.empty
|
53
|
+
EmptySubscription.instance
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'rx_ruby/subscriptions/subscription'
|
4
|
+
require 'rx_ruby/subscriptions/composite_subscription'
|
5
|
+
require 'rx_ruby/testing/test_subscription'
|
6
|
+
|
7
|
+
module RxRuby
|
8
|
+
|
9
|
+
class ColdObservable
|
10
|
+
|
11
|
+
attr_reader :messages, :subscriptions
|
12
|
+
|
13
|
+
def initialize(scheduler, *args)
|
14
|
+
raise 'scheduler cannot be nil' unless scheduler
|
15
|
+
|
16
|
+
@scheduler = scheduler
|
17
|
+
@messages = args
|
18
|
+
@subscriptions = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def subscribe(observer)
|
22
|
+
raise 'observer cannot be nil' unless observer
|
23
|
+
|
24
|
+
subscriptions.push(TestSubscription.new @scheduler.clock)
|
25
|
+
index = subscriptions.length - 1
|
26
|
+
|
27
|
+
d = CompositeSubscription.new
|
28
|
+
|
29
|
+
messages.each do |message|
|
30
|
+
notification = message.value
|
31
|
+
|
32
|
+
d.push(@scheduler.schedule_at_relative_with_state(nil, message.time, lambda {|scheduler1, state1|
|
33
|
+
notification.accept observer
|
34
|
+
Subscription.empty
|
35
|
+
}))
|
36
|
+
end
|
37
|
+
|
38
|
+
return Subscription.create do
|
39
|
+
subscriptions[index] = TestSubscription.new(subscriptions[index].subscribe, @scheduler.clock)
|
40
|
+
d.unsubscribe
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'rx_ruby/core/observable'
|
4
|
+
require 'rx_ruby/subscriptions/subscription'
|
5
|
+
require 'rx_ruby/testing/test_subscription'
|
6
|
+
|
7
|
+
module RxRuby
|
8
|
+
|
9
|
+
class HotObservable
|
10
|
+
include Observable
|
11
|
+
|
12
|
+
attr_reader :messages, :subscriptions
|
13
|
+
|
14
|
+
def initialize(scheduler, *args)
|
15
|
+
raise 'scheduler cannot be nil' unless scheduler
|
16
|
+
|
17
|
+
@scheduler = scheduler
|
18
|
+
@messages = args
|
19
|
+
@subscriptions = []
|
20
|
+
@observers = []
|
21
|
+
|
22
|
+
@messages.each do |message|
|
23
|
+
notification = message.value
|
24
|
+
@scheduler.schedule_at_relative_with_state(nil, message.time, lambda {|scheduler1, state1|
|
25
|
+
|
26
|
+
@observers.clone.each {|observer| notification.accept observer }
|
27
|
+
|
28
|
+
Subscription.empty
|
29
|
+
})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def subscribe(observer)
|
34
|
+
raise 'observer cannot be nil' unless observer
|
35
|
+
|
36
|
+
@observers.push observer
|
37
|
+
subscriptions.push (TestSubscription.new @scheduler.clock)
|
38
|
+
|
39
|
+
index = subscriptions.length - 1
|
40
|
+
|
41
|
+
Subscription.create do
|
42
|
+
@observers.delete observer
|
43
|
+
subscriptions[index] = TestSubscription.new(subscriptions[index].subscribe, @scheduler.clock)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'rx_ruby/core/observer'
|
4
|
+
require 'rx_ruby/core/notification'
|
5
|
+
require 'rx_ruby/testing/recorded'
|
6
|
+
|
7
|
+
module RxRuby
|
8
|
+
|
9
|
+
class MockObserver
|
10
|
+
include Observer
|
11
|
+
|
12
|
+
attr_reader :messages
|
13
|
+
|
14
|
+
def initialize(scheduler)
|
15
|
+
raise 'scheduler cannot be nil' unless scheduler
|
16
|
+
|
17
|
+
@scheduler = scheduler
|
18
|
+
@messages = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_next(value)
|
22
|
+
messages.push(Recorded.new(@scheduler.clock, Notification.create_on_next(value)))
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_error(error)
|
26
|
+
messages.push(Recorded.new(@scheduler.clock, Notification.create_on_error(error)))
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_completed
|
30
|
+
messages.push(Recorded.new(@scheduler.clock, Notification.create_on_completed))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'rx_ruby/core/notification'
|
4
|
+
require 'rx_ruby/testing/recorded'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
|
8
|
+
# Module to write unit tests for applications and libraries built using Reactive Extensions.
|
9
|
+
module ReactiveTest
|
10
|
+
|
11
|
+
# Default virtual time used for creation of observable sequences in ReactiveTest-based unit tests.
|
12
|
+
CREATED = 100
|
13
|
+
|
14
|
+
# Default virtual time used to subscribe to observable sequences in ReactiveTest-based unit tests.
|
15
|
+
SUBSCRIBED = 200
|
16
|
+
|
17
|
+
# Default virtual time used to dispose subscriptions in ReactiveTest-based unit tests.
|
18
|
+
DISPOSED = 1000
|
19
|
+
|
20
|
+
# Factory method for an on_next notification record at a given time with a given value.
|
21
|
+
def on_next(ticks, value)
|
22
|
+
Recorded.new(ticks, Notification.create_on_next(value))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Factory method for writing an assert that checks for an on_next notification record at a given time, using the specified predicate to check the value.
|
26
|
+
def on_next_predicate(ticks, &block)
|
27
|
+
n = OnNextPredicate.new(&block)
|
28
|
+
Recorded.new(ticks, n)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Factory method for an on_error notification record at a given time with a given error.
|
32
|
+
def on_error(ticks, error)
|
33
|
+
Recorded.new(ticks, Notification.create_on_error(error))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Factory method for writing an assert that checks for an on_error notification record at a given time, using the specified predicate to check the exception.
|
37
|
+
def on_error_predicate(ticks, &block)
|
38
|
+
n = OnErrorPredicate.new(&block)
|
39
|
+
Recorded.new(ticks, n)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Factory method for an OnCompleted notification record at a given time.
|
43
|
+
def on_completed(ticks)
|
44
|
+
Recorded.new(ticks, Notification.create_on_completed)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Factory method for a subscription record based on a given subscription and unsubscribe time.
|
48
|
+
def subscribe(subscribe, unsubscribe)
|
49
|
+
TestSubscription.new(subscribe, unsubscribe)
|
50
|
+
end
|
51
|
+
|
52
|
+
def assert_messages(expected, actual)
|
53
|
+
assert_equal expected.length, actual.length, "The size of messages differ"
|
54
|
+
|
55
|
+
for i in 0..expected.length - 1
|
56
|
+
assert_equal expected[i].time, actual[i].time, "The messages[#{i}].time differ"
|
57
|
+
assert_equal expected[i].value, actual[i].value, "The messages[#{i}].value differ"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def assert_subscriptions(expected, actual)
|
62
|
+
assert_equal expected.length, actual.length
|
63
|
+
|
64
|
+
for i in 0..expected.length - 1
|
65
|
+
assert (expected[i] == actual[i])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class OnNextPredicate
|
70
|
+
|
71
|
+
def initialize(&action)
|
72
|
+
@action = action
|
73
|
+
end
|
74
|
+
|
75
|
+
def ==(other)
|
76
|
+
other && other.on_next? && @action.call(other.value)
|
77
|
+
end
|
78
|
+
alias_method :eql?, :==
|
79
|
+
end
|
80
|
+
|
81
|
+
class OnErrorPredicate
|
82
|
+
|
83
|
+
def initialize(&action)
|
84
|
+
@action = action
|
85
|
+
end
|
86
|
+
|
87
|
+
def ==(other)
|
88
|
+
other && other.on_error? && @action.call(other.error)
|
89
|
+
end
|
90
|
+
alias_method :eql?, :==
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
module RxRuby
|
4
|
+
|
5
|
+
# Record of a value including the virtual time it was produced on.
|
6
|
+
class Recorded < Struct.new(:time, :value)
|
7
|
+
|
8
|
+
def initialize(time, value)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#{value} @ #{time}"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
2
|
+
|
3
|
+
require 'rx_ruby/concurrency/virtual_time_scheduler'
|
4
|
+
require 'rx_ruby/subscriptions/subscription'
|
5
|
+
require 'rx_ruby/testing/cold_observable'
|
6
|
+
require 'rx_ruby/testing/hot_observable'
|
7
|
+
require 'rx_ruby/testing/mock_observer'
|
8
|
+
require 'rx_ruby/testing/reactive_test'
|
9
|
+
|
10
|
+
module RxRuby
|
11
|
+
|
12
|
+
# Virtual time scheduler used for testing applications and libraries built using Reactive Extensions.
|
13
|
+
class TestScheduler < VirtualTimeScheduler
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
super(0)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Schedules an action to be executed at due_time.
|
20
|
+
def schedule_at_absolute_with_state(state, due_time, action)
|
21
|
+
raise 'action cannot be nil' unless action
|
22
|
+
|
23
|
+
due_time = clock + 1 if due_time <= clock
|
24
|
+
|
25
|
+
super(state, due_time, action)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Adds a relative virtual time to an absolute virtual time value.
|
29
|
+
def add(absolute, relative)
|
30
|
+
absolute + relative
|
31
|
+
end
|
32
|
+
|
33
|
+
# Converts the absolute time value to a Time value.
|
34
|
+
def to_time(absolute)
|
35
|
+
Time.at absolute
|
36
|
+
end
|
37
|
+
|
38
|
+
# Converts the time span value to a relative time value.
|
39
|
+
def to_relative(time_span)
|
40
|
+
time_span
|
41
|
+
end
|
42
|
+
|
43
|
+
# Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and unsubscribe the subscription.
|
44
|
+
def configure(options = {})
|
45
|
+
options.each {|key,_|
|
46
|
+
unless [:created, :subscribed, :disposed].include? key
|
47
|
+
raise ArgumentError, "Should be spcified whether :created, :subscribed or :disposed, but the #{key.inspect}"
|
48
|
+
end
|
49
|
+
}
|
50
|
+
o = {
|
51
|
+
:created => ReactiveTest::CREATED,
|
52
|
+
:subscribed => ReactiveTest::SUBSCRIBED,
|
53
|
+
:disposed => ReactiveTest::DISPOSED
|
54
|
+
}.merge(options)
|
55
|
+
|
56
|
+
source = nil
|
57
|
+
subscription = nil
|
58
|
+
observer = create_observer
|
59
|
+
|
60
|
+
schedule_at_absolute_with_state(nil, o[:created], lambda {|scheduler, state|
|
61
|
+
source = yield
|
62
|
+
Subscription.empty
|
63
|
+
})
|
64
|
+
|
65
|
+
schedule_at_absolute_with_state(nil, o[:subscribed], lambda {|scheduler, state|
|
66
|
+
subscription = source.subscribe observer
|
67
|
+
Subscription.empty
|
68
|
+
})
|
69
|
+
|
70
|
+
schedule_at_absolute_with_state(nil, o[:disposed], lambda {|scheduler, state|
|
71
|
+
subscription.unsubscribe
|
72
|
+
Subscription.empty
|
73
|
+
})
|
74
|
+
|
75
|
+
start
|
76
|
+
|
77
|
+
observer
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a hot observable using the specified timestamped notification messages.
|
81
|
+
def create_hot_observable(*args)
|
82
|
+
HotObservable.new(self, *args)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates a cold observable using the specified timestamped notification messages.
|
86
|
+
def create_cold_observable(*args)
|
87
|
+
ColdObservable.new(self, *args)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Creates an observer that records received notification messages and timestamps those.
|
91
|
+
def create_observer
|
92
|
+
MockObserver.new self
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|