def test_should_work_with_asynchronous_subscribers(self): subject = PublishSubject() def gen_observers(): for i in range(10): o1 = TestObserver() subject.subscribe(o1, self.scheduler) yield o1 obs_list = list(gen_observers()) for i in range(10): ack = subject.on_next(i) self.assertFalse(ack.has_value) for o in obs_list: o.ack.on_next(continue_ack) o.ack.on_completed() self.assertTrue(ack.has_value) # todo: e+1?? self.assertEqual(sum(sum(o.received) for o in obs_list), sum(e + 1 for e in range(i)) * 10) subject.on_completed() self.assertTrue(all(o.is_completed for o in obs_list))
def test_should_emit_from_the_point_of_subscription_forward(self): subject = PublishSubject() self.assertIsInstance(subject.on_next(1), Continue) self.assertIsInstance(subject.on_next(2), Continue) self.assertIsInstance(subject.on_next(3), Continue) o1 = TestObserver() o1.immediate_continue = 5 subject.unsafe_subscribe(o1, self.scheduler, CurrentThreadScheduler()) self.assertIsInstance(subject.on_next(4), Continue) self.assertIsInstance(subject.on_next(5), Continue) self.assertIsInstance(subject.on_next(6), Continue) subject.on_completed() self.assertEqual(sum(o1.received), 15) self.assertTrue(o1.is_completed)
def share(self): """ Converts this observable into a multicast observable that backpressures only after each subscribed observer backpressures. Note that this observable is subscribed when the multicast observable is subscribed for the first time. Therefore, this observable is never subscribed more than once. :return: multicast observable """ observable = ConnectableObservable( source=self, subject=PublishSubject()).ref_count() return ObservableOp(observable)
def test_subscribe_after_complete_should_complete_immediately(self): subject = PublishSubject() subject.on_completed() o1 = TestObserver() subject.subscribe(o1, self.scheduler) self.assertTrue(o1.is_completed)
def test_unsubscribe_after_on_complete(self): subject = PublishSubject() o1 = TestObserver() d = subject.subscribe(o1, self.scheduler) subject.on_next(1) subject.on_completed() self.scheduler.advance_by(1) d.dispose() self.assertListEqual(o1.received, [1])
def test_on_error_should_terminate_current_and_future_subscribers(self): subject = PublishSubject() dummy = Exception('dummy') def gen_observers(): for i in range(10): o1 = TestObserver() subject.subscribe(o1, self.scheduler) yield o1 obs_list = list(gen_observers()) subject.on_next(1) subject.on_error(dummy) o1 = TestObserver() subject.subscribe(o1, self.scheduler) for obs in obs_list: self.assertListEqual(obs.received, [1]) self.assertEqual(obs.was_thrown, dummy) self.assertEqual(o1.was_thrown, dummy)
def test_should_work_synchronously_for_synchronous_subscribers(self): subject = PublishSubject() def gen_observers(): for i in range(10): o1 = TestObserver() o1.immediate_continue = 5 subject.subscribe(o1, self.scheduler) yield o1 obs_list = list(gen_observers()) self.assertIsInstance(subject.on_next(1), Continue) self.assertIsInstance(subject.on_next(2), Continue) self.assertIsInstance(subject.on_next(3), Continue) subject.on_completed() self.assertEqual(sum(sum(o.received) for o in obs_list), 60) self.assertTrue(all(o.is_completed for o in obs_list))
def on_next_left(left_val): # left has been requested because of initial state, # or because right is higher than left left_elem[0] = left_val left_ack[0] = Ack() publish_subject[0] = PublishSubject() # send next inner observable; subscribe needs to happen immediately outer_ack[0] = left_observer[0].on_next( (left_val, publish_subject[0])) # todo: make this private with lock: has_left_elem[0] = True if has_right_elem[0]: has_right = True else: # race condition: left element is receieved first has_right = False if has_right: right_val = right_elem[0] if left_is_lower(left_val, right_val): # right is higher than left # send (empty) inner observable and request new left; don't discard right element # inner observable is empty, because left has just been received # discard left element has_left_elem[0] = False left_elem[0] = None # complete (empty) inner observable publish_subject[0].on_completed() # continue next left element after outer acknowledgment return outer_ack[0] if left_is_higher(left_val, right_val): # right is lower than left, discard right and request new right # this is possible in initial phase or if is_lower and is_higher are not tight assert right_ack[0] is not None, 'missing acknowledgment' with lock: # avoids completing observer twice # discard right element has_right_elem[0] = False right_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: left_observer[0].on_completed() right_observer[0].on_completed() return Stop() ack = right_observer[0].on_next((False, right_val)) # request new right ack.connect_ack(next_ack=right_ack[0]) else: # left is equal to right, send right element, request new right with lock: # avoids completing observer twice # discard right element has_right_elem[0] = False right_elem[0] = None if has_completed[0]: complete_observer = True else: complete_observer = False if complete_observer: left_observer[0].on_completed() right_observer[0].on_completed() return Stop() # send right element ack = publish_subject[0].on_next(right_val) ack2 = right_observer[0].on_next((True, right_val)) # request new right element ack.connect_ack_2(ack2=ack2, next_ack=right_ack[0]) return_ack = left_ack[0].merge_ack(outer_ack[0]) # return left_ack[0] return return_ack
def __init__(self, source, subject=None): super().__init__() self.source = source self.subject = subject or PublishSubject() self.has_subscription = False self.subscription = None