qmk_firmware/quantum/wear_leveling/tests/backing_mocks.cpp
Nick Brassel 01ecf332ff
Generic wear-leveling algorithm (#16996)
* Initial import of wear-leveling algorithm.

* Alignment.

* Docs tweaks.

* Lock/unlock.

* Update quantum/wear_leveling/wear_leveling_internal.h

Co-authored-by: Stefan Kerkmann <karlk90@pm.me>

* More tests, fix issue with consolidation when unlocked.

* More tests.

* Review comments.

* Add plumbing for FNV1a.

* Another test checking that checksum mismatch clears the cache.

* Check that the write log still gets played back.

Co-authored-by: Stefan Kerkmann <karlk90@pm.me>
2022-06-27 07:18:21 +10:00

155 lines
5.4 KiB
C++

// Copyright 2022 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "backing_mocks.hpp"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Backing Store Mock implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void MockBackingStore::reset_instance() {
for (auto&& e : backing_storage)
e.reset();
locked = true;
backing_erasure_count = 0;
backing_max_write_count = 0;
backing_total_write_count = 0;
backing_init_invoke_count = 0;
backing_unlock_invoke_count = 0;
backing_erase_invoke_count = 0;
backing_write_invoke_count = 0;
backing_lock_invoke_count = 0;
init_success_callback = [](std::uint64_t) { return true; };
erase_success_callback = [](std::uint64_t) { return true; };
unlock_success_callback = [](std::uint64_t) { return true; };
write_success_callback = [](std::uint64_t, std::uint32_t) { return true; };
lock_success_callback = [](std::uint64_t) { return true; };
write_log.clear();
}
bool MockBackingStore::init(void) {
++backing_init_invoke_count;
if (init_success_callback) {
return init_success_callback(backing_init_invoke_count);
}
return true;
}
bool MockBackingStore::unlock(void) {
++backing_unlock_invoke_count;
EXPECT_TRUE(is_locked()) << "Attempted to unlock but was not locked";
locked = false;
if (unlock_success_callback) {
return unlock_success_callback(backing_unlock_invoke_count);
}
return true;
}
bool MockBackingStore::erase(void) {
++backing_erase_invoke_count;
// Erase each slot
for (std::size_t i = 0; i < backing_storage.size(); ++i) {
// Drop out of erase early with failure if we need to
if (erase_success_callback && !erase_success_callback(backing_erase_invoke_count)) {
append_log(true);
return false;
}
backing_storage[i].erase();
}
// Keep track of the erase in the write log so that we can verify during tests
append_log(true);
++backing_erasure_count;
return true;
}
bool MockBackingStore::write(uint32_t address, backing_store_int_t value) {
++backing_write_invoke_count;
// precondition: value's buffer size already matches BACKING_STORE_WRITE_SIZE
EXPECT_TRUE(address % BACKING_STORE_WRITE_SIZE == 0) << "Supplied address was not aligned with the backing store integral size";
EXPECT_TRUE(address + BACKING_STORE_WRITE_SIZE <= WEAR_LEVELING_BACKING_SIZE) << "Address would result of out-of-bounds access";
EXPECT_FALSE(is_locked()) << "Write was attempted without being unlocked first";
// Drop out of write early with failure if we need to
if (write_success_callback && !write_success_callback(backing_write_invoke_count, address)) {
return false;
}
// Write the complement as we're simulating flash memory -- 0xFF means 0x00
std::size_t index = address / BACKING_STORE_WRITE_SIZE;
backing_storage[index].set(~value);
// Keep track of the write log so that we can verify during tests
append_log(address, value);
// Keep track of the total number of writes into the backing store
++backing_total_write_count;
return true;
}
bool MockBackingStore::lock(void) {
++backing_lock_invoke_count;
EXPECT_FALSE(is_locked()) << "Attempted to lock but was not unlocked";
locked = true;
if (lock_success_callback) {
return lock_success_callback(backing_lock_invoke_count);
}
return true;
}
bool MockBackingStore::read(uint32_t address, backing_store_int_t& value) const {
// precondition: value's buffer size already matches BACKING_STORE_WRITE_SIZE
EXPECT_TRUE(address % BACKING_STORE_WRITE_SIZE == 0) << "Supplied address was not aligned with the backing store integral size";
EXPECT_TRUE(address + BACKING_STORE_WRITE_SIZE <= WEAR_LEVELING_BACKING_SIZE) << "Address would result of out-of-bounds access";
// Read and take the complement as we're simulating flash memory -- 0xFF means 0x00
std::size_t index = address / BACKING_STORE_WRITE_SIZE;
value = ~backing_storage[index].get();
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Backing Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern "C" bool backing_store_init(void) {
return MockBackingStore::Instance().init();
}
extern "C" bool backing_store_unlock(void) {
return MockBackingStore::Instance().unlock();
}
extern "C" bool backing_store_erase(void) {
return MockBackingStore::Instance().erase();
}
extern "C" bool backing_store_write(uint32_t address, backing_store_int_t value) {
return MockBackingStore::Instance().write(address, value);
}
extern "C" bool backing_store_lock(void) {
return MockBackingStore::Instance().lock();
}
extern "C" bool backing_store_read(uint32_t address, backing_store_int_t* value) {
return MockBackingStore::Instance().read(address, *value);
}