1 // Copyright 2023 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // End-to-end test for MoqtClient/MoqtServer.
6 //
7 // IMPORTANT NOTE:
8 // This test mostly exists to test the two classes mentioned above. When
9 // possible, moqt_integration_test should be used instead, as it does not use
10 // real clocks or I/O and thus has less overhead.
11
12 #include <memory>
13 #include <string>
14 #include <utility>
15
16 #include "absl/functional/bind_front.h"
17 #include "absl/status/status.h"
18 #include "absl/status/statusor.h"
19 #include "absl/strings/string_view.h"
20 #include "quiche/quic/core/io/quic_event_loop.h"
21 #include "quiche/quic/core/quic_server_id.h"
22 #include "quiche/quic/moqt/moqt_session.h"
23 #include "quiche/quic/moqt/tools/moqt_client.h"
24 #include "quiche/quic/moqt/tools/moqt_server.h"
25 #include "quiche/quic/platform/api/quic_ip_address.h"
26 #include "quiche/quic/platform/api/quic_socket_address.h"
27 #include "quiche/quic/platform/api/quic_test_loopback.h"
28 #include "quiche/quic/test_tools/crypto_test_utils.h"
29 #include "quiche/quic/tools/quic_event_loop_tools.h"
30 #include "quiche/common/platform/api/quiche_logging.h"
31 #include "quiche/common/platform/api/quiche_test.h"
32 #include "quiche/common/quiche_callbacks.h"
33
34 namespace moqt::test {
35 namespace {
36
37 constexpr absl::string_view kNotFoundPath = "/not-found";
38
UnexpectedClose(absl::string_view reason)39 void UnexpectedClose(absl::string_view reason) {
40 ADD_FAILURE() << "Unexpected close of MoQT session with reason: " << reason;
41 }
42
43 class MoqtEndToEndTest : public quiche::test::QuicheTest {
44 public:
MoqtEndToEndTest()45 MoqtEndToEndTest()
46 : server_(quic::test::crypto_test_utils::ProofSourceForTesting(),
47 absl::bind_front(&MoqtEndToEndTest::ServerBackend, this)) {
48 quic::QuicIpAddress host = quic::TestLoopback();
49 bool success = server_.quic_server().CreateUDPSocketAndListen(
50 quic::QuicSocketAddress(host, /*port=*/0));
51 QUICHE_CHECK(success);
52 server_address_ =
53 quic::QuicSocketAddress(host, server_.quic_server().port());
54 event_loop_ = server_.quic_server().event_loop();
55 }
56
ServerBackend(absl::string_view path)57 absl::StatusOr<MoqtConfigureSessionCallback> ServerBackend(
58 absl::string_view path) {
59 QUICHE_LOG(INFO) << "Server: Received a request for path " << path;
60 if (path == kNotFoundPath) {
61 return absl::NotFoundError("404 test endpoint");
62 }
63 return [](MoqtSession* session) {
64 session->callbacks().session_established_callback = []() {
65 QUICHE_LOG(INFO) << "Server: session established";
66 };
67 session->callbacks().session_terminated_callback =
68 [](absl::string_view reason) {
69 QUICHE_LOG(INFO)
70 << "Server: session terminated with reason: " << reason;
71 };
72 };
73 }
74
CreateClient()75 std::unique_ptr<MoqtClient> CreateClient() {
76 return std::make_unique<MoqtClient>(
77 server_address_, quic::QuicServerId("test.example.com", 443),
78 quic::test::crypto_test_utils::ProofVerifierForTesting(), event_loop_);
79 }
80
RunEventsUntil(quiche::UnretainedCallback<bool ()> callback)81 bool RunEventsUntil(quiche::UnretainedCallback<bool()> callback) {
82 return quic::ProcessEventsUntil(event_loop_, callback);
83 }
84
85 private:
86 MoqtServer server_;
87 quic::QuicEventLoop* event_loop_;
88 quic::QuicSocketAddress server_address_;
89 };
90
TEST_F(MoqtEndToEndTest,SuccessfulHandshake)91 TEST_F(MoqtEndToEndTest, SuccessfulHandshake) {
92 MoqtSessionCallbacks callbacks;
93 bool established = false;
94 bool deleted = false;
95 callbacks.session_established_callback = [&] { established = true; };
96 callbacks.session_terminated_callback = UnexpectedClose;
97 callbacks.session_deleted_callback = [&] { deleted = true; };
98 std::unique_ptr<MoqtClient> client = CreateClient();
99 client->Connect("/test", std::move(callbacks));
100 bool success = RunEventsUntil([&] { return established; });
101 EXPECT_TRUE(success);
102 EXPECT_FALSE(deleted);
103 client.reset();
104 EXPECT_TRUE(deleted);
105 }
106
TEST_F(MoqtEndToEndTest,HandshakeFailed404)107 TEST_F(MoqtEndToEndTest, HandshakeFailed404) {
108 MoqtSessionCallbacks callbacks;
109 bool resolved = false;
110 callbacks.session_established_callback = [&] {
111 ADD_FAILURE() << "Established session when 404 expected";
112 resolved = true;
113 };
114 callbacks.session_terminated_callback = [&](absl::string_view error) {
115 resolved = true;
116 };
117 std::unique_ptr<MoqtClient> client = CreateClient();
118 client->Connect(std::string(kNotFoundPath), std::move(callbacks));
119 bool success = RunEventsUntil([&] { return resolved; });
120 EXPECT_TRUE(success);
121 }
122
123 } // namespace
124 } // namespace moqt::test
125