From 1ef526c695df4b37aa184867fb5b62c93118aa02 Mon Sep 17 00:00:00 2001 From: Joel Kronqvist Date: Sun, 24 Mar 2024 10:00:19 +0200 Subject: Refactored configuration parsing to a separate file and added unit tests to it --- .gitignore | 1 + Makefile | 11 ++- config-parser.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ config-parser.h | 12 +++ stdu.c | 152 ++----------------------------------- tests.c | 8 ++ 6 files changed, 262 insertions(+), 148 deletions(-) create mode 100644 config-parser.c create mode 100644 config-parser.h diff --git a/.gitignore b/.gitignore index 26e8cf8..09bf9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ stdu stdu.o intmath.o +config-parser.o test diff --git a/Makefile b/Makefile index 685f42a..3c3b8ae 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -stdu : stdu.o intmath.o intmath.h - cc -o stdu stdu.o intmath.o +stdu : stdu.o intmath.o intmath.h config-parser.h config-parser.o + cc -o stdu stdu.o intmath.o config-parser.o stdu.o : stdu.c cc -c stdu.c @@ -8,9 +8,12 @@ stdu.o : stdu.c intmath.o : intmath.c minitest.h intmath.h cc -c intmath.c -test : tests.c minitest.h intmath.c +config-parser.o : config-parser.c config-parser.h minitest.h + cc -c config-parser.c + +test : tests.c minitest.h intmath.c config-parser.c cc -o test tests.c ./test clean : - rm stdu stdu.o intmath.o test + rm stdu stdu.o intmath.o test config-parser.o diff --git a/config-parser.c b/config-parser.c new file mode 100644 index 0000000..cd97dc7 --- /dev/null +++ b/config-parser.c @@ -0,0 +1,226 @@ + + +#include +#include +#include +#include +#include + +#include "minitest.h" +#include "config-parser.h" + +Result parse_config(int argc, char* argv[]) { + Result res; + res.success = false; + Config tmp; + tmp.precision = 0; + tmp.human_readable = false; + + size_t i = 1; + char* argument; + char* precision = NULL; + while (i < argc) { + argument = argv[i]; + int comp1 = strcmp(argument, "--precision") == 0; + if ( + comp1 || strncmp(argument, "-p", 2) == 0 + ) { + if (precision != NULL) { + char* error_msg = malloc(44); + snprintf( + error_msg, + 44, + "precision can't be specified multiple times" + ); + res.result = error_msg; + return res; + } + i++; + if (strlen(argument) != 2 && !comp1) { + precision = argument + 2; + continue; + } + if (i == argc) { + char* error_msg = malloc(57); + snprintf( + error_msg, + 57, + "`%s` should be followed by an integer argument", + argument + ); + res.result = error_msg; + return res; + } + precision = argv[i]; + } else if ( + strcmp(argument, "--human-readable") == 0 + || strcmp(argument, "-h") == 0 + ) { + if (tmp.human_readable != false) { + char* error_msg = malloc(52); + snprintf( + error_msg, + 52, + "human readability can't be specified multiple times" + ); + res.result = error_msg; + return res; + } + tmp.human_readable = true; + } else { + size_t msg_size = 21 + strlen(argument); + char* error_msg = malloc(msg_size); + snprintf( + error_msg, + msg_size, + "unknown argument: `%s`", + argument + ); + res.result = error_msg; + return res; + } + + i++; + } + + if (precision != NULL) { + char* endptr; + errno = 0; + tmp.precision = (int) strtol(precision, &endptr, 10); + if (errno != 0) { + char* error_msg = malloc(64); + char* errno_str = strerror(errno); + snprintf( + error_msg, + 64, + "invalid precision: %s", + errno_str + ); + free(errno_str); + res.result = error_msg; + return res; + } + if (*endptr != '\0') { + char* error_msg = malloc(53); + snprintf( + error_msg, + 53, + "the precision may not contain non-numeric characters", + precision + ); + res.result = error_msg; + return res; + } + if (tmp.precision <= 0) { + char* error_msg = malloc(50); + snprintf( + error_msg, + 50, + "the precision can't be less than or equal to zero" + ); + res.result = error_msg; + return res; + } + } + + Config* conf = malloc(sizeof(int) + sizeof(bool)); + conf->precision = tmp.precision; + conf->human_readable = tmp.human_readable; + + res.success = true; + res.result = conf; + return res; +} + +char* test_default_config() { + int argc = 1; + char* argv[] = { "stdu" }; + Result res = parse_config(argc, argv); + Config* conf = (Config*) res.result; + mt_assert_eq(res.success, true); + mt_assert_eq(conf->precision, 0); + mt_assert_eq(conf->human_readable, false); + return 0; +} + +char* test_precision_parsing() { + int argc; + Result res; + Config* conf; + char* error_msg; + + argc = 3; + char* argv[] = { "stdu", "--precision", "3" }; + res = parse_config(argc, argv); + conf = (Config*) res.result; + mt_assert_eq(res.success, true); + mt_assert_eq(conf->precision, 3); + mt_assert_eq(conf->human_readable, false); + free(conf); + + argc = 2; + char* argv2[] = { "stdu", "-p" }; + res = parse_config(argc, argv2); + mt_assert( + res.success == false, + "not specifying precision after `-p` didn't error" + ); + free(error_msg); + + argc = 4; + char* argv3[] = { "stdu", "--precision", "1", "-p3" }; + res = parse_config(argc, argv3); + mt_assert( + res.success == false, + "specifying precision multiple times didn't error" + ); + free(res.result); + + return 0; + +} + +char* test_human_readability_parsing() { + int argc; + Result res; + Config* conf; + char* error_msg; + + argc = 2; + char* argv[] = { + "stdu", + "--human-readable" + }; + res = parse_config(argc, argv); + conf = (Config*) res.result; + mt_assert_eq(res.success, true); + mt_assert_eq(conf->precision, 0); + mt_assert_eq(conf->human_readable, true); + free(conf); + + argc = 3; + char* argv3[] = { + "stdu", + "-h", + "-h" + }; + res = parse_config(argc, argv3); + error_msg = (char*) res.result; + mt_assert_eq(res.success, false); + mt_assert( + strcmp( + error_msg, + "human readability can't be specified multiple times" + ), + "should error on double argument of human readability" + ); + free(error_msg); + + return 0; +} + +void parsing_tests() { + mt_run_test(test_default_config); + mt_run_test(test_precision_parsing); + mt_run_test(test_human_readability_parsing); +} diff --git a/config-parser.h b/config-parser.h new file mode 100644 index 0000000..1026a98 --- /dev/null +++ b/config-parser.h @@ -0,0 +1,12 @@ + +struct Config { + int precision; + bool human_readable; +}; +typedef struct Config Config; +struct Result { + bool success; + void* result; +}; +typedef struct Result Result; +Result/*Config*/ parse_config(int argc, char* argv[]); diff --git a/stdu.c b/stdu.c index 43038a8..f7f5511 100644 --- a/stdu.c +++ b/stdu.c @@ -6,144 +6,11 @@ #include #include "intmath.h" +#include "config-parser.h" int tests_run = 0; int tests_failed = 0; -struct Config { - int precision; - bool human_readable; -}; -typedef struct Config Config; -struct Result { - bool success; - void* result; -}; -typedef struct Result Result; - -Result parse_config(int argc, char* argv[]) { - Result res; - res.success = false; - Config tmp; - tmp.precision = 0; - tmp.human_readable = false; - - size_t i = 1; - char* argument; - char* precision = NULL; - while (i < argc) { - argument = argv[i]; - int comp1 = strcmp(argument, "--precision") == 0; - if ( - comp1 || strncmp(argument, "-p", 2) == 0 - ) { - if (precision != NULL) { - char* error_msg = malloc(44); - snprintf( - error_msg, - 44, - "precision can't be specified multiple times" - ); - res.result = error_msg; - return res; - } - i++; - if (strlen(argument) != 2 && !comp1) { - precision = argument + 2; - continue; - } - if (i == argc) { - char* error_msg = malloc(57); - snprintf( - error_msg, - 57, - "`%s` should be followed by an integer argument", - argument - ); - res.result = error_msg; - return res; - } - precision = argv[i]; - } else if ( - strcmp(argument, "--human-readable") == 0 - || strcmp(argument, "-h") == 0 - ) { - if (tmp.human_readable != false) { - char* error_msg = malloc(52); - snprintf( - error_msg, - 52, - "human readability can't be specified multiple times" - ); - res.result = error_msg; - return res; - } - tmp.human_readable = true; - } else { - size_t msg_size = 21 + strlen(argument); - char* error_msg = malloc(msg_size); - snprintf( - error_msg, - msg_size, - "unknown argument: `%s`", - argument - ); - res.result = error_msg; - return res; - } - - i++; - } - - if (precision != NULL) { - char* endptr; - errno = 0; - tmp.precision = (int) strtol(precision, &endptr, 10); - if (errno != 0) { - char* error_msg = malloc(64); - char* errno_str = strerror(errno); - snprintf( - error_msg, - 64, - "invalid precision: %s", - errno_str - ); - free(errno_str); - res.result = error_msg; - return res; - } - if (*endptr != '\0') { - char* error_msg = malloc(53); - snprintf( - error_msg, - 53, - "the precision may not contain non-numeric characters", - precision - ); - res.result = error_msg; - return res; - } - if (tmp.precision <= 0) { - char* error_msg = malloc(50); - snprintf( - error_msg, - 50, - "the precision can't be less than or equal to zero" - ); - res.result = error_msg; - return res; - } - } - - Config* conf = malloc(sizeof(int) + sizeof(bool)); - conf->precision = tmp.precision; - conf->human_readable = tmp.human_readable; - - res.success = true; - res.result = conf; - return res; -} - int main (int argc, char* argv[]) { Result res = parse_config(argc, argv); if (res.success == false) { @@ -157,14 +24,11 @@ int main (int argc, char* argv[]) { } Config* conf = (Config*) res.result; - int precision = conf->precision; - bool human_readable = conf->human_readable; - printf("precision: %d\n", precision); - if (human_readable) printf("human readable\n"); + if (conf->human_readable) printf("human readable\n"); unsigned int base = 10; - if (human_readable && precision == 0) { - precision = 1; + if (conf->human_readable && conf->precision == 0) { + conf->precision = 1; base = 1024; } @@ -172,10 +36,10 @@ int main (int argc, char* argv[]) { size_t bufsize = 1; unsigned int bytes_read = 0; size_t new_read_bytes = 0; - if (precision != 0) { + if (conf->precision != 0) { blocksize = exp_notated_to_int(int_ceiled_exponent_notation_base( bytes_read + 1, - precision, + conf->precision, base)) - bytes_read; bufsize = 1024; @@ -214,10 +78,10 @@ int main (int argc, char* argv[]) { /* resizing buffer and blocksize to read as much as possible * at once */ - if (precision == 0) continue; + if (conf->precision == 0) continue; blocksize = exp_notated_to_int(int_ceiled_exponent_notation_base( bytes_read + 1, - precision, + conf->precision, base)) - bytes_read; if (blocksize > bufsize) { diff --git a/tests.c b/tests.c index 138a994..f4a047d 100644 --- a/tests.c +++ b/tests.c @@ -1,13 +1,21 @@ #include "minitest.h" +#include "config-parser.c" #include "intmath.c" int tests_run = 0; int tests_failed = 0; +void intmath_tests(); +void main_tests(); + int main() { printf("[INFO] Running tests...\n\n"); + printf("[INFO] Running main-tests...\n"); + parsing_tests(); + printf("\n"); + printf("[INFO] Running intmath-tests...\n"); intmath_tests(); printf("\n"); -- cgit v1.2.3