From 0e49b35f12afb07a66d73c06233665474b473163 Mon Sep 17 00:00:00 2001 From: erius Date: Thu, 24 Oct 2024 17:48:55 +0300 Subject: [PATCH] Added delimeter and settings constants to FileTape Removed unneccesary copy and move constructors Added constructor for tmp FileTape Added Catch2 for unit testing Added example unit tests --- include/tapelib/filetape.h | 56 ++++++++++++++++----- include/tapelib/tape.h | 8 +-- src/filetape.cpp | 35 +++++++++++--- tests/CMakeLists.txt | 3 ++ tests/assets/filetape_test_data.txt | 5 ++ tests/filetape_tests.cpp | 75 ++++++++++++++++++++++++++++- 6 files changed, 157 insertions(+), 25 deletions(-) create mode 100644 tests/assets/filetape_test_data.txt diff --git a/include/tapelib/filetape.h b/include/tapelib/filetape.h index 4921792..804b078 100644 --- a/include/tapelib/filetape.h +++ b/include/tapelib/filetape.h @@ -2,6 +2,7 @@ #define FILE_TAPE_H #include "tape.h" +#include #include #include #include @@ -19,39 +20,68 @@ struct FileTapeSettings { unsigned int seek_backwards_delay_ms; }; +const static struct FileTapeSettings DEFAULT_FT_SETTINGS = + FileTapeSettings{.read_delay_ms = 0, + .write_delay_ms = 0, + .seek_forward_delay_ms = 0, + .seek_backwards_delay_ms = 0}; + +const static char FT_DELIMETER = '\n'; + /** * Mock tape implementation that uses text files as data source. * * Simulates tape operations by artificially pausing the execution of some * methods using time intervals, specified in command line arguments or a - * configuration file. Each cell is represented as a seperate line in a file, - * cell data is stored as plain text. + * configuration file. Each cell is represented as a plain text 32-bit int + * separated by FT_DELIMETER. End of the file tape is indicated either by EOF + * or some arbitrary data that cannot be parsed to int32_t */ class FileTape : public Tape { private: std::fstream file; FileTapeSettings settings; + size_t prev_line_pos = 0; + bool at_first_line = false; public: - FileTape &operator=(const FileTape &) = delete; - FileTape(const FileTape &) = delete; - FileTape(FileTape &&) = delete; - FileTape &operator=(FileTape &&) = delete; - ~FileTape() override; - /** - * Initialize a new instance of FileTape that will open a file with the - * specified name. File must be read and write accessible. + * Initializes a new instance of FileTape that will open an existing file + * with the specified name, already filled with data. File must be read and + * write accessible. * * @param file_name Name of a file to be opened. - * @param settings File tape settings + * @param settings FileTape settings */ - explicit FileTape(const std::string &file_name, FileTapeSettings settings); + explicit FileTape(const std::string &file_name, + FileTapeSettings settings = DEFAULT_FT_SETTINGS); + + /** + * Initializes a new instance of FileTape that will create and open a file + * with the specified name, and write *size* zeroes into it. + * File must not exist and directory must be write accessible. + * + * @param size Size of a newly created FileTpae + * @param file_name Name of a file to be created and opened + * @param settings FileTape settings + */ + explicit FileTape(size_t size, const std::string &file_name, + FileTapeSettings settings = DEFAULT_FT_SETTINGS); + + /** + * Initializes a new instance of FileTape that will create and open a + * temporary file, and write *size* zeroes into it. + * + * @param size Size of a newly created FileTape + * @param settings FileTape settings + */ + explicit FileTape(size_t size, + FileTapeSettings settings = DEFAULT_FT_SETTINGS); /** * Advances the underlying fstream to a new line. * - * @return false if EOF is reached, otherwise true. + * @return false if empty line is reached, otherwise true. */ bool seek_forward() override; diff --git a/include/tapelib/tape.h b/include/tapelib/tape.h index d54c7d7..7179eef 100644 --- a/include/tapelib/tape.h +++ b/include/tapelib/tape.h @@ -17,11 +17,11 @@ namespace tape { */ class Tape { public: - Tape(const Tape &) = default; - Tape(Tape &&) = default; - Tape &operator=(const Tape &) = default; - Tape &operator=(Tape &&) = default; Tape() = default; + Tape(const Tape &) = default; + Tape(Tape &&) = delete; + Tape &operator=(const Tape &) = default; + Tape &operator=(Tape &&) = delete; virtual ~Tape() = default; /** diff --git a/src/filetape.cpp b/src/filetape.cpp index 21d6655..fa802ff 100644 --- a/src/filetape.cpp +++ b/src/filetape.cpp @@ -1,20 +1,41 @@ #include "filetape.h" +#include #include +#include #include #include -tape::FileTape::FileTape(const std::string &file_name, - FileTapeSettings settings) +using tape::FileTape; + +FileTape::FileTape(const std::string &file_name, FileTapeSettings settings) : settings(settings) { this->file = std::fstream(file_name); } -bool tape::FileTape::seek_forward() { return true; } +FileTape::FileTape(size_t size, const std::string &file_name, + FileTapeSettings settings) + : FileTape(file_name, settings) { + this->file << "0"; + for (int i = 0; i < size; i++) { + this->file << FT_DELIMETER << "0"; + } +} -bool tape::FileTape::seek_backwards() { return true; } +FileTape::FileTape(size_t size, FileTapeSettings settings) + : settings(settings) { + std::array file_name{}; + std::tmpnam(file_name.data()); + this->file = std::fstream(file_name.data()); + this->file << "0"; + for (int i = 0; i < size; i++) { + this->file << FT_DELIMETER << "0"; + } +} -int32_t tape::FileTape::read() { return 0; } +bool FileTape::seek_forward() { return true; } -void tape::FileTape::write(int32_t data) {} +bool FileTape::seek_backwards() { return true; } -tape::FileTape::~FileTape() { this->file.close(); } +int32_t FileTape::read() { return 0; } + +void FileTape::write(int32_t data) {} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 16dc957..7bb6c28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,7 @@ +find_package(Catch2 3 REQUIRED) + add_executable(filetape_tests ${PROJECT_SOURCE_DIR}/tests/filetape_tests.cpp) target_link_libraries(filetape_tests PRIVATE tapelib) +target_link_libraries(filetape_tests PRIVATE Catch2::Catch2WithMain) add_test(filetape_tests filetape_tests) diff --git a/tests/assets/filetape_test_data.txt b/tests/assets/filetape_test_data.txt new file mode 100644 index 0000000..8a1218a --- /dev/null +++ b/tests/assets/filetape_test_data.txt @@ -0,0 +1,5 @@ +1 +2 +3 +4 +5 diff --git a/tests/filetape_tests.cpp b/tests/filetape_tests.cpp index 44e82e2..17c408e 100644 --- a/tests/filetape_tests.cpp +++ b/tests/filetape_tests.cpp @@ -1 +1,74 @@ -int main(int argc, char *argv[]) { return 0; } +#include "filetape.h" +#include +#include + +// NOLINTBEGIN(readability-function-cognitive-complexity) +TEST_CASE("Reading data from a FileTape", "[filetape]") { + tape::FileTape tape("assets/filetape_test_data.txt"); + + SECTION("Read all data sequentially") { + REQUIRE(tape.read() == 1); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.read() == 2); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.read() == 3); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.read() == 4); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.read() == 5); + } + + SECTION("Read data non-sequentially") { + REQUIRE(tape.read() == 1); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.read() == 2); + REQUIRE(tape.seek_backwards() == true); + REQUIRE(tape.read() == 1); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.read() == 4); + REQUIRE(tape.seek_backwards() == true); + REQUIRE(tape.seek_backwards() == true); + REQUIRE(tape.read() == 2); + } +} + +TEST_CASE("Seeking forward and backwards", "[filetape]") { + tape::FileTape tape("assets/filetape_test_data.txt"); + + SECTION("Rewinding at the beginning of a file tape") { + REQUIRE(tape.seek_backwards() == false); + REQUIRE(tape.read() == 1); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.read() == 2); + REQUIRE(tape.seek_backwards() == true); + REQUIRE(tape.seek_backwards() == false); + REQUIRE(tape.read() == 1); + } + + SECTION("Seeking at the end of a file tape") { + const int size = 5; + // seek to end of a file tape + for (int i = 0; i < size - 1; i++) { + REQUIRE(tape.seek_forward() == true); + } + REQUIRE(tape.seek_forward() == false); + REQUIRE(tape.read() == 5); + REQUIRE(tape.seek_backwards() == true); + REQUIRE(tape.read() == 4); + REQUIRE(tape.seek_forward() == true); + REQUIRE(tape.seek_forward() == false); + REQUIRE(tape.read() == 5); + } +} + +TEST_CASE("Writing to a file tape", "[filetape]") { + // init a temp file to modify + // std::string tmp_file_name = "/tmp/" + // std::unique_ptr tmp_file(std::tmpfile()); + // std::fstream tmp(tmp_file); + // std::fstream tmp(tmp_file); + // tape::FileTape tape("assets/filetape_test_data.txt"); +} +// NOLINTEND(readability-function-cognitive-complexity)