Commit 2b75617d authored by Reilly Grant's avatar Reilly Grant Committed by Chromium LUCI CQ

serial: Fix getSignals() error handling

This change fixes error handling for the getSignals() method. It was
assumed that the Mojo reply value could be null on error but this was
not allowed by serial.mojom. This meant that if a call to
GetControlSignals() failed it would trigger a Mojo connection failure
rather than only reporting the (potentially recoverable) error.

Tests exercising this case on both the renderer and browser process
sides have been added.

Bug: 1156864
Change-Id: Ife5c953d5f6748c0290fae2f2d53c5415c1faba6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2580747
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835293}
parent 0d088443
...@@ -188,8 +188,8 @@ interface SerialPort { ...@@ -188,8 +188,8 @@ interface SerialPort {
// all buffered data to be transmitted by the port. // all buffered data to be transmitted by the port.
Drain() => (); Drain() => ();
// Reads current control signals (DCD, CTS, etc.). // Reads current control signals (DCD, CTS, etc.). Returns null on failure.
GetControlSignals() => (SerialPortControlSignals signals); GetControlSignals() => (SerialPortControlSignals? signals);
// Sets one or more control signals and returns result. // Sets one or more control signals and returns result.
SetControlSignals(SerialHostControlSignals signals) => (bool success); SetControlSignals(SerialHostControlSignals signals) => (bool success);
......
...@@ -24,20 +24,38 @@ class FakeSerialIoHandler : public SerialIoHandler { ...@@ -24,20 +24,38 @@ class FakeSerialIoHandler : public SerialIoHandler {
FakeSerialIoHandler() FakeSerialIoHandler()
: SerialIoHandler(base::FilePath(), /*ui_thread_task_runner=*/nullptr) {} : SerialIoHandler(base::FilePath(), /*ui_thread_task_runner=*/nullptr) {}
void SimulateOpenFailure(bool fail) { fail_open_ = fail; }
void SimulateGetControlSignalsFailure(bool fail) {
fail_get_control_signals_ = fail;
}
void SimulateSetControlSignalsFailure(bool fail) {
fail_set_control_signals_ = fail;
}
// SerialIoHandler implementation
void Open(const mojom::SerialConnectionOptions& options, void Open(const mojom::SerialConnectionOptions& options,
OpenCompleteCallback callback) override { OpenCompleteCallback callback) override {
std::move(callback).Run(true); std::move(callback).Run(!fail_open_);
} }
void Flush(mojom::SerialPortFlushMode mode) const override {} void Flush(mojom::SerialPortFlushMode mode) const override {}
void Drain() override {} void Drain() override {}
mojom::SerialPortControlSignalsPtr GetControlSignals() const override { mojom::SerialPortControlSignalsPtr GetControlSignals() const override {
return mojom::SerialPortControlSignals::New(); if (fail_get_control_signals_)
return nullptr;
return input_signals_.Clone();
} }
bool SetControlSignals( bool SetControlSignals(
const mojom::SerialHostControlSignals& control_signals) override { const mojom::SerialHostControlSignals& control_signals) override {
if (fail_set_control_signals_)
return false;
output_signals_ = control_signals;
return true; return true;
} }
...@@ -57,10 +75,20 @@ class FakeSerialIoHandler : public SerialIoHandler { ...@@ -57,10 +75,20 @@ class FakeSerialIoHandler : public SerialIoHandler {
QueueWriteCompleted(/*bytes_written=*/0, mojom::SerialSendError::NONE); QueueWriteCompleted(/*bytes_written=*/0, mojom::SerialSendError::NONE);
} }
bool ConfigurePortImpl() override { return true; } bool ConfigurePortImpl() override {
// Open() is overridden so this should never be called.
ADD_FAILURE() << "ConfigurePortImpl() should not be reached.";
return false;
}
private: private:
~FakeSerialIoHandler() override = default; ~FakeSerialIoHandler() override = default;
mojom::SerialPortControlSignals input_signals_;
mojom::SerialHostControlSignals output_signals_;
bool fail_open_ = false;
bool fail_get_control_signals_ = false;
bool fail_set_control_signals_ = false;
}; };
} // namespace } // namespace
...@@ -72,17 +100,17 @@ class SerialPortImplTest : public DeviceServiceTestBase { ...@@ -72,17 +100,17 @@ class SerialPortImplTest : public DeviceServiceTestBase {
void operator=(const SerialPortImplTest& other) = delete; void operator=(const SerialPortImplTest& other) = delete;
~SerialPortImplTest() override = default; ~SerialPortImplTest() override = default;
void CreatePort( scoped_refptr<FakeSerialIoHandler> CreatePort(
mojo::Remote<mojom::SerialPort>* port, mojo::Remote<mojom::SerialPort>* port,
mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher>* watcher) { mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher>* watcher) {
auto io_handler = base::MakeRefCounted<FakeSerialIoHandler>();
mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote; mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote;
*watcher = mojo::MakeSelfOwnedReceiver( *watcher = mojo::MakeSelfOwnedReceiver(
std::make_unique<mojom::SerialPortConnectionWatcher>(), std::make_unique<mojom::SerialPortConnectionWatcher>(),
watcher_remote.InitWithNewPipeAndPassReceiver()); watcher_remote.InitWithNewPipeAndPassReceiver());
base::RunLoop loop; base::RunLoop loop;
SerialPortImpl::OpenForTesting( SerialPortImpl::OpenForTesting(
base::MakeRefCounted<FakeSerialIoHandler>(), io_handler, mojom::SerialConnectionOptions::New(), mojo::NullRemote(),
mojom::SerialConnectionOptions::New(), mojo::NullRemote(),
std::move(watcher_remote), std::move(watcher_remote),
base::BindLambdaForTesting( base::BindLambdaForTesting(
[&](mojo::PendingRemote<mojom::SerialPort> pending_remote) { [&](mojo::PendingRemote<mojom::SerialPort> pending_remote) {
...@@ -91,6 +119,7 @@ class SerialPortImplTest : public DeviceServiceTestBase { ...@@ -91,6 +119,7 @@ class SerialPortImplTest : public DeviceServiceTestBase {
loop.Quit(); loop.Quit();
})); }));
loop.Run(); loop.Run();
return io_handler;
} }
void CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer, void CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer,
...@@ -190,6 +219,61 @@ TEST_F(SerialPortImplTest, FlushRead) { ...@@ -190,6 +219,61 @@ TEST_F(SerialPortImplTest, FlushRead) {
watcher_loop.Run(); watcher_loop.Run();
} }
TEST_F(SerialPortImplTest, OpenFailure) {
auto io_handler = base::MakeRefCounted<FakeSerialIoHandler>();
io_handler->SimulateOpenFailure(true);
mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<mojom::SerialPortConnectionWatcher>(),
watcher_remote.InitWithNewPipeAndPassReceiver());
base::RunLoop loop;
SerialPortImpl::OpenForTesting(
io_handler, mojom::SerialConnectionOptions::New(), mojo::NullRemote(),
std::move(watcher_remote),
base::BindLambdaForTesting(
[&](mojo::PendingRemote<mojom::SerialPort> pending_remote) {
EXPECT_FALSE(pending_remote.is_valid());
loop.Quit();
}));
loop.Run();
}
TEST_F(SerialPortImplTest, GetControlSignalsFailure) {
mojo::Remote<mojom::SerialPort> serial_port;
mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
scoped_refptr<FakeSerialIoHandler> io_handler =
CreatePort(&serial_port, &watcher);
io_handler->SimulateGetControlSignalsFailure(true);
base::RunLoop loop;
serial_port->GetControlSignals(base::BindLambdaForTesting(
[&](mojom::SerialPortControlSignalsPtr signals) {
EXPECT_FALSE(signals);
loop.Quit();
}));
loop.Run();
}
TEST_F(SerialPortImplTest, SetControlSignalsFailure) {
mojo::Remote<mojom::SerialPort> serial_port;
mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
scoped_refptr<FakeSerialIoHandler> io_handler =
CreatePort(&serial_port, &watcher);
io_handler->SimulateSetControlSignalsFailure(true);
base::RunLoop loop;
auto signals = mojom::SerialHostControlSignals::New();
signals->has_dtr = true;
signals->dtr = true;
serial_port->SetControlSignals(std::move(signals),
base::BindLambdaForTesting([&](bool success) {
EXPECT_FALSE(success);
loop.Quit();
}));
loop.Run();
}
TEST_F(SerialPortImplTest, FlushWrite) { TEST_F(SerialPortImplTest, FlushWrite) {
mojo::Remote<mojom::SerialPort> serial_port; mojo::Remote<mojom::SerialPort> serial_port;
mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher;
......
...@@ -93,11 +93,13 @@ class FakeSerialPort { ...@@ -93,11 +93,13 @@ class FakeSerialPort {
ringIndicator: false, ringIndicator: false,
dataSetReady: false dataSetReady: false
}; };
this.inputSignalFailure_ = false;
this.outputSignals_ = { this.outputSignals_ = {
dataTerminalReady: false, dataTerminalReady: false,
requestToSend: false, requestToSend: false,
break: false break: false
}; };
this.outputSignalFailure_ = false;
} }
open(options, client) { open(options, client) {
...@@ -170,10 +172,18 @@ class FakeSerialPort { ...@@ -170,10 +172,18 @@ class FakeSerialPort {
this.inputSignals_ = signals; this.inputSignals_ = signals;
} }
simulateInputSignalFailure(fail) {
this.inputSignalFailure_ = fail;
}
get outputSignals() { get outputSignals() {
return this.outputSignals_; return this.outputSignals_;
} }
simulateOutputSignalFailure(fail) {
this.outputSignalFailure_ = fail;
}
writable() { writable() {
if (this.writable_) if (this.writable_)
return Promise.resolve(); return Promise.resolve();
...@@ -241,6 +251,10 @@ class FakeSerialPort { ...@@ -241,6 +251,10 @@ class FakeSerialPort {
} }
async getControlSignals() { async getControlSignals() {
if (this.inputSignalFailure_) {
return {signals: null};
}
const signals = { const signals = {
dcd: this.inputSignals_.dataCarrierDetect, dcd: this.inputSignals_.dataCarrierDetect,
cts: this.inputSignals_.clearToSend, cts: this.inputSignals_.clearToSend,
...@@ -251,6 +265,10 @@ class FakeSerialPort { ...@@ -251,6 +265,10 @@ class FakeSerialPort {
} }
async setControlSignals(signals) { async setControlSignals(signals) {
if (this.outputSignalFailure_) {
return {success: false};
}
if (signals.hasDtr) { if (signals.hasDtr) {
this.outputSignals_.dataTerminalReady = signals.dtr; this.outputSignals_.dataTerminalReady = signals.dtr;
} }
......
...@@ -41,3 +41,23 @@ serial_test(async (t, fake) => { ...@@ -41,3 +41,23 @@ serial_test(async (t, fake) => {
signals = await port.getSignals(); signals = await port.getSignals();
assert_object_equals(signals, expectedSignals, 'DSR set'); assert_object_equals(signals, expectedSignals, 'DSR set');
}, 'getSignals() returns the current state of input control signals'); }, 'getSignals() returns the current state of input control signals');
serial_test(async (t, fake) => {
const {port, fakePort} = await getFakeSerialPort(fake);
await port.open({baudRate: 9600});
fakePort.simulateInputSignalFailure(true);
await promise_rejects_dom(t, 'NetworkError', port.getSignals());
fakePort.simulateInputSignalFailure(false);
const expectedSignals = {
dataCarrierDetect: false,
clearToSend: false,
ringIndicator: false,
dataSetReady: false
};
const signals = await port.getSignals();
assert_object_equals(signals, expectedSignals);
await port.close();
}, 'getSignals() rejects on failure');
...@@ -43,3 +43,17 @@ serial_test(async (t, fake) => { ...@@ -43,3 +43,17 @@ serial_test(async (t, fake) => {
expectedSignals.break = false; expectedSignals.break = false;
assert_object_equals(fakePort.outputSignals, expectedSignals, 'invert'); assert_object_equals(fakePort.outputSignals, expectedSignals, 'invert');
}, 'setSignals() modifies the state of the port'); }, 'setSignals() modifies the state of the port');
serial_test(async (t, fake) => {
const {port, fakePort} = await getFakeSerialPort(fake);
await port.open({baudRate: 9600});
fakePort.simulateOutputSignalFailure(true);
await promise_rejects_dom(t, 'NetworkError', port.setSignals({break: true}));
fakePort.simulateOutputSignalFailure(false);
await port.setSignals({break: true});
assert_true(fakePort.outputSignals.break);
await port.close();
}, 'setSignals() rejects on failure');
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment