crashes = []; return new ChildMessageOutcome($event, function (int $status) { $this->crashes[] = $status; }); } public function test_a_killed_child_without_reply_is_answered_once_both_signals_are_in() { foreach ([[true, false], [false, true]] as [$streamFirst]) { $outcome = $this->outcome(); $outcome->frameReceived('{"event":"atc-traffic.subscribe[x1]:progress","data":[]}'); if ($streamFirst) { $outcome->streamClosed(); $this->assertSame([], $this->crashes, 'No verdict before the exit status is known.'); $outcome->exited(self::KILLED); } else { $outcome->exited(self::KILLED); $this->assertSame([], $this->crashes, 'No verdict while frames may still be unread.'); $outcome->streamClosed(); } $this->assertSame([self::KILLED], $this->crashes); } } public function test_a_fatal_exit_without_reply_is_answered() { $outcome = $this->outcome(); $outcome->streamClosed(); $outcome->exited(self::EXIT_FATAL); $this->assertSame([self::EXIT_FATAL], $this->crashes); } public function test_a_child_that_replied_is_left_alone_even_if_it_crashed_afterwards() { foreach (['response', 'error'] as $suffix) { $outcome = $this->outcome(); $outcome->frameReceived('{"event":"atc-traffic.subscribe[x1]:' . $suffix . '","data":{"ok":true},"channel":"websocket"}'); $outcome->streamClosed(); $outcome->exited(self::EXIT_FATAL); $this->assertTrue($outcome->replied()); $this->assertSame([], $this->crashes, "A {$suffix} frame counts as the reply."); } } public function test_frames_for_other_events_or_broadcasts_are_not_a_reply() { $outcome = $this->outcome('dashboard.index'); $outcome->frameReceived('B:{"event":"dashboard.index:response"}'); $outcome->frameReceived('{"event":"dashboard.indexer:response"}'); $outcome->frameReceived('{"data":1,"event":"dashboard.index:response"}'); $outcome->frameReceived(null); $this->assertFalse($outcome->replied()); } public function test_event_names_are_matched_in_their_json_encoded_form() { $event = 'admin/user.show[a"b]'; $outcome = $this->outcome($event); $outcome->frameReceived(json_encode(['event' => $event . ':response', 'data' => null])); $this->assertTrue($outcome->replied()); } public function test_a_clean_exit_or_an_unknown_status_sends_nothing() { foreach ([self::EXIT_OK, null] as $status) { $outcome = $this->outcome(); $outcome->streamClosed(); $outcome->exited($status); $this->assertSame([], $this->crashes); } } public function test_the_verdict_is_given_only_once() { $outcome = $this->outcome(); $outcome->streamClosed(); $outcome->exited(self::KILLED); $outcome->streamClosed(); $outcome->exited(self::KILLED); $this->assertCount(1, $this->crashes); } }