blob: b71d5c44d3f38a88f867b613503a428fec64b5ae [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "xefi.h"
#include <lib/efi/testing/mock_serial_io.h>
#include <lib/efi/testing/mock_simple_text_input.h>
#include <lib/efi/testing/stub_boot_services.h>
#include <efi/protocol/serial-io.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace {
using ::efi::MatchGuid;
using ::efi::MockBootServices;
using ::efi::MockSerialIoProtocol;
using ::efi::MockSimpleTextInputProtocol;
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::Test;
const efi_handle kImageHandle = reinterpret_cast<efi_handle>(0x10);
// Test fixture to set up and tear down XEFI state.
class XefiTest : public Test {
public:
// Reset xefi global variables so state doesn't bleed between tests.
void TearDown() override { memset(&xefi_global_state, 0, sizeof(xefi_global_state)); }
// Sets up the state and mock expectations for a future call to xefi_init().
void SetupXefi(MockBootServices& mock_services, efi_serial_io_protocol* serial,
efi_simple_text_input_protocol* text_input = nullptr) {
system_table_ = efi_system_table{
.ConIn = text_input,
.ConOut = &output_protocol_,
.BootServices = mock_services.services(),
};
if (serial) {
EXPECT_CALL(mock_services, LocateProtocol(MatchGuid(EFI_SERIAL_IO_PROTOCOL_GUID), _, _))
.WillOnce(DoAll(SetArgPointee<2>(serial), Return(EFI_SUCCESS)));
} else {
EXPECT_CALL(mock_services, LocateProtocol(MatchGuid(EFI_SERIAL_IO_PROTOCOL_GUID), _, _))
.WillOnce(Return(EFI_LOAD_ERROR));
}
}
protected:
efi_simple_text_output_protocol output_protocol_;
efi_system_table system_table_;
};
TEST_F(XefiTest, InitWithoutSerial) {
MockBootServices mock_services;
SetupXefi(mock_services, nullptr);
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ(gSys, &system_table_);
EXPECT_EQ(gImg, kImageHandle);
EXPECT_EQ(gBS, mock_services.services());
EXPECT_EQ(gConOut, &output_protocol_);
EXPECT_EQ(gSerial, nullptr);
}
TEST_F(XefiTest, InitWithSerial) {
MockBootServices mock_services;
efi_serial_io_protocol serial_protocol;
SetupXefi(mock_services, &serial_protocol);
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ(gSys, &system_table_);
EXPECT_EQ(gImg, kImageHandle);
EXPECT_EQ(gBS, mock_services.services());
EXPECT_EQ(gConOut, &output_protocol_);
EXPECT_EQ(gSerial, &serial_protocol);
}
TEST_F(XefiTest, GetcSerialPoll) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
EXPECT_CALL(mock_serial, SetAttributes).WillRepeatedly(Return(EFI_SUCCESS));
EXPECT_CALL(mock_input, ReadKeyStroke).WillRepeatedly(Return(EFI_NOT_READY));
mock_serial.ExpectRead("x");
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ('x', xefi_getc(0));
}
TEST_F(XefiTest, GetcInputPoll) {
MockBootServices mock_services;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, nullptr, mock_input.protocol());
mock_input.ExpectReadKeyStroke('z');
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ('z', xefi_getc(0));
}
TEST_F(XefiTest, GetcInputTakesPrecedence) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
EXPECT_CALL(mock_serial, SetAttributes).WillRepeatedly(Return(EFI_SUCCESS));
EXPECT_CALL(mock_serial, Read).Times(0);
mock_input.ExpectReadKeyStroke('z');
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ('z', xefi_getc(0));
}
TEST_F(XefiTest, GetcPollNoCharacter) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
EXPECT_CALL(mock_serial, SetAttributes).WillRepeatedly(Return(EFI_SUCCESS));
EXPECT_CALL(mock_serial, Read).WillOnce(Return(EFI_TIMEOUT));
EXPECT_CALL(mock_input, ReadKeyStroke).WillOnce(Return(EFI_NOT_READY));
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ(-1, xefi_getc(0));
}
TEST_F(XefiTest, GetcTimer) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
// Mock 3 "not ready" loops, then find a character on the 4th.
EXPECT_CALL(mock_services, CreateEvent(EVT_TIMER, _, _, _, _)).WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, SetTimer(_, TimerRelative, _)).WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, CheckEvent(_)).Times(3).WillRepeatedly(Return(EFI_NOT_READY));
EXPECT_CALL(mock_services, CloseEvent(_)).WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_serial, SetAttributes).WillRepeatedly(Return(EFI_SUCCESS));
EXPECT_CALL(mock_serial, Read).Times(3).WillRepeatedly(Return(EFI_TIMEOUT));
{
InSequence seq;
EXPECT_CALL(mock_input, ReadKeyStroke).Times(3).WillRepeatedly(Return(EFI_NOT_READY));
mock_input.ExpectReadKeyStroke('z');
}
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ('z', xefi_getc(100));
}
TEST_F(XefiTest, GetcTimeout) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
// Mock 2 "not ready" loops, then timeout on the 3rd.
EXPECT_CALL(mock_services, CreateEvent(EVT_TIMER, _, _, _, _)).WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, SetTimer(_, TimerRelative, _)).WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, CheckEvent(_))
.WillOnce(Return(EFI_NOT_READY))
.WillOnce(Return(EFI_NOT_READY))
.WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, CloseEvent(_)).WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_serial, SetAttributes).WillRepeatedly(Return(EFI_SUCCESS));
EXPECT_CALL(mock_serial, Read).Times(3).WillRepeatedly(Return(EFI_TIMEOUT));
EXPECT_CALL(mock_input, ReadKeyStroke).Times(3).WillRepeatedly(Return(EFI_NOT_READY));
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ(-1, xefi_getc(100));
}
TEST_F(XefiTest, SerialAttributesFailure) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
EXPECT_CALL(mock_serial, SetAttributes).WillOnce(Return(EFI_DEVICE_ERROR));
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ(-1, xefi_getc(0));
}
TEST_F(XefiTest, CreateTimerFailure) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
EXPECT_CALL(mock_serial, SetAttributes).WillRepeatedly(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, CreateEvent(EVT_TIMER, _, _, _, _))
.WillOnce(Return(EFI_OUT_OF_RESOURCES));
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ(-1, xefi_getc(100));
}
TEST_F(XefiTest, SetTimerFailure) {
MockBootServices mock_services;
MockSerialIoProtocol mock_serial;
MockSimpleTextInputProtocol mock_input;
SetupXefi(mock_services, mock_serial.protocol(), mock_input.protocol());
EXPECT_CALL(mock_serial, SetAttributes).WillRepeatedly(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, CreateEvent(EVT_TIMER, _, _, _, _)).WillOnce(Return(EFI_SUCCESS));
EXPECT_CALL(mock_services, SetTimer(_, TimerRelative, _)).WillOnce(Return(EFI_INVALID_PARAMETER));
EXPECT_CALL(mock_services, CloseEvent(_)).WillOnce(Return(EFI_SUCCESS));
xefi_init(kImageHandle, &system_table_);
EXPECT_EQ(-1, xefi_getc(100));
}
} // namespace