Tests: Add disk driver tests
parent
bb49ce039e
commit
9d2d94d22b
|
|
@ -317,6 +317,8 @@ if(LIBICSNEO_BUILD_TESTS)
|
|||
|
||||
add_executable(libicsneo-tests
|
||||
test/main.cpp
|
||||
test/diskdriverreadtest.cpp
|
||||
test/diskdriverwritetest.cpp
|
||||
test/eventmanagertest.cpp
|
||||
test/ethernetpacketizertest.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
#include "diskdrivertest.h"
|
||||
|
||||
TEST_F(DiskDriverTest, Read) {
|
||||
std::array<uint8_t, 128> buf;
|
||||
buf.fill(0u);
|
||||
const auto amountRead = readLogicalDisk(0, buf.data(), buf.size());
|
||||
EXPECT_TRUE(amountRead.has_value());
|
||||
EXPECT_EQ(amountRead, buf.size());
|
||||
EXPECT_EQ(buf[0], TEST_STRING[0]);
|
||||
EXPECT_EQ(buf[126], 126u);
|
||||
EXPECT_EQ(driver->readCalls, 1u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, ReadUnaligned) {
|
||||
std::array<uint8_t, 120> buf;
|
||||
buf.fill(0u);
|
||||
const auto amountRead = readLogicalDisk(1, buf.data(), buf.size());
|
||||
EXPECT_TRUE(amountRead.has_value());
|
||||
EXPECT_EQ(amountRead, buf.size());
|
||||
EXPECT_EQ(buf[0], TEST_STRING[1]);
|
||||
EXPECT_EQ(buf[110], 111u);
|
||||
EXPECT_EQ(driver->readCalls, 1u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, ReadUnalignedLong) {
|
||||
std::array<uint8_t, 500> buf;
|
||||
buf.fill(0u);
|
||||
const auto amountRead = readLogicalDisk(300, buf.data(), buf.size());
|
||||
EXPECT_TRUE(amountRead.has_value());
|
||||
EXPECT_EQ(amountRead, buf.size());
|
||||
EXPECT_EQ(buf[0], 300 & 0xFF);
|
||||
EXPECT_EQ(buf[110], 410 & 0xFF);
|
||||
EXPECT_EQ(driver->readCalls, 3u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, ReadPastEnd) {
|
||||
std::array<uint8_t, 500> buf;
|
||||
buf.fill(0u);
|
||||
expectedErrors.push({ APIEvent::Type::EOFReached, APIEvent::Severity::Error });
|
||||
const auto amountRead = readLogicalDisk(1000, buf.data(), buf.size());
|
||||
EXPECT_TRUE(amountRead.has_value());
|
||||
EXPECT_EQ(amountRead, 24u);
|
||||
EXPECT_EQ(buf[0], 1000 & 0xFF);
|
||||
EXPECT_EQ(buf[23], 1023 & 0xFF);
|
||||
EXPECT_EQ(driver->readCalls, 2u); // One for the read, another to check EOF
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, ReadBadStartingPos) {
|
||||
std::array<uint8_t, 500> buf;
|
||||
buf.fill(0u);
|
||||
expectedErrors.push({ APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error });
|
||||
const auto amountRead = readLogicalDisk(2000, buf.data(), buf.size());
|
||||
EXPECT_FALSE(amountRead.has_value());
|
||||
EXPECT_EQ(driver->readCalls, 1u); // One to check EOF
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
#ifndef __DISKDRIVERTEST_H_
|
||||
#define __DISKDRIVERTEST_H_
|
||||
|
||||
#include "icsneo/disk/diskreaddriver.h"
|
||||
#include "icsneo/disk/diskwritedriver.h"
|
||||
#include "icsneo/platform/optional.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
|
||||
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:
|
||||
Disk::Access getAccess() const override { return Disk::Access::EntireCard; }
|
||||
std::pair<uint32_t, uint32_t> getBlockSizeBounds() const override { return { 8, 256 }; }
|
||||
|
||||
optional<uint64_t> readLogicalDiskAligned(Communication&, device_eventhandler_t,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds) 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 nullopt;
|
||||
|
||||
optional<uint64_t> readAmount = std::min(amount, mockDisk.size() - pos);
|
||||
if(readAmount > 0u)
|
||||
memcpy(into, mockDisk.data() + pos, static_cast<size_t>(*readAmount));
|
||||
|
||||
// So that the test can mess with atomicity
|
||||
if(afterReadHook)
|
||||
afterReadHook();
|
||||
|
||||
return readAmount;
|
||||
}
|
||||
|
||||
optional<uint64_t> writeLogicalDiskAligned(Communication&, device_eventhandler_t report, uint64_t pos,
|
||||
const uint8_t* atomicBuf, const uint8_t* from, uint64_t amount, std::chrono::milliseconds) 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 nullopt;
|
||||
|
||||
optional<uint64_t> writeAmount = std::min(amount, mockDisk.size() - pos);
|
||||
if(writeAmount > 0u) {
|
||||
if(atomicBuf) {
|
||||
if(supportsAtomic) {
|
||||
atomicityChecks++;
|
||||
if(memcmp(mockDisk.data() + pos, atomicBuf, static_cast<size_t>(*writeAmount)))
|
||||
return RetryAtomic; // Atomic check failed
|
||||
} else {
|
||||
report(APIEvent::Type::AtomicOperationCompletedNonatomically, NonatomicSeverity);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(mockDisk.data() + pos, from, static_cast<size_t>(*writeAmount));
|
||||
}
|
||||
return writeAmount;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 1024> mockDisk;
|
||||
size_t readCalls = 0;
|
||||
size_t writeCalls = 0;
|
||||
size_t atomicityChecks = 0;
|
||||
bool supportsAtomic = true; // Ability to simulate a driver that doesn't support atomic writes
|
||||
std::function<void(void)> afterReadHook;
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
optional<uint64_t> readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t amount) {
|
||||
return driver->readLogicalDisk(*com, onError, pos, into, amount /* default timeout */);
|
||||
}
|
||||
|
||||
optional<uint64_t> writeLogicalDisk(uint64_t pos, const uint8_t* from, uint64_t amount) {
|
||||
return driver->writeLogicalDisk(*com, onError, *driver, pos, from, amount /* default timeout */);
|
||||
}
|
||||
|
||||
optional<MockDiskDriver> driver;
|
||||
|
||||
std::queue< std::pair<APIEvent::Type, APIEvent::Severity> > expectedErrors;
|
||||
device_eventhandler_t onError;
|
||||
|
||||
// We will dereference this but the driver base should never access it
|
||||
Communication* const com = nullptr;
|
||||
};
|
||||
|
||||
#endif // __DISKDRIVERTEST_H_
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
#include "diskdrivertest.h"
|
||||
|
||||
TEST_F(DiskDriverTest, Write) {
|
||||
const auto amountWritten = writeLogicalDisk(0u, reinterpret_cast<const uint8_t*>(TEST_OVERWRITE_STRING), sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_TRUE(amountWritten.has_value());
|
||||
EXPECT_EQ(amountWritten, sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_STREQ(reinterpret_cast<char*>(driver->mockDisk.data()), TEST_OVERWRITE_STRING);
|
||||
EXPECT_EQ(driver->mockDisk[sizeof(TEST_OVERWRITE_STRING) + 1], TEST_STRING[sizeof(TEST_OVERWRITE_STRING) + 1]);
|
||||
EXPECT_EQ(driver->mockDisk[126], 126u);
|
||||
EXPECT_EQ(driver->atomicityChecks, 1u);
|
||||
EXPECT_EQ(driver->readCalls, 1u);
|
||||
EXPECT_EQ(driver->writeCalls, 1u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, WriteNoAtomicityCheck) {
|
||||
driver->supportsAtomic = false;
|
||||
expectedErrors.push({ APIEvent::Type::AtomicOperationCompletedNonatomically, APIEvent::Severity::EventInfo });
|
||||
const auto amountWritten = writeLogicalDisk(0u, reinterpret_cast<const uint8_t*>(TEST_OVERWRITE_STRING), sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_TRUE(amountWritten.has_value());
|
||||
EXPECT_EQ(amountWritten, sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_STREQ(reinterpret_cast<char*>(driver->mockDisk.data()), TEST_OVERWRITE_STRING);
|
||||
EXPECT_EQ(driver->mockDisk[sizeof(TEST_OVERWRITE_STRING) + 1], TEST_STRING[sizeof(TEST_OVERWRITE_STRING) + 1]);
|
||||
EXPECT_EQ(driver->mockDisk[126], 126u);
|
||||
EXPECT_EQ(driver->atomicityChecks, 0u);
|
||||
EXPECT_EQ(driver->readCalls, 1u);
|
||||
EXPECT_EQ(driver->writeCalls, 1u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, WriteUnaligned) {
|
||||
const auto amountWritten = writeLogicalDisk(3, reinterpret_cast<const uint8_t*>(TEST_OVERWRITE_STRING), sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_TRUE(amountWritten.has_value());
|
||||
EXPECT_EQ(amountWritten, sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_EQ(driver->mockDisk[0], TEST_STRING[0]);
|
||||
EXPECT_EQ(driver->mockDisk[5], TEST_OVERWRITE_STRING[2]);
|
||||
EXPECT_EQ(driver->mockDisk[110], 110u);
|
||||
EXPECT_EQ(driver->atomicityChecks, 1u);
|
||||
EXPECT_EQ(driver->readCalls, 1u);
|
||||
EXPECT_EQ(driver->writeCalls, 1u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, WriteUnalignedLong) {
|
||||
std::array<uint8_t, 500> buf;
|
||||
for(size_t i = 0; i < buf.size(); i++)
|
||||
buf[i] = static_cast<uint8_t>((buf.size() - i) + 20);
|
||||
const auto amountWritten = writeLogicalDisk(300, buf.data(), buf.size());
|
||||
EXPECT_TRUE(amountWritten.has_value());
|
||||
EXPECT_EQ(amountWritten, buf.size());
|
||||
EXPECT_EQ(driver->mockDisk[0], TEST_STRING[0]);
|
||||
EXPECT_EQ(driver->mockDisk[330], ((buf.size() - 30) + 20) & 0xFF);
|
||||
EXPECT_EQ(driver->atomicityChecks, 3u);
|
||||
EXPECT_EQ(driver->readCalls, 3u);
|
||||
EXPECT_EQ(driver->writeCalls, 3u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, WriteUnalignedLongAtomicityFailures) {
|
||||
std::array<uint8_t, 500> buf;
|
||||
for(size_t i = 0; i < buf.size(); i++)
|
||||
buf[i] = static_cast<uint8_t>((buf.size() - i) + 20);
|
||||
for(int i = 0; i < 4; i++)
|
||||
expectedErrors.push({ APIEvent::Type::AtomicOperationRetried, APIEvent::Severity::EventInfo });
|
||||
|
||||
int i = 0;
|
||||
driver->afterReadHook = [&i, this]() {
|
||||
switch(i) {
|
||||
case 0: driver->mockDisk[295] = uint8_t(0xCD); break;
|
||||
case 1: break; // We don't mess with this one so the first block can be written
|
||||
case 2: driver->mockDisk[600] = uint8_t(0xDC); break;
|
||||
case 3: driver->mockDisk[602] = uint8_t(0xDC); break;
|
||||
case 4: break; // We don't mess with this one so the second block can be written
|
||||
case 5: driver->mockDisk[777] = uint8_t(0x22); break;
|
||||
case 6: break; // We don't mess with this one so the third block can be written
|
||||
}
|
||||
i++;
|
||||
};
|
||||
|
||||
const auto amountWritten = writeLogicalDisk(300, buf.data(), buf.size());
|
||||
EXPECT_TRUE(amountWritten.has_value());
|
||||
EXPECT_EQ(amountWritten, buf.size());
|
||||
EXPECT_EQ(driver->mockDisk[0], TEST_STRING[0]);
|
||||
EXPECT_EQ(driver->mockDisk[295], 0xCDu); // If the atomic worked correctly this write won't have gotten trampled
|
||||
// Our writes happen after both of these, so they overwrite the 0xDC values
|
||||
EXPECT_EQ(driver->mockDisk[600], ((buf.size() - 300) + 20) & 0xFF);
|
||||
EXPECT_EQ(driver->mockDisk[602], ((buf.size() - 302) + 20) & 0xFF);
|
||||
|
||||
EXPECT_EQ(driver->atomicityChecks, 7u);
|
||||
EXPECT_EQ(driver->readCalls, 7u);
|
||||
EXPECT_EQ(driver->writeCalls, 7u);
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, WritePastEnd) {
|
||||
expectedErrors.push({ APIEvent::Type::EOFReached, APIEvent::Severity::Error });
|
||||
const auto amountWritten = writeLogicalDisk(1020, reinterpret_cast<const uint8_t*>(TEST_OVERWRITE_STRING), sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_TRUE(amountWritten.has_value());
|
||||
EXPECT_EQ(amountWritten, 4u);
|
||||
EXPECT_EQ(driver->mockDisk[0], TEST_STRING[0]);
|
||||
EXPECT_EQ(driver->mockDisk[1019], 1019 & 0xFF);
|
||||
EXPECT_EQ(driver->mockDisk[1020], TEST_OVERWRITE_STRING[0]);
|
||||
EXPECT_EQ(driver->mockDisk[1023], TEST_OVERWRITE_STRING[3]);
|
||||
EXPECT_EQ(driver->writeCalls, 1u); // One for the write, another to check EOF
|
||||
}
|
||||
|
||||
TEST_F(DiskDriverTest, WriteBadStartingPos) {
|
||||
expectedErrors.push({ APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error });
|
||||
const auto amountWritten = writeLogicalDisk(2000, reinterpret_cast<const uint8_t*>(TEST_OVERWRITE_STRING), sizeof(TEST_OVERWRITE_STRING));
|
||||
EXPECT_FALSE(amountWritten.has_value());
|
||||
EXPECT_EQ(driver->atomicityChecks, 0u);
|
||||
EXPECT_EQ(driver->readCalls, 1u);
|
||||
EXPECT_EQ(driver->writeCalls, 0u); // We never even attempt the write
|
||||
}
|
||||
Loading…
Reference in New Issue