Added FileTape constuctor that initializes FileTape from vector of uint32_t

Slight rewrite of fill_file_with_empty_cells
Adjusted seek offset in some FileTape operations
Unit tests now work with vector of uint32_t as source of test data
This commit is contained in:
Egor 2024-10-26 16:10:14 +03:00
parent 680ec28b57
commit 4397e519bf
4 changed files with 89 additions and 51 deletions

View file

@ -4,6 +4,7 @@
#include "tape.h" #include "tape.h"
#include <chrono> #include <chrono>
#include <fstream> #include <fstream>
#include <vector>
using std::chrono::milliseconds; using std::chrono::milliseconds;
@ -55,7 +56,7 @@ class FileTape : public Tape {
std::string file_name; std::string file_name;
std::fstream file; std::fstream file;
FileTapeSettings settings; FileTapeSettings settings;
bool temp = false; bool tmp = false;
public: public:
/** /**
@ -81,24 +82,45 @@ class FileTape : public Tape {
/** /**
* Initializes a new instance of FileTape that will create and open a file * Initializes a new instance of FileTape that will create and open a file
* with the specified name, and write *size* zeroes into it. * with the specified name and fill it with *cells* amount of zeroed cells.
* File must not exist and directory must be write accessible. * File must not exist and directory must be write accessible.
* *
* Writing operations when filling file with zeroes will not introduce any
* delay.
*
* @param size Size of a newly created FileTpae * @param size Size of a newly created FileTpae
* @param file_name Name of a file to be created and opened * @param file_name Name of a file to be created and opened
* @param settings FileTape settings * @param settings FileTape settings
*/ */
FileTape(size_t size, std::string file_name, FileTape(size_t cells, std::string file_name,
FileTapeSettings settings = FT_DEFAULT_SETTINGS); FileTapeSettings settings = FT_DEFAULT_SETTINGS);
/** /**
* Initializes a new instance of FileTape that will create and open a * Initializes a new instance of FileTape that will create and open a
* temporary file, and write *size* zeroes into it. * temporary file and fill it with *cells* amount of zeroed cells.
*
* Writing operations when filling file with zeroes will not introduce any
* delay.
* *
* @param size Size of a newly created FileTape * @param size Size of a newly created FileTape
* @param settings FileTape settings * @param settings FileTape settings
*/ */
FileTape(size_t size, FileTapeSettings settings = FT_DEFAULT_SETTINGS); FileTape(size_t cells, FileTapeSettings settings = FT_DEFAULT_SETTINGS);
/**
* Initializes a new instance of FileTape that will create and open a
* temporary file, and fill it with the data from the provided vector.
*
* Writing operations when copying vector to a FileTape will not have any
* delay.
* Use this constructor ONLY FOR TESTING. Do NOT use it to copy one
* FileTape's data to another.
*
* @param data Vector of uint32 contents of which will be copied to FileTape
* @param settings FileTape settings
*/
FileTape(const std::vector<uint32_t> &data,
FileTapeSettings settings = FT_DEFAULT_SETTINGS);
/** /**
* Advances the underlying fstream to a new line. * Advances the underlying fstream to a new line.
@ -128,11 +150,15 @@ class FileTape : public Tape {
*/ */
void write(uint32_t data) override; void write(uint32_t data) override;
/**
* Close the undetlying fstream, and, in case the FileTape is marked as
* temporary (tmp is set to true), remove the temporary file
*/
~FileTape() override; ~FileTape() override;
// Define getters in header file since they are implicitly inlined // Define getters in header file since they are implicitly inlined
bool is_temp() const { return this->temp; } bool is_tmp() const { return this->tmp; }
const FileTapeSettings &get_settings() const { return this->settings; } const FileTapeSettings &get_settings() const { return this->settings; }

View file

@ -1,5 +1,8 @@
#include "filetape.h" #include "filetape.h"
#include <filesystem> #include <filesystem>
#include <fstream>
#include <iomanip>
#include <string>
#include <thread> #include <thread>
using std::fstream; using std::fstream;
@ -14,11 +17,11 @@ std::string generate_tmp_file_name() {
return tmp_file_name.data(); return tmp_file_name.data();
} }
void fill_file_with_empty_cells(size_t size, fstream &file) { void fill_file_with_empty_cells(size_t cells, fstream &file) {
file << std::setfill('0') << std::setw(tape::FT_CELL_SIZE + 1); file << std::setfill('0') << std::setw(tape::FT_CELL_SIZE) << '0';
for (int i = 0; i < size; i++) { for (size_t i = 1; i < cells; i++) {
file << tape::FT_DELIMETER << std::setfill('0') file << tape::FT_DELIMETER << std::setfill('0')
<< std::setw(tape::FT_CELL_SIZE + 1); << std::setw(tape::FT_CELL_SIZE) << '0';
} }
file.seekg(0, fstream::beg); file.seekg(0, fstream::beg);
} }
@ -28,20 +31,37 @@ FileTape::FileTape(std::string file_name, FileTapeSettings settings)
this->file = fstream(file_name); this->file = fstream(file_name);
} }
FileTape::FileTape(size_t size, std::string file_name, FileTape::FileTape(size_t cells, std::string file_name,
FileTapeSettings settings) FileTapeSettings settings)
: FileTape(file_name, settings) { : FileTape(file_name, settings) {
this->file = this->file =
fstream(this->file_name, fstream::in | fstream::out | fstream::trunc); fstream(this->file_name, fstream::in | fstream::out | fstream::trunc);
fill_file_with_empty_cells(size, this->file); fill_file_with_empty_cells(cells, this->file);
} }
FileTape::FileTape(size_t size, FileTapeSettings settings) FileTape::FileTape(size_t cells, FileTapeSettings settings)
: settings(settings), temp(true) { : settings(settings), tmp(true) {
this->file_name = generate_tmp_file_name(); this->file_name = generate_tmp_file_name();
this->file = this->file =
fstream(this->file_name, fstream::in | fstream::out | fstream::trunc); fstream(this->file_name, fstream::in | fstream::out | fstream::trunc);
fill_file_with_empty_cells(size, this->file); fill_file_with_empty_cells(cells, this->file);
}
FileTape::FileTape(const std::vector<uint32_t> &data, FileTapeSettings settings)
: settings(settings), tmp(true) {
this->file_name = generate_tmp_file_name();
this->file =
fstream(this->file_name, fstream::in | fstream::out | fstream::trunc);
if (data.empty()) {
return;
}
this->file << std::setfill('0') << std::setw(FT_CELL_SIZE)
<< std::to_string(data[0]);
for (size_t i = 1; i < data.size(); i++) {
this->file << FT_DELIMETER << std::setfill('0')
<< std::setw(FT_CELL_SIZE) << std::to_string(data[i]);
}
this->file.seekg(0, fstream::beg);
} }
bool FileTape::seek_backwards() { bool FileTape::seek_backwards() {
@ -56,10 +76,10 @@ bool FileTape::seek_backwards() {
bool FileTape::seek_forward() { bool FileTape::seek_forward() {
std::this_thread::sleep_for(this->settings.seek_forward_delay); std::this_thread::sleep_for(this->settings.seek_forward_delay);
this->file.seekg(FT_SEEK_OFFSET, fstream::cur); this->file.seekg(FT_SEEK_OFFSET, fstream::cur);
this->file.peek(); this->file.peek(); // peek to check if eof is reached
if (this->file.eof()) { if (this->file.eof()) {
this->file.clear(); this->file.clear(); // reset fstream
this->file.seekg(-FT_SEEK_OFFSET, fstream::end); this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::end);
return false; return false;
} }
return true; return true;
@ -70,8 +90,8 @@ uint32_t FileTape::read() {
uint32_t data = 0; uint32_t data = 0;
this->file >> data; this->file >> data;
if (this->file.eof()) { if (this->file.eof()) {
this->file.clear(); this->file.clear(); // reset fstream
this->file.seekg(-FT_SEEK_OFFSET + 2, fstream::end); this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::end);
return data; return data;
} }
this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::cur); this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::cur);
@ -89,7 +109,7 @@ void FileTape::write(uint32_t data) {
FileTape::~FileTape() { FileTape::~FileTape() {
this->file.close(); this->file.close();
if (this->is_temp()) { if (this->is_tmp()) {
std::filesystem::remove(this->file_name); std::filesystem::remove(this->file_name);
} }
} }

View file

@ -5,4 +5,3 @@ target_link_libraries(filetape_tests PRIVATE tapelib)
target_link_libraries(filetape_tests PRIVATE Catch2::Catch2WithMain) target_link_libraries(filetape_tests PRIVATE Catch2::Catch2WithMain)
add_test(filetape_tests filetape_tests) add_test(filetape_tests filetape_tests)
add_compile_definitions(FILETAPE_TEST_FILE=\"${PROJECT_SOURCE_DIR}/tests/assets/filetape_test_data.txt\")

View file

@ -1,61 +1,54 @@
#include "filetape.h" #include "filetape.h"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <iostream>
#include <vector>
const static std::string TEST_DATA = "0000000001\n" const static std::vector<uint32_t> TEST_DATA = {1, 12345, 0, 2222222222,
"0000012345\n" 4294967295};
"0000000000\n"
"2222222222\n"
"4294967295";
tape::FileTape prepare_test_filetape() {
tape::FileTape tape(0);
return tape;
}
// NOLINTBEGIN(readability-function-cognitive-complexity) // NOLINTBEGIN(readability-function-cognitive-complexity)
TEST_CASE("Reading data from a FileTape", "[filetape]") { TEST_CASE("Reading data from a FileTape", "[filetape]") {
tape::FileTape tape(FILETAPE_TEST_FILE); tape::FileTape tape(TEST_DATA);
SECTION("Read all data sequentially") { SECTION("Read all data sequentially") {
REQUIRE(tape.read() == 1); REQUIRE(tape.read() == TEST_DATA[0]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.read() == 2); REQUIRE(tape.read() == TEST_DATA[1]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.read() == 3); REQUIRE(tape.read() == TEST_DATA[2]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.read() == 4); REQUIRE(tape.read() == TEST_DATA[3]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.read() == 5); REQUIRE(tape.read() == TEST_DATA[4]);
} }
SECTION("Read data non-sequentially") { SECTION("Read data non-sequentially") {
REQUIRE(tape.read() == 1); REQUIRE(tape.read() == TEST_DATA[0]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.read() == 2); REQUIRE(tape.read() == TEST_DATA[1]);
REQUIRE(tape.seek_backwards() == true); REQUIRE(tape.seek_backwards() == true);
REQUIRE(tape.read() == 1); REQUIRE(tape.read() == TEST_DATA[0]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.read() == 4); REQUIRE(tape.read() == TEST_DATA[3]);
REQUIRE(tape.seek_backwards() == true); REQUIRE(tape.seek_backwards() == true);
REQUIRE(tape.seek_backwards() == true); REQUIRE(tape.seek_backwards() == true);
REQUIRE(tape.read() == 2); REQUIRE(tape.read() == TEST_DATA[1]);
} }
} }
TEST_CASE("Seeking forward and backwards", "[filetape]") { TEST_CASE("Seeking forward and backwards", "[filetape]") {
tape::FileTape tape(FILETAPE_TEST_FILE); tape::FileTape tape(TEST_DATA);
SECTION("Rewinding at the beginning of a file tape") { SECTION("Rewinding at the beginning of a file tape") {
REQUIRE(tape.seek_backwards() == false); REQUIRE(tape.seek_backwards() == false);
REQUIRE(tape.read() == 1); REQUIRE(tape.read() == TEST_DATA[0]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.read() == 2); REQUIRE(tape.read() == TEST_DATA[1]);
REQUIRE(tape.seek_backwards() == true); REQUIRE(tape.seek_backwards() == true);
REQUIRE(tape.seek_backwards() == false); REQUIRE(tape.seek_backwards() == false);
REQUIRE(tape.read() == 1); REQUIRE(tape.read() == TEST_DATA[0]);
} }
SECTION("Seeking at the end of a file tape") { SECTION("Seeking at the end of a file tape") {
@ -65,12 +58,12 @@ TEST_CASE("Seeking forward and backwards", "[filetape]") {
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
} }
REQUIRE(tape.seek_forward() == false); REQUIRE(tape.seek_forward() == false);
REQUIRE(tape.read() == 5); REQUIRE(tape.read() == TEST_DATA[4]);
REQUIRE(tape.seek_backwards() == true); REQUIRE(tape.seek_backwards() == true);
REQUIRE(tape.read() == 4); REQUIRE(tape.read() == TEST_DATA[3]);
REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true);
REQUIRE(tape.seek_forward() == false); REQUIRE(tape.seek_forward() == false);
REQUIRE(tape.read() == 5); REQUIRE(tape.read() == TEST_DATA[4]);
} }
} }