aboutsummaryrefslogtreecommitdiff
path: root/kernel/src/filesystem/ext2/inode.tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/src/filesystem/ext2/inode.tests.cpp')
-rw-r--r--kernel/src/filesystem/ext2/inode.tests.cpp159
1 files changed, 159 insertions, 0 deletions
diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp
new file mode 100644
index 0000000..795ff10
--- /dev/null
+++ b/kernel/src/filesystem/ext2/inode.tests.cpp
@@ -0,0 +1,159 @@
+#include "kernel/filesystem/ext2/inode.hpp"
+
+#include "kernel/devices/storage/management.hpp"
+#include "kernel/filesystem/ext2/filesystem.hpp"
+#include "kernel/filesystem/filesystem.hpp"
+#include "kernel/test_support/cpu.hpp"
+#include "kernel/test_support/devices/block_device.hpp"
+#include "kernel/test_support/filesystem/ext2.hpp"
+#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp"
+
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <cstddef>
+#include <filesystem>
+#include <string_view>
+
+SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]")
+{
+ GIVEN("an ext2 filesystem")
+ {
+ auto fs = kernel::filesystem::ext2::filesystem{};
+
+ THEN("the inode is initialized and has the kind regular")
+ {
+ auto inode = kernel::filesystem::ext2::inode{&fs};
+ REQUIRE(inode.is_regular());
+ REQUIRE(!inode.is_directory());
+ REQUIRE(!inode.is_device());
+ }
+ }
+
+ GIVEN("no filesystem (null pointer)")
+ {
+ THEN("constructing an inode with a null filesystem pointer panics")
+ {
+ REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode{nullptr}, kernel::tests::cpu::halt);
+ }
+ }
+}
+
+SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 inode reads from real image",
+ "[filesystem][ext2][inode][img]")
+{
+ auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img";
+
+ GIVEN("a mounted ext2 filesystem and a regular file inode")
+ {
+ REQUIRE(std::filesystem::exists(image_path));
+ REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path}));
+
+ auto boot_device = kernel::devices::storage::management::get().determine_boot_device();
+ REQUIRE(boot_device != nullptr);
+
+ auto fs = kernel::filesystem::ext2::filesystem{};
+ REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success);
+
+ auto information = fs.lookup(fs.root_inode(), "information");
+ REQUIRE(information != nullptr);
+ auto file = fs.lookup(information, "info_1.txt");
+ REQUIRE(file != nullptr);
+ REQUIRE(file->is_regular());
+
+ THEN("reading from offset zero returns expected file prefix")
+ {
+ auto buffer = kstd::vector<std::byte>(6);
+ auto const bytes_read = file->read(buffer.data(), 0, buffer.size());
+
+ REQUIRE(bytes_read == 6);
+
+ auto const text = std::string_view{reinterpret_cast<char const *>(buffer.data()), bytes_read};
+ REQUIRE(text == "info_1");
+ }
+
+ THEN("reading with an offset returns the expected byte")
+ {
+ auto buffer = kstd::vector<std::byte>(1);
+ auto const bytes_read = file->read(buffer.data(), 5, buffer.size());
+
+ REQUIRE(bytes_read == 1);
+ REQUIRE(static_cast<char>(buffer[0]) == '1');
+ }
+ }
+}
+
+SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesystem][ext2][inode]")
+{
+ auto const block_size = 1024;
+ GIVEN("an ext2 inode without mapped data blocks")
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "mock", block_size, 64 * block_size);
+ REQUIRE(device != nullptr);
+ kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device);
+
+ auto fs = kernel::filesystem::ext2::filesystem{};
+ REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success);
+
+ auto inode = kernel::filesystem::ext2::inode{&fs};
+ inode.m_data.blocks = 2;
+ inode.m_data.block[0] = 0;
+
+ auto buffer = kstd::vector<std::byte>(32, std::byte{0xAB});
+
+ THEN("no bytes are read")
+ {
+ auto const bytes_read = inode.read(buffer.data(), 0, buffer.size());
+ REQUIRE(bytes_read == 0);
+ }
+ }
+}
+
+SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]")
+{
+ auto const block_size = 1024;
+ GIVEN("an ext2 inode with two direct blocks and a block size of 1024 bytes")
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "mock", block_size, 64 * block_size);
+ REQUIRE(device != nullptr);
+ kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device);
+
+ auto fs = kernel::filesystem::ext2::filesystem{};
+ REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success);
+
+ auto inode = kernel::filesystem::ext2::inode{&fs};
+ inode.m_data.blocks = 2;
+ inode.m_data.block[0] = 20;
+ kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6);
+ inode.m_data.block[1] = 21;
+ kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size, "World!", 6);
+
+ auto buffer = kstd::vector<std::byte>(12, std::byte{0x00});
+
+ THEN("reading across the block boundary returns the combined content")
+ {
+ auto const bytes_read = inode.read(buffer.data(), block_size - 6, buffer.size());
+ REQUIRE(bytes_read == 12);
+
+ auto const text = std::string_view{reinterpret_cast<char const *>(buffer.data()), bytes_read};
+ REQUIRE(text == "Hello World!");
+ }
+ }
+}
+
+SCENARIO("Ext2 inode write is not implemented", "[filesystem][ext2][inode]")
+{
+ GIVEN("an ext2 inode")
+ {
+ auto fs = kernel::filesystem::ext2::filesystem{};
+ auto inode = kernel::filesystem::ext2::inode{&fs};
+
+ THEN("writing to the inode panics")
+ {
+ auto buffer = kstd::vector<std::byte>(32, std::byte{0x00});
+ REQUIRE_THROWS_AS(inode.write(buffer.data(), 0, buffer.size()), kernel::tests::cpu::halt);
+ }
+ }
+}