• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_QUIC_QUIC_SOCKET_DATA_PROVIDER_H_
6 #define NET_QUIC_QUIC_SOCKET_DATA_PROVIDER_H_
7 
8 #include <map>
9 #include <memory>
10 #include <optional>
11 #include <set>
12 #include <string>
13 
14 #include "base/memory/weak_ptr.h"
15 #include "base/run_loop.h"
16 #include "net/quic/quic_test_packet_printer.h"
17 #include "net/socket/socket_test_util.h"
18 #include "net/third_party/quiche/src/quiche/quic/core/quic_packets.h"
19 
20 namespace net::test {
21 
22 // A `SocketDataProvider` specifically designed to handle QUIC's packet-based
23 // nature, and to give useful errors when things do not go as planned. This
24 // fills the same purpose as `MockQuicData` and it should be straightforward to
25 // "upgrade" a use of `MockQuicData` to this class when adding or modifying
26 // tests.
27 //
28 // To use: create a new `QuicSocketDataProvider`, then add expected reads and
29 // writes to it using the `AddRead` and `AddWrite` methods. Each read or write
30 // must have a short, unique name that will appear in logs and error messages.
31 // Once the provider is populated, add it to a `MockClientSocketFactory` with
32 // `AddSocketDataProvider`.
33 //
34 // Each `Add` method creates an "expectation" that some event will occur on the
35 // socket. A write expectation signals that the system under test will call
36 // `Write` with a packet matching the given data. A read expectation signals
37 // that the SUT will call `Read`, and the data in the expectation will be
38 // returned.
39 //
40 // Expectations can be adjusted when they are created by chaining method calls,
41 // such as setting the mode. Expectations are consumed in a partial order: each
42 // expectation specifies the expectations which must be consumed before it can
43 // be consumed. By default, each expectation must come after the previously
44 // added expectation, but the `After` method can be used to adjust this ordering
45 // for cases where the order is unimportant or might vary. For example, an ACK
46 // might be written before or after a read of stream data.
47 //
48 // When a Write expectation is not met, such as write data not matching the
49 // expected packet, the Write call will result in `ERR_UNEXPECTED`.
50 //
51 // Use `--vmodule=quic_socket_data_provider*=1` in the test command-line to see
52 // additional logging from this module.
53 class QuicSocketDataProvider : public SocketDataProvider {
54  public:
55   class Expectation {
56    public:
57     enum class Type { READ, WRITE, PAUSE };
58 
59     Expectation(Expectation&) = delete;
60     Expectation& operator=(Expectation&) = delete;
61     Expectation(Expectation&&);
62     Expectation& operator=(Expectation&&);
63     ~Expectation();
64 
65     // Set the mode for this expectation, where the default is ASYNC. If a `Read
66     // or `Write` call occurs for a sync expectation when its preconditions have
67     // not been met, the test will fail.
Mode(IoMode mode)68     Expectation& Mode(IoMode mode) {
69       mode_ = mode;
70       return *this;
71     }
Sync()72     Expectation& Sync() {
73       Mode(SYNCHRONOUS);
74       return *this;
75     }
76 
77     // Indicate that this expectation cannot be consumed until the named
78     // expectation has been consumed.
79     Expectation& After(std::string name);
80 
81     // Set the TOS byte for this expectation.
TosByte(uint8_t tos_byte)82     Expectation& TosByte(uint8_t tos_byte) {
83       tos_byte_ = tos_byte;
84       return *this;
85     }
86 
name()87     const std::string& name() const { return name_; }
type()88     Type type() const { return type_; }
consumed()89     bool consumed() const { return consumed_; }
after()90     const std::set<std::string>& after() const { return after_; }
rv()91     int rv() const { return rv_; }
packet()92     const std::unique_ptr<quic::QuicEncryptedPacket>& packet() const {
93       return packet_;
94     }
mode()95     IoMode mode() const { return mode_; }
tos_byte()96     uint8_t tos_byte() const { return tos_byte_; }
97 
98     static std::string TypeToString(Type type);
99 
100    protected:
101     friend class QuicSocketDataProvider;
102 
103     Expectation(std::string name,
104                 Type type,
105                 int rv,
106                 std::unique_ptr<quic::QuicEncryptedPacket> packet);
107 
set_name(std::string name)108     void set_name(std::string name) { name_ = name; }
109     void Consume();
110 
111    private:
112     // Name for this packet, used in sequencing and logging.
113     std::string name_;
114 
115     // Type of expectation.
116     Type type_;
117 
118     // True when this expectation has been consumed; that is, it has been
119     // matched with a call to Read or Write and that call has returned or its
120     // callback has been called.
121     bool consumed_ = false;
122 
123     // Expectations which must be consumed before this one, by name.
124     std::set<std::string> after_;
125 
126     int rv_;
127     std::unique_ptr<quic::QuicEncryptedPacket> packet_;
128     IoMode mode_ = ASYNC;
129     uint8_t tos_byte_ = 0;
130   };
131 
132   // A PausePoint is just the index into the array of expectations.
133   using PausePoint = size_t;
134 
135   explicit QuicSocketDataProvider(quic::ParsedQuicVersion version);
136   ~QuicSocketDataProvider() override;
137 
138   // Adds a read which will result in `packet`. A reference to the provided
139   // expectation is returned, which can be used to update the settings for that
140   // expectation. The more-specific version taking `QuicReceivedPacket` also
141   // sets the TOS byte based on the packet's ECN codepoint.
142   Expectation& AddRead(std::string name,
143                        std::unique_ptr<quic::QuicReceivedPacket> packet);
144   Expectation& AddRead(std::string name,
145                        std::unique_ptr<quic::QuicEncryptedPacket> packet);
146 
147   // Adds a read error return. A reference to the provided expectation is
148   // returned, which can be used to update the settings for that expectation.
149   Expectation& AddReadError(std::string name, int rv);
150 
151   // Adds a write which will expect the given packet and return the given
152   // result. A reference to the provided packet is returned, which can be used
153   // to update the settings for the packet.
154   Expectation& AddWrite(std::string name,
155                         std::unique_ptr<quic::QuicEncryptedPacket> packet,
156                         int rv = OK);
157 
158   // Adds a write error return. A reference to the provided expectation is
159   // returned, which can be used to update the settings for that expectation.
160   Expectation& AddWriteError(std::string name, int rv);
161 
162   // Adds a Pause point, returning a handle that can be used later to wait for
163   // and resume execution. Any expectations that come "after" the pause point
164   // will not be consumed until the pause is reached and execution is resumed.
165   //
166   // Note that this is not compatible with
167   // `SequencedSocketData::RunUntilPaused()`.
168   PausePoint AddPause(std::string name);
169 
170   // Checks if all data has been consumed.
171   bool AllDataConsumed() const;
172 
173   // Run the main loop until the given pause point is reached. If a different
174   // pause point is reached, this will fail. Note that the results of any
175   // `Read` or `Write` calls before the pause point might not be complete, if
176   // those results were delivered asynchronously.
177   void RunUntilPause(PausePoint pause_point);
178 
179   // Resumes I/O after it is paused.
180   void Resume();
181 
182   // Run the main loop until all expectations have been consumed. Note that the
183   // results of any `Read` or `Write` calls might not be complete, if those
184   // results were delivered asynchronously.
185   void RunUntilAllConsumed();
186 
187   // SocketDataProvider implementation.
188   MockRead OnRead() override;
189   MockWriteResult OnWrite(const std::string& data) override;
190   bool AllReadDataConsumed() const override;
191   bool AllWriteDataConsumed() const override;
192   void CancelPendingRead() override;
193   void Reset() override;
194 
195  private:
196   // Find indexes of expectations of the given type that are ready to consume.
197   std::optional<size_t> FindReadyExpectations(Expectation::Type type);
198 
199   // Find a single ready operation, if any. Fails if multiple expectations of
200   // the given type are ready. The corresponding expectation is marked as
201   // consumed, and a task is scheduled to consume any expectations that become
202   // ready as a result.
203   std::optional<MockRead> ConsumeNextRead();
204   std::optional<MockWriteResult> ConsumeNextWrite();
205 
206   // Consume any expectations that have become ready after a change to another
207   // expectation. This is called in a task automatically after one or more calls
208   // to `ExepctationsConsumed`.
209   void MaybeConsumeExpectations();
210 
211   // Update state after an expectation has been consumed.
212   void ExpectationConsumed();
213 
214   // Verify that the packet matches `write_pending_`.
215   bool VerifyWriteData(QuicSocketDataProvider::Expectation& expectation);
216 
217   // Generate a comma-separated list of expectation names.
218   std::string ExpectationList(const std::vector<size_t>& indices);
219 
220   std::vector<Expectation> expectations_;
221   bool pending_maybe_consume_expectations_ = false;
222   std::map<size_t, std::set<size_t>> dependencies_;
223   bool read_pending_ = false;
224   std::optional<std::string> write_pending_ = std::nullopt;
225   QuicPacketPrinter printer_;
226   std::optional<size_t> paused_at_;
227   std::unique_ptr<base::RunLoop> run_until_run_loop_;
228 
229   base::WeakPtrFactory<QuicSocketDataProvider> weak_factory_{this};
230 };
231 
232 }  // namespace net::test
233 
234 #endif  // NET_QUIC_QUIC_SOCKET_DATA_PROVIDER_H_
235