diff --git a/sources.mk b/sources.mk index 7d66eb4..d1d5e45 100644 --- a/sources.mk +++ b/sources.mk @@ -18,8 +18,18 @@ SRCS = $(SRC_DIR)/main.c \ $(SRC_DIR)/validator/validator.c TESTS_DIR = tests -TESTS = $(TESTS_DIR)/test_utils.c \ - $(TESTS_DIR)/test_map.c \ - $(TESTS_DIR)/test_transpile.c \ - $(TESTS_DIR)/test_cli.c \ - $(TESTS_DIR)/test_integration.c +TESTS = $(TESTS_DIR)/test_integration.c \ + $(TESTS_DIR)/io/test_streams.c \ + $(TESTS_DIR)/utils/io/test_read_line.c \ + $(TESTS_DIR)/utils/string/test_starts_with.c \ + $(TESTS_DIR)/utils/string/test_extract_fence_ext.c \ + $(TESTS_DIR)/utils/string/test_extract_file_ext.c \ + $(TESTS_DIR)/utils/string/test_infer_ext_from_filename.c \ + $(TESTS_DIR)/cli/test_cli.c \ + $(TESTS_DIR)/cli/test_help.c \ + $(TESTS_DIR)/map/test_map_core.c \ + $(TESTS_DIR)/map/test_map_io.c \ + $(TESTS_DIR)/transpile/test_transpile_core.c \ + $(TESTS_DIR)/transpile/test_transpile_fence.c \ + $(TESTS_DIR)/transpile/test_transpile_state.c \ + $(TESTS_DIR)/transpile/test_transpile_code.c diff --git a/tests/cli/test_cli.c b/tests/cli/test_cli.c new file mode 100644 index 0000000..a31b362 --- /dev/null +++ b/tests/cli/test_cli.c @@ -0,0 +1,162 @@ +#include + +#include "cli.h" + +Test(cli, parse_ext_short) +{ + t_args args; + char *argv[] = {"c-md", "-e", "c"}; + int8_t ret; + + ret = cli_parse(&args, 3, argv); + + cr_assert_eq(ret, 0); + cr_assert_not_null(args.ext); + cr_assert_str_eq(args.ext, "c"); + cr_assert_eq(args.show_help, 0); +} + +Test(cli, parse_ext_long) +{ + t_args args; + char *argv[] = {"c-md", "--ext", "python"}; + int8_t ret; + + ret = cli_parse(&args, 3, argv); + + cr_assert_eq(ret, 0); + cr_assert_not_null(args.ext); + cr_assert_str_eq(args.ext, "python"); +} + +/* +** TESTS: cli/cli.c - Input option +*/ + +Test(cli, parse_input_short) +{ + t_args args; + char *argv[] = {"c-md", "-i", "input.md"}; + int8_t ret; + + ret = cli_parse(&args, 3, argv); + + cr_assert_eq(ret, 0); + cr_assert_not_null(args.input); + cr_assert_str_eq(args.input, "input.md"); +} + +Test(cli, parse_input_long) +{ + t_args args; + char *argv[] = {"c-md", "--input", "input.md"}; + int8_t ret; + + ret = cli_parse(&args, 3, argv); + + cr_assert_eq(ret, 0); + cr_assert_not_null(args.input); + cr_assert_str_eq(args.input, "input.md"); +} + +/* +** TESTS: cli/cli.c - Output option +*/ + +Test(cli, parse_output) +{ + t_args args; + char *argv[] = {"c-md", "-o", "output.c"}; + int8_t ret; + + ret = cli_parse(&args, 3, argv); + + cr_assert_eq(ret, 0); + cr_assert_not_null(args.output); + cr_assert_str_eq(args.output, "output.c"); +} + +/* +** TESTS: cli/cli.c - Map option +*/ + +Test(cli, parse_map_short) +{ + t_args args; + char *argv[] = {"c-md", "-m", "output.map"}; + int8_t ret; + + ret = cli_parse(&args, 3, argv); + + cr_assert_eq(ret, 0); + cr_assert_not_null(args.map_path); + cr_assert_str_eq(args.map_path, "output.map"); +} + +Test(cli, parse_map_long) +{ + t_args args; + char *argv[] = {"c-md", "--map", "output.map"}; + int8_t ret; + + ret = cli_parse(&args, 3, argv); + + cr_assert_eq(ret, 0); + cr_assert_not_null(args.map_path); + cr_assert_str_eq(args.map_path, "output.map"); +} + +/* +** TESTS: cli/cli.c - Multiple arguments +*/ + +Test(cli, parse_multiple_args) +{ + t_args args; + char *argv[] = { + "c-md", "-e", "c", "-i", "input.c.md", + "-o", "output.c", "-m", "output.map" + }; + int8_t ret; + + ret = cli_parse(&args, 9, argv); + + cr_assert_eq(ret, 0); + cr_assert_str_eq(args.ext, "c"); + cr_assert_str_eq(args.input, "input.c.md"); + cr_assert_str_eq(args.output, "output.c"); + cr_assert_str_eq(args.map_path, "output.map"); + cr_assert_eq(args.show_help, 0); +} + +/* +** TESTS: cli/cli.c - Edge cases +*/ + +Test(cli, parse_no_args) +{ + t_args args; + char *argv[] = {"c-md"}; + int8_t ret; + + ret = cli_parse(&args, 1, argv); + + cr_assert_eq(ret, 0); + cr_assert_null(args.ext); + cr_assert_null(args.input); + cr_assert_null(args.output); + cr_assert_null(args.map_path); + cr_assert_eq(args.show_help, 0); +} + +//TODO: mute stderr +Test(cli, parse_invalid_arg) +{ + t_args args; + char *argv[] = {"c-md", "-x"}; + int8_t ret; + + ret = cli_parse(&args, 2, argv); + + cr_assert_neq(ret, 0); +} diff --git a/tests/cli/test_help.c b/tests/cli/test_help.c new file mode 100644 index 0000000..4c06be7 --- /dev/null +++ b/tests/cli/test_help.c @@ -0,0 +1,32 @@ +#include + +#include "cli.h" + +/* + ** TESTS: cli/cli.c - Help option + */ + +Test(cli, parse_help_short) +{ + t_args args; + char *argv[] = {"c-md", "-h"}; + int8_t ret; + + ret = cli_parse(&args, 2, argv); + + cr_assert_eq(ret, 0); + cr_assert_eq(args.show_help, 1); +} + +Test(cli, parse_help_long) +{ + t_args args; + char *argv[] = {"c-md", "--help"}; + int8_t ret; + + ret = cli_parse(&args, 2, argv); + + cr_assert_eq(ret, 0); + cr_assert_eq(args.show_help, 1); +} + diff --git a/tests/io/test_streams.c b/tests/io/test_streams.c new file mode 100644 index 0000000..808d9d1 --- /dev/null +++ b/tests/io/test_streams.c @@ -0,0 +1,103 @@ +#include +#include + +#include "io.h" + +/* +** TESTS: io/streams.c +*/ + +Test(io, open_with_files) +{ + t_io io; + int8_t ret; + + FILE *f = fopen("/tmp/test_io_in.md", "w"); + fprintf(f, "test\n"); + fclose(f); + + ret = io_open(&io, "/tmp/test_io_in.md", "/tmp/test_io_out.c"); + + cr_assert_eq(ret, 0); + cr_assert_not_null(io.in); + cr_assert_not_null(io.out); + cr_assert_neq(io.in, stdin); + cr_assert_neq(io.out, stdout); + + io_close(&io); + remove("/tmp/test_io_in.md"); + remove("/tmp/test_io_out.c"); +} + +Test(io, open_with_stdin_stdout) +{ + t_io io; + int8_t ret; + + ret = io_open(&io, NULL, NULL); + + cr_assert_eq(ret, 0); + cr_assert_eq(io.in, stdin); + cr_assert_eq(io.out, stdout); + + io_close(&io); +} + +Test(io, open_with_invalid_input) +{ + t_io io; + int8_t ret; + + ret = io_open(&io, "/tmp/nonexistent_file_123456.md", "/tmp/test_io_out.c"); + + cr_assert_eq(ret, 1); +} + +Test(io, open_input_only) +{ + t_io io; + int8_t ret; + + FILE *f = fopen("/tmp/test_io_input.md", "w"); + fprintf(f, "test\n"); + fclose(f); + + ret = io_open(&io, "/tmp/test_io_input.md", NULL); + + cr_assert_eq(ret, 0); + cr_assert_not_null(io.in); + cr_assert_neq(io.in, stdin); + cr_assert_eq(io.out, stdout); + + io_close(&io); + remove("/tmp/test_io_input.md"); +} + +Test(io, open_output_only) +{ + t_io io; + int8_t ret; + + ret = io_open(&io, NULL, "/tmp/test_io_output.c"); + + cr_assert_eq(ret, 0); + cr_assert_eq(io.in, stdin); + cr_assert_not_null(io.out); + cr_assert_neq(io.out, stdout); + + io_close(&io); + remove("/tmp/test_io_output.c"); +} + +Test(io, close_does_not_close_stdin_stdout) +{ + t_io io; + + io.in = stdin; + io.out = stdout; + + io_close(&io); + + cr_assert_eq(io.in, stdin); + cr_assert_eq(io.out, stdout); +} diff --git a/tests/map/test_map_core.c b/tests/map/test_map_core.c new file mode 100644 index 0000000..edaae81 --- /dev/null +++ b/tests/map/test_map_core.c @@ -0,0 +1,118 @@ +#include +#include +#include + +#include "map.h" + +/* +** TESTS: map/core.c - Initialization +*/ + +Test(map_core, init) +{ + t_map map; + + map_init(&map); + cr_assert_null(map.ranges); + cr_assert_eq(map.count, 0); + cr_assert_eq(map.capacity, 0); +} + +/* +** TESTS: map/core.c - Adding ranges +*/ + +Test(map_core, add_single_range) +{ + t_map map; + + map_init(&map); + map_add(&map, 10, 20, 5, 15); + + cr_assert_not_null(map.ranges); + cr_assert_eq(map.count, 1); + cr_assert_geq(map.capacity, 1); + + cr_assert_eq(map.ranges[0].src_start, 10); + cr_assert_eq(map.ranges[0].src_end, 20); + cr_assert_eq(map.ranges[0].dst_start, 5); + cr_assert_eq(map.ranges[0].dst_end, 15); + + map_free(&map); +} + +Test(map_core, add_multiple_ranges) +{ + t_map map; + + map_init(&map); + map_add(&map, 1, 5, 1, 5); + map_add(&map, 10, 15, 7, 12); + map_add(&map, 20, 25, 14, 19); + + cr_assert_eq(map.count, 3); + cr_assert_geq(map.capacity, 3); + + cr_assert_eq(map.ranges[0].src_start, 1); + cr_assert_eq(map.ranges[1].src_start, 10); + cr_assert_eq(map.ranges[2].src_start, 20); + + map_free(&map); +} + +/* +** TESTS: map/core.c - Dynamic growth +*/ + +Test(map_core, capacity_growth) +{ + t_map map; + size_t i; + + map_init(&map); + + for (i = 0; i < 100; i++) + { + map_add(&map, i, i + 10, i, i + 10); + } + + cr_assert_eq(map.count, 100); + cr_assert_geq(map.capacity, 100); + + for (i = 0; i < 100; i++) + { + cr_assert_eq(map.ranges[i].src_start, i); + cr_assert_eq(map.ranges[i].src_end, i + 10); + } + + map_free(&map); +} + +/* +** TESTS: map/core.c - Memory management +*/ + +Test(map_core, free_after_init) +{ + t_map map; + + map_init(&map); + map_free(&map); + + cr_assert_null(map.ranges); + cr_assert_eq(map.count, 0); + cr_assert_eq(map.capacity, 0); +} + +Test(map_core, free_after_add) +{ + t_map map; + + map_init(&map); + map_add(&map, 1, 10, 1, 10); + map_free(&map); + + cr_assert_null(map.ranges); + cr_assert_eq(map.count, 0); + cr_assert_eq(map.capacity, 0); +} diff --git a/tests/map/test_map_io.c b/tests/map/test_map_io.c new file mode 100644 index 0000000..1dd6c67 --- /dev/null +++ b/tests/map/test_map_io.c @@ -0,0 +1,78 @@ +#include +#include + +#include "map.h" + +/* +** TESTS: map/io.c - Writing to file +*/ + +Test(map_io, write_to_file) +{ + t_map map; + FILE *f; + char buffer[256]; + const char *path = "/tmp/test_c_md_map.map"; + + map_init(&map); + map_add(&map, 2, 8, 1, 7); + map_add(&map, 15, 22, 9, 16); + + cr_assert_eq(map_write(&map, path, "main.c.md", "main.c"), 0); + + f = fopen(path, "r"); + cr_assert_not_null(f); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "C-MD MAP v1\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "source: main.c.md\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "target: main.c\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "---\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "2-8:1-7\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "15-22:9-16\n"); + + fclose(f); + remove(path); + map_free(&map); +} + +Test(map_io, write_empty_map) +{ + t_map map; + FILE *f; + char buffer[256]; + const char *path = "/tmp/test_c_md_empty_map.map"; + + map_init(&map); + + cr_assert_eq(map_write(&map, path, "test.c.md", "test.c"), 0); + + f = fopen(path, "r"); + cr_assert_not_null(f); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "C-MD MAP v1\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "source: test.c.md\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "target: test.c\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "---\n"); + + fclose(f); + remove(path); + map_free(&map); +} diff --git a/tests/test_integration.c b/tests/test_integration.c new file mode 100644 index 0000000..d1dcbe5 --- /dev/null +++ b/tests/test_integration.c @@ -0,0 +1,197 @@ +#include +#include + +#include "cli.h" +#include "validator.h" +#include "transpile.h" +#include "map.h" + +static const char *sample_input = + "# Sample C Program\n" + "\n" + "This is a simple C program.\n" + "\n" + "```c\n" + "#include \n" + "```\n" + "\n" + "Main function:\n" + "\n" + "```c\n" + "int main(void) {\n" + " printf(\"Hello, World!\\n\");\n" + " return 0;\n" + "}\n" + "```\n"; + +/* +** TESTS: Integration - File mode +*/ + +Test(integration, file_mode_basic) +{ + const char *in_path = "/tmp/test_integration_in.c.md"; + const char *out_path = "/tmp/test_integration_out.c"; + FILE *in; + FILE *out; + FILE *f; + t_map map; + char buffer[256]; + int8_t ret; + + f = fopen(in_path, "w"); + fprintf(f, "%s", sample_input); + fclose(f); + + in = fopen(in_path, "r"); + out = fopen(out_path, "w"); + map_init(&map); + ret = transpile(in, out, "c", &map); + map_free(&map); + fclose(in); + fclose(out); + + cr_assert_eq(ret, 0); + + f = fopen(out_path, "r"); + cr_assert_not_null(f); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "#include \n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "\n"); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "int main(void) {\n"); + + fclose(f); + remove(in_path); + remove(out_path); +} + +Test(integration, file_mode_with_map) +{ + const char *in_path = "/tmp/test_integration_map_in.c.md"; + const char *out_path = "/tmp/test_integration_map_out.c"; + const char *map_path = "/tmp/test_integration.map"; + FILE *in; + FILE *out; + FILE *f; + t_map map; + char buffer[256]; + int8_t ret; + + f = fopen(in_path, "w"); + fprintf(f, "%s", sample_input); + fclose(f); + + in = fopen(in_path, "r"); + out = fopen(out_path, "w"); + map_init(&map); + ret = transpile(in, out, "c", &map); + if (0 == ret) + ret = map_write(&map, map_path, in_path, out_path); + map_free(&map); + fclose(in); + fclose(out); + + cr_assert_eq(ret, 0); + + f = fopen(map_path, "r"); + cr_assert_not_null(f); + + fgets(buffer, sizeof(buffer), f); + cr_assert_str_eq(buffer, "C-MD MAP v1\n"); + + fclose(f); + remove(in_path); + remove(out_path); + remove(map_path); +} + +/* +** TESTS: Integration - Validator +*/ + +Test(integration, validator_with_input_and_output) +{ + t_args args; + + args.ext = "c"; + args.input = "test.c.md"; + args.output = "test.c"; + args.map_path = NULL; + args.show_help = 0; + + cr_assert_eq(validator_validate_args(&args), 0); +} + +Test(integration, validator_stdin_missing_ext) +{ + t_args args; + + args.ext = NULL; + args.input = NULL; + args.output = "test.c"; + args.map_path = NULL; + args.show_help = 0; + + cr_assert_eq(validator_validate_args(&args), 1); +} + +Test(integration, validator_file_missing_ext) +{ + t_args args; + + args.ext = NULL; + args.input = "readme.md"; + args.output = NULL; + args.map_path = NULL; + args.show_help = 0; + + cr_assert_eq(validator_validate_args(&args), 2); +} + +Test(integration, validator_extract_ext_from_input) +{ + t_args args; + + args.ext = NULL; + args.input = "test.c.md"; + args.output = "test.c"; + args.map_path = NULL; + args.show_help = 0; + + cr_assert_eq(validator_validate_args(&args), 0); + cr_assert_not_null(args.ext); + cr_assert_str_eq(args.ext, "c"); +} + +/* +** TESTS: Integration - CLI + Validator +*/ + +Test(integration, cli_and_validator_pipe_mode) +{ + t_args args; + char *argv[] = {"c-md", "-e", "c"}; + + cli_parse(&args, 3, argv); + cr_assert_eq(validator_validate_args(&args), 0); + cr_assert_str_eq(args.ext, "c"); + cr_assert_null(args.input); + cr_assert_null(args.output); +} + +Test(integration, cli_and_validator_file_mode) +{ + t_args args; + char *argv[] = {"c-md", "-e", "c", "-i", "input.md", "-o", "output.c"}; + + cli_parse(&args, 7, argv); + cr_assert_eq(validator_validate_args(&args), 0); + cr_assert_str_eq(args.ext, "c"); + cr_assert_str_eq(args.input, "input.md"); + cr_assert_str_eq(args.output, "output.c"); +} diff --git a/tests/transpile/test_transpile_code.c b/tests/transpile/test_transpile_code.c new file mode 100644 index 0000000..d24a3a4 --- /dev/null +++ b/tests/transpile/test_transpile_code.c @@ -0,0 +1,103 @@ +#include +#include + +#include "internal/transpile_internal.h" + +/* +** TESTS: transpile/code.c - Handle code lines +*/ + +Test(transpile_code, handle_single_line) +{ + t_state s; + FILE *out; + t_map map; + char buffer[256]; + int8_t ret; + + out = fopen("/tmp/test_code_out.c", "w"); + map_init(&map); + state_init(&s, NULL, out, "c", &map); + + ret = handle_code_line(&s, "int x = 42;\n"); + + cr_assert_eq(ret, 0); + cr_assert_eq(s.dst_line, 1); + + fclose(out); + + out = fopen("/tmp/test_code_out.c", "r"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "int x = 42;\n"); + fclose(out); + + map_free(&map); + remove("/tmp/test_code_out.c"); +} + +Test(transpile_code, handle_multiple_lines) +{ + t_state s; + FILE *out; + t_map map; + char buffer[256]; + int8_t ret; + + out = fopen("/tmp/test_code_multi.c", "w"); + map_init(&map); + state_init(&s, NULL, out, "c", &map); + + ret = handle_code_line(&s, "int main() {\n"); + cr_assert_eq(ret, 0); + cr_assert_eq(s.dst_line, 1); + + ret = handle_code_line(&s, " return 0;\n"); + cr_assert_eq(ret, 0); + cr_assert_eq(s.dst_line, 2); + + ret = handle_code_line(&s, "}\n"); + cr_assert_eq(ret, 0); + cr_assert_eq(s.dst_line, 3); + + fclose(out); + + out = fopen("/tmp/test_code_multi.c", "r"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "int main() {\n"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, " return 0;\n"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "}\n"); + fclose(out); + + map_free(&map); + remove("/tmp/test_code_multi.c"); +} + +Test(transpile_code, handle_empty_line) +{ + t_state s; + FILE *out; + t_map map; + char buffer[256]; + int8_t ret; + + out = fopen("/tmp/test_code_empty.c", "w"); + map_init(&map); + state_init(&s, NULL, out, "c", &map); + + ret = handle_code_line(&s, "\n"); + + cr_assert_eq(ret, 0); + cr_assert_eq(s.dst_line, 1); + + fclose(out); + + out = fopen("/tmp/test_code_empty.c", "r"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "\n"); + fclose(out); + + map_free(&map); + remove("/tmp/test_code_empty.c"); +} diff --git a/tests/transpile/test_transpile_core.c b/tests/transpile/test_transpile_core.c new file mode 100644 index 0000000..772a608 --- /dev/null +++ b/tests/transpile/test_transpile_core.c @@ -0,0 +1,173 @@ +#include +#include + +#include "transpile.h" +#include "map.h" + +static const char *sample_md_1 = + "# Test\n" + "```c\n" + "int main() {\n" + " return 0;\n" + "}\n" + "```\n"; + +static const char *sample_md_2 = + "# Multiple blocks\n" + "```c\n" + "#include \n" + "```\n" + "Some text\n" + "```c\n" + "int main() {\n" + " return 0;\n" + "}\n" + "```\n"; + +/* +** TESTS: transpile/core.c - Single code block +*/ + +Test(transpile, single_code_block) +{ + FILE *in; + FILE *out; + t_map map; + char buffer[256]; + int8_t ret; + + in = fopen("/tmp/test_transpile_in.md", "w"); + fprintf(in, "%s", sample_md_1); + fclose(in); + + in = fopen("/tmp/test_transpile_in.md", "r"); + out = fopen("/tmp/test_transpile_out.c", "w"); + + map_init(&map); + ret = transpile(in, out, "c", &map); + + fclose(in); + fclose(out); + + cr_assert_eq(ret, 0); + cr_assert_eq(map.count, 1); + cr_assert_eq(map.ranges[0].src_start, 3); + cr_assert_eq(map.ranges[0].src_end, 5); + cr_assert_eq(map.ranges[0].dst_start, 1); + cr_assert_eq(map.ranges[0].dst_end, 3); + + out = fopen("/tmp/test_transpile_out.c", "r"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "int main() {\n"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, " return 0;\n"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "}\n"); + fclose(out); + + map_free(&map); + remove("/tmp/test_transpile_in.md"); + remove("/tmp/test_transpile_out.c"); +} + +/* +** TESTS: transpile/core.c - Multiple blocks +*/ + +Test(transpile, multiple_blocks) +{ + FILE *in; + FILE *out; + t_map map; + char buffer[256]; + int8_t ret; + + in = fopen("/tmp/test_transpile_multi_in.md", "w"); + fprintf(in, "%s", sample_md_2); + fclose(in); + + in = fopen("/tmp/test_transpile_multi_in.md", "r"); + out = fopen("/tmp/test_transpile_multi_out.c", "w"); + + map_init(&map); + ret = transpile(in, out, "c", &map); + + fclose(in); + fclose(out); + + cr_assert_eq(ret, 0); + cr_assert_eq(map.count, 2); + + out = fopen("/tmp/test_transpile_multi_out.c", "r"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "#include \n"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "\n"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "int main() {\n"); + fclose(out); + + map_free(&map); + remove("/tmp/test_transpile_multi_in.md"); + remove("/tmp/test_transpile_multi_out.c"); +} + +/* +** TESTS: transpile/core.c - Edge cases +*/ + +Test(transpile, empty_input) +{ + FILE *in; + FILE *out; + t_map map; + int8_t ret; + + in = fopen("/tmp/test_transpile_empty_in.md", "w"); + fclose(in); + + in = fopen("/tmp/test_transpile_empty_in.md", "r"); + out = fopen("/tmp/test_transpile_empty_out.c", "w"); + + map_init(&map); + ret = transpile(in, out, "c", &map); + + fclose(in); + fclose(out); + + cr_assert_eq(ret, 0); + cr_assert_eq(map.count, 0); + + map_free(&map); + remove("/tmp/test_transpile_empty_in.md"); + remove("/tmp/test_transpile_empty_out.c"); +} + +Test(transpile, no_matching_blocks) +{ + FILE *in; + FILE *out; + t_map map; + int8_t ret; + const char *no_match = "```python\nprint('hello')\n```\n"; + + in = fopen("/tmp/test_transpile_nomatch_in.md", "w"); + fprintf(in, "%s", no_match); + fclose(in); + + in = fopen("/tmp/test_transpile_nomatch_in.md", "r"); + out = fopen("/tmp/test_transpile_nomatch_out.c", "w"); + + map_init(&map); + ret = transpile(in, out, "c", &map); + + fclose(in); + fclose(out); + + cr_assert_eq(ret, 0); + cr_assert_eq(map.count, 0); + + map_free(&map); + remove("/tmp/test_transpile_nomatch_in.md"); + remove("/tmp/test_transpile_nomatch_out.c"); +} diff --git a/tests/transpile/test_transpile_fence.c b/tests/transpile/test_transpile_fence.c new file mode 100644 index 0000000..bdaa897 --- /dev/null +++ b/tests/transpile/test_transpile_fence.c @@ -0,0 +1,55 @@ +#include +#include + +#include "transpile.h" +#include "map.h" + +static const char *sample_md_mixed = + "# Mixed languages\n" + "```python\n" + "print('hello')\n" + "```\n" + "```c\n" + "int x = 42;\n" + "```\n"; + +/* +** TESTS: transpile/fence.c - Filter by extension +*/ + +Test(transpile, filter_by_extension) +{ + FILE *in; + FILE *out; + t_map map; + char buffer[256]; + int8_t ret; + + in = fopen("/tmp/test_transpile_filter_in.md", "w"); + fprintf(in, "%s", sample_md_mixed); + fclose(in); + + in = fopen("/tmp/test_transpile_filter_in.md", "r"); + out = fopen("/tmp/test_transpile_filter_out.c", "w"); + + map_init(&map); + ret = transpile(in, out, "c", &map); + + fclose(in); + fclose(out); + + cr_assert_eq(ret, 0); + cr_assert_eq(map.count, 1); + cr_assert_eq(map.ranges[0].src_start, 6); + cr_assert_eq(map.ranges[0].src_end, 6); + + out = fopen("/tmp/test_transpile_filter_out.c", "r"); + fgets(buffer, sizeof(buffer), out); + cr_assert_str_eq(buffer, "int x = 42;\n"); + cr_assert_null(fgets(buffer, sizeof(buffer), out)); + fclose(out); + + map_free(&map); + remove("/tmp/test_transpile_filter_in.md"); + remove("/tmp/test_transpile_filter_out.c"); +} diff --git a/tests/transpile/test_transpile_state.c b/tests/transpile/test_transpile_state.c new file mode 100644 index 0000000..93b812f --- /dev/null +++ b/tests/transpile/test_transpile_state.c @@ -0,0 +1,64 @@ +#include +#include + +#include "internal/transpile_internal.h" + +/* +** TESTS: transpile/state.c - State initialization +*/ + +Test(transpile_state, init_sets_all_fields) +{ + t_state s; + FILE *in; + FILE *out; + t_map map; + const char *ext = "c"; + + in = fopen("/tmp/test_state_in.md", "w"); + out = fopen("/tmp/test_state_out.c", "w"); + map_init(&map); + + state_init(&s, in, out, ext, &map); + + cr_assert_eq(s.in, in); + cr_assert_eq(s.out, out); + cr_assert_str_eq(s.ext, "c"); + cr_assert_eq(s.map, &map); + cr_assert_eq(s.src_line, 0); + cr_assert_eq(s.dst_line, 0); + cr_assert_eq(s.block_src_start, 0); + cr_assert_eq(s.block_dst_start, 0); + cr_assert_eq(s.in_block, 0); + cr_assert_eq(s.first_block, 1); + + fclose(in); + fclose(out); + map_free(&map); + remove("/tmp/test_state_in.md"); + remove("/tmp/test_state_out.c"); +} + +Test(transpile_state, init_with_different_extension) +{ + t_state s; + FILE *in; + FILE *out; + t_map map; + const char *ext = "py"; + + in = fopen("/tmp/test_state_in.md", "w"); + out = fopen("/tmp/test_state_out.py", "w"); + map_init(&map); + + state_init(&s, in, out, ext, &map); + + cr_assert_str_eq(s.ext, "py"); + cr_assert_eq(s.first_block, 1); + + fclose(in); + fclose(out); + map_free(&map); + remove("/tmp/test_state_in.md"); + remove("/tmp/test_state_out.py"); +} diff --git a/tests/utils/io/test_read_line.c b/tests/utils/io/test_read_line.c new file mode 100644 index 0000000..1636388 --- /dev/null +++ b/tests/utils/io/test_read_line.c @@ -0,0 +1,59 @@ +#include +#include +#include + +#include "utils.h" + +/* +** TESTS: io/read_line.c +*/ + +Test(utils_io, read_line_from_file) +{ + FILE *f; + char *line; + + f = fopen("/tmp/test_c_md_utils.txt", "w"); + cr_assert_not_null(f); + fprintf(f, "line1\n"); + fprintf(f, "line2\n"); + fclose(f); + + f = fopen("/tmp/test_c_md_utils.txt", "r"); + cr_assert_not_null(f); + + line = read_line(f); + cr_assert_not_null(line); + cr_assert_str_eq(line, "line1\n"); + free(line); + + line = read_line(f); + cr_assert_not_null(line); + cr_assert_str_eq(line, "line2\n"); + free(line); + + line = read_line(f); + cr_assert_null(line); + + fclose(f); + remove("/tmp/test_c_md_utils.txt"); +} + +Test(utils_io, read_line_empty_file) +{ + FILE *f; + char *line; + + f = fopen("/tmp/test_c_md_empty.txt", "w"); + cr_assert_not_null(f); + fclose(f); + + f = fopen("/tmp/test_c_md_empty.txt", "r"); + cr_assert_not_null(f); + + line = read_line(f); + cr_assert_null(line); + + fclose(f); + remove("/tmp/test_c_md_empty.txt"); +} diff --git a/tests/utils/string/test_extract_fence_ext.c b/tests/utils/string/test_extract_fence_ext.c new file mode 100644 index 0000000..20db930 --- /dev/null +++ b/tests/utils/string/test_extract_fence_ext.c @@ -0,0 +1,50 @@ +#include +#include + +#include "utils.h" + +/* +** TESTS: string/extract_fence_ext.c +*/ + +Test(utils_string, extract_fence_ext_valid) +{ + char *ext; + + ext = extract_fence_ext("```c"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "c"); + free(ext); + + ext = extract_fence_ext("```python"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "python"); + free(ext); + + ext = extract_fence_ext("``` c "); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "c"); + free(ext); + + ext = extract_fence_ext("```\tc\n"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "c"); + free(ext); +} + +Test(utils_string, extract_fence_ext_invalid) +{ + char *ext; + + ext = extract_fence_ext("```"); + cr_assert_null(ext); + + ext = extract_fence_ext("```\n"); + cr_assert_null(ext); + + ext = extract_fence_ext("not a fence"); + cr_assert_null(ext); + + ext = extract_fence_ext(""); + cr_assert_null(ext); +} diff --git a/tests/utils/string/test_extract_file_ext.c b/tests/utils/string/test_extract_file_ext.c new file mode 100644 index 0000000..cf9f9e9 --- /dev/null +++ b/tests/utils/string/test_extract_file_ext.c @@ -0,0 +1,45 @@ +#include + +#include "utils.h" + +/* +** TESTS: string/extract_file_ext.c +*/ + +Test(utils_string, extract_file_ext_valid) +{ + const char *ext; + + ext = extract_file_ext("file.c"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "c"); + + ext = extract_file_ext("test.md"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "md"); + + ext = extract_file_ext("path/to/file.txt"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "txt"); + + ext = extract_file_ext("file.c.md"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "md"); +} + +Test(utils_string, extract_file_ext_invalid) +{ + const char *ext; + + ext = extract_file_ext("noext"); + cr_assert_null(ext); + + ext = extract_file_ext(".hidden"); + cr_assert_null(ext); + + ext = extract_file_ext(""); + cr_assert_null(ext); + + ext = extract_file_ext("path/to/dir/"); + cr_assert_null(ext); +} diff --git a/tests/utils/string/test_infer_ext_from_filename.c b/tests/utils/string/test_infer_ext_from_filename.c new file mode 100644 index 0000000..e5f8f96 --- /dev/null +++ b/tests/utils/string/test_infer_ext_from_filename.c @@ -0,0 +1,63 @@ +#include +#include +#include + +#include "utils.h" + +/* +** TESTS: string/infer_ext_from_filename.c +*/ + +Test(utils_string, infer_ext_from_filename_valid) +{ + const char *ext; + + ext = infer_ext_from_filename("test.c.md"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "c"); + + ext = infer_ext_from_filename("main.py.md"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "py"); + + ext = infer_ext_from_filename("path/to/file.js.md"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "js"); +} + +Test(utils_string, infer_ext_from_filename_invalid) +{ + const char *ext; + + ext = infer_ext_from_filename("test.md"); + cr_assert_null(ext); + + ext = infer_ext_from_filename("noext"); + cr_assert_null(ext); + + ext = infer_ext_from_filename("test.c"); + cr_assert_null(ext); + + ext = infer_ext_from_filename(""); + cr_assert_null(ext); + + ext = infer_ext_from_filename(NULL); + cr_assert_null(ext); +} + +Test(utils_string, infer_ext_from_filename_edge_cases) +{ + const char *ext; + + ext = infer_ext_from_filename(".c.md"); + cr_assert_null(ext); + + ext = infer_ext_from_filename("a.c.md"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "c"); + + ext = infer_ext_from_filename("a.cpp.md"); + cr_assert_not_null(ext); + cr_assert_str_eq(ext, "cpp"); +} + diff --git a/tests/utils/string/test_starts_with.c b/tests/utils/string/test_starts_with.c new file mode 100644 index 0000000..55c9025 --- /dev/null +++ b/tests/utils/string/test_starts_with.c @@ -0,0 +1,21 @@ +#include + +#include "utils.h" + +/* +** TESTS: string/starts_with.c +*/ + +Test(utils_string, starts_with_true) +{ + cr_assert_eq(starts_with("```c", "```"), 1); + cr_assert_eq(starts_with("hello world", "hello"), 1); + cr_assert_eq(starts_with("abc", "abc"), 1); +} + +Test(utils_string, starts_with_false) +{ + cr_assert_eq(starts_with("hello", "world"), 0); + cr_assert_eq(starts_with("abc", "abcd"), 0); + cr_assert_eq(starts_with("", "test"), 0); +}