diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index be916fbd..71a2971c 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -41,7 +41,6 @@ on: - '.github/workflows/*.yml' - '!.github/workflows/coverity.yml' pull_request: - # The branches below must be a subset of the branches above paths-ignore: - 'docs/**' - '**.adoc' diff --git a/CMakeLists.txt b/CMakeLists.txt index 33d9fb23..0f33b082 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -410,6 +410,8 @@ if(WITH_TESTS) # DATA_BIN_FILE is packaged filesystem itself set(DATA_BIN_FILE ${DATA_BIN_DIR}/fs.bin) set(DATA_BIN_FILE_2 ${DATA_BIN_DIR}/fs2.bin) + set(TEST_PACKAGE_DESCRIPTOR "${CMAKE_CURRENT_SOURCE_DIR}/tests/package_descriptor/pd") + endif(WITH_TESTS) @@ -794,7 +796,7 @@ add_library(dwarfs-wr STATIC "src/dir-io.cpp" "src/file-io.cpp" "src/dl-ctl.cpp" - "src/tebako-cmdline-helpers.cpp" + "src/tebako-cmdline.cpp" "src/tebako-io-helpers.cpp" "src/tebako-io-root.cpp" "src/tebako-kfd.cpp" @@ -805,7 +807,7 @@ add_library(dwarfs-wr STATIC "src/tebako-fd.cpp" "src/tebako-dirent.cpp" "src/tebako-package-descriptor.cpp" - "include/tebako-cmdline-helpers.h" + "include/tebako-cmdline.h" "include/tebako-common.h" "include/tebako-config.h" "include/tebako-defines.h" @@ -919,8 +921,6 @@ if(WITH_TESTS) set(TESTS_OUTSIDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_files") - set(TESTS_THE_OTHER_MEMFS_IMAGE "${CMAKE_CURRENT_BINARY_DIR}/fs2.bin") - add_custom_target(PACKAGED_FILESYSTEM_STEP_1 ALL COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_SOURCE_DIR}/tests/tebako-fs.cpp COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_SOURCE_DIR}/tests/tebako-fs2.cpp @@ -960,7 +960,7 @@ if(WITH_TESTS) add_custom_target(PACKAGED_FILESYSTEM_STEP_3 ALL COMMAND ${GNU_BASH} -c "chmod -w ${DATA_TEST_DIR}/directory-2/file-in-directory-2.txt" COMMAND ${MKDWARFS} --force --progress simple -o ${DATA_BIN_FILE} -i ${DATA_TEST_DIR} - COMMAND ${MKDWARFS} --force --progress simple -o ${DATA_BIN_FILE_2} -i ${DATA_TEST_DIR_2} + COMMAND ${MKDWARFS} --force --progress simple -o ${DATA_BIN_FILE_2} -i ${DATA_TEST_DIR_2} --header ${TEST_PACKAGE_DESCRIPTOR} COMMAND ${CMAKE_COMMAND} -E rm -rf ${DATA_TEST_DIR}/*-link-to-* ${DATA_TEST_DIR}/directory-1/*-link-to-* ${DATA_TEST_DIR}/directory-1/*empty.* ${DATA_TEST_DIR}/*-outside-of-memfs COMMAND ${CMAKE_COMMAND} -E rm -rf ${DATA_TEST_DIR_2}/*-link-to-* ${DATA_TEST_DIR_2}/directory-1/*-link-to-* @@ -972,7 +972,7 @@ if(WITH_TESTS) else(WITH_LINK_TESTS) add_custom_target(PACKAGED_FILESYSTEM_STEP_3 ALL COMMAND ${MKDWARFS} --force --progress simple -o ${DATA_BIN_FILE} -i ${DATA_TEST_DIR} - COMMAND ${MKDWARFS} --force --progress simple -o ${DATA_BIN_FILE_2} -i ${DATA_TEST_DIR_2} + COMMAND ${MKDWARFS} --force --progress simple -o ${DATA_BIN_FILE_2} -i ${DATA_TEST_DIR_2} --header ${TEST_PACKAGE_DESCRIPTOR} COMMAND ${CMAKE_COMMAND} -E rm -rf ${DATA_TEST_DIR}/directory-1/*empty.* BYPRODUCTS ${DATA_BIN_FILE} ${DATA_BIN_FILE_2} ) @@ -987,7 +987,7 @@ if(WITH_TESTS) @ONLY ) -# feat: exectable application stub +# feat: executable application stub # configure_file( # ${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/tebako-fs2.cpp.in # ${CMAKE_CURRENT_SOURCE_DIR}/tests/tebako-fs2.cpp @@ -1317,7 +1317,7 @@ install(FILES "include/tebako-config.h" "include/tebako-defines.h" "include/tebako-io.h" - "include/tebako-cmdline-helpers.h" + "include/tebako-cmdline.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tebako ) diff --git a/include/tebako-cmdline-helpers.h b/include/tebako-cmdline.h similarity index 51% rename from include/tebako-cmdline-helpers.h rename to include/tebako-cmdline.h index 67f63d8f..77fc1fa8 100644 --- a/include/tebako-cmdline-helpers.h +++ b/include/tebako-cmdline.h @@ -29,11 +29,62 @@ #pragma once +#include + namespace tebako { -int build_arguments_for_extract(int* argc, char*** argv, const char* fs_mount_point); -std::pair build_arguments(const std::vector& new_argv, - const char* fs_mount_point, - const char* fs_entry_point); -std::pair, std::vector> parse_arguments(int argc, char** argv); -void process_mountpoints(const std::vector& mountpoints); + +class cmdline_args { + int argc; + const char** argv; + + std::vector mountpoints; + std::vector other_args; + + std::string extract_folder; + std::string app_image; + + std::optional package; + + int new_argc; + char** new_argv; + char* new_argv_memory; + + bool extract; + bool run; + + public: + cmdline_args(int argc, const char** argv) + : argc(argc), argv(argv), extract(false), run(false), new_argc(0), new_argv(nullptr), new_argv_memory(nullptr) + { + } + ~cmdline_args() + { + if (new_argv_memory) { + delete[] new_argv_memory; + } + if (new_argv) { + delete[] new_argv; + } + } + + void parse_arguments(void); + void process_mountpoints(); + void process_package(); + + void build_arguments(const char* fs_mount_point, const char* fs_entry_point); + void build_arguments_for_extract(const char* fs_mount_point); + void build_arguments_for_run(const char* fs_mount_point, const char* fs_entry_point); + + std::vector const& get_mountpoints() { return mountpoints; } + std::vector const& get_args() { return other_args; } + + int get_argc() { return new_argc; } + char** get_argv() { return new_argv; } + + bool with_application() { return run; } + std::string const& get_application_image() { return app_image; } + + std::optional const& get_package() { return package; } +}; + } // namespace tebako diff --git a/include/tebako-package-descriptor.h b/include/tebako-package-descriptor.h index 90b98785..c06ecff0 100644 --- a/include/tebako-package-descriptor.h +++ b/include/tebako-package-descriptor.h @@ -55,7 +55,7 @@ class package_descriptor { package_descriptor() = delete; // Constructor for deserialization - explicit package_descriptor(const std::vector& buffer); + explicit package_descriptor(const std::vector& buffer); // Constructor from version strings and other parameters package_descriptor(const std::string& ruby_version, const std::string& tebako_version, @@ -63,7 +63,7 @@ class package_descriptor { const std::string& entry_point, const std::optional& cwd); // Serialize the object to a binary format - std::vector serialize() const; + std::vector serialize() const; uint16_t get_ruby_version_major() const { return ruby_version_major; } uint16_t get_ruby_version_minor() const { return ruby_version_minor; } @@ -75,10 +75,10 @@ class package_descriptor { const std::string& get_entry_point() const { return entry_point; } const std::optional& get_cwd() const { return cwd; } - static constexpr bool is_little_endian() + static bool is_little_endian() { uint16_t number = 1; - return *(reinterpret_cast(&number)) == 1; + return *(reinterpret_cast(&number)) == 1; } static uint16_t to_little_endian(uint16_t value) diff --git a/src/tebako-cmdline-helpers.cpp b/src/tebako-cmdline.cpp similarity index 54% rename from src/tebako-cmdline-helpers.cpp rename to src/tebako-cmdline.cpp index 08772a87..e9ee4dc1 100644 --- a/src/tebako-cmdline-helpers.cpp +++ b/src/tebako-cmdline.cpp @@ -36,112 +36,185 @@ #include #include -#include +#include namespace tebako { +void cmdline_args::build_arguments(const char* fs_mount_point, const char* fs_entry_point) +{ + if (fs_mount_point == nullptr || fs_entry_point == nullptr || fs_mount_point[0] == 0 || fs_entry_point[0] == 0) { + throw std::invalid_argument("Internal error: fs_mount_point and fs_entry_point must be non-null and non-empty"); + } + + if (extract) { + build_arguments_for_extract(fs_mount_point); + } + else { + build_arguments_for_run(fs_mount_point, fs_entry_point); + } +} + // build_arguments_for_extract // Builds command line arguments to process --tebako-extract // ruby -e "require 'fileutils'; FileUtils.copy_entry '',argv[2] || 'source_filesystem'" -int build_arguments_for_extract(int* argc, char*** argv, const char* fs_mount_point) +void cmdline_args::build_arguments_for_extract(const char* fs_mount_point) { - int ret = -1; - std::string dest = std::string(((*argc) < 3 ? "source_filesystem" : (*argv)[2])); - std::string cmd = std::string("require 'fileutils'; FileUtils.copy_entry '") + fs_mount_point + "', '" + dest + "'"; - printf("Extracting tebako image to '%s' \n", dest.c_str()); - size_t new_argv_size = 3 + cmd.size() + 1 + strlen((*argv)[0]) + 1; - char** new_argv = new char*[3]; - char* argv_memory = new char[new_argv_size]; - if (new_argv != NULL && argv_memory != NULL) { - strcpy(argv_memory, (*argv)[0]); + std::string cmd = + std::string("require 'fileutils'; FileUtils.copy_entry '") + fs_mount_point + "', '" + extract_folder + "'"; + printf("Extracting tebako image to '%s' \n", extract_folder.c_str()); + size_t new_argv_size = 3 + cmd.size() + 1 + strlen(argv[0]) + 1; + new_argv = new char*[3]; + char* argv_memory = new_argv_memory = new char[new_argv_size]; + if (new_argv != nullptr && argv_memory != nullptr) { + strcpy(argv_memory, argv[0]); new_argv[0] = argv_memory; - argv_memory += (strlen((*argv)[0]) + 1); + argv_memory += (strlen(argv[0]) + 1); strcpy(argv_memory, "-e"); new_argv[1] = argv_memory; argv_memory += 3; strcpy(argv_memory, cmd.c_str()); new_argv[2] = argv_memory; - *argv = new_argv; - (*argc) = 3; - ret = 0; + new_argc = 3; + } + else { + throw std::bad_alloc(); } - return ret; } -std::pair build_arguments(const std::vector& new_argv, - const char* fs_mount_point, - const char* fs_entry_point) +void cmdline_args::build_arguments_for_run(const char* fs_mount_point, const char* fs_entry_point) { - if (fs_mount_point == nullptr || fs_entry_point == nullptr || fs_mount_point[0] == 0 || fs_entry_point[0] == 0) { - throw std::invalid_argument("Internal error: fs_mount_point and fs_entry_point must be non-null and non-empty"); - } - size_t new_argv_size = strlen(fs_mount_point) + strlen(fs_entry_point) + 1; - for (const auto& arg : new_argv) { + for (const auto& arg : other_args) { new_argv_size += (arg.length() + 1); } - char* argv_memory = new char[new_argv_size]; - if (!argv_memory) + char* argv_memory = new_argv_memory = new char[new_argv_size]; + if (!argv_memory) { throw std::bad_alloc(); + } // Build the new argv array - char** new_argv_final = new char*[new_argv.size() + 1]; - memcpy(argv_memory, new_argv[0].c_str(), new_argv[0].length() + 1); - new_argv_final[0] = argv_memory; - argv_memory += (new_argv[0].length() + 1); + new_argv = new char*[other_args.size() + 1]; + memcpy(argv_memory, other_args[0].c_str(), other_args[0].length() + 1); + new_argv[0] = argv_memory; + argv_memory += (other_args[0].length() + 1); // Add tebako fs_mount_point and fs_entry_point memcpy(argv_memory, fs_mount_point, strlen(fs_mount_point)); - new_argv_final[1] = argv_memory; + new_argv[1] = argv_memory; argv_memory += strlen(fs_mount_point); memcpy(argv_memory, fs_entry_point, strlen(fs_entry_point) + 1); argv_memory += (strlen(fs_entry_point) + 1); // Copy remaining arguments - for (size_t i = 1; i < new_argv.size(); i++) { - memcpy(argv_memory, new_argv[i].c_str(), new_argv[i].length() + 1); - new_argv_final[i + 1] = argv_memory; - argv_memory += (new_argv[i].length() + 1); + for (size_t i = 1; i < other_args.size(); i++) { + memcpy(argv_memory, other_args[i].c_str(), other_args[i].length() + 1); + new_argv[i + 1] = argv_memory; + argv_memory += (other_args[i].length() + 1); } - // Return the new argc and argv - return {static_cast(new_argv.size() + 1), new_argv_final}; + new_argc = other_args.size() + 1; } -std::pair, std::vector> parse_arguments(int argc, char** argv) +void cmdline_args::parse_arguments(void) { const std::string error_msg = - "Error: --tebako-mount must be followed by a rule (e.g., --tebako-mount :)"; - std::vector tebako_mount_args; - std::vector other_args; + "Error: --tebako-mount shall be followed by a rule (e.g., --tebako-mount :)"; + + const std::string error_msg_run = + "Error: --tebako-run shall be followed by the application image file name (e.g., --tebako-run=)"; + + const std::string error_msg_run_nodup = "Error: --tebako-run option can be provided only once"; + + const std::string run_key = "--tebako-run"; + const std::string run_key_ex = run_key + "="; + const std::string mount_key = "--tebako-mount"; + const std::string mount_key_ex = mount_key + "="; + const std::string extract_key = "--tebako-extract"; + const std::string extract_key_ex = extract_key + "="; + const std::string extract_dest = "source_filesystem"; for (int i = 0; i < argc; i++) { std::string arg = argv[i]; + // Handle "--tebako-run=value" case + if (arg.rfind(run_key_ex, 0) == 0) { + std::string value = arg.substr(13); + + if (!value.empty()) { + if (run) { + throw std::invalid_argument(error_msg_run_nodup); + } + run = true; + app_image = value; + continue; + } + else { + throw std::invalid_argument(error_msg_run); + } + } + // Handle "--tebako-mount=value" case - if (arg.rfind("--tebako-mount=", 0) == 0) { // Check if it starts with "--tebako-mount=" - std::string value = arg.substr(15); // Extract the part after "--tebako-mount=" + if (arg.rfind(mount_key_ex, 0) == 0) { + std::string value = arg.substr(15); if (!value.empty()) { - tebako_mount_args.push_back(value); // Add the value part to tebako_mount_args + mountpoints.push_back(value); continue; } else { - throw std::invalid_argument(error_msg); // Handle case where value is empty + throw std::invalid_argument(error_msg); } } + // Handle "--tebako-extract=value" case + if (arg.rfind(extract_key_ex, 0) == 0) { + extract = true; + std::string value = arg.substr(17); + + if (!value.empty()) { + extract_folder = value; + } + else { + extract_folder = extract_dest; + } + return; + } + + // Handle "--tebako-run" without '=' + if (arg == run_key) { + if (run) { + throw std::invalid_argument(error_msg_run_nodup); + } + run = true; + // Ensure there is a next argument + if (i + 1 < argc) { + std::string next_arg = argv[i + 1]; + + // Check if the next argument is valid + if (next_arg[0] != '-') { // It's not a flag + app_image = next_arg; + i += 1; // Skip the next argument as it is the rule + continue; + } + else { + throw std::invalid_argument(error_msg_run); + } + } + // If "--tebako-mount" is at the end of args without a rule, raise an error + throw std::invalid_argument(error_msg_run); + } + // Handle "--tebako-mount" without '=' - if (arg == "--tebako-mount") { + if (arg == mount_key) { // Ensure there is a next argument if (i + 1 < argc) { std::string next_arg = argv[i + 1]; // Check if the next argument is valid if (next_arg[0] != '-') { // It's not a flag - tebako_mount_args.push_back(next_arg); + mountpoints.push_back(next_arg); i += 1; // Skip the next argument as it is the rule continue; } @@ -149,19 +222,37 @@ std::pair, std::vector> parse_arguments(in throw std::invalid_argument(error_msg); } } - // If "--tebako-mount" is at the end of args without a rule, raise an error throw std::invalid_argument(error_msg); } + // Handle "--tebako-extract" without '=' + if (arg.rfind(extract_key, 0) == 0) { + extract = true; + if (i + 1 < argc) { + std::string next_arg = argv[i + 1]; + + // Check if the next argument is valid + if (next_arg[0] != '-') { // It's not a flag + extract_folder = next_arg; + i += 1; + } + else { + extract_folder = extract_dest; + } + } + else { + extract_folder = extract_dest; + } + return; + } + // Add other arguments as they are other_args.push_back(arg); } - - return std::make_pair(tebako_mount_args, other_args); } -void process_mountpoints(const std::vector& mountpoints) +void cmdline_args::process_mountpoints() { for (const auto& item : mountpoints) { // Split item by the first ':' or '>' @@ -184,7 +275,7 @@ void process_mountpoints(const std::vector& mountpoints) // Check that both filename and target are not empty if (filename.empty() || target.empty()) { - throw std::invalid_argument("Invalid input: path or filename or terget is empty in " + item); + throw std::invalid_argument("Invalid input: path or filename or target is empty in " + item); } struct stat st; @@ -227,4 +318,24 @@ void process_mountpoints(const std::vector& mountpoints) } } } + +void cmdline_args::process_package() +{ + std::ifstream file(app_image, std::ios::binary | std::ios::ate); + if (!file) { + throw std::invalid_argument("Path " + app_image + " does not exist"); + } + size_t size = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector buffer; + buffer.resize(size); + + if (!file.read(buffer.data(), size)) { + throw std::invalid_argument("Failed to load filesystem image from " + app_image); + } + + package = package_descriptor(buffer); +} + } // namespace tebako diff --git a/src/tebako-package-descriptor.cpp b/src/tebako-package-descriptor.cpp index 8481562a..3b53be2a 100644 --- a/src/tebako-package-descriptor.cpp +++ b/src/tebako-package-descriptor.cpp @@ -35,7 +35,7 @@ namespace tebako { const char* package_descriptor::signature = "TAMATEBAKO"; // Constructor for deserialization -package_descriptor::package_descriptor(const std::vector& buffer) +package_descriptor::package_descriptor(const std::vector& buffer) { size_t offset = 0; @@ -81,7 +81,7 @@ package_descriptor::package_descriptor(const std::vector& buffer) read_from_buffer(entry_point.data(), entry_point_size); // Read cwd presence flag and content if present - uint8_t cwd_present; + char cwd_present; read_from_buffer(&cwd_present, sizeof(cwd_present)); if (cwd_present) { uint16_t cwd_size; @@ -119,11 +119,11 @@ package_descriptor::package_descriptor(const std::string& ruby_version, } // Serialize the object to a binary format -std::vector package_descriptor::serialize() const +std::vector package_descriptor::serialize() const { - std::vector buffer; + std::vector buffer; auto append_to_buffer = [&buffer](const void* data, size_t size) { - const uint8_t* bytes = static_cast(data); + const char* bytes = static_cast(data); buffer.insert(buffer.end(), bytes, bytes + size); }; @@ -148,7 +148,7 @@ std::vector package_descriptor::serialize() const append_to_buffer(entry_point.data(), entry_point_size); // Append cwd presence flag and content if present - uint8_t cwd_present = cwd.has_value() ? 1 : 0; + char cwd_present = cwd.has_value() ? 1 : 0; append_to_buffer(&cwd_present, sizeof(cwd_present)); if (cwd) { uint16_t cwd_size = static_cast(cwd->size()); diff --git a/tests/package_descriptor/pd b/tests/package_descriptor/pd new file mode 100644 index 00000000..ad849268 Binary files /dev/null and b/tests/package_descriptor/pd differ diff --git a/tests/resources/tebako-fs.cpp.in b/tests/resources/tebako-fs.cpp.in index d2d45d8c..d921179e 100644 --- a/tests/resources/tebako-fs.cpp.in +++ b/tests/resources/tebako-fs.cpp.in @@ -44,7 +44,7 @@ const char * tests_outside_dir(void) { } const char * tests_the_other_memfs_image(void) { - return "@TESTS_THE_OTHER_MEMFS_IMAGE@"; + return "@DATA_BIN_FILE_2@"; } #ifdef __cplusplus diff --git a/tests/tests-cmdline-helpers.cpp b/tests/tests-cmdline-helpers.cpp deleted file mode 100644 index df7f5086..00000000 --- a/tests/tests-cmdline-helpers.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/** - * - * Copyright (c) 2024, [Ribose Inc](https://www.ribose.com). - * All rights reserved. - * This file is a part of tebako (libdwarfs-wr) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "tests.h" -#include - -namespace tebako { - -// Test case 1: Basic case with no additional arguments -TEST(ExtractCmdlineTest, no_additional_args) -{ - int argc = 1; - - char** p; - char** argv = p = new char*[2]; - char* pp[1]; - - argv[0] = pp[0] = strdup("program_name"); - argv[1] = nullptr; - - const char* fs_mount_point = "/mnt/point"; - - int result = build_arguments_for_extract(&argc, &argv, fs_mount_point); - - EXPECT_EQ(result, 0); - EXPECT_EQ(argc, 3); - EXPECT_STREQ(argv[0], "program_name"); - EXPECT_STREQ(argv[1], "-e"); - EXPECT_STREQ(argv[2], "require 'fileutils'; FileUtils.copy_entry '/mnt/point', 'source_filesystem'"); - - free(pp[0]); - delete[] p; - - delete[] argv[0]; - delete[] argv; -} - -// Test case 2: With a custom destination argument -TEST(ExtractCmdlineTest, custom_destination_arg) -{ - int argc = 3; - - char** p; - char** argv = p = new char*[4]; - char* pp[3]; - - argv[0] = pp[0] = strdup("program_name"); - argv[1] = pp[1] = strdup("--tebako-extract"); - argv[2] = pp[2] = strdup("custom_dest"); - argv[3] = nullptr; - - const char* fs_mount_point = "/mnt/point"; - - int result = build_arguments_for_extract(&argc, &argv, fs_mount_point); - - EXPECT_EQ(result, 0); - EXPECT_EQ(argc, 3); - EXPECT_STREQ(argv[0], "program_name"); - EXPECT_STREQ(argv[1], "-e"); - EXPECT_STREQ(argv[2], "require 'fileutils'; FileUtils.copy_entry '/mnt/point', 'custom_dest'"); - - free(pp[0]); - free(pp[1]); - free(pp[2]); - delete[] p; - - delete[] argv[0]; - delete[] argv; -} - -// Test case 3: Long fs_mount_point input -TEST(ExtractCmdlineTest, long_fs_mount_point) -{ - int argc = 1; - - char** p; - char** argv = p = new char*[2]; - char* pp[1]; - - argv[0] = pp[0] = strdup("program_name"); - argv[1] = nullptr; - - std::string long_fs_mount_point(5000, 'a'); // Very long string - - int result = build_arguments_for_extract(&argc, &argv, long_fs_mount_point.c_str()); - - EXPECT_EQ(result, 0); - EXPECT_EQ(argc, 3); - EXPECT_STREQ(argv[0], "program_name"); - EXPECT_STREQ(argv[1], "-e"); - std::string expected_cmd = - "require 'fileutils'; FileUtils.copy_entry '" + long_fs_mount_point + "', 'source_filesystem'"; - EXPECT_STREQ(argv[2], expected_cmd.c_str()); - - free(pp[0]); - delete[] p; - - delete[] argv[0]; - delete[] argv; -} - -TEST(ProcessArgumentsTest, handles_mount_rule) -{ - const int argc = 6; - const char* argv[argc] = {"program", "--tebako-mount", "d1:m1", "--tebako-mount", "d2:m2", "other"}; - - auto [tebako_mount_args, other_args] = parse_arguments(argc, const_cast(argv)); - - // Check that we correctly extracted the tebako-mount rules - EXPECT_EQ(tebako_mount_args.size(), 2); - EXPECT_EQ(tebako_mount_args[0], "d1:m1"); - EXPECT_EQ(tebako_mount_args[1], "d2:m2"); - - // Check that we correctly placed other arguments - EXPECT_EQ(other_args.size(), 2); - EXPECT_EQ(other_args[0], "program"); - EXPECT_EQ(other_args[1], "other"); -} - -TEST(ProcessArgumentsTest, handles_mount_rule_with_eq) -{ - const int argc = 5; - const char* argv[argc] = {"program", "--tebako-mount=d1:m1", "--tebako-mount", "d2:m2", "other"}; - - auto [tebako_mount_args, other_args] = parse_arguments(argc, const_cast(argv)); - - // Check that we correctly extracted the tebako-mount rules - EXPECT_EQ(tebako_mount_args.size(), 2); - EXPECT_EQ(tebako_mount_args[0], "d1:m1"); - EXPECT_EQ(tebako_mount_args[1], "d2:m2"); - - // Check that we correctly placed other arguments - EXPECT_EQ(other_args.size(), 2); - EXPECT_EQ(other_args[0], "program"); - EXPECT_EQ(other_args[1], "other"); -} - -TEST(ProcessArgumentsTest, throws_error_on_missing_rule) -{ - const int argc = 2; - const char* argv[argc] = {"program", "--tebako-mount"}; - - // Expect an exception due to missing rule - EXPECT_THROW(parse_arguments(argc, const_cast(argv)), std::invalid_argument); -} - -TEST(ProcessArgumentsTest, throws_error_on_missing_rule_after_eq) -{ - const int argc = 2; - const char* argv[argc] = {"program", "--tebako-mount="}; - - // Expect an exception due to missing rule - EXPECT_THROW(parse_arguments(argc, const_cast(argv)), std::invalid_argument); -} - -TEST(ProcessArgumentsTest, throws_error_on_option_in_place_of_rule) -{ - const int argc = 3; - const char* argv[argc] = {"program", "--tebako-mount", "--option"}; - - // Expect an exception due to missing rule after "=" - EXPECT_THROW(parse_arguments(argc, const_cast(argv)), std::invalid_argument); -} - -TEST(ProcessArgumentsTest, throws_error_on_on_no_rule) -{ - const int argc = 2; - const char* argv[argc] = {"program", "--tebako-mount"}; - - // Expect an exception due to missing rule after "=" - EXPECT_THROW(parse_arguments(argc, const_cast(argv)), std::invalid_argument); -} - -TEST(ProcessArgumentsTest, handles_no_tebako_mount_argument) -{ - const int argc = 3; - const char* argv[argc] = {"program", "arg1", "arg2"}; - - auto [tebako_mount_args, other_args] = parse_arguments(argc, const_cast(argv)); - - // No tebako-mount arguments should be found - EXPECT_TRUE(tebako_mount_args.empty()); - - // Check that all arguments were placed in other_args - EXPECT_EQ(other_args.size(), 3); - EXPECT_EQ(other_args[0], "program"); - EXPECT_EQ(other_args[1], "arg1"); - EXPECT_EQ(other_args[2], "arg2"); -} - -// Helper function to clean up argv memory -static void clean_up_argv(int argc, char** argv) -{ - if (argv) { - delete[] argv[0]; // This deletes the memory block used for all strings - delete[] argv; // This deletes the array of char pointers - } -} - -// Test Case 1: Basic Case -TEST(BuildArgumentsTest, basic_case) -{ - std::vector new_argv = {"program", "arg1", "arg2"}; - const char* fs_mount_point = "/mnt"; - const char* fs_entry_point = "/local/entry"; - - auto [argc, argv] = build_arguments(new_argv, fs_mount_point, fs_entry_point); - - EXPECT_EQ(argc, 4); - EXPECT_STREQ(argv[0], "program"); - EXPECT_STREQ(argv[1], "/mnt/local/entry"); - EXPECT_STREQ(argv[2], "arg1"); - EXPECT_STREQ(argv[3], "arg2"); - - clean_up_argv(argc, argv); -} - -// Test Case 2: No Arguments -TEST(BuildArgumentsTest, no_arguments) -{ - std::vector new_argv = {"program"}; - const char* fs_mount_point = "/mnt"; - const char* fs_entry_point = "/local/entry"; - - auto [argc, argv] = build_arguments(new_argv, fs_mount_point, fs_entry_point); - - EXPECT_EQ(argc, 2); - EXPECT_STREQ(argv[0], "program"); - EXPECT_STREQ(argv[1], "/mnt/local/entry"); - - clean_up_argv(argc, argv); -} - -// Test Case 3: Long Strings -TEST(BuildArgumentsTest, long_strings) -{ - std::vector new_argv = {"program", std::string(1000, 'a'), std::string(2000, 'b')}; - const char* fs_mount_point = "/mnt"; - const char* fs_entry_point = "/local/entry"; - - auto [argc, argv] = build_arguments(new_argv, fs_mount_point, fs_entry_point); - - EXPECT_EQ(argc, 4); - EXPECT_STREQ(argv[0], "program"); - EXPECT_STREQ(argv[1], "/mnt/local/entry"); - EXPECT_EQ(std::string(argv[2]), std::string(1000, 'a')); - EXPECT_EQ(std::string(argv[3]), std::string(2000, 'b')); - - clean_up_argv(argc, argv); -} - -// Test Case 4: Special Characters -TEST(BuildArgumentsTest, special_characters) -{ - std::vector new_argv = {"program", "arg 1", "arg\t2", "arg@3"}; - const char* fs_mount_point = "/mnt"; - const char* fs_entry_point = "/local/entry"; - - auto [argc, argv] = build_arguments(new_argv, fs_mount_point, fs_entry_point); - - EXPECT_EQ(argc, 5); - EXPECT_STREQ(argv[0], "program"); - EXPECT_STREQ(argv[1], "/mnt/local/entry"); - EXPECT_STREQ(argv[2], "arg 1"); - EXPECT_STREQ(argv[3], "arg\t2"); - EXPECT_STREQ(argv[4], "arg@3"); - - clean_up_argv(argc, argv); -} - -// Test Case 5: Empty Mount Point and Entry Point -TEST(BuildArgumentsTest, empty_mount_or_entry_points) -{ - std::vector new_argv = {"program", "arg1"}; - const char* fs_mount_point = "/mnt"; - const char* fs_entry_point = "entry"; - - EXPECT_THROW(build_arguments(new_argv, "", fs_entry_point), std::invalid_argument); - ; - EXPECT_THROW(build_arguments(new_argv, fs_mount_point, ""), std::invalid_argument); - ; -} - -// Test Case 6: Null Mount or Entry Point -TEST(BuildArgumentsTest, null_mount_or_entry_points) -{ - std::vector new_argv = {"program", "arg1"}; - - EXPECT_THROW(build_arguments(new_argv, nullptr, "/entry"), std::invalid_argument); - EXPECT_THROW(build_arguments(new_argv, "/mnt", nullptr), std::invalid_argument); -} - -} // namespace tebako \ No newline at end of file diff --git a/tests/tests-cmdline.cpp b/tests/tests-cmdline.cpp new file mode 100644 index 00000000..72f5bbfa --- /dev/null +++ b/tests/tests-cmdline.cpp @@ -0,0 +1,497 @@ +/** + * + * Copyright (c) 2024, [Ribose Inc](https://www.ribose.com). + * All rights reserved. + * This file is a part of tebako (libdwarfs-wr) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "tests.h" +#include + +namespace tebako { + +// Test case 1: Basic case with no additional arguments +TEST(CmdlineArgsTest, extract_no_additional_args) +{ + const int argc = 2; + const char* argv[argc] = {"program_name", "--tebako-extract"}; + const char* fs_mount_point = "/mnt/point"; + + cmdline_args args(argc, argv); + args.parse_arguments(); + args.build_arguments_for_extract(fs_mount_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 3); + EXPECT_STREQ(new_argv[0], "program_name"); + EXPECT_STREQ(new_argv[1], "-e"); + EXPECT_STREQ(new_argv[2], "require 'fileutils'; FileUtils.copy_entry '/mnt/point', 'source_filesystem'"); +} + +TEST(CmdlineArgsTest, extract_no_additional_args_eq_case_1) +{ + const int argc = 2; + const char* argv[argc] = {"program_name", "--tebako-extract="}; + const char* fs_mount_point = "/mnt/point"; + + cmdline_args args(argc, argv); + args.parse_arguments(); + args.build_arguments_for_extract(fs_mount_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 3); + EXPECT_STREQ(new_argv[0], "program_name"); + EXPECT_STREQ(new_argv[1], "-e"); + EXPECT_STREQ(new_argv[2], "require 'fileutils'; FileUtils.copy_entry '/mnt/point', 'source_filesystem'"); +} + +TEST(CmdlineArgsTest, extract_no_additional_args_eq_case_2) +{ + const int argc = 3; + const char* argv[argc] = {"program_name", "--tebako-extract=", "--other-arg"}; + const char* fs_mount_point = "/mnt/point"; + + cmdline_args args(argc, argv); + args.parse_arguments(); + args.build_arguments_for_extract(fs_mount_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 3); + EXPECT_STREQ(new_argv[0], "program_name"); + EXPECT_STREQ(new_argv[1], "-e"); + EXPECT_STREQ(new_argv[2], "require 'fileutils'; FileUtils.copy_entry '/mnt/point', 'source_filesystem'"); +} + +// Test case 2: With a custom destination argument +TEST(CmdlineArgsTest, extract_custom_destination_arg) +{ + const int argc = 3; + const char* argv[argc] = {"program_name", "--tebako-extract", "custom_dest"}; + + const char* fs_mount_point = "/mnt/point"; + + cmdline_args args(argc, argv); + args.parse_arguments(); + args.build_arguments_for_extract(fs_mount_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 3); + EXPECT_STREQ(new_argv[0], "program_name"); + EXPECT_STREQ(new_argv[1], "-e"); + EXPECT_STREQ(new_argv[2], "require 'fileutils'; FileUtils.copy_entry '/mnt/point', 'custom_dest'"); +} + +TEST(CmdlineArgsTest, extract_custom_destination_arg_eq) +{ + const int argc = 2; + const char* argv[argc] = {"program_name", "--tebako-extract=custom_dest_other"}; + + const char* fs_mount_point = "/mnt/point"; + + cmdline_args args(argc, argv); + args.parse_arguments(); + args.build_arguments(fs_mount_point, "/local/test.rb"); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 3); + EXPECT_STREQ(new_argv[0], "program_name"); + EXPECT_STREQ(new_argv[1], "-e"); + EXPECT_STREQ(new_argv[2], "require 'fileutils'; FileUtils.copy_entry '/mnt/point', 'custom_dest_other'"); +} + +// Test case 3: Long fs_mount_point input +TEST(CmdlineArgsTest, extract_long_fs_mount_point) +{ + const int argc = 2; + const char* argv[argc] = {"program_name", "--tebako-extract"}; + + std::string long_fs_mount_point(5000, 'a'); // Very long string + + cmdline_args args(argc, argv); + args.parse_arguments(); + args.build_arguments_for_extract(long_fs_mount_point.c_str()); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 3); + EXPECT_STREQ(new_argv[0], "program_name"); + EXPECT_STREQ(new_argv[1], "-e"); + std::string expected_cmd = + "require 'fileutils'; FileUtils.copy_entry '" + long_fs_mount_point + "', 'source_filesystem'"; + EXPECT_STREQ(new_argv[2], expected_cmd.c_str()); +} + +TEST(CmdlineArgsTest, run_rule) +{ + const int argc = 6; + const char* argv[argc] = {"program", "--tebako-run", "d1", "--tebako-mount", "d2:m2", "other"}; + + cmdline_args args(argc, argv); + + args.parse_arguments(); + auto other_args = args.get_args(); + auto mountpoints = args.get_mountpoints(); + + EXPECT_TRUE(args.with_application()); + EXPECT_EQ(args.get_application_image(), "d1"); + + // Check that we correctly extracted the tebako-mount rules + EXPECT_EQ(mountpoints.size(), 1); + EXPECT_EQ(mountpoints[0], "d2:m2"); + + // Check that we correctly placed other arguments + EXPECT_EQ(other_args.size(), 2); + EXPECT_EQ(other_args[0], "program"); + EXPECT_EQ(other_args[1], "other"); +} + +TEST(CmdlineArgsTest, run_rule_eq) +{ + const int argc = 5; + const char* argv[argc] = {"program", "--tebako-mount", "d2:m2", "other", "--tebako-run=d100"}; + + cmdline_args args(argc, argv); + + args.parse_arguments(); + auto other_args = args.get_args(); + auto mountpoints = args.get_mountpoints(); + + EXPECT_TRUE(args.with_application()); + EXPECT_EQ(args.get_application_image(), "d100"); + + // Check that we correctly extracted the tebako-mount rules + EXPECT_EQ(mountpoints.size(), 1); + EXPECT_EQ(mountpoints[0], "d2:m2"); + + // Check that we correctly placed other arguments + EXPECT_EQ(other_args.size(), 2); + EXPECT_EQ(other_args[0], "program"); + EXPECT_EQ(other_args[1], "other"); +} + +TEST(CmdlineArgsTest, run_rule_error_on_missing_rule_case_1) +{ + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-run"}; + cmdline_args args(argc, argv); + + // Expect an exception due to missing rule + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, run_rule_error_on_missing_rule_case_2) +{ + const int argc = 3; + const char* argv[argc] = {"program", "--tebako-run", "--other"}; + cmdline_args args(argc, argv); + + // Expect an exception due to missing rule + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, run_rule_error_on_missing_rule_case_3) +{ + const int argc = 3; + const char* argv[argc] = {"program", "--tebako-run=", "other"}; + cmdline_args args(argc, argv); + + // Expect an exception due to missing rule + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, run_rule_error_on_duplicate_rule) +{ + const int argc = 4; + const char* argv[argc] = {"program", "--tebako-run=d1", "--tebako-run=d2", "other"}; + cmdline_args args(argc, argv); + + // Expect an exception due to duplicate rule + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, mount_rule) +{ + const int argc = 6; + const char* argv[argc] = {"program", "--tebako-mount", "d1:m1", "--tebako-mount", "d2:m2", "other"}; + + cmdline_args args(argc, argv); + + args.parse_arguments(); + auto other_args = args.get_args(); + auto mountpoints = args.get_mountpoints(); + + // Check that we correctly extracted the tebako-mount rules + EXPECT_EQ(mountpoints.size(), 2); + EXPECT_EQ(mountpoints[0], "d1:m1"); + EXPECT_EQ(mountpoints[1], "d2:m2"); + + // Check that we correctly placed other arguments + EXPECT_EQ(other_args.size(), 2); + EXPECT_EQ(other_args[0], "program"); + EXPECT_EQ(other_args[1], "other"); +} + +TEST(CmdlineArgsTest, mount_rule_with_eq) +{ + const int argc = 5; + const char* argv[argc] = {"program", "--tebako-mount=d1:m1", "--tebako-mount", "d2:m2", "other"}; + cmdline_args args(argc, argv); + + args.parse_arguments(); + auto other_args = args.get_args(); + auto mountpoints = args.get_mountpoints(); + + // Check that we correctly extracted the tebako-mount rules + EXPECT_EQ(mountpoints.size(), 2); + EXPECT_EQ(mountpoints[0], "d1:m1"); + EXPECT_EQ(mountpoints[1], "d2:m2"); + + // Check that we correctly placed other arguments + EXPECT_EQ(other_args.size(), 2); + EXPECT_EQ(other_args[0], "program"); + EXPECT_EQ(other_args[1], "other"); +} + +TEST(CmdlineArgsTest, mount_throws_error_on_missing_rule) +{ + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount"}; + cmdline_args args(argc, argv); + + // Expect an exception due to missing rule + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, mount_throws_error_on_missing_rule_after_eq) +{ + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount="}; + cmdline_args args(argc, argv); + + // Expect an exception due to missing rule + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, mount_throws_error_on_option_in_place_of_rule) +{ + const int argc = 3; + const char* argv[argc] = {"program", "--tebako-mount", "--option"}; + cmdline_args args(argc, argv); + + // Expect an exception due to missing rule after "=" + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, mount_throws_error_on_on_no_rule) +{ + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount"}; + cmdline_args args(argc, argv); + + // Expect an exception due to missing rule after "=" + EXPECT_THROW(args.parse_arguments(), std::invalid_argument); +} + +TEST(CmdlineArgsTest, mount_handles_no_tebako_mount_argument) +{ + const int argc = 3; + const char* argv[argc] = {"program", "arg1", "arg2"}; + cmdline_args args(argc, argv); + + args.parse_arguments(); + auto other_args = args.get_args(); + + // No tebako-mount arguments should be found + EXPECT_TRUE(args.get_mountpoints().empty()); + + // Check that all arguments were placed in other_args + EXPECT_EQ(other_args.size(), 3); + EXPECT_EQ(other_args[0], "program"); + EXPECT_EQ(other_args[1], "arg1"); + EXPECT_EQ(other_args[2], "arg2"); +} + +// Test Case 1: Basic Case +TEST(CmdlineArgsTest, mount_basic_case) +{ + const int argc = 3; + const char* argv[argc] = {"program", "arg1", "arg2"}; + cmdline_args args(argc, argv); + + const char* fs_mount_point = "/mnt"; + const char* fs_entry_point = "/local/entry"; + + args.parse_arguments(); + args.build_arguments(fs_mount_point, fs_entry_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 4); + EXPECT_STREQ(new_argv[0], "program"); + EXPECT_STREQ(new_argv[1], "/mnt/local/entry"); + EXPECT_STREQ(new_argv[2], "arg1"); + EXPECT_STREQ(new_argv[3], "arg2"); +} + +// Test Case 2: No Arguments +TEST(CmdlineArgsTest, mount_no_arguments) +{ + const int argc = 1; + const char* argv[argc] = {"program"}; + + cmdline_args args(argc, argv); + + const char* fs_mount_point = "/mnt"; + const char* fs_entry_point = "/local/entry"; + + args.parse_arguments(); + args.build_arguments(fs_mount_point, fs_entry_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 2); + EXPECT_STREQ(new_argv[0], "program"); + EXPECT_STREQ(new_argv[1], "/mnt/local/entry"); +} + +// Test Case 3: Long Strings +TEST(CmdlineArgsTest, mount_long_strings) +{ + std::string arg2 = std::string(1000, 'a'), arg3 = std::string(2000, 'b'); + + const int argc = 3; + const char* argv[argc] = {"program", arg2.c_str(), arg3.c_str()}; + + cmdline_args args(argc, argv); + + const char* fs_mount_point = "/mnt"; + const char* fs_entry_point = "/local/entry"; + + args.parse_arguments(); + args.build_arguments(fs_mount_point, fs_entry_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 4); + EXPECT_STREQ(new_argv[0], "program"); + EXPECT_STREQ(new_argv[1], "/mnt/local/entry"); + EXPECT_EQ(std::string(new_argv[2]), std::string(1000, 'a')); + EXPECT_EQ(std::string(new_argv[3]), std::string(2000, 'b')); +} + +// Test Case 4: Special Characters +TEST(CmdlineArgsTest, mount_special_characters) +{ + const int argc = 4; + const char* argv[argc] = {"program", "arg 1", "arg\t2", "arg@3"}; + + cmdline_args args(argc, argv); + + const char* fs_mount_point = "/mnt"; + const char* fs_entry_point = "/lcal/entry"; + + args.parse_arguments(); + args.build_arguments(fs_mount_point, fs_entry_point); + + int new_argc = args.get_argc(); + char** new_argv = args.get_argv(); + + EXPECT_EQ(new_argc, 5); + EXPECT_STREQ(new_argv[0], "program"); + EXPECT_STREQ(new_argv[1], "/mnt/lcal/entry"); + EXPECT_STREQ(new_argv[2], "arg 1"); + EXPECT_STREQ(new_argv[3], "arg\t2"); + EXPECT_STREQ(new_argv[4], "arg@3"); +} + +// Test Case 5: Empty Mount Point and Entry Point +TEST(CmdlineArgsTest, mount_empty_mount_or_entry_points) +{ + const int argc = 2; + const char* argv[argc] = {"program", "arg1"}; + cmdline_args args(argc, argv); + + args.parse_arguments(); + + EXPECT_THROW(args.build_arguments("", "/lcal/entry"), std::invalid_argument); + EXPECT_THROW(args.build_arguments("/mnt", ""), std::invalid_argument); +} + +// Test Case 6: Null Mount or Entry Point +TEST(CmdlineArgsTest, mount_null_mount_or_entry_points) +{ + const int argc = 2; + const char* argv[argc] = {"program", "arg1"}; + cmdline_args args(argc, argv); + + args.parse_arguments(); + + EXPECT_THROW(args.build_arguments(nullptr, "/entry"), std::invalid_argument); + EXPECT_THROW(args.build_arguments("/mnt", nullptr), std::invalid_argument); +} + +TEST(CmdlineArgsTest, process_package_descriptor) +{ + const int argc = 2; + std::string pkg = std::string("--tebako-run=") + tests_the_other_memfs_image(); + const char* argv[argc] = {"program", pkg.c_str()}; + cmdline_args args(argc, argv); + + args.parse_arguments(); + + EXPECT_TRUE(args.with_application()); + EXPECT_EQ(args.get_application_image(), tests_the_other_memfs_image()); + + args.process_package(); + + auto package = args.get_package(); + + EXPECT_TRUE(package.has_value()); + EXPECT_EQ(package->get_ruby_version_major(), 3); + EXPECT_EQ(package->get_ruby_version_minor(), 2); + EXPECT_EQ(package->get_ruby_version_patch(), 5); + EXPECT_EQ(package->get_tebako_version_major(), 0); + EXPECT_EQ(package->get_tebako_version_minor(), 10); + EXPECT_EQ(package->get_tebako_version_patch(), 1); + EXPECT_EQ(package->get_mount_point(), "/__tebako_memfs__"); + EXPECT_EQ(package->get_entry_point(), "/local/tebako-test-run.rb"); + EXPECT_FALSE(package->get_cwd().has_value()); +} + +} // namespace tebako \ No newline at end of file diff --git a/tests/tests-package-descriptor.cpp b/tests/tests-package-descriptor.cpp index 14905633..691a25be 100644 --- a/tests/tests-package-descriptor.cpp +++ b/tests/tests-package-descriptor.cpp @@ -35,21 +35,21 @@ namespace tebako { TEST(PackageDescriptorTest, construct_from_buffer) { // Create a buffer with serialized data in big-endian format - std::vector buffer = {'T', 'A', 'M', 'A', 'T', 'E', 'B', 'A', 'K', 'O', // signature - - 0x01, 0x00, // ruby_version_major - 0x02, 0x00, // ruby_version_minor - 0x03, 0x00, // ruby_version_patch - 0x04, 0x00, // tebako_version_major - 0x05, 0x00, // tebako_version_minor - 0x06, 0x00, // tebako_version_patch - // mount_point - 0x04, 0x00, 'm', 'n', 't', '/', - // entry_point - 0x07, 0x00, 'm', 'a', 'i', 'n', '.', 'r', 'b', - // cwd (optional indicator + value) - 0x01, // cwd present indicator - 0x0A, 0x00, '/', 'h', 'o', 'm', 'e', '/', 'u', 's', 'e', 'r'}; + std::vector buffer = {'T', 'A', 'M', 'A', 'T', 'E', 'B', 'A', 'K', 'O', // signature + + 0x01, 0x00, // ruby_version_major + 0x02, 0x00, // ruby_version_minor + 0x03, 0x00, // ruby_version_patch + 0x04, 0x00, // tebako_version_major + 0x05, 0x00, // tebako_version_minor + 0x06, 0x00, // tebako_version_patch + // mount_point + 0x04, 0x00, 'm', 'n', 't', '/', + // entry_point + 0x07, 0x00, 'm', 'a', 'i', 'n', '.', 'r', 'b', + // cwd (optional indicator + value) + 0x01, // cwd present indicator + 0x0A, 0x00, '/', 'h', 'o', 'm', 'e', '/', 'u', 's', 'e', 'r'}; // Convert the buffer to little-endian if necessary for (size_t i = 0; i < 6; ++i) { @@ -85,7 +85,7 @@ TEST(PackageDescriptorTest, construct_from_buffer) TEST(PackageDescriptorTest, construct_from_buffer_no_cwd) { // Create a buffer with serialized data in big-endian format - std::vector buffer = { + std::vector buffer = { 'T', 'A', 'M', 'A', 'T', 'E', 'B', 'A', 'K', 'O', // signature 0x01, 0x00, // ruby_version_major @@ -142,11 +142,11 @@ TEST(PackageDescriptorTest, serialize) package_descriptor pd(ruby_version, tebako_version, mount_point, entry_point, cwd); // Serialize the object - std::vector buffer = pd.serialize(); + std::vector buffer = pd.serialize(); // Verify the serialized data size_t expected_size = std::strlen(package_descriptor::signature) + 9 * sizeof(uint16_t) + mount_point.size() + - entry_point.size() + sizeof(uint8_t) + cwd->size(); + entry_point.size() + sizeof(char) + cwd->size(); EXPECT_EQ(buffer.size(), expected_size); // Verify the signature @@ -194,11 +194,11 @@ TEST(PackageDescriptorTest, serialize_no_cwd) package_descriptor pd(ruby_version, tebako_version, mount_point, entry_point, cwd); // Serialize the object - std::vector buffer = pd.serialize(); + std::vector buffer = pd.serialize(); // Verify the serialized data size_t expected_size = std::strlen(package_descriptor::signature) + 8 * sizeof(uint16_t) + mount_point.size() + - entry_point.size() + sizeof(uint8_t); + entry_point.size() + sizeof(char); EXPECT_EQ(buffer.size(), expected_size); // Verify the signature @@ -306,7 +306,7 @@ TEST(PackageDescriptorTest, construct_from_invalid_tebako_version) TEST(PackageDescriptorTest, deserialize_small_buffer_fixed_fields) { // Create a buffer that is too small for fixed-size fields - std::vector buffer = { + std::vector buffer = { 'T', 'A', 'M', 'A', 'T', 'E', 'B', 'A', 'K', 'O', // signature 0x00, 0x01, // ruby_version_major @@ -321,7 +321,7 @@ TEST(PackageDescriptorTest, deserialize_small_buffer_fixed_fields) TEST(PackageDescriptorTest, deserialize_small_buffer_mount_point) { // Create a buffer that is too small for mount_point - std::vector buffer = { + std::vector buffer = { 'T', 'A', 'M', 'A', 'T', 'E', 'B', 'A', 'K', 'O', // signature 0x00, 0x01, // ruby_version_major @@ -341,7 +341,7 @@ TEST(PackageDescriptorTest, deserialize_small_buffer_mount_point) TEST(PackageDescriptorTest, deserialize_small_buffer_entry_point) { // Create a buffer that is too small for entry_point - std::vector buffer = { + std::vector buffer = { 'T', 'A', 'M', 'A', 'T', 'E', 'B', 'A', 'K', 'O', // signature 0x00, 0x01, // ruby_version_major @@ -362,7 +362,7 @@ TEST(PackageDescriptorTest, deserialize_small_buffer_entry_point) TEST(PackageDescriptorTest, deserialize_small_buffer_cwd) { // Create a buffer that is too small for cwd - std::vector buffer = { + std::vector buffer = { 'T', 'A', 'M', 'A', 'T', 'E', 'B', 'A', 'K', 'O', // signature 0x00, 0x01, // ruby_version_major @@ -386,7 +386,7 @@ TEST(PackageDescriptorTest, deserialize_small_buffer_cwd) TEST(PackageDescriptorTest, construct_from_buffer_wrong_signature) { // Create a buffer with serialized data in big-endian format - std::vector buffer = { + std::vector buffer = { 'T', 'A', 'M', 'A', 'T', 'E', 'V', 'A', 'K', 'O', // signature 0x01, 0x00, // ruby_version_major diff --git a/tests/tests-process-mps.cpp b/tests/tests-process-mps.cpp index cfd6b16b..d16e276f 100644 --- a/tests/tests-process-mps.cpp +++ b/tests/tests-process-mps.cpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include namespace tebako { @@ -95,43 +95,67 @@ uint32_t ProcessMountpointsTest::test_root_ino, ProcessMountpointsTest::test_dir // Test: Valid mount point TEST_F(ProcessMountpointsTest, valid_mountpoint) { - std::vector mountpoints = {"directory-1/mpoint:/tmp/local_target"}; - EXPECT_NO_THROW(process_mountpoints(mountpoints)); + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=directory-1/mpoint:/tmp/local_target"}; + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_NO_THROW(args.process_mountpoints()); EXPECT_TRUE(sync_tebako_mount_table::get_tebako_mount_table().check(test_dir_ino, "mpoint")); } TEST_F(ProcessMountpointsTest, absolute_path) { - std::vector mountpoints = {"/directory-1/mpoint:/tmp/local_target"}; - EXPECT_THROW(process_mountpoints(mountpoints), std::invalid_argument); + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=/directory-1/mpoint:/tmp/local_target"}; + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_THROW(args.process_mountpoints(), std::invalid_argument); } // Test: Missing ':' separator TEST_F(ProcessMountpointsTest, missing_separator) { - std::vector mountpoints = {"directory-1/mpoint"}; - EXPECT_THROW(process_mountpoints(mountpoints), std::invalid_argument); + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=directory-1/mpoint"}; + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_THROW(args.process_mountpoints(), std::invalid_argument); } // Test: Empty path or filename TEST_F(ProcessMountpointsTest, empty_path) { - std::vector mountpoints = {":/tmp/local_target"}; - EXPECT_THROW(process_mountpoints(mountpoints), std::invalid_argument); + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=:/tmp/local_target"}; + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_THROW(args.process_mountpoints(), std::invalid_argument); } // Test: Empty target TEST_F(ProcessMountpointsTest, empty_target) { - std::vector mountpoints = {"directory-1/mpoint:"}; - EXPECT_THROW(process_mountpoints(mountpoints), std::invalid_argument); + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=directory-1/mpoint:"}; + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_THROW(args.process_mountpoints(), std::invalid_argument); } // Test: Invalid path (dwarfs_stat returns error) TEST_F(ProcessMountpointsTest, invalid_path) { - std::vector mountpoints = {"invalid/path:local_target"}; - EXPECT_THROW(process_mountpoints(mountpoints), std::invalid_argument); + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=invalid/path:local_target"}; + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_THROW(args.process_mountpoints(), std::invalid_argument); } /* @@ -146,10 +170,13 @@ tests_the_other_memfs_image()}; EXPECT_THROW(process_mountpoints(mountpoints), s // Test: Multiple valid mount points TEST_F(ProcessMountpointsTest, mltiple_valid_mountpoints) { - std::vector mountpoints = {"directory-1/mpoint1:target1", "directory-1/mpoint2:target2", - "directory-1/mpoint3:target3"}; + const int argc = 4; + const char* argv[argc] = {"program", "--tebako-mount=directory-1/mpoint1:target1", + "--tebako-mount=directory-1/mpoint2:target2", "--tebako-mount=directory-1/mpoint3:target3"}; + cmdline_args args(argc, argv); + args.parse_arguments(); - EXPECT_NO_THROW(process_mountpoints(mountpoints)); + EXPECT_NO_THROW(args.process_mountpoints()); EXPECT_TRUE(sync_tebako_mount_table::get_tebako_mount_table().check(test_dir_ino, "mpoint1")); EXPECT_TRUE(sync_tebako_mount_table::get_tebako_mount_table().check(test_dir_ino, "mpoint2")); @@ -159,23 +186,37 @@ TEST_F(ProcessMountpointsTest, mltiple_valid_mountpoints) // Test: Mount to root TEST_F(ProcessMountpointsTest, mount_to_root) { - std::vector mountpoints = {"tmp:/tmp/tebako"}; + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=tmp:/tmp/tebako"}; + cmdline_args args(argc, argv); + args.parse_arguments(); - EXPECT_NO_THROW(process_mountpoints(mountpoints)); + EXPECT_NO_THROW(args.process_mountpoints()); EXPECT_TRUE(sync_tebako_mount_table::get_tebako_mount_table().check(test_root_ino, "tmp")); } TEST_F(ProcessMountpointsTest, valid_dwarfs_mount) { - std::vector mountpoints = {std::string("directory-1/dfs-link>") + tests_the_other_memfs_image()}; - EXPECT_NO_THROW(process_mountpoints(mountpoints)); + auto mp = std::string("--tebako-mount=directory-1/dfs-link>") + tests_the_other_memfs_image(); + + const int argc = 2; + const char* argv[argc] = {"program", mp.c_str()}; + + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_NO_THROW(args.process_mountpoints()); EXPECT_TRUE(sync_tebako_mount_table::get_tebako_mount_table().check(test_dir_ino, "dfs-link")); } TEST_F(ProcessMountpointsTest, no_file_dwarfs_mount) { - std::vector mountpoints = {"directory-1/dfs-link>/tmp/nofile"}; - EXPECT_THROW(process_mountpoints(mountpoints), std::invalid_argument); + const int argc = 2; + const char* argv[argc] = {"program", "--tebako-mount=directory-1/dfs-link>/tmp/nofile"}; + cmdline_args args(argc, argv); + args.parse_arguments(); + + EXPECT_THROW(args.process_mountpoints(), std::invalid_argument); } } // namespace tebako diff --git a/version.txt b/version.txt index b60d7196..ac39a106 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.8.4 +0.9.0