1 // Copyright 2017 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.
5 #include "services/device/serial/serial_port_impl.h"
7 #include "base/test/bind.h"
8 #include "mojo/public/cpp/bindings/pending_remote.h"
9 #include "mojo/public/cpp/bindings/remote.h"
10 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
11 #include "mojo/public/cpp/system/data_pipe.h"
12 #include "mojo/public/cpp/system/simple_watcher.h"
13 #include "services/device/device_service_test_base.h"
14 #include "services/device/public/mojom/serial.mojom.h"
15 #include "services/device/serial/serial_io_handler.h"
21 class FakeSerialIoHandler : public SerialIoHandler {
24 : SerialIoHandler(base::FilePath(), /*ui_thread_task_runner=*/nullptr) {}
26 void SimulateOpenFailure(bool fail) { fail_open_ = fail; }
28 void SimulateGetControlSignalsFailure(bool fail) {
29 fail_get_control_signals_ = fail;
32 void SimulateSetControlSignalsFailure(bool fail) {
33 fail_set_control_signals_ = fail;
36 // SerialIoHandler implementation
37 void Open(const mojom::SerialConnectionOptions& options,
38 OpenCompleteCallback callback) override {
39 std::move(callback).Run(!fail_open_);
42 void Flush(mojom::SerialPortFlushMode mode) const override {}
43 void Drain() override {}
45 mojom::SerialPortControlSignalsPtr GetControlSignals() const override {
46 if (fail_get_control_signals_)
49 return input_signals_.Clone();
52 bool SetControlSignals(
53 const mojom::SerialHostControlSignals& control_signals) override {
54 if (fail_set_control_signals_)
57 output_signals_ = control_signals;
61 mojom::SerialConnectionInfoPtr GetPortInfo() const override {
62 return mojom::SerialConnectionInfo::New();
65 void ReadImpl() override {}
67 void WriteImpl() override {}
69 void CancelReadImpl() override {
70 ReadCompleted(/*bytes_read=*/0, mojom::SerialReceiveError::NONE);
73 void CancelWriteImpl() override {
74 WriteCompleted(/*bytes_written=*/0, mojom::SerialSendError::NONE);
77 bool ConfigurePortImpl() override {
78 // Open() is overridden so this should never be called.
79 ADD_FAILURE() << "ConfigurePortImpl() should not be reached.";
84 ~FakeSerialIoHandler() override = default;
86 mojom::SerialPortControlSignals input_signals_;
87 mojom::SerialHostControlSignals output_signals_;
88 bool fail_open_ = false;
89 bool fail_get_control_signals_ = false;
90 bool fail_set_control_signals_ = false;
95 class SerialPortImplTest : public DeviceServiceTestBase {
97 SerialPortImplTest() = default;
98 SerialPortImplTest(const SerialPortImplTest& other) = delete;
99 void operator=(const SerialPortImplTest& other) = delete;
100 ~SerialPortImplTest() override = default;
102 scoped_refptr<FakeSerialIoHandler> CreatePort(
103 mojo::Remote<mojom::SerialPort>* port,
104 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher>* watcher) {
105 auto io_handler = base::MakeRefCounted<FakeSerialIoHandler>();
106 mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote;
107 *watcher = mojo::MakeSelfOwnedReceiver(
108 std::make_unique<mojom::SerialPortConnectionWatcher>(),
109 watcher_remote.InitWithNewPipeAndPassReceiver());
111 SerialPortImpl::OpenForTesting(
112 io_handler, mojom::SerialConnectionOptions::New(), mojo::NullRemote(),
113 std::move(watcher_remote),
114 base::BindLambdaForTesting(
115 [&](mojo::PendingRemote<mojom::SerialPort> pending_remote) {
116 EXPECT_TRUE(pending_remote.is_valid());
117 port->Bind(std::move(pending_remote));
124 void CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer,
125 mojo::ScopedDataPipeConsumerHandle* consumer) {
126 MojoCreateDataPipeOptions options;
127 options.struct_size = sizeof(MojoCreateDataPipeOptions);
128 options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
129 options.element_num_bytes = 1;
130 options.capacity_num_bytes = 64;
132 MojoResult result = mojo::CreateDataPipe(&options, *producer, *consumer);
133 DCHECK_EQ(result, MOJO_RESULT_OK);
136 mojo::ScopedDataPipeConsumerHandle StartReading(
137 mojom::SerialPort* serial_port) {
138 mojo::ScopedDataPipeProducerHandle producer;
139 mojo::ScopedDataPipeConsumerHandle consumer;
140 CreateDataPipe(&producer, &consumer);
141 serial_port->StartReading(std::move(producer));
145 mojo::ScopedDataPipeProducerHandle StartWriting(
146 mojom::SerialPort* serial_port) {
147 mojo::ScopedDataPipeProducerHandle producer;
148 mojo::ScopedDataPipeConsumerHandle consumer;
149 CreateDataPipe(&producer, &consumer);
150 serial_port->StartWriting(std::move(consumer));
155 TEST_F(SerialPortImplTest, WatcherClosedWhenPortClosed) {
156 mojo::Remote<mojom::SerialPort> serial_port;
157 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
158 CreatePort(&serial_port, &watcher);
160 // To start with both the serial port connection and the connection watcher
161 // connection should remain open.
162 serial_port.FlushForTesting();
163 EXPECT_TRUE(serial_port.is_connected());
164 watcher->FlushForTesting();
165 EXPECT_TRUE(watcher);
167 // When the serial port connection is closed the watcher connection should be
170 watcher->FlushForTesting();
171 EXPECT_FALSE(watcher);
174 TEST_F(SerialPortImplTest, PortClosedWhenWatcherClosed) {
175 mojo::Remote<mojom::SerialPort> serial_port;
176 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
177 CreatePort(&serial_port, &watcher);
179 // To start with both the serial port connection and the connection watcher
180 // connection should remain open.
181 serial_port.FlushForTesting();
182 EXPECT_TRUE(serial_port.is_connected());
183 watcher->FlushForTesting();
184 EXPECT_TRUE(watcher);
186 // When the watcher connection is closed, for safety, the serial port
187 // connection should also be closed.
189 serial_port.FlushForTesting();
190 EXPECT_FALSE(serial_port.is_connected());
193 TEST_F(SerialPortImplTest, FlushRead) {
194 mojo::Remote<mojom::SerialPort> serial_port;
195 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
196 CreatePort(&serial_port, &watcher);
198 mojo::ScopedDataPipeConsumerHandle consumer = StartReading(serial_port.get());
200 // Calling Flush(kReceive) should cause the data pipe to close.
201 base::RunLoop watcher_loop;
202 mojo::SimpleWatcher pipe_watcher(
203 FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC);
204 EXPECT_EQ(pipe_watcher.Watch(consumer.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
205 MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
206 base::BindLambdaForTesting(
207 [&](MojoResult result,
208 const mojo::HandleSignalsState& state) {
209 EXPECT_EQ(result, MOJO_RESULT_OK);
210 EXPECT_TRUE(state.peer_closed());
216 serial_port->Flush(mojom::SerialPortFlushMode::kReceive, loop.QuitClosure());
221 TEST_F(SerialPortImplTest, OpenFailure) {
222 auto io_handler = base::MakeRefCounted<FakeSerialIoHandler>();
223 io_handler->SimulateOpenFailure(true);
225 mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote;
226 mojo::MakeSelfOwnedReceiver(
227 std::make_unique<mojom::SerialPortConnectionWatcher>(),
228 watcher_remote.InitWithNewPipeAndPassReceiver());
230 SerialPortImpl::OpenForTesting(
231 io_handler, mojom::SerialConnectionOptions::New(), mojo::NullRemote(),
232 std::move(watcher_remote),
233 base::BindLambdaForTesting(
234 [&](mojo::PendingRemote<mojom::SerialPort> pending_remote) {
235 EXPECT_FALSE(pending_remote.is_valid());
241 TEST_F(SerialPortImplTest, GetControlSignalsFailure) {
242 mojo::Remote<mojom::SerialPort> serial_port;
243 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
244 scoped_refptr<FakeSerialIoHandler> io_handler =
245 CreatePort(&serial_port, &watcher);
246 io_handler->SimulateGetControlSignalsFailure(true);
249 serial_port->GetControlSignals(base::BindLambdaForTesting(
250 [&](mojom::SerialPortControlSignalsPtr signals) {
251 EXPECT_FALSE(signals);
257 TEST_F(SerialPortImplTest, SetControlSignalsFailure) {
258 mojo::Remote<mojom::SerialPort> serial_port;
259 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
260 scoped_refptr<FakeSerialIoHandler> io_handler =
261 CreatePort(&serial_port, &watcher);
262 io_handler->SimulateSetControlSignalsFailure(true);
265 auto signals = mojom::SerialHostControlSignals::New();
266 signals->has_dtr = true;
268 serial_port->SetControlSignals(std::move(signals),
269 base::BindLambdaForTesting([&](bool success) {
270 EXPECT_FALSE(success);
276 TEST_F(SerialPortImplTest, FlushWrite) {
277 mojo::Remote<mojom::SerialPort> serial_port;
278 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
279 CreatePort(&serial_port, &watcher);
281 mojo::ScopedDataPipeProducerHandle producer = StartWriting(serial_port.get());
283 // Calling Flush(kTransmit) should cause the data pipe to close.
284 base::RunLoop watcher_loop;
285 mojo::SimpleWatcher pipe_watcher(
286 FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC);
287 EXPECT_EQ(pipe_watcher.Watch(producer.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
288 MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
289 base::BindLambdaForTesting(
290 [&](MojoResult result,
291 const mojo::HandleSignalsState& state) {
292 EXPECT_EQ(result, MOJO_RESULT_OK);
293 EXPECT_TRUE(state.peer_closed());
299 serial_port->Flush(mojom::SerialPortFlushMode::kTransmit, loop.QuitClosure());
304 TEST_F(SerialPortImplTest, Drain) {
305 mojo::Remote<mojom::SerialPort> serial_port;
306 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
307 CreatePort(&serial_port, &watcher);
309 mojo::ScopedDataPipeProducerHandle producer = StartWriting(serial_port.get());
311 // Drain() will wait for the data pipe to close before replying.
315 serial_port->Drain(loop.QuitClosure());
319 TEST_F(SerialPortImplTest, Close) {
320 mojo::Remote<mojom::SerialPort> serial_port;
321 mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
322 CreatePort(&serial_port, &watcher);
325 serial_port->Close(/*flush=*/true, loop.QuitClosure());
329 } // namespace device