aboutsummaryrefslogtreecommitdiff
path: root/tests.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests.c')
-rw-r--r--tests.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/tests.c b/tests.c
new file mode 100644
index 0000000..0eb56ef
--- /dev/null
+++ b/tests.c
@@ -0,0 +1,353 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "version_compare.h"
+
+struct TestCase_strings {
+ char *s;
+ const char *result;
+};
+
+static struct TestCase_strings test_cases_collapse_whitespace[] = {
+ {"", ""},
+ {" ", ""},
+ {" ", ""},
+ {" leading", "leading"},
+ {"trailing ", "trailing"},
+ {" leading", "leading"},
+ {" leading and trailing ", "leading and trailing"},
+ {"This line will be collapsed", "This line will be collapsed"},
+};
+
+static struct TestCase_strings test_cases_lstrip[] = {
+ {"", ""},
+ {" ", ""},
+ {" ", ""},
+ {" leading", "leading"},
+ {"trailing ", "trailing "},
+ {" leading", "leading"},
+ {" leading and trailing ", "leading and trailing "},
+ {"This line will be collapsed", "This line will be collapsed"},
+};
+
+static struct TestCase_strings test_cases_rstrip[] = {
+ {"", ""},
+ {" ", ""},
+ {" ", ""},
+ {" leading", " leading"},
+ {"trailing ", "trailing"},
+ {" leading", " leading"},
+ {" leading and trailing ", " leading and trailing"},
+ {"This line will be collapsed", "This line will be collapsed"},
+};
+
+struct TestCase_version_compare {
+ char *a, *op, *b;
+ int result;
+};
+
+static struct TestCase_version_compare test_cases_version_compare[] = {
+ {"0", "=", "0", 1},
+ {"0", "<", "1",1},
+ {"0", "<=", "1",1},
+ {"0", ">", "1",0},
+ {"0", ">=", "1",0},
+ {"0", "!=", "1",1},
+
+ {"1a", "=", "1b", 0},
+ {"1a", "<", "1b", 1},
+ {"1a", "<=", "1b", 1},
+ {"1a", ">", "1b", 0},
+ {"1a", ">=", "1b", 0},
+ {"1a", "!=", "1b", 1},
+
+ {"1.0", "=", "1.0.0", 1},
+ {"1.0", "<", "1.0.0", 0},
+ {"1.0", "<=", "1.0.0", 1},
+ {"1.0", ">", "1.0.0", 0},
+ {"1.0", ">=", "1.0.0", 1},
+ {"1.0", "!=", "1.0.0", 0},
+
+ {"1.0a", "=", "1.0.0", 0},
+ {"1.0a", "<", "1.0.0", 0},
+ {"1.0a", "<=", "1.0.0", 0},
+ {"1.0a", ">", "1.0.0", 1},
+ {"1.0a", ">=", "1.0.0", 1},
+ {"1.0a", "!=", "1.0.0", 1},
+
+ {"2022.1", "=", "2022.4", 0},
+ {"2022.1", "<", "2022.4", 1},
+ {"2022.1", "<=", "2022.4", 1},
+ {"2022.1", ">", "2022.4", 0},
+ {"2022.1", ">=", "2022.4", 0},
+ {"2022.1", "!=", "2022.4", 1},
+
+ {"2022.4", "=", "2022.1", 0},
+ {"2022.4", "<", "2022.1", 0},
+ {"2022.4", "<=", "2022.1", 0},
+ {"2022.4", ">", "2022.1", 1},
+ {"2022.4", ">=", "2022.1", 1},
+ {"2022.4", "!=", "2022.1", 1},
+};
+
+static struct TestCase_version_compare error_cases_version_compare[] = {
+ // nonsense++
+ {"", "=", "", -1},
+ {" ", "=", " ", -1},
+ {"a", "", "a", -1},
+ {"a", "", "b", -1},
+ {"a", "@", "b", -1},
+};
+
+static int run_cases_version_compare(struct TestCase_version_compare tests[], size_t size) {
+ int failed = 0;
+ for (size_t i = 0; i < size; i++) {
+ int result = 0;
+ struct TestCase_version_compare *test = &tests[i];
+ int op = version_parse_operator(test->op);
+ result = version_compare(op, test->a, test->b);
+
+ printf("%s %s %s is %s", test->a, test->op, test->b, result ? "TRUE" : "FALSE" );
+ if (test->result != result) {
+ printf(" [FAILED: got %d, expected %d]\n", result, test->result);
+ failed++;
+ } else {
+ puts("");
+ }
+ }
+ return failed;
+}
+
+typedef char *(*strfn) (char **s);
+
+static int run_cases_string(struct TestCase_strings tests[], size_t size, strfn fn) {
+ int failed = 0;
+ for (size_t i = 0; i < size; i++) {
+ int result = 0;
+ struct TestCase_strings *test = &tests[i];
+ char *s = strdup(test->s);
+
+ fn(&s);
+ result = strcmp(test->result, s);
+
+ printf("'%s' is %s", s, !result ? "CORRECT" : "INCORRECT" );
+ if (result) {
+ printf(" [FAILED: got '%s', expected '%s']\n", s, test->result);
+ failed++;
+ } else {
+ puts("");
+ }
+ free(s);
+ }
+ return failed;
+}
+
+
+char **make_argv(int *argc, char *argv[]) {
+ char **args;
+ *argc = 0;
+
+ for (size_t i = 0; argv[i] != NULL; i++) {
+ *argc = *argc + 1;
+ }
+
+ //char **args = calloc(*argc + 1, sizeof(*argv));
+ args = calloc(*argc + 1, sizeof(*argv));
+ if (!args) {
+ perror("unable to allocate args array");
+ return NULL;
+ }
+
+ args[0] = strdup("version_compare");
+ if (!args[0]) {
+ perror("unable to allocate mock program name");
+ return NULL;
+ }
+
+ for (int i = 0; i < *argc; i++) {
+ args[i + 1] = strdup(argv[i]);
+ if (!args[i + 1]) {
+ perror("unable to allocate argument in array");
+ return NULL;
+ }
+ }
+ *argc = *argc + 1;
+ return args;
+}
+
+void free_argv(int argc, char *argv[]) {
+ for (int i = 0; i < argc; i++) {
+ free(argv[i]);
+ }
+ free(argv);
+}
+
+int run_program(char *argv[]) {
+ int result = 0;
+ int argc = 0;
+ char **args = NULL;
+
+ args = make_argv(&argc, argv);
+ if (!args) {
+ perror("make_argv failed");
+ return -1;
+ }
+ const char *filename = "stdout.log";
+ char data[255] = {0};
+ char *end = NULL;
+ int o_stdout;
+ int save_stdout;
+
+
+ o_stdout = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (o_stdout == -1) {
+ perror("unable to open stdout log");
+ goto run_program_failed;
+ }
+
+ save_stdout = dup(fileno(stdout));
+ if (save_stdout == -1) {
+ perror("unable to duplicate stdout");
+ goto run_program_failed;
+ }
+
+ fflush(stdout);
+ if (dup2(o_stdout, fileno(stdout)) == -1) {
+ perror("unable to redirect stdout");
+ goto run_program_failed;
+ }
+
+ result = entry(argc, args);
+
+ fflush(stdout);
+ close(o_stdout);
+
+ if (dup2(save_stdout, fileno(stdout)) == -1) {
+ free_argv(argc, args);
+ perror("unable to restore stdout");
+ goto run_program_failed;
+ }
+ close(save_stdout);
+
+ if (result < 0) {
+ free_argv(argc, args);
+ return result;
+ }
+
+ if (access(filename, F_OK) < 0) {
+ perror(filename);
+ goto run_program_failed;
+ }
+
+ FILE *fp = fopen(filename, "r");
+ if (!fp) {
+ perror("unable to open output log file");
+ goto run_program_failed;
+ }
+
+ if ((fgets(data, sizeof(data) - 1, fp)) == NULL) {
+ fprintf(stderr, "no output recorded in main program execution\n");
+ remove(filename);
+ fclose(fp);
+ goto run_program_failed;
+ }
+
+ result = (int)strtol(data, &end, 10);
+ if (!end) {
+ fprintf(stderr, "unexpected error in main program execution\n");
+ remove(filename);
+ fclose(fp);
+ goto run_program_failed;
+ }
+
+ fclose(fp);
+ remove(filename);
+ free_argv(argc, args);
+ return result;
+
+run_program_failed:
+ free_argv(argc, args);
+ return -1;
+}
+
+static int run_cases_program_split(struct TestCase_version_compare tests[], size_t size) {
+ int failed = 0;
+ for (size_t i = 0; i < size; i++) {
+ int result = 0;
+ struct TestCase_version_compare *test = &tests[i];
+ result = run_program((char *[]){test->a, test->op, test->b, NULL});
+
+ printf("%s %s %s is %s", test->a, test->op, test->b, result ? "TRUE" : "FALSE" );
+ if (test->result != result) {
+ printf(" [FAILED: got %d, expected %d]\n", result, test->result);
+ failed++;
+ } else {
+ puts("");
+ }
+ }
+ return failed;
+}
+
+static int run_cases_program_standalone(struct TestCase_version_compare tests[], size_t size) {
+ int failed = 0;
+ for (size_t i = 0; i < size; i++) {
+ int result = 0;
+ struct TestCase_version_compare *test = &tests[i];
+ char data[255] = {0};
+
+ snprintf(data, sizeof(data) - 1, "%s %s %s", test->a, test->op, test->b);
+ result = run_program((char *[]){data, NULL});
+
+ printf("%s %s %s is %s", test->a, test->op, test->b, result ? "TRUE" : "FALSE" );
+ if (test->result != result) {
+ printf(" [FAILED: got %d, expected %d]\n", result, test->result);
+ failed++;
+ } else {
+ puts("");
+ }
+ }
+ return failed;
+}
+
+int main() {
+ int failed = 0;
+
+ printf("\nTEST version_compare()\n");
+ failed += run_cases_version_compare(test_cases_version_compare,
+ sizeof(test_cases_version_compare) / sizeof(test_cases_version_compare[0]));
+ printf("\nTEST version_compare errors()\n");
+ failed += run_cases_version_compare(error_cases_version_compare,
+ sizeof(error_cases_version_compare) / sizeof(error_cases_version_compare[0]));
+ printf("\nTEST collapse_whitespace()\n");
+ failed += run_cases_string(test_cases_collapse_whitespace,
+ sizeof(test_cases_collapse_whitespace) / sizeof(test_cases_collapse_whitespace[0]),
+ &collapse_whitespace);
+ printf("\nTEST lstrip()\n");
+ failed += run_cases_string(test_cases_lstrip,
+ sizeof(test_cases_lstrip) / sizeof(test_cases_lstrip[0]),
+ &lstrip);
+ printf("\nTEST rstrip()\n");
+ failed += run_cases_string(test_cases_rstrip,
+ sizeof(test_cases_rstrip) / sizeof(test_cases_rstrip[0]),
+ &rstrip);
+
+ printf("\nTEST main program entry point (split string)\n");
+ failed += run_cases_program_split(test_cases_version_compare,
+ sizeof(test_cases_version_compare) / sizeof(test_cases_version_compare[0]));
+
+ printf("\nTEST main program entry point errors (split string)\n");
+ failed += run_cases_program_split(error_cases_version_compare,
+ sizeof(error_cases_version_compare) / sizeof(error_cases_version_compare[0]));
+
+ printf("\nTEST main program entry point (standalone string)\n");
+ failed += run_cases_program_standalone(test_cases_version_compare,
+ sizeof(test_cases_version_compare) / sizeof(test_cases_version_compare[0]));
+
+ printf("\nTEST main program entry point errors (standalone string)\n");
+ failed += run_cases_program_standalone(error_cases_version_compare,
+ sizeof(error_cases_version_compare) / sizeof(error_cases_version_compare[0]));
+
+ return failed != 0;
+}