diff --git a/.gitignore b/.gitignore index 7fd7729..052d80b 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ Makefile api_test ccos_disk_tool + +build/ +.cache/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e61237d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "clangd.arguments": [ + "--compile-commands-dir=build" + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e872e3c..cf4a0af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,16 @@ cmake_minimum_required(VERSION 3.10) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + project(ccos_disk_tool) set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${SOURCE_DIR}) +option(CCOS_ENABLE_TESTS "Build and run tests (Criterion)" OFF) + add_library(ccos_api STATIC ${SOURCE_DIR}/string_utils.h ${SOURCE_DIR}/string_utils.c @@ -26,10 +32,8 @@ add_executable(ccos_disk_tool TARGET_LINK_LIBRARIES(ccos_disk_tool ccos_api) -project(api_test) - -add_executable(api_test - ${SOURCE_DIR}/api_test.c - ) - -TARGET_LINK_LIBRARIES(api_test ccos_api) +if(CCOS_ENABLE_TESTS) + include(CTest) + enable_testing() + add_subdirectory(tests) +endif() diff --git a/README.md b/README.md index 87c3560..16e8454 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,29 @@ OPTIONS: -l, --in-place Write changes to the original image ``` +## Build + +To build the project, run the following commands: + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build --parallel +``` + +After building, the compiled binary will be located at `build/ccos_disk_tool`. + +## Run Tests + +To run the tests, use the following commands: + +```bash +cmake -S . -B build -DCCOS_ENABLE_TESTS=ON +cmake --build build --parallel +ctest --test-dir build --output-on-failure +``` + +Tests depend on the [Criterion](https://github.com/Snaipe/Criterion) library, which must be installed before running the tests. + ## Examples ### Working with bubble memory images or other non-standard images diff --git a/cmake/FindCriterion.cmake b/cmake/FindCriterion.cmake new file mode 100644 index 0000000..cf8d8d4 --- /dev/null +++ b/cmake/FindCriterion.cmake @@ -0,0 +1,100 @@ +# This file is licensed under the WTFPL version 2 -- you can see the full +# license over at http://www.wtfpl.net/txt/copying/ +# +# Find Criterion. +# +# Module defines the following imported library: +# Criterion::Criterion +# +# Module defines the following result variables: +# Criterion_FOUND - True if the Criterion library has been found +# Criterion_INCLUDE_DIRS - Include directories needed to use Criterion +# Criterion_LIBRARIES - Libraries needed to link to Criterion +# Criterion_VERSION - The version of the Criterion which was found +# +# Cache variables: +# Criterion_INCLUDE_DIR - The directory containing criterion/criterion.h +# Criterion_LIBRARY - The path to the Criterion library +# +# Example usage: +# find_package(Criterion) +# target_link_libraries(foo PRIVATE Criterion::Criterion) + +set(_criterionReason "") + +# Try pkg-config. +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_Criterion QUIET criterion) +endif() + +find_path( + Criterion_INCLUDE_DIR + criterion/criterion.h + HINTS ${PC_Criterion_INCLUDE_DIRS} + DOC "The directory containing criterion/criterion.h" +) +mark_as_advanced(Criterion_INCLUDE_DIR) + +if(NOT Criterion_INCLUDE_DIR) + string(APPEND _criterionReason "criterion/criterion.h not found. ") +endif() + +find_library( + Criterion_LIBRARY + NAMES criterion libcriterion + HINTS ${PC_Criterion_LIBRARY_DIRS} + DOC "The path to the Criterion library" +) +mark_as_advanced(Criterion_LIBRARY) + +if(NOT Criterion_LIBRARY) + string(APPEND _criterionReason "Criterion library (libcriterion) not found. ") +endif() + +# Get Criterion version from pkg-config if available. Conditional check ensures +# that Criterion found by pkg-config is the one found by the find_path() and +# find_library(). +if(PC_Criterion_VERSION AND Criterion_INCLUDE_DIR IN_LIST PC_Criterion_INCLUDE_DIRS) + set(Criterion_VERSION ${PC_Criterion_VERSION}) +endif() + +include(FindPackageHandleStandardArgs) +# Handle the REQUIRED, QUIET, and version-related arguments of find_package(). +find_package_handle_standard_args( + Criterion + REQUIRED_VARS Criterion_LIBRARY Criterion_INCLUDE_DIR + VERSION_VAR Criterion_VERSION + HANDLE_VERSION_RANGE + REASON_FAILURE_MESSAGE "${_criterionReason}" +) + +unset(_criterionReason) + +if(NOT Criterion_FOUND) + return() +endif() + +set(Criterion_INCLUDE_DIRS ${Criterion_INCLUDE_DIR}) +set(Criterion_LIBRARIES ${Criterion_LIBRARY}) + +if(NOT TARGET Criterion::Criterion) + if(IS_ABSOLUTE "${Criterion_LIBRARY}") + add_library(Criterion::Criterion UNKNOWN IMPORTED) + set_target_properties( + Criterion::Criterion + PROPERTIES IMPORTED_LOCATION "${Criterion_LIBRARY}" + ) + else() + add_library(Criterion::Criterion INTERFACE IMPORTED) + set_target_properties( + Criterion::Criterion + PROPERTIES IMPORTED_LIBNAME "${Criterion_LIBRARY}" + ) + endif() + + set_target_properties( + Criterion::Criterion + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Criterion_INCLUDE_DIRS}" + ) +endif() \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..507e0c8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,9 @@ +find_package(Criterion REQUIRED) + +add_executable(ccos_tests + ${CMAKE_CURRENT_LIST_DIR}/api_test.c + ) + +target_link_libraries(ccos_tests PRIVATE ccos_api Criterion::Criterion) + +add_test(NAME ccos_tests COMMAND ccos_tests) diff --git a/api_test.c b/tests/api_test.c similarity index 74% rename from api_test.c rename to tests/api_test.c index 81b48c6..3b404f1 100644 --- a/api_test.c +++ b/tests/api_test.c @@ -2,23 +2,15 @@ // Created by kirill on 04.06.2020. // +#include + #include #include -#include -#include - -#define __STR(X) #X -#define STR(X) __STR(X) -#define ASSERT(CONDITION) \ - do { \ - if (!(CONDITION)) { \ - fprintf(stderr, "%s:%d: \"%s\" Failed!\n", __FUNCTION__, __LINE__, STR(CONDITION)); \ - return -1; \ - } \ - } while (0) +#include +#include -uint8_t test_inode_data[] = { +static const uint8_t test_inode_data[] = { 0x98U, 0x00U, 0x00U, 0x00U, 0x84U, 0xD0U, 0x00U, 0x00U, 0x15U, 0x47U, 0x52U, 0x69U, 0x44U, 0x50U, 0x61U, 0x69U, 0x6EU, 0x74U, 0x7EU, 0x52U, 0x75U, 0x6EU, 0x20U, 0x43U, 0x61U, 0x6EU, 0x76U, 0x61U, 0x73U, 0x7EU, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -52,43 +44,35 @@ uint8_t test_inode_data[] = { 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0x00U, 0x00U, 0x00U, 0x00U}; -int check_image_test() { +Test(ccos_image, check_image) { uint8_t data[0x200] = {0}; - ASSERT(ccos_check_image(data) == 0); + cr_assert_eq(ccos_check_image(data), 0); data[0] = 'I'; data[1] = 'M'; data[2] = 'D'; data[3] = ' '; - ASSERT(ccos_check_image(data) == -1); + cr_assert_eq(ccos_check_image(data), -1); memset(data, 0, 0x200); data[0] = 0xEB; data[2] = 0x90; *((uint16_t *)(&data[0x1FE])) = 0xAA55; - ASSERT(ccos_check_image(data) == -1); - return 0; + cr_assert_eq(ccos_check_image(data), -1); } -int ccos_get_file_version_test() { - ccos_inode_t inode = *((ccos_inode_t *)&test_inode_data); +Test(ccos_image, get_file_version) { + ccos_inode_t inode; + memcpy(&inode, test_inode_data, sizeof(inode)); version_t version = ccos_get_file_version(&inode); - ASSERT(version.major == 3); - ASSERT(version.minor == 1); - ASSERT(version.patch == 5); - return 0; + cr_assert_eq(version.major, 3); + cr_assert_eq(version.minor, 1); + cr_assert_eq(version.patch, 5); } -int ccos_get_file_name_test() { - ccos_inode_t inode = *((ccos_inode_t *)&test_inode_data); +Test(ccos_image, get_file_name) { + ccos_inode_t inode; + memcpy(&inode, test_inode_data, sizeof(inode)); short_string_t *name = ccos_get_file_name(&inode); - ASSERT(name->length == 21); - ASSERT(!strncmp(name->data, "GRiDPaint~Run Canvas~", name->length)); - return 0; -} - -int main() { - ASSERT(check_image_test() == 0); - ASSERT(ccos_get_file_version_test() == 0); - ASSERT(ccos_get_file_name_test() == 0); - printf("All tests completed!\n"); - return 0; + cr_assert_not_null(name); + cr_assert_eq(name->length, 21); + cr_assert_eq(strncmp(name->data, "GRiDPaint~Run Canvas~", name->length), 0); }