diff --git a/CMakeLists.txt b/CMakeLists.txt index c39d784..cdcac9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,11 +34,13 @@ endif() add_executable( tests ${SOURCES} - test/dirname_basename.cpp test/converter.cpp test/directory.cpp test/fat.cpp test/file_data.cpp + test/dirname_basename.cpp + test/ls.cpp + test/mkdir.cpp ) target_link_libraries(tests GTest::gtest_main) diff --git a/src/CLI/CLI.cpp b/src/CLI/CLI.cpp index 83e90ac..4e14371 100644 --- a/src/CLI/CLI.cpp +++ b/src/CLI/CLI.cpp @@ -110,7 +110,7 @@ auto CLI::ls(std::vector args) -> void { } auto files = file_system_->ls("/"); - for (auto const &file : files) { std::cout << file.to_string(verbose) << '\n'; } + for (auto const &file : files) { std::cout << file.to_string("/", verbose) << '\n'; } } auto CLI::mkdir(std::vector args) -> void { diff --git a/src/FileSystem/FileData/FileData.cpp b/src/FileSystem/FileData/FileData.cpp index 9f31673..5834e3a 100644 --- a/src/FileSystem/FileData/FileData.cpp +++ b/src/FileSystem/FileData/FileData.cpp @@ -1,5 +1,7 @@ #include "FileData.hpp" +#include + FileData::FileData(std::string name, FileSize size, std::uint64_t first_cluster_index, bool is_directory) : name_(std::move(name)), size_(size.bytes), first_cluster_index_(first_cluster_index), is_directory_(is_directory) {} @@ -63,9 +65,9 @@ auto FileData::from_bytes(std::vector const &bytes) -> FileData { return FileData(name, FileSize{size}, first_cluster_index, is_directory); } -auto FileData::to_string(bool verbose) const -> std::string { +auto FileData::to_string(std::string delimiter, bool verbose) const -> std::string { if (!verbose) { - std::string is_directory_string = is_directory_ ? "/" : ""; + std::string is_directory_string = is_directory_ ? std::move(delimiter) : ""; return name_ + is_directory_string; } diff --git a/src/FileSystem/FileData/FileData.hpp b/src/FileSystem/FileData/FileData.hpp index 6010f2c..8139d3a 100644 --- a/src/FileSystem/FileData/FileData.hpp +++ b/src/FileSystem/FileData/FileData.hpp @@ -35,7 +35,7 @@ class FileData { [[nodiscard]] auto to_bytes() const -> std::vector; static auto from_bytes(std::vector const &bytes) -> FileData; - [[nodiscard]] auto to_string(bool verbose = false) const -> std::string; + [[nodiscard]] auto to_string(std::string delimiter, bool verbose = false) const -> std::string; [[nodiscard]] static auto file_data_size() noexcept -> std::uint64_t; }; diff --git a/src/FileSystem/FileSystem.cpp b/src/FileSystem/FileSystem.cpp index 571a1b5..d5c56db 100644 --- a/src/FileSystem/FileSystem.cpp +++ b/src/FileSystem/FileSystem.cpp @@ -17,7 +17,7 @@ FileSystem::FileSystem(std::string const &path) { path_resolver_ = std::make_unique(PATH_DELIMITER, disk_reader_, fat_, settings_.cluster_size); if (!is_root_dir_created()) create_root_dir(); - working_dir_ = std::make_shared("/", FileData::FileSize{root_dir_size()}, 0, true); + working_dir_ = std::make_shared(path_resolver_->delimiter(), FileData::FileSize{root_dir_size()}, 0, true); } void FileSystem::make(std::string const &path, FSMaker::Settings const &settings, bool allow_big) { @@ -27,9 +27,12 @@ void FileSystem::make(std::string const &path, FSMaker::Settings const &settings auto FileSystem::get_settings() const noexcept -> FSMaker::Settings const & { return settings_; } auto FileSystem::ls(std::string const &path) const -> std::vector { + if (path != path_resolver_->delimiter()) throw std::invalid_argument("Can ls only root directory"); + auto size = root_dir_size(); - auto file_reader = FileReader(FileData("/", FileData::FileSize{size}, 0, true), FileHandler::FileOffset(0), fat_, - settings_.cluster_size, disk_reader_, FileReader::BlockSize(size)); + auto file_reader = + FileReader(FileData(path_resolver_->delimiter(), FileData::FileSize{size}, 0, true), FileHandler::FileOffset(0), + fat_, settings_.cluster_size, disk_reader_, FileReader::BlockSize(size)); auto root_dir = Directory::from_bytes(file_reader.read_block()); return root_dir.files(); } @@ -44,11 +47,11 @@ auto FileSystem::basename(std::string const &path) -> std::string { auto FileSystem::mkdir(std::string const &path) -> void { auto parent_dir_data = get_parent_dir_data(path); - if (!parent_dir_data.has_value()) throw std::runtime_error("Cannot create directory in non-existing directory"); - if (!parent_dir_data.value().is_directory()) throw std::runtime_error("Cannot create directory in file"); + if (!parent_dir_data.has_value()) throw std::invalid_argument("Cannot create directory in non-existing directory"); + if (!parent_dir_data.value().is_directory()) throw std::invalid_argument("Cannot create directory in file"); auto dir_name = basename(path); - if (search(dir_name).has_value()) throw std::runtime_error("Directory already exists"); + if (search(dir_name).has_value()) throw std::invalid_argument("Directory already exists"); auto new_dir_data = write_new_dir(parent_dir_data.value(), dir_name); @@ -70,8 +73,8 @@ auto FileSystem::is_root_dir_created() const noexcept -> bool { return fat_->is_ auto FileSystem::create_root_dir() -> void { auto first_cluster = fat_->allocate(); - auto file_writer = FileWriter(FileData("/", FileData::FileSize{0}, first_cluster, true), FileHandler::FileOffset(0), - fat_, settings_.cluster_size, disk_writer_); + auto file_writer = FileWriter(FileData(path_resolver_->delimiter(), FileData::FileSize{0}, first_cluster, true), + FileHandler::FileOffset(0), fat_, settings_.cluster_size, disk_writer_); auto root_dir_bytes = Directory::make_root().to_bytes(); file_writer.write_block(root_dir_bytes); @@ -79,14 +82,15 @@ auto FileSystem::create_root_dir() -> void { auto FileSystem::root_dir_size() const noexcept -> std::uint64_t { auto file_reader = - FileReader(FileData("/", FileData::FileSize{FileData::file_data_size()}, 0, true), FileHandler::FileOffset(0), - fat_, settings_.cluster_size, disk_reader_, FileReader::BlockSize(FileData::file_data_size())); + FileReader(FileData(path_resolver_->delimiter(), FileData::FileSize{FileData::file_data_size()}, 0, true), + FileHandler::FileOffset(0), fat_, settings_.cluster_size, disk_reader_, + FileReader::BlockSize(FileData::file_data_size())); auto root_dir_file_data = FileData::from_bytes(file_reader.read_block()); return root_dir_file_data.size().bytes; } auto FileSystem::root_dir_file_data() const -> FileData { - return FileData("/", FileData::FileSize{root_dir_size()}, 0, true); + return FileData(path_resolver_->delimiter(), FileData::FileSize{root_dir_size()}, 0, true); } auto FileSystem::search(std::string const &path) const -> std::optional { @@ -95,7 +99,6 @@ auto FileSystem::search(std::string const &path) const -> std::optional std::optional { auto parent_dir_path = dirname(path); - std::cout << "Parent dir path: " << parent_dir_path << '\n'; return search(parent_dir_path); } @@ -120,6 +123,10 @@ auto FileSystem::update_parent_dir(FileData const &parent_dir_data, FileData con auto parent_dir_writer = FileWriter(parent_dir_data, FileHandler::FileOffset(0), fat_, settings_.cluster_size, disk_writer_); parent_dir_writer.write_block(parent_dir.to_bytes()); + + if (parent_dir_data.first_cluster_index() == working_dir_->first_cluster_index()) { + working_dir_->set_size(FileData::FileSize{parent_dir.to_bytes().size()}); + } } auto operator<<(std::ostream &out_stream, FileSystem const &file_system) -> std::ostream & { diff --git a/test/ls.cpp b/test/ls.cpp new file mode 100644 index 0000000..45204ec --- /dev/null +++ b/test/ls.cpp @@ -0,0 +1,36 @@ +#include "../src/FileSystem/FileSystem.hpp" +#include + +class LsTest : public testing::Test { +protected: + // NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes) + std::string const PATH = "test.fs"; + std::uint64_t const SIZE = 1024; + std::uint64_t const CLUSTER_SIZE = 64; + + std::unique_ptr file_system_; + // NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes) + + void SetUp() override { + FileSystem::make(PATH, {SIZE, CLUSTER_SIZE}); + + file_system_ = std::make_unique(PATH); + } + + void TearDown() override { std::filesystem::remove(PATH); } +}; + +TEST_F(LsTest, Empty) { + auto const list = file_system_->ls("/"); + EXPECT_EQ(list.size(), 2); + EXPECT_EQ(list[0].name(), "."); + EXPECT_EQ(list[1].name(), ".."); +} + +TEST_F(LsTest, NonExistent) { + EXPECT_THROW(auto const list = file_system_->ls("/non_existent"), std::invalid_argument); +} + +TEST_F(LsTest, NonExistentNested) { + EXPECT_THROW(auto const list = file_system_->ls("/non_existent/nested"), std::invalid_argument); +} \ No newline at end of file diff --git a/test/mkdir.cpp b/test/mkdir.cpp new file mode 100644 index 0000000..e732e20 --- /dev/null +++ b/test/mkdir.cpp @@ -0,0 +1,45 @@ +#include "../src/FileSystem/FileSystem.hpp" +#include + +class MkdirTest : public testing::Test { +protected: + // NOLINTBEGIN(cppcoreguidelines-non-private-member-variables-in-classes) + std::string const PATH = "test.fs"; + std::uint64_t const SIZE = 2024; + std::uint64_t const CLUSTER_SIZE = 64; + + std::unique_ptr file_system_; + // NOLINTEND(cppcoreguidelines-non-private-member-variables-in-classes) + + void SetUp() override { + FileSystem::make(PATH, {SIZE, CLUSTER_SIZE}); + + file_system_ = std::make_unique(PATH); + } + + void TearDown() override { std::filesystem::remove(PATH); } +}; + +TEST_F(MkdirTest, Empty) { + file_system_->mkdir("./a"); + file_system_->mkdir("b"); + file_system_->mkdir("/c"); + auto const list = file_system_->ls("/"); + EXPECT_EQ(list.size(), 5); + EXPECT_EQ(list[0].name(), "."); + EXPECT_EQ(list[1].name(), ".."); + EXPECT_EQ(list[2].name(), "a"); + EXPECT_EQ(list[3].name(), "b"); + EXPECT_EQ(list[4].name(), "c"); +} + +TEST_F(MkdirTest, NonExistent) { EXPECT_THROW(file_system_->mkdir("/non_existent/test"), std::invalid_argument); } + +TEST_F(MkdirTest, NonExistentNested) { + EXPECT_THROW(file_system_->mkdir("/non_existent/nested/test"), std::invalid_argument); +} + +TEST_F(MkdirTest, AlreadyExists) { + file_system_->mkdir("/test"); + EXPECT_THROW(file_system_->mkdir("/test"), std::invalid_argument); +} \ No newline at end of file