#ifndef __DISKDRIVERTEST_H_ #define __DISKDRIVERTEST_H_ #include "icsneo/disk/diskreaddriver.h" #include "icsneo/disk/diskwritedriver.h" #include "gtest/gtest.h" #include #include #include using namespace icsneo; #define TEST_STRING "The quick brown fox jumps over the lazy dog." #define TEST_OVERWRITE_STRING "test fun" class MockDiskDriver : public Disk::ReadDriver, public Disk::WriteDriver { public: std::pair getBlockSizeBounds() const override { return { 8, 256 }; } std::optional readLogicalDiskAligned(Communication&, device_eventhandler_t, uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds, Disk::MemoryType) override { readCalls++; EXPECT_EQ(pos % getBlockSizeBounds().first, 0); // Ensure the alignment rules are respected EXPECT_LE(amount, getBlockSizeBounds().second); EXPECT_EQ(amount % getBlockSizeBounds().first, 0); if(pos > mockDisk.size()) // EOF return std::nullopt; std::optional readAmount = std::min(amount, mockDisk.size() - pos); if(readAmount > 0u) memcpy(into, mockDisk.data() + pos, static_cast(*readAmount)); // So that the test can mess with atomicity if(afterReadHook) afterReadHook(); return readAmount; } std::optional writeLogicalDiskAligned(Communication&, device_eventhandler_t /* report */, uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds, Disk::MemoryType) override { writeCalls++; EXPECT_EQ(pos % getBlockSizeBounds().first, 0); // Ensure the alignment rules are respected EXPECT_LE(amount, getBlockSizeBounds().second); EXPECT_EQ(amount % getBlockSizeBounds().first, 0); if(pos > mockDisk.size()) // EOF return std::nullopt; std::optional writeAmount = std::min(amount, mockDisk.size() - pos); if(writeAmount > 0u) { memcpy(mockDisk.data() + pos, from, static_cast(*writeAmount)); } return writeAmount; } std::array mockDisk; size_t readCalls = 0; size_t writeCalls = 0; std::function afterReadHook; private: Disk::Access getPossibleAccess() const override { return Disk::Access::EntireCard; } }; class DiskDriverTest : public ::testing::Test { protected: // Start with a clean instance of MockDiskDriver for every test void SetUp() override { onError = [this](APIEvent::Type t, APIEvent::Severity s) { if(expectedErrors.empty()) { // Unless caught by the test, the driver should not throw errors EXPECT_TRUE(false); } else { const auto expected = expectedErrors.front(); expectedErrors.pop(); EXPECT_EQ(expected.first, t); if(expected.second != APIEvent::Severity::Any) { EXPECT_EQ(expected.second, s); } } }; driver.emplace(); // Populate with some fake data memcpy(driver->mockDisk.data(), TEST_STRING, sizeof(TEST_STRING)); for (size_t i = sizeof(TEST_STRING); i < driver->mockDisk.size(); i++) driver->mockDisk[i] = uint8_t(i & 0xFF); } void TearDown() override { driver.reset(); } std::optional readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t amount) { return driver->readLogicalDisk(*com, onError, pos, into, amount /* default timeout */); } std::optional writeLogicalDisk(uint64_t pos, const uint8_t* from, uint64_t amount) { return driver->writeLogicalDisk(*com, onError, *driver, pos, from, amount /* default timeout */); } std::optional driver; std::queue< std::pair > expectedErrors; device_eventhandler_t onError; // We will dereference this but the driver base should never access it Communication* const com = nullptr; }; #endif // __DISKDRIVERTEST_H_