diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..7ab1b4d --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,75 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v4 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} diff --git a/CMakeLists.txt b/CMakeLists.txt index fea55f2..23c2a19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,31 +3,8 @@ project(argsparse) include(${CMAKE_CURRENT_LIST_DIR}/cmake/argsparse-lib.cmake) -add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/test) - add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/example) -# set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) - -# set(CMAKE_CXX_STANDARD 17) -# set(CMAKE_CXX_STANDARD_REQUIRED YES) - -# include(${CMAKE_CURRENT_LIST_DIR}/cmake/googletest.cmake) - -# include(${CMAKE_CURRENT_LIST_DIR}/cmake/argsparse-lib.cmake) +enable_testing() -# # Main source directory -# include_directories(${CMAKE_CURRENT_LIST_DIR}/.) - -# list(APPEND TestFiles -# ${CMAKE_CURRENT_LIST_DIR}/test/argsparseTests.cpp -# ) - -# # Test executable -# add_executable(${PROJECT_NAME}-test ${CMAKE_CURRENT_LIST_DIR}/test/TestMain.cpp ${TestFiles}) -# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}-lib gtest gmock_main) -# target_include_directories(${PROJECT_NAME}-test PUBLIC ${googletest_SOURCE_DIR}/googlemock/include) - -# include(CTest) - -# add_test(NAME ${PROJECT_NAME}-test COMMAND ${PROJECT_NAME}-test) +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/test) diff --git a/argsparse.c b/argsparse.c index 1c99fe8..5d7bafe 100644 --- a/argsparse.c +++ b/argsparse.c @@ -7,15 +7,16 @@ * */ -#include "iterate.h" #include "internal_funcs.h" +#include "iterate.h" #include +#include +#include +#include #include #include #include -#include -#include ARG_DATA_HANDLE argsparse_create(const char* title) { @@ -38,22 +39,6 @@ void argsparse_free(ARG_DATA_HANDLE handle) } } -ARG_ARGUMENT_HANDLE argsparse_create_argument_with_value(ARG_TYPE type, const char* name, const char* desc, ARG_VALUE* value) -{ - ARG_ARGUMENT_HANDLE p = create_argument(name, desc); - p->type = type; - if (value) - { - memcpy(&p->value, value, sizeof(ARG_VALUE)); - // if no pointer given point to the unused ARG_VALUE memory - if (type == ARGSPARSE_TYPE_FLAG && p->value.flagptr == NULL) - { - p->value.flagptr = (int*)(&p->value.flagptr + 1); - } - } - return p; -} - char* argsparse_get_shortopts(ARG_DATA_HANDLE handle) { return (handle) ? handle->shortopts : NULL; @@ -81,49 +66,16 @@ int argsparse_argument_count(ARG_DATA_HANDLE handle) return ret; } -ARG_ERROR argsparse_put_argument(ARG_DATA_HANDLE handle, ARG_ARGUMENT_HANDLE* href) +ARG_ERROR argsparse_add(ARG_DATA_HANDLE handle, const char* name, const char* description, ARG_TYPE type, const ARG_VALUE* value) { - ARG_ARGUMENT_HANDLE exists = iterate_arguments_return_on_zero(handle, predicate_compare_name, (*href)->name); - if (exists) - { - // free if not the same - if (exists != *href) - { - free_argument(href); - } - return ERROR_EXISTS; - } - - if (handle->count >= ARGSPARSE_MAX_ARGS) - { - free_argument(href); - return ERROR_MAX_ARGS; - } - handle->count++; - HARGPARSE_ARG_LINKED new_link = calloc(1, sizeof(t_argparse_argument_linked)); - new_link->argument = *href; - *href = NULL; - - generate_short_name(handle, new_link->argument); - if (handle->arguments) - { - HARGPARSE_ARG_LINKED next = handle->arguments; - while (next->next) - { - next = next->next; - } - next->next = new_link; - } - else - handle->arguments = new_link; - - return ERROR_NONE; + ARG_ARGUMENT_HANDLE h = create_argument(type, name, description, value); + return put_argument(handle, &h); } ARG_ERROR argsparse_add_help(ARG_DATA_HANDLE handle) { - ARG_ARGUMENT_HANDLE p = argsparse_create_argument_with_value(ARGSPARSE_TYPE_NONE, "help", "Print this message", NULL); - return argsparse_put_argument(handle, &p); + ARG_ARGUMENT_HANDLE p = create_argument(ARGSPARSE_TYPE_NONE, "help", "Print this message", NULL); + return put_argument(handle, &p); } ARG_ERROR argsparse_add_int(ARG_DATA_HANDLE handle, const char* name, const char* description, int value) @@ -131,9 +83,9 @@ ARG_ERROR argsparse_add_int(ARG_DATA_HANDLE handle, const char* name, const char ARG_VALUE argvalue; argvalue.intvalue = value; - ARG_ARGUMENT_HANDLE p = argsparse_create_argument_with_value(ARGSPARSE_TYPE_INT, name, description, &argvalue); + ARG_ARGUMENT_HANDLE p = create_argument(ARGSPARSE_TYPE_INT, name, description, &argvalue); - return argsparse_put_argument(handle, &p); + return put_argument(handle, &p); } ARG_ERROR argsparse_add_double(ARG_DATA_HANDLE handle, const char* name, const char* description, double value) @@ -141,9 +93,9 @@ ARG_ERROR argsparse_add_double(ARG_DATA_HANDLE handle, const char* name, const c ARG_VALUE argvalue; argvalue.doublevalue = value; - ARG_ARGUMENT_HANDLE p = argsparse_create_argument_with_value(ARGSPARSE_TYPE_DOUBLE, name, description, &argvalue); + ARG_ARGUMENT_HANDLE p = create_argument(ARGSPARSE_TYPE_DOUBLE, name, description, &argvalue); - return argsparse_put_argument(handle, &p); + return put_argument(handle, &p); } ARG_ERROR argsparse_add_cstr(ARG_DATA_HANDLE handle, const char* name, const char* description, const char* value) @@ -151,17 +103,17 @@ ARG_ERROR argsparse_add_cstr(ARG_DATA_HANDLE handle, const char* name, const cha ARG_VALUE argvalue; copy_to_argument_string(argvalue.stringvalue, value); - ARG_ARGUMENT_HANDLE p = argsparse_create_argument_with_value(ARGSPARSE_TYPE_STRING, name, description, &argvalue); - return argsparse_put_argument(handle, &p); + ARG_ARGUMENT_HANDLE p = create_argument(ARGSPARSE_TYPE_STRING, name, description, &argvalue); + return put_argument(handle, &p); } ARG_ERROR argsparse_add_flag(ARG_DATA_HANDLE handle, const char* name, const char* description, int value, int* ptr_to_value) { ARG_VALUE argvalue = {0, }; argvalue.flagptr = ptr_to_value; - ARG_ARGUMENT_HANDLE p = argsparse_create_argument_with_value(ARGSPARSE_TYPE_FLAG, name, description, &argvalue); + ARG_ARGUMENT_HANDLE p = create_argument(ARGSPARSE_TYPE_FLAG, name, description, &argvalue); p->flag_init.flagvalue = value; - return argsparse_put_argument(handle, &p); + return put_argument(handle, &p); } int argsparse_parse_args(ARG_DATA_HANDLE handle, char* const *argv, int argc) @@ -280,14 +232,64 @@ void argsparse_show_arguments(ARG_DATA_HANDLE handle) // Internal functions // //////////////////////// -static ARG_ARGUMENT_HANDLE create_argument(const char* name, const char* desc) +static ARG_ARGUMENT_HANDLE create_argument(ARG_TYPE type, const char* name, const char* desc, const ARG_VALUE* value) { ARG_ARGUMENT_HANDLE p = calloc(1, sizeof(argsparse_argument_t)); copy_to_argument_string(p->name, name); copy_to_argument_string(p->description, desc); + + p->type = type; + if (value) + { + memcpy(&p->value, value, sizeof(ARG_VALUE)); + // if no pointer given point to the unused ARG_VALUE memory + if (type == ARGSPARSE_TYPE_FLAG && p->value.flagptr == NULL) + { + p->value.flagptr = (int*)(&p->value.flagptr + 1); + } + } return p; } +static ARG_ERROR put_argument(ARG_DATA_HANDLE handle, ARG_ARGUMENT_HANDLE* href) +{ + ARG_ARGUMENT_HANDLE exists = iterate_arguments_return_on_zero(handle, predicate_compare_name, (*href)->name); + if (exists) + { + // free if not the same + if (exists != *href) + { + free_argument(href); + } + return ERROR_EXISTS; + } + + if (handle->count >= ARGSPARSE_MAX_ARGS) + { + free_argument(href); + return ERROR_MAX_ARGS; + } + handle->count++; + HARGPARSE_ARG_LINKED new_link = calloc(1, sizeof(t_argparse_argument_linked)); + new_link->argument = *href; + *href = NULL; + + generate_short_name(handle, new_link->argument); + if (handle->arguments) + { + HARGPARSE_ARG_LINKED next = handle->arguments; + while (next->next) + { + next = next->next; + } + next->next = new_link; + } + else + handle->arguments = new_link; + + return ERROR_NONE; +} + static HARGPARSE_ARG_LINKED free_linked_argument(HARGPARSE_ARG_LINKED linked) { HARGPARSE_ARG_LINKED next = NULL; diff --git a/argsparse.h b/argsparse.h index e7b592e..fb08f92 100644 --- a/argsparse.h +++ b/argsparse.h @@ -30,12 +30,11 @@ typedef enum _argsparse_errors { } e_argsparse_errors; typedef enum _argsparse_type { - /// @brief illegal value + /// @brief illegal/reserved value ARGSPARSE_TYPE_NONE = -1, ARGSPARSE_TYPE_STRING, ARGSPARSE_TYPE_INT, ARGSPARSE_TYPE_DOUBLE, - /// @brief value of flag can be tested against zero ARGSPARSE_TYPE_FLAG, /// @brief count of legal types ARGSPARSE_TYPE_CNT @@ -76,22 +75,24 @@ typedef enum _argsparse_errors ARG_ERROR; ARG_DATA_HANDLE argsparse_create(const char* title); /// @brief Allocate arguments structure -/// @param title -/// @return handle +/// @param handle void argsparse_free(ARG_DATA_HANDLE handle); -ARG_ARGUMENT_HANDLE argsparse_create_argument_with_value(ARG_TYPE type, const char* name, const char* description, ARG_VALUE* value); - -/// @brief Add argument moves argument ownership to handle -/// @param handle Handle to allocated arguments structure -/// @param argument Handle to allocated argument +/// @brief Adds argument using the structured format +/// @param handle +/// @param name +/// @param description +/// @param type +/// @param value /// @return /// ERROR_NONE(0) - success /// /// ERROR_EXISTS - argument with same name already exists /// /// ERROR_MAX_ARGS(1) - ARGSPARSE_MAX_ARGS reached, not added -ARG_ERROR argsparse_put_argument(ARG_DATA_HANDLE handle, ARG_ARGUMENT_HANDLE* argument); +/// @note Smells like internal, but having invested +/// a quite a lot to testing it decided to drag it along. +ARG_ERROR argsparse_add(ARG_DATA_HANDLE handle, const char* name, const char* description, ARG_TYPE type, const ARG_VALUE* value); /// @brief Add help option showing usage with exit /// @param handle allocated arguments structure handle @@ -158,23 +159,43 @@ ARG_ERROR argsparse_add_flag(ARG_DATA_HANDLE handle, const char* name, const cha /// @brief Parse cmdline argument against added arguments /// @param handle Handle to allocated arguments structure -/// @param argsv -/// @param argc +/// @param argsv +/// @param argc int argsparse_parse_args(ARG_DATA_HANDLE handle, char* const* argv, int argc); /// @brief Prints usage message -/// @param handle +/// @param handle void argsparse_show_usage(ARG_DATA_HANDLE handle, const char* const executable); /// @brief Prints argument values -/// @param handle +/// @param handle void argsparse_show_arguments(ARG_DATA_HANDLE handle); +/// @brief Get title +/// @param handle +/// @return string const char* argsparse_get_title(ARG_DATA_HANDLE handle); + +/// @brief Get short options +/// @param handle +/// @return char* argsparse_get_shortopts(ARG_DATA_HANDLE handle); +/// @brief Get argument by name +/// @param handle +/// @param name +/// @return handle to argument ARG_ARGUMENT_HANDLE argsparse_argument_by_name(ARG_DATA_HANDLE handle, const char* name); + +/// @brief Get argument by short name +/// @param handle +/// @param name +/// @return handle to argument ARG_ARGUMENT_HANDLE argsparse_argument_by_short_name(ARG_DATA_HANDLE handle, int shortname); + +/// @brief Get argument count +/// @param handle +/// @return count int argsparse_argument_count(ARG_DATA_HANDLE handle); #if defined( __cplusplus ) diff --git a/internal_funcs.c b/internal_funcs.c index 61a9122..8a9bc8e 100644 --- a/internal_funcs.c +++ b/internal_funcs.c @@ -116,59 +116,68 @@ int parse_value(ARG_VALUE* ref, ARG_TYPE type, const char* str_value) int set_short_option(char c, ARG_DATA_HANDLE handle, ARG_ARGUMENT_HANDLE arg) { - int ret = -1; - char* used = handle->shortopts; + int ret = ERROR_EXISTS; + char* opt = handle->shortopts; // iterate used until terminating 0 - while (*used) + while (*opt) { - // used - break the loop and move to next longname character - if (*used == c) + if (*opt != ':' && *opt == c) { break; } - used++; + opt++; } // unused - set value and break - if (*used == 0) + if (*opt == 0) { - *used = c; - *(used + 1) = (arg->type == ARGSPARSE_TYPE_FLAG) || (arg->type == ARGSPARSE_TYPE_NONE) ? 0 : ':'; + *opt = c; + if ((arg->type == ARGSPARSE_TYPE_FLAG) || (arg->type == ARGSPARSE_TYPE_NONE)) + { + *(opt + 1) = '\0'; + } + else + { + *(opt + 1) = ':'; + *(opt + 2) = '\0'; + } arg->name_short = c; - ret = 0; + ret = ERROR_NONE; } + return ret; } +char iterate_set_of_chars_for_short(const char* sopts, const char* charset) +{ + if (charset != NULL && sopts != NULL) + { + size_t sopts_len = strlen(sopts); + while (*charset) + { + char c = *charset; + char* used = sopts_len <= 0 ? NULL : strchr(sopts, c); + if (used == NULL) + { + // found unused char + return c; + } + charset++; + } + } + return 0; +} + void generate_short_name(ARG_DATA_HANDLE handle, ARG_ARGUMENT_HANDLE arg) { if (arg->type == ARGSPARSE_TYPE_FLAG) return; char* longname = arg->name; - char c; - // iterate characters of longname as long as necessary - while(c = *longname) - { - if (set_short_option(c, handle, arg)) - { - longname++; - continue; - } - break; - } - // none of the longname characters accepted - if (!c) + char shortopt = iterate_set_of_chars_for_short(handle->shortopts, longname); + if (shortopt == '\0') { - // iterate characters from 'a' until the short option is set - c = 'a'; - while (1) - { - if (set_short_option(c, handle, arg)) - { - c++; - continue; - } - break; - } + shortopt = iterate_set_of_chars_for_short(handle->shortopts, "abcdefghiklmnopqrstuvwxyz"); } + + set_short_option(shortopt, handle, arg); } \ No newline at end of file diff --git a/internal_funcs.h b/internal_funcs.h index 88c8284..a1e03a7 100644 --- a/internal_funcs.h +++ b/internal_funcs.h @@ -3,9 +3,22 @@ #include "internal_types.h" -HARGPARSE_ARG_LINKED free_linked_argument(HARGPARSE_ARG_LINKED arg); -ARG_ARGUMENT_HANDLE create_argument(const char* name, const char* desc); -void free_argument(ARG_ARGUMENT_HANDLE* handle); +#include + +static ARG_ARGUMENT_HANDLE create_argument(ARG_TYPE type, const char* name, const char* description, const ARG_VALUE* value); +static void free_argument(ARG_ARGUMENT_HANDLE* handle); +static HARGPARSE_ARG_LINKED free_linked_argument(HARGPARSE_ARG_LINKED arg); + +/// @brief Add argument moves argument ownership to handle +/// @param handle Handle to allocated arguments structure +/// @param argument Handle to allocated argument +/// @return +/// ERROR_NONE(0) - success +/// +/// ERROR_EXISTS - argument with same name already exists +/// +/// ERROR_MAX_ARGS(1) - ARGSPARSE_MAX_ARGS reached, not added +static ARG_ERROR put_argument(ARG_DATA_HANDLE handle, ARG_ARGUMENT_HANDLE* argument); void copy_to_argument_string(char* dest, const char* source); int parse_value(ARG_VALUE* ref, ARG_TYPE type, const char* value); diff --git a/iterate.h b/iterate.h index 8857f89..80c3c63 100644 --- a/iterate.h +++ b/iterate.h @@ -3,16 +3,16 @@ #include "internal_types.h" -ARG_ARGUMENT_HANDLE iterate_arguments_return_on_zero(ARG_DATA_HANDLE handle, int(*predicate)(int, ARG_ARGUMENT_HANDLE, void*), void* data); +static ARG_ARGUMENT_HANDLE iterate_arguments_return_on_zero(ARG_DATA_HANDLE handle, int(*predicate)(int, ARG_ARGUMENT_HANDLE, void*), void* data); -int action_show_argument_value(int idx, ARG_ARGUMENT_HANDLE arg, void* data); -int action_show_argument_usage(int idx, ARG_ARGUMENT_HANDLE arg, void* data); -int action_long_option_width(int idx, ARG_ARGUMENT_HANDLE arg, void* data); -int action_mark_parsed_flags(int idx, ARG_ARGUMENT_HANDLE arg, void* data); -int action_do_option_long(int idx, ARG_ARGUMENT_HANDLE arg, void* data); -int action_count(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int action_show_argument_value(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int action_show_argument_usage(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int action_long_option_width(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int action_mark_parsed_flags(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int action_do_option_long(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int action_count(int idx, ARG_ARGUMENT_HANDLE arg, void* data); -int predicate_compare_name(int idx, ARG_ARGUMENT_HANDLE arg, void* data); -int predicate_compare_short_name(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int predicate_compare_name(int idx, ARG_ARGUMENT_HANDLE arg, void* data); +static int predicate_compare_short_name(int idx, ARG_ARGUMENT_HANDLE arg, void* data); #endif \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4dec15b..8a8ab55 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,6 +21,5 @@ add_executable(${PROJECT_NAME}-test ${SourceFiles}) target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}-lib gtest gmock_main) target_include_directories(${PROJECT_NAME}-test PUBLIC ${googletest_SOURCE_DIR}/googlemock/include) -include(CTest) - -add_test(NAME ${PROJECT_NAME}-test COMMAND ${PROJECT_NAME}-test) +include(GoogleTest) +gtest_discover_tests(${PROJECT_NAME}-test) diff --git a/test/argsparseTests.cpp b/test/argsparseTests.cpp index dddb825..38c80dd 100644 --- a/test/argsparseTests.cpp +++ b/test/argsparseTests.cpp @@ -118,9 +118,10 @@ TEST_F(TEST_FIXTURE, ShouldAppendShortOptions) char* shortopts = argsparse_get_shortopts(gHandle); ASSERT_EQ(0, *shortopts); - ARG_ARGUMENT_HANDLE harg = argsparse_create_argument_with_value(ARG_TYPE::ARGSPARSE_TYPE_INT, "integer", "description", NULL); - argsparse_put_argument(gHandle, &harg); + ASSERT_EQ(ERROR_NONE, argsparse_add_int(gHandle, "integer", "description", 0)); ASSERT_STREQ("i:", shortopts); + ASSERT_EQ(ERROR_NONE, argsparse_add_cstr(gHandle, "string", "description", "value")); + ASSERT_STREQ("i:s:", shortopts); } TEST_F(TEST_FIXTURE, ShouldNotAppendSameOption) @@ -129,37 +130,11 @@ TEST_F(TEST_FIXTURE, ShouldNotAppendSameOption) char* shortopts = argsparse_get_shortopts(gHandle); ASSERT_EQ(0, *shortopts); - ARG_ARGUMENT_HANDLE harg = argsparse_create_argument_with_value(ARG_TYPE::ARGSPARSE_TYPE_INT, "integer", "description", NULL); - argsparse_put_argument(gHandle, &harg); - ASSERT_THAT(harg, IsNull()); - harg = argsparse_create_argument_with_value(ARG_TYPE::ARGSPARSE_TYPE_INT, "integer", "description", NULL); - ARG_ERROR err = argsparse_put_argument(gHandle, &harg); - ASSERT_EQ(ERROR_EXISTS, err); + ASSERT_EQ(ERROR_NONE, argsparse_add_int(gHandle, "integer", "description", 0)); + ASSERT_EQ(ERROR_EXISTS, argsparse_add_int(gHandle, "integer", "description", 0)); ASSERT_STREQ("i:", shortopts); } -TEST_F(TEST_FIXTURE, ShouldAddManyArguments) -{ - ARG_ERROR err = ERROR_NONE; - gHandle = argsparse_create(NULL); - char flag[] = { 'f', 'l', 'a', 'g', '?','?','?', 0 }; - const char* fmt = "flag%d"; - for (int i = 0; i <= ARGSPARSE_MAX_ARGS; i++) - { - sprintf(flag, fmt, i); - ARG_ARGUMENT_HANDLE h = argsparse_create_argument_with_value( - (ARG_TYPE)(i % ARGSPARSE_TYPE_CNT), - flag, - "This is one of the many flags created", - NULL); - err = argsparse_put_argument(gHandle, &h); - if (err != ERROR_NONE) - break; - } - ASSERT_EQ(ARG_ERROR::ERROR_MAX_ARGS, err); - ASSERT_EQ(ARGSPARSE_MAX_ARGS, argsparse_argument_count(gHandle)); -} - TEST_F(TEST_FIXTURE, ParsesAllOptionTypes) { int flgValue = 0; @@ -356,11 +331,11 @@ TEST_F(TEST_FIXTURE, ShouldParseOptionLongString2) ASSERT_EQ(0, strncmp(expvalue, arg->value.stringvalue, strlen(expvalue))); } -TEST_F(TEST_FIXTURE, ShouldSplitParseWhitespaceString) +TEST_F(TEST_FIXTURE, SplitsWhitespaceString) { int argc = 0; const char* defvalue = "1234.4321"; - const char* expvalue = "4321 1234"; + char expvalue[] = "4321 1234"; sprintf(gBuffer, "%s%s", "program --string ", expvalue); tokenise_to_argc_argv(gBuffer, &argc, gArgv, ARGV_SIZE, print_arguments); @@ -371,8 +346,10 @@ TEST_F(TEST_FIXTURE, ShouldSplitParseWhitespaceString) ASSERT_EQ(0, strncmp(defvalue, arg->value.stringvalue, strlen(defvalue))); ASSERT_EQ(1, argsparse_parse_args(gHandle, gArgv, argc)); ASSERT_EQ(1, arg->parsed); - ASSERT_EQ(1, strncmp(expvalue, arg->value.stringvalue, strlen(expvalue))); - ASSERT_EQ(0, strncmp(expvalue, arg->value.stringvalue, 4)); + ASSERT_STRNE(expvalue, arg->value.stringvalue); + // cut the string + *(char*)strchr(expvalue, ' ') = '\0'; + ASSERT_STREQ(expvalue, arg->value.stringvalue); } TEST_F(TEST_FIXTURE, ShouldParseDoubleQuotedWhitespaceString) @@ -380,12 +357,15 @@ TEST_F(TEST_FIXTURE, ShouldParseDoubleQuotedWhitespaceString) int argc = 0; const char* defvalue = "1234.4321"; const char* expvalue = "4321 1234"; + // tokenise_to_argc_argv does handle double quoted string + // will have to do it manually char buffer[50] = "program\000--string\0004321 1234"; gArgv[0] = buffer; gArgv[1] = buffer + 8; gArgv[2] = buffer + 17; argc = 3; print_arguments(gArgv, argc); + gHandle = argsparse_create(NULL); argsparse_add_cstr(gHandle, "string", "This is a string", defvalue); ARG_ARGUMENT_HANDLE arg = argsparse_argument_by_name(gHandle, "string"); @@ -424,37 +404,23 @@ TEST_P(TEST_FIXTURE, ShouldAddArgument) TestParams params = GetParam(); gHandle = argsparse_create(NULL); ASSERT_EQ(0, argsparse_argument_count(gHandle)); - ARG_ARGUMENT_HANDLE h = argsparse_create_argument_with_value(params.Type, "argument", "This is an argument", NULL); - ASSERT_THAT(h, NotNull()); - argsparse_put_argument(gHandle, &h); - ASSERT_THAT(h, IsNull()); + ASSERT_EQ(ERROR_NONE, argsparse_add(gHandle, "argument", "This is and argument", params.Type, &(params.Value))); ASSERT_EQ(1, argsparse_argument_count(gHandle)); -} - -TEST_P(TEST_FIXTURE, ShouldInitializeValue) -{ - TestParams param = GetParam(); - gHandle = argsparse_create(NULL); - ASSERT_EQ(0, argsparse_argument_count(gHandle)); - - ARG_ARGUMENT_HANDLE h = argsparse_create_argument_with_value(param.Type, "flag", "This is an argument", &(param.Value)); - - argsparse_put_argument(gHandle, &h); - ARG_ARGUMENT_HANDLE arg = argsparse_argument_by_name(gHandle, "flag"); + ARG_ARGUMENT_HANDLE arg = argsparse_argument_by_name(gHandle, "argument"); ARG_VALUE value = arg->value; - switch (param.Type) + switch (params.Type) { case ARGSPARSE_TYPE_FLAG: - ASSERT_EQ(value.flagptr, param.Value.flagptr); + ASSERT_EQ(value.flagptr, params.Value.flagptr); break; case ARGSPARSE_TYPE_STRING: - ASSERT_STREQ(value.stringvalue, param.Value.stringvalue); + ASSERT_STREQ(value.stringvalue, params.Value.stringvalue); break; case ARGSPARSE_TYPE_INT: - ASSERT_EQ(value.intvalue, param.Value.intvalue); + ASSERT_EQ(value.intvalue, params.Value.intvalue); break; case ARGSPARSE_TYPE_DOUBLE: - ASSERT_DOUBLE_EQ(value.doublevalue, param.Value.doublevalue); + ASSERT_DOUBLE_EQ(value.doublevalue, params.Value.doublevalue); break; default: GTEST_FAIL();