| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "hoth_unittest.hpp" |
| |
| #include <hoth.hpp> |
| #include <xyz/openbmc_project/Control/Hoth/error.hpp> |
| |
| #include <chrono> |
| #include <condition_variable> |
| #include <functional> |
| #include <mutex> |
| #include <stdexcept> |
| #include <string> |
| #include <thread> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| using namespace std::literals; |
| using sdbusplus::error::xyz::openbmc_project::control::hoth::CommandFailure; |
| using sdbusplus::error::xyz::openbmc_project::control::hoth::ExpectedInfoNotFound; |
| using sdbusplus::error::xyz::openbmc_project::control::hoth::FirmwareFailure; |
| using sdbusplus::error::xyz::openbmc_project::control::hoth::InterfaceError; |
| using sdbusplus::error::xyz::openbmc_project::control::hoth::ResponseFailure; |
| using google::hoth::Hoth; |
| using google::hoth::internal::FirmwareUpdater; |
| using google::hoth::internal::FirmwareMtdUpdater; |
| using google::hoth::internal::Persistence; |
| using google::hoth::internal::Side; |
| |
| using ::testing::_; |
| using ::testing::ContainerEq; |
| using ::testing::InSequence; |
| using ::testing::InvokeWithoutArgs; |
| using ::testing::Return; |
| using ::testing::Throw; |
| |
| static constexpr std::chrono::milliseconds threadDelay(100); |
| // Wait up to 100ms for loops when waiting for change in status |
| static constexpr std::chrono::milliseconds loopDelay(10); |
| static constexpr uint8_t maxLoopCount = 10; |
| |
| class HothUpdateTest : public HothTest |
| { |
| public: |
| void asyncFnWait() |
| { |
| std::unique_lock<std::mutex> lck(cv_m); |
| // Wait until the end of the test |
| cv.wait(lck); |
| } |
| |
| protected: |
| void waitWhileInProgress(const std::function<Hoth::FirmwareUpdateStatus()> &func) |
| { |
| do { |
| std::this_thread::sleep_for(loopDelay); |
| result = func(); |
| loopCount++; |
| } while (result == Hoth::FirmwareUpdateStatus::InProgress && |
| loopCount < maxLoopCount); |
| } |
| |
| std::condition_variable cv; |
| std::mutex cv_m; |
| |
| std::vector<uint8_t> mockFirmware; |
| Hoth::FirmwareUpdateStatus result = Hoth::FirmwareUpdateStatus::None; |
| uint8_t loopCount = 0; |
| }; |
| |
| // Does not use the HothUpdateTest TEST_F because creating multiple Hoth will |
| // lead to memory leaks. |
| TEST(HothUpdateTestMtd, noMtd) |
| { |
| // Ensure that calls to updateFirmware() and getFirmwareUpdateStatus() |
| // return proper errors in the case that we don't support the mtd firmware |
| // update protocol. |
| std::unique_ptr<FirmwareUpdater> firmwareUpdater = |
| std::make_unique<FirmwareMtdUpdater>(nullptr, nullptr); |
| sdbusplus::SdBusMock sdbus_mock; |
| sdbusplus::bus::bus bus = sdbusplus::get_mocked_new(&sdbus_mock); |
| std::unique_ptr<Hoth> hoth = std::make_unique<Hoth>( |
| bus, "/test/path", nullptr, nullptr, nullptr, firmwareUpdater.get()); |
| |
| EXPECT_EQ(Hoth::FirmwareUpdateStatus::None, |
| hoth->getFirmwareUpdateStatus()); |
| EXPECT_NO_THROW(hoth->updateFirmware({})); |
| std::this_thread::sleep_for(threadDelay); |
| EXPECT_EQ(Hoth::FirmwareUpdateStatus::Error, |
| hoth->getFirmwareUpdateStatus()); |
| } |
| |
| TEST_F(HothUpdateTest, updateAlreadyInProgressFails) |
| { |
| // Call updateFirmware() once and make flashCopy() wait until the end of the |
| // test. Calling updateFirmware() once more will throw a FirmwareFailure as |
| // we detect an update already in progress. |
| EXPECT_CALL(mtd, findPartition(_)).WillOnce(Return("")); |
| EXPECT_CALL(mtd, flashCopy(_, _, _)) |
| .WillOnce(InvokeWithoutArgs(this, &HothUpdateTest::asyncFnWait)); |
| |
| hoth->updateFirmware(mockFirmware); |
| EXPECT_THROW(hoth->updateFirmware(mockFirmware), FirmwareFailure); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothUpdateTest, updateUncheckedSucceeds) |
| { |
| // Call updateFirmware() twice, once to start off a process and then a |
| // second time without checking the status. This ensures we don't require |
| // callers to check the status of a previous update that completed. |
| EXPECT_CALL(mtd, findPartition(_)).Times(2).WillRepeatedly(Return("")); |
| EXPECT_CALL(mtd, flashCopy(_, _, _)).Times(2); |
| |
| hoth->updateFirmware(mockFirmware); |
| std::this_thread::sleep_for(threadDelay); |
| hoth->updateFirmware(mockFirmware); |
| } |
| |
| TEST_F(HothUpdateTest, beforeUpdateGetFirmwareUpdateStatusReturnsDefaultStatus) |
| { |
| // If getFirmwareUpdateStatus() is called before updateFirmware() has been |
| // called, we expect the default lastStatus to be returned |
| EXPECT_EQ(hoth->getFirmwareUpdateStatus(), |
| Hoth::FirmwareUpdateStatus::None); |
| } |
| |
| TEST_F(HothUpdateTest, duringUpdateGetFirmwareUpdateStatusReturnsInProgress) |
| { |
| // If getFirmwareUpdateStatus() is called while flashCopy() is still in |
| // progress, we expect inProgress status |
| EXPECT_CALL(mtd, findPartition(_)).WillOnce(Return("")); |
| EXPECT_CALL(mtd, flashCopy(_, _, _)) |
| .WillOnce(InvokeWithoutArgs(this, &HothUpdateTest::asyncFnWait)); |
| |
| hoth->updateFirmware(mockFirmware); |
| EXPECT_EQ(hoth->getFirmwareUpdateStatus(), |
| Hoth::FirmwareUpdateStatus::InProgress); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothUpdateTest, successfulGetFirmwareUpdateStatusReturnsDone) |
| { |
| // If getFirmwareUpdateStatus() is called after updateFirmware() has |
| // finished and it did not return an exception, we expect Done status |
| EXPECT_CALL(mtd, findPartition(_)).WillOnce(Return("")); |
| EXPECT_CALL(mtd, flashCopy(_, _, _)).WillOnce(Return()); |
| |
| hoth->updateFirmware(mockFirmware); |
| // Ensure our flashCopy thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getFirmwareUpdateStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothUpdateTest, exceptionThrownGetFirmwareUpdateStatusReturnsError) |
| { |
| // If getFirmwareUpdateStatus() is called after updateFirmware() has thrown |
| // an exception, we expect Error status |
| EXPECT_CALL(mtd, findPartition(_)).WillOnce(Return("")); |
| EXPECT_CALL(mtd, flashCopy(_, _, _)) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->updateFirmware(mockFirmware); |
| // Ensure our flashCopy thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getFirmwareUpdateStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| TEST_F(HothUpdateTest, getFirmwareUpdateStatusTwiceDone) |
| { |
| // If getFirmwareUpdateStatus() is called after getFirmwareUpdateStatus() |
| // already returned Done once, we expect the status to have been cached |
| EXPECT_CALL(mtd, findPartition(_)).WillOnce(Return("")); |
| EXPECT_CALL(mtd, flashCopy(_, _, _)).WillOnce(Return()); |
| |
| hoth->updateFirmware(mockFirmware); |
| // Ensure our flashCopy thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getFirmwareUpdateStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| EXPECT_EQ(hoth->getFirmwareUpdateStatus(), |
| Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothUpdateTest, getFirmwareUpdateStatusTwiceError) |
| { |
| // If getFirmwareUpdateStatus() is called after getFirmwareUpdateStatus() |
| // already returned Error once, we expect the status to have been cached |
| EXPECT_CALL(mtd, findPartition(_)).WillOnce(Return("")); |
| EXPECT_CALL(mtd, flashCopy(_, _, _)) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->updateFirmware(mockFirmware); |
| // Ensure our flashCopy thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getFirmwareUpdateStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| EXPECT_EQ(hoth->getFirmwareUpdateStatus(), |
| Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| class HothPayloadTest : public HothTest |
| { |
| public: |
| void asyncFnWait() |
| { |
| std::unique_lock<std::mutex> lck(cv_m); |
| // Wait until the end of the test |
| cv.wait(lck); |
| } |
| |
| protected: |
| void waitWhileInProgress(const std::function<Hoth::FirmwareUpdateStatus()> &func) |
| { |
| do { |
| std::this_thread::sleep_for(loopDelay); |
| result = func(); |
| loopCount++; |
| } while (result == Hoth::FirmwareUpdateStatus::InProgress && |
| loopCount < maxLoopCount); |
| } |
| |
| std::condition_variable cv; |
| std::mutex cv_m; |
| |
| Hoth::FirmwareUpdateStatus result; |
| const std::string examplePath = "/run/initramfs/test-image"; |
| uint8_t loopCount = 0; |
| }; |
| |
| class HothPayloadInitiateTest : public HothPayloadTest |
| {}; |
| |
| TEST_F(HothPayloadInitiateTest, initiateAlreadyInProgressFails) |
| { |
| // Call initiatePayload() once and make the async function wait until the |
| // end of the test. Calling initiatePayload() once more will throw an |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->initiatePayload(); |
| EXPECT_THROW(hoth->initiatePayload(), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, sendAlreadyInProgressFails) |
| { |
| // Call sendPayload() once and make the async function wait until the |
| // end of the test. Calling initiatePayload() will throw an |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->sendPayload(examplePath); |
| EXPECT_THROW(hoth->initiatePayload(), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, verifyAlreadyInProgressFails) |
| { |
| // Call verifyPayload() once and make the async function wait until the |
| // end of the test. Calling initiatePayload() once more will throw an |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->verifyPayload(); |
| EXPECT_THROW(hoth->initiatePayload(), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, beforeInitiatePayloadReturnsDefaultStatus) |
| { |
| // If getInitiatePayloadStatus() is called before initiatePayload() has been |
| // called, we expect the default lastStatus to be returned |
| EXPECT_EQ(hoth->getInitiatePayloadStatus(), |
| Hoth::FirmwareUpdateStatus::None); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, duringInitiatePayloadReturnsInProgress) |
| { |
| // If getInitiatePayloadStatus() is called while initiate() is still in |
| // progress, we expect inProgress status |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->initiatePayload(); |
| EXPECT_EQ(hoth->getInitiatePayloadStatus(), |
| Hoth::FirmwareUpdateStatus::InProgress); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, successfulInitiatePayloadReturnsDone) |
| { |
| // If getInitiatePayloadStatus() is called after initiatePayload() has |
| // finished and it did not return an exception, we expect Done status |
| EXPECT_CALL(payloadUpdate, initiate()).WillOnce(Return()); |
| |
| hoth->initiatePayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getInitiatePayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, initiateAfterSuccessfulSendReturnsDone) |
| { |
| // If initiatePayload() is called after a successful sendPayload(), we |
| // expect initiatePayload() to succeed |
| InSequence seq; |
| |
| EXPECT_CALL(payloadUpdate, send(examplePath)).WillOnce(Return()); |
| EXPECT_CALL(payloadUpdate, initiate()).WillOnce(Return()); |
| |
| hoth->sendPayload(examplePath); |
| // Ensure our send thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getSendPayloadStatus(); }); |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| |
| hoth->initiatePayload(); |
| loopCount = 0; |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getInitiatePayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, exceptionThrownReturnsError) |
| { |
| // If getInitiatePayloadStatus() is called after initiatePayload() has |
| // thrown an exception, we expect Error status |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->initiatePayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getInitiatePayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, getInitiatePayloadStatusTwiceDone) |
| { |
| // If getInitiatePayloadStatus() is called after getInitiatePayloadStatus() |
| // already returned Done once, we expect the status to have been cached |
| EXPECT_CALL(payloadUpdate, initiate()).WillOnce(Return()); |
| |
| hoth->initiatePayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getInitiatePayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| EXPECT_EQ(hoth->getInitiatePayloadStatus(), |
| Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadInitiateTest, getInitiatePayloadStatusTwiceError) |
| { |
| // If getInitiatePayloadStatus() is called after getInitiatePayloadStatus() |
| // already returned Error once, we expect the status to have been cached |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->initiatePayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getInitiatePayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| EXPECT_EQ(hoth->getInitiatePayloadStatus(), |
| Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| class HothPayloadSendTest : public HothPayloadTest |
| {}; |
| |
| TEST_F(HothPayloadSendTest, sendAlreadyInProgressFails) |
| { |
| // Call sendPayload() once and make the async function wait until the |
| // end of the test. Calling sendPayload() once more will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->sendPayload(examplePath); |
| EXPECT_THROW(hoth->sendPayload(examplePath), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadSendTest, initiateAlreadyInProgressFails) |
| { |
| // Call initiatePayload() once and make the async function wait until the |
| // end of the test. Calling sendPayload() will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->initiatePayload(); |
| EXPECT_THROW(hoth->sendPayload(examplePath), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadSendTest, verifyAlreadyInProgressFails) |
| { |
| // Call verifyPayload() once and make the async function wait until the |
| // end of the test. Calling sendPayload() once more will throw an |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->verifyPayload(); |
| EXPECT_THROW(hoth->sendPayload(examplePath), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadSendTest, emptyPathSendPayloadFails) |
| { |
| // Calling sendPayload with empty path strings will throw a FirmwareFailure. |
| EXPECT_THROW(hoth->sendPayload(""), FirmwareFailure); |
| } |
| |
| TEST_F(HothPayloadSendTest, beforeSendPayloadReturnsDefaultStatus) |
| { |
| // If getSendPayloadStatus() is called before sendPayload() has been |
| // called, we expect the default lastStatus to be returned |
| EXPECT_EQ(hoth->getSendPayloadStatus(), Hoth::FirmwareUpdateStatus::None); |
| } |
| |
| TEST_F(HothPayloadSendTest, duringSendPayloadReturnsInProgress) |
| { |
| // If getSendPayloadStatus() is called while send() is still in |
| // progress, we expect inProgress status |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->sendPayload(examplePath); |
| EXPECT_EQ(hoth->getSendPayloadStatus(), |
| Hoth::FirmwareUpdateStatus::InProgress); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadSendTest, successfulSendPayloadReturnsDone) |
| { |
| // If getSendPayloadStatus() is called after sendPayload() has |
| // finished and it did not return an exception, we expect Done status |
| EXPECT_CALL(payloadUpdate, send(examplePath)).WillOnce(Return()); |
| |
| hoth->sendPayload(examplePath); |
| // Ensure our send thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getSendPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadSendTest, sendAfterSuccessfulInitiateReturnsDone) |
| { |
| // If sendPayload() is called after a successful initiatePayload(), we |
| // expect sendPayload() to succeed |
| InSequence seq; |
| |
| EXPECT_CALL(payloadUpdate, initiate()).WillOnce(Return()); |
| EXPECT_CALL(payloadUpdate, send(examplePath)).WillOnce(Return()); |
| |
| hoth->initiatePayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getInitiatePayloadStatus(); }); |
| |
| hoth->sendPayload(examplePath); |
| loopCount = 0; |
| // Ensure our send thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getSendPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadSendTest, exceptionThrownReturnsError) |
| { |
| // If getSendPayloadStatus() is called after sendPayload() has |
| // thrown an exception, we expect Error status |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->sendPayload(examplePath); |
| // Ensure our send thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getSendPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| TEST_F(HothPayloadSendTest, getSendPayloadStatusTwiceDone) |
| { |
| // If getSendPayloadStatus() is called after getSendPayloadStatus() |
| // already returned Done once, we expect the status to have been cached |
| EXPECT_CALL(payloadUpdate, send(examplePath)).WillOnce(Return()); |
| |
| hoth->sendPayload(examplePath); |
| // Ensure our send thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getSendPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| EXPECT_EQ(hoth->getSendPayloadStatus(), Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadSendTest, getSendPayloadStatusTwiceError) |
| { |
| // If getSendPayloadStatus() is called after getSendPayloadStatus() |
| // already returned Error once, we expect the status to have been cached |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->sendPayload(examplePath); |
| // Ensure our send thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getSendPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| EXPECT_EQ(hoth->getSendPayloadStatus(), Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| class HothPayloadEraseTest : public HothPayloadTest |
| { |
| protected: |
| static constexpr uint32_t goodOffset = 0; |
| static constexpr uint32_t goodSize = 4096; |
| static constexpr uint32_t badOffset = 1; |
| static constexpr uint32_t badSize = goodSize + 1; |
| }; |
| |
| TEST_F(HothPayloadEraseTest, initiateAlreadyInProgressFails) |
| { |
| // Call initiatePayload() once and make the async function wait until the |
| // end of the test. Calling erasePayload() will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->initiatePayload(); |
| EXPECT_THROW(hoth->erasePayload(goodOffset, goodSize), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadEraseTest, sendAlreadyInProgressFails) |
| { |
| // Call sendPayload() once and make the async function wait until the |
| // end of the test. Calling erasePayload() will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->sendPayload(examplePath); |
| EXPECT_THROW(hoth->erasePayload(goodOffset, goodSize), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadEraseTest, verifyAlreadyInProgressFails) |
| { |
| // Call verifyPayload() once and make the async function wait until the |
| // end of the test. Calling erasePayload() once more will throw an |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->verifyPayload(); |
| EXPECT_THROW(hoth->erasePayload(goodOffset, goodSize), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadEraseTest, notAlignedOffsetErasePayloadThrows) |
| { |
| // If offset given is not aligned, erasePayload() will throw |
| EXPECT_THROW(hoth->erasePayload(badOffset, goodSize), CommandFailure); |
| } |
| |
| TEST_F(HothPayloadEraseTest, notAlignedSizeErasePayloadThrows) |
| { |
| // If size given is not aligned, erasePayload() will throw |
| EXPECT_THROW(hoth->erasePayload(goodOffset, badSize), CommandFailure); |
| } |
| |
| TEST_F(HothPayloadEraseTest, successfulErasePayloadNoThrow) |
| { |
| // If erase() does not throw, verifyPayload() will not throw |
| EXPECT_CALL(payloadUpdate, erase(goodOffset, goodSize)).WillOnce(Return()); |
| |
| EXPECT_NO_THROW(hoth->erasePayload(goodOffset, goodSize)); |
| } |
| |
| TEST_F(HothPayloadEraseTest, exceptionThrownErasePayloadThrows) |
| { |
| // If erase() throws, erasePayload() will also throw |
| EXPECT_CALL(payloadUpdate, erase(goodOffset, goodSize)) |
| .WillOnce(Throw(ResponseFailure())); |
| |
| EXPECT_THROW(hoth->erasePayload(goodOffset, goodSize), ResponseFailure); |
| } |
| |
| class HothPayloadVerifyTest : public HothPayloadTest |
| {}; |
| |
| TEST_F(HothPayloadVerifyTest, initiateAlreadyInProgressFails) |
| { |
| // Call initiatePayload() once and make the async function wait until the |
| // end of the test. Calling verifyPayload() will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->initiatePayload(); |
| EXPECT_THROW(hoth->verifyPayload(), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, sendAlreadyInProgressFails) |
| { |
| // Call sendPayload() once and make the async function wait until the |
| // end of the test. Calling verifyPayload() will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->sendPayload(examplePath); |
| EXPECT_THROW(hoth->verifyPayload(), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, verifyAlreadyInProgressFails) |
| { |
| // Call verifyPayload() once and make the async function wait until the |
| // end of the test. Calling verifyPayload() once more will throw an |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->verifyPayload(); |
| EXPECT_THROW(hoth->verifyPayload(), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, beforeVerifyPayloadReturnsDefaultStatus) |
| { |
| // If getVerifyPayloadStatus() is called before verifyPayload() has been |
| // called, we expect the default lastStatus to be returned |
| EXPECT_EQ(hoth->getVerifyPayloadStatus(), Hoth::FirmwareUpdateStatus::None); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, duringVerifyPayloadReturnsInProgress) |
| { |
| // If getVerifyPayloadStatus() is called while verify() is still in |
| // progress, we expect inProgress status |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->verifyPayload(); |
| EXPECT_EQ(hoth->getVerifyPayloadStatus(), |
| Hoth::FirmwareUpdateStatus::InProgress); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, successfulVerifyPayloadReturnsDone) |
| { |
| // If getVerifyPayloadStatus() is called after verifyPayload() has |
| // finished and it did not return an exception, we expect Done status |
| EXPECT_CALL(payloadUpdate, verify()).WillOnce(Return()); |
| |
| hoth->verifyPayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getVerifyPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, verifyAfterSuccessfulSendReturnsDone) |
| { |
| // If verifyPayload() is called after a successful sendPayload(), we |
| // expect verifyPayload() to succeed |
| InSequence seq; |
| |
| EXPECT_CALL(payloadUpdate, send(examplePath)).WillOnce(Return()); |
| EXPECT_CALL(payloadUpdate, verify()).WillOnce(Return()); |
| |
| hoth->sendPayload(examplePath); |
| // Ensure our send thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getSendPayloadStatus(); }); |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| |
| hoth->verifyPayload(); |
| loopCount = 0; |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getVerifyPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, exceptionThrownReturnsError) |
| { |
| // If getVerifyPayloadStatus() is called after verifyPayload() has |
| // thrown an exception, we expect Error status |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->verifyPayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getVerifyPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, getInitiatePayloadStatusTwiceDone) |
| { |
| // If getVerifyPayloadStatus() is called after getVerifyPayloadStatus() |
| // already returned Done once, we expect the status to have been cached |
| EXPECT_CALL(payloadUpdate, verify()).WillOnce(Return()); |
| |
| hoth->verifyPayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getVerifyPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Done); |
| EXPECT_EQ(hoth->getVerifyPayloadStatus(), Hoth::FirmwareUpdateStatus::Done); |
| } |
| |
| TEST_F(HothPayloadVerifyTest, getVerifyPayloadStatusTwiceError) |
| { |
| // If getVerifyPayloadStatus() is called after getVerifyPayloadStatus() |
| // already returned Error once, we expect the status to have been cached |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(Throw(std::runtime_error("This is a simulated exception"))); |
| |
| hoth->verifyPayload(); |
| // Ensure our initiate thread has exited |
| waitWhileInProgress([thread = hoth.get()] { return thread->getVerifyPayloadStatus(); }); |
| |
| EXPECT_EQ(result, Hoth::FirmwareUpdateStatus::Error); |
| EXPECT_EQ(hoth->getVerifyPayloadStatus(), |
| Hoth::FirmwareUpdateStatus::Error); |
| } |
| |
| class HothPayloadActivateTest : public HothPayloadTest |
| { |
| protected: |
| const static bool expectedPersistence = true; |
| }; |
| |
| TEST_F(HothPayloadActivateTest, initiateAlreadyInProgressFails) |
| { |
| // Call initiatePayload() once and make the async function wait until the |
| // end of the test. Calling activatePayload() will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, initiate()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->initiatePayload(); |
| EXPECT_THROW(hoth->activatePayload(expectedPersistence), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadActivateTest, sendAlreadyInProgressFails) |
| { |
| // Call sendPayload() once and make the async function wait until the |
| // end of the test. Calling activatePayload() will throw a |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, send(examplePath)) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->sendPayload(examplePath); |
| EXPECT_THROW(hoth->activatePayload(expectedPersistence), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadActivateTest, verifyAlreadyInProgressFails) |
| { |
| // Call verifyPayload() once and make the async function wait until the |
| // end of the test. Calling verifyPayload() once more will throw an |
| // InterfaceError as we detect an async function already in progress. |
| EXPECT_CALL(payloadUpdate, verify()) |
| .WillOnce(InvokeWithoutArgs(this, &HothPayloadTest::asyncFnWait)); |
| |
| hoth->verifyPayload(); |
| EXPECT_THROW(hoth->verifyPayload(), InterfaceError); |
| // Release the thread |
| std::this_thread::sleep_for(threadDelay); |
| cv.notify_one(); |
| } |
| |
| TEST_F(HothPayloadActivateTest, getStatusThrowsExceptionFails) |
| { |
| // If getStatus() throws an exception, expect activatePayload to throw the |
| // same exception |
| EXPECT_CALL(payloadUpdate, getStatus()).WillOnce(Throw(ResponseFailure())); |
| |
| EXPECT_THROW(hoth->activatePayload(expectedPersistence), ResponseFailure); |
| } |
| |
| TEST_F(HothPayloadActivateTest, activateThrowsExceptionFails) |
| { |
| // If activate() throws an exception, expect activatePayload to throw the |
| // same exception |
| payload_update_status expectedResponse; |
| |
| EXPECT_CALL(payloadUpdate, getStatus()).WillOnce(Return(expectedResponse)); |
| EXPECT_CALL(payloadUpdate, activate(_, _)) |
| .WillOnce(Throw(ResponseFailure())); |
| |
| EXPECT_THROW(hoth->activatePayload(expectedPersistence), ResponseFailure); |
| } |
| |
| TEST_F(HothPayloadActivateTest, activateSuccessful) |
| { |
| // Call activatePayload successfully and expect the correct configuration to |
| // be passed in as parameters |
| static constexpr uint8_t activeHalf = 0; |
| static constexpr uint8_t otherHalf = 1; |
| payload_update_status expectedResponse; |
| expectedResponse.a_valid = 1; |
| expectedResponse.b_valid = 2; |
| expectedResponse.active_half = activeHalf; |
| expectedResponse.next_half = 0; |
| expectedResponse.persistent_half = 0; |
| |
| EXPECT_CALL(payloadUpdate, getStatus()).WillOnce(Return(expectedResponse)); |
| EXPECT_CALL(payloadUpdate, |
| activate(Side(otherHalf), |
| Persistence(expectedPersistence))) |
| .WillOnce(Return()); |
| |
| hoth->activatePayload(expectedPersistence); |
| } |
| |
| TEST_F(HothPayloadActivateTest, deactivateSuccessful) |
| { |
| // Call deactivatePayload successfully and expect the correct configuration |
| // to be passed in as parameters |
| static constexpr uint8_t activeHalf = 0; |
| payload_update_status expectedResponse; |
| expectedResponse.a_valid = 1; |
| expectedResponse.b_valid = 2; |
| expectedResponse.active_half = activeHalf; |
| expectedResponse.next_half = 1; |
| expectedResponse.persistent_half = 0; |
| |
| EXPECT_CALL(payloadUpdate, getStatus()).WillOnce(Return(expectedResponse)); |
| EXPECT_CALL(payloadUpdate, activate(Side(activeHalf), |
| Persistence(true))) |
| .WillOnce(Return()); |
| |
| hoth->deactivatePayload(); |
| } |
| |
| class HothPayloadSizeTest : public HothPayloadTest |
| {}; |
| |
| ACTION_P(ReadSize, max_size) |
| { |
| if (max_size < arg0 + arg1.size()) |
| { |
| throw ResponseFailure(); |
| } |
| } |
| |
| TEST_F(HothPayloadSizeTest, badFlash) |
| { |
| EXPECT_CALL(payloadUpdate, read(_, _)).WillRepeatedly(ReadSize(0u)); |
| EXPECT_THROW(hoth->getPayloadSize(), InterfaceError); |
| } |
| |
| TEST_F(HothPayloadSizeTest, goodSizes) |
| { |
| for (uint32_t i = 1; i < 17; ++i) |
| { |
| EXPECT_CALL(payloadUpdate, read(_, _)).WillRepeatedly(ReadSize(i)); |
| EXPECT_EQ(hoth->getPayloadSize(), i); |
| EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&payloadUpdate)); |
| } |
| } |
| |
| class HothCommandTest : public HothTest |
| { |
| protected: |
| static const std::vector<uint8_t> testLoadCBKCmdSlot1; |
| static const std::vector<uint8_t> testLoadCBKDataCmdSlot1; |
| static const std::vector<uint8_t> testLockCBKSlotCmdSlot1; |
| static const std::vector<uint8_t> testIncCBKCmdSlot1; |
| static const std::vector<uint8_t> testProdUnwrapCmdSlot1; |
| static const std::vector<uint8_t> testLoadCBKCmdSlot2; |
| static const std::vector<uint8_t> testLoadCBKDataCmdSlot2; |
| static const std::vector<uint8_t> testLockCBKSlotCmdSlot2; |
| static const std::vector<uint8_t> testIncCBKCmdSlot2; |
| static const std::vector<uint8_t> testProdUnwrapCmdSlot2; |
| |
| static const std::vector<uint8_t> testLoadTokenCmd; |
| static const std::vector<uint8_t> testNonCriticalCmd; |
| static const std::vector<uint8_t> fakeHostCmdResponse; |
| }; |
| |
| // Format begin |
| // || echeader ||major|minor| |
| // |param cnt |param size | reserve || slot id || |
| const std::vector<uint8_t> HothCommandTest::testLoadCBKCmdSlot1 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x0A, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testLoadCBKDataCmdSlot1 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x0B, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testLockCBKSlotCmdSlot1 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x15, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testIncCBKCmdSlot1 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x17, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testProdUnwrapCmdSlot1 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x05, 0x00, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testLoadCBKCmdSlot2 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x0A, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testLoadCBKDataCmdSlot2 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x0B, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testLockCBKSlotCmdSlot2 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x15, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testIncCBKCmdSlot2 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x01, 0x17, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testProdUnwrapCmdSlot2 = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x05, 0x00, |
| 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00}; |
| |
| // Format end |
| |
| const std::vector<uint8_t> HothCommandTest::testLoadTokenCmd = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x05, 0x05, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::testNonCriticalCmd = { |
| 0x03, 0x37, 0x03, 0x3e, 0x00, 0x00, 0x20, 0x00, 0x05, 0x07, 0x00, 0x00}; |
| |
| const std::vector<uint8_t> HothCommandTest::fakeHostCmdResponse = {0x01, 0x23}; |
| |
| TEST_F(HothCommandTest, criticalCommand) |
| { |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testLoadTokenCmd))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommandAsync(ContainerEq(testLoadTokenCmd))) |
| .WillRepeatedly(Return(0)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testLoadCBKCmdSlot1))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testLoadCBKCmdSlot2))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testLoadCBKDataCmdSlot1))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testLoadCBKDataCmdSlot2))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testLockCBKSlotCmdSlot1))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testLockCBKSlotCmdSlot2))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testIncCBKCmdSlot1))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testIncCBKCmdSlot2))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testProdUnwrapCmdSlot1))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testProdUnwrapCmdSlot2))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| // LoadTokens is banned except for sendTrustedHostCommand |
| const std::vector<uint8_t> bannedRsp = {3, 249, 4, 0, 0, 0, 0, 0}; |
| EXPECT_THAT(hoth->sendHostCommand(testLoadCBKCmdSlot1), |
| ContainerEq(fakeHostCmdResponse)); |
| EXPECT_THAT(hoth->sendHostCommand(testLoadCBKCmdSlot2), |
| ContainerEq(bannedRsp)); |
| EXPECT_THAT(hoth->sendHostCommand(testLoadCBKDataCmdSlot1), |
| ContainerEq(fakeHostCmdResponse)); |
| EXPECT_THAT(hoth->sendHostCommand(testLoadCBKDataCmdSlot2), |
| ContainerEq(bannedRsp)); |
| EXPECT_THAT(hoth->sendHostCommand(testLockCBKSlotCmdSlot1), |
| ContainerEq(fakeHostCmdResponse)); |
| EXPECT_THAT(hoth->sendHostCommand(testLockCBKSlotCmdSlot2), |
| ContainerEq(bannedRsp)); |
| EXPECT_THAT(hoth->sendHostCommand(testIncCBKCmdSlot1), |
| ContainerEq(fakeHostCmdResponse)); |
| EXPECT_THAT(hoth->sendHostCommand(testIncCBKCmdSlot2), |
| ContainerEq(bannedRsp)); |
| EXPECT_THAT(hoth->sendHostCommand(testProdUnwrapCmdSlot1), |
| ContainerEq(fakeHostCmdResponse)); |
| EXPECT_THAT(hoth->sendHostCommand(testProdUnwrapCmdSlot2), |
| ContainerEq(bannedRsp)); |
| EXPECT_THAT(hoth->sendHostCommand(testLoadTokenCmd), |
| ContainerEq(bannedRsp)); |
| EXPECT_THAT(hoth->sendTrustedHostCommand(testLoadTokenCmd), |
| ContainerEq(fakeHostCmdResponse)); |
| } |
| |
| TEST_F(HothCommandTest, nonCriticalCommand) |
| { |
| EXPECT_CALL(hostCmd, sendCommand(ContainerEq(testNonCriticalCmd))) |
| .WillRepeatedly(Return(fakeHostCmdResponse)); |
| EXPECT_CALL(hostCmd, sendCommandAsync(ContainerEq(testNonCriticalCmd))) |
| .WillRepeatedly(Return(0)); |
| // Non critical commands are allowed |
| EXPECT_THAT(hoth->sendHostCommand(testNonCriticalCmd), |
| ContainerEq(fakeHostCmdResponse)); |
| EXPECT_EQ(hoth->sendHostCommandAsync(testNonCriticalCmd), 0); |
| EXPECT_THAT(hoth->sendTrustedHostCommand(testNonCriticalCmd), |
| ContainerEq(fakeHostCmdResponse)); |
| } |
| |
| class HothEcUtilTest : public HothTest |
| {}; |
| |
| class HothEcUtilTimingTest : public HothEcUtilTest |
| { |
| protected: |
| ec_response_statistics statistic; |
| }; |
| |
| TEST_F(HothEcUtilTimingTest, timingNotIncludedFails) |
| { |
| statistic.valid_words = 10; |
| EXPECT_CALL(ecUtil, getHothStatistics()).WillOnce(Return(statistic)); |
| EXPECT_THROW(hoth->getTotalBootTime(), ExpectedInfoNotFound); |
| } |
| TEST_F(HothEcUtilTimingTest, timingInfoGood) |
| { |
| statistic.valid_words = 18; |
| EXPECT_CALL(ecUtil, getHothStatistics()).WillOnce(Return(statistic)); |
| EXPECT_NO_THROW(hoth->getTotalBootTime()); |
| } |