mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-06-21 05:14:26 -04:00
Add file reader (#1155)
* Add file reader * Add a simple test example of parsing settings. * Use new FileLineReader to parse Glass presets * Trim CRLF from Glass preset name
This commit is contained in:
parent
a5c7eb2fbc
commit
34fefd1cad
11 changed files with 659 additions and 325 deletions
|
@ -22,267 +22,7 @@
|
|||
#include "doctest.h"
|
||||
#include "file.hpp"
|
||||
#include "file_wrapper.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
/* Mocks the File interface with a backing string. */
|
||||
class MockFile {
|
||||
public:
|
||||
using Error = File::Error;
|
||||
using Offset = File::Offset;
|
||||
using Size = File::Size;
|
||||
template <typename T>
|
||||
using Result = File::Result<T>;
|
||||
|
||||
MockFile(std::string data)
|
||||
: data_{std::move(data)} {}
|
||||
|
||||
Size size() { return data_.size(); }
|
||||
|
||||
Result<Offset> seek(uint32_t offset) {
|
||||
if ((int32_t)offset < 0)
|
||||
return {static_cast<Error>(FR_BAD_SEEK)};
|
||||
|
||||
auto previous = offset_;
|
||||
|
||||
if (offset > size())
|
||||
data_.resize(offset);
|
||||
|
||||
offset_ = offset;
|
||||
return previous;
|
||||
}
|
||||
|
||||
Result<Offset> truncate() {
|
||||
data_.resize(offset_);
|
||||
return offset_;
|
||||
}
|
||||
|
||||
Result<Size> read(void* data, Size bytes_to_read) {
|
||||
if (offset_ + bytes_to_read > size())
|
||||
bytes_to_read = size() - offset_;
|
||||
|
||||
if (bytes_to_read == 0 || bytes_to_read > size()) // NB: underflow wrap
|
||||
return 0;
|
||||
|
||||
memcpy(data, &data_[offset_], bytes_to_read);
|
||||
offset_ += bytes_to_read;
|
||||
return bytes_to_read;
|
||||
}
|
||||
|
||||
Result<Size> write(const void* data, Size bytes_to_write) {
|
||||
auto new_offset = offset_ + bytes_to_write;
|
||||
if (new_offset >= size())
|
||||
data_.resize(new_offset);
|
||||
|
||||
memcpy(&data_[offset_], data, bytes_to_write);
|
||||
offset_ = new_offset;
|
||||
return bytes_to_write;
|
||||
}
|
||||
|
||||
Optional<Error> sync() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string data_;
|
||||
uint32_t offset_{0};
|
||||
};
|
||||
|
||||
/* Verifies correctness of MockFile. */
|
||||
TEST_SUITE("Test MockFile") {
|
||||
SCENARIO("File size") {
|
||||
GIVEN("Empty string") {
|
||||
MockFile f{""};
|
||||
|
||||
THEN("size() should be 0.") {
|
||||
CHECK_EQ(f.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("Not empty string") {
|
||||
MockFile f{"abc"};
|
||||
|
||||
THEN("size() should be string length.") {
|
||||
CHECK_EQ(f.size(), 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("File seek") {
|
||||
GIVEN("Valid file") {
|
||||
MockFile f{"abc\ndef"};
|
||||
auto init_size = f.size();
|
||||
|
||||
WHEN("seek()") {
|
||||
f.seek(4);
|
||||
THEN("offset_ should be updated.") {
|
||||
CHECK_EQ(f.offset_, 4);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("seek() negative offset") {
|
||||
auto r = f.seek(-1);
|
||||
|
||||
THEN("Result should be bad_seek.") {
|
||||
CHECK(r.is_error());
|
||||
CHECK_EQ(r.error().code(), FR_BAD_SEEK);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("seek() offset is size()") {
|
||||
auto r = f.seek(f.size());
|
||||
|
||||
THEN("File should not grow.") {
|
||||
CHECK(r.is_ok());
|
||||
CHECK_EQ(f.size(), init_size);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("seek() offset > size()") {
|
||||
auto r = f.seek(f.size() + 1);
|
||||
|
||||
THEN("File should grow.") {
|
||||
CHECK(r.is_ok());
|
||||
CHECK_EQ(f.size(), init_size + 1);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("seek() offset < size()") {
|
||||
auto r = f.seek(1);
|
||||
|
||||
THEN("Result should be ok.") {
|
||||
CHECK(r.is_ok());
|
||||
}
|
||||
|
||||
r = f.seek(3);
|
||||
|
||||
THEN("Result should be previous offset") {
|
||||
CHECK(r);
|
||||
CHECK_EQ(*r, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("File read") {
|
||||
GIVEN("Valid file") {
|
||||
MockFile f{"abc\ndef"};
|
||||
|
||||
const auto buf_len = 10;
|
||||
std::string buf;
|
||||
buf.resize(buf_len);
|
||||
|
||||
WHEN("Reading") {
|
||||
auto r = f.read(&buf[0], 3);
|
||||
|
||||
THEN("Result should be number of bytes read") {
|
||||
CHECK(r);
|
||||
CHECK_EQ(*r, 3);
|
||||
}
|
||||
|
||||
buf.resize(*r);
|
||||
THEN("Buffer should contain read data") {
|
||||
CHECK_EQ(buf.length(), 3);
|
||||
CHECK_EQ(buf, "abc");
|
||||
}
|
||||
|
||||
r = f.read(&buf[0], 3);
|
||||
THEN("Reading should continue where it left off") {
|
||||
CHECK_EQ(buf.length(), 3);
|
||||
CHECK_EQ(buf, "\nde");
|
||||
}
|
||||
|
||||
r = f.read(&buf[0], 3);
|
||||
THEN("Reading should stop at the end of the file") {
|
||||
CHECK(r);
|
||||
CHECK_EQ(*r, 1);
|
||||
|
||||
buf.resize(*r);
|
||||
CHECK_EQ(buf.length(), 1);
|
||||
CHECK_EQ(buf, "f");
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Reading block larger than file size") {
|
||||
auto r = f.read(&buf[0], buf_len);
|
||||
buf.resize(*r);
|
||||
|
||||
THEN("It should read to file end.") {
|
||||
CHECK(r);
|
||||
CHECK_EQ(*r, 7);
|
||||
CHECK_EQ(buf, f.data_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("File write") {
|
||||
GIVEN("Valid file") {
|
||||
MockFile f{"abc\ndef"};
|
||||
|
||||
WHEN("Writing over existing region") {
|
||||
f.write("xyz", 3);
|
||||
|
||||
THEN("It should overwrite") {
|
||||
CHECK_EQ(f.data_, "xyz\ndef");
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Writing over past end") {
|
||||
f.seek(f.size());
|
||||
f.write("xyz", 3);
|
||||
|
||||
THEN("It should extend file and write") {
|
||||
CHECK_EQ(f.size(), 10);
|
||||
CHECK_EQ(f.data_, "abc\ndefxyz");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This scenario was tested on device.
|
||||
SCENARIO("File truncate") {
|
||||
GIVEN("Valid file") {
|
||||
MockFile f{"hello world"};
|
||||
|
||||
WHEN("truncating at offset 5") {
|
||||
f.seek(5);
|
||||
f.truncate();
|
||||
|
||||
THEN("resulting file should be 'hello'.") {
|
||||
CHECK_EQ(f.size(), 5);
|
||||
CHECK_EQ(f.data_, "hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("File truncate") {
|
||||
GIVEN("Valid file") {
|
||||
MockFile f{"abc\ndef"};
|
||||
auto init_size = f.size();
|
||||
|
||||
WHEN("R/W pointer at end") {
|
||||
f.seek(f.size());
|
||||
f.truncate();
|
||||
|
||||
THEN("It should not change size.") {
|
||||
CHECK_EQ(f.size(), init_size);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("R/W pointer in middle") {
|
||||
f.seek(3);
|
||||
f.truncate();
|
||||
|
||||
THEN("It should change size.") {
|
||||
CHECK_EQ(f.size(), 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "mock_file.hpp"
|
||||
|
||||
TEST_SUITE_BEGIN("Test BufferWrapper");
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue