Skip to content

Commit 91d64cb

Browse files
committed
Improve testing and output
1 parent a70a3db commit 91d64cb

6 files changed

Lines changed: 195 additions & 50 deletions

File tree

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,17 @@ enable_testing()
1111

1212
add_subdirectory(src)
1313

14+
if(BUILD_TESTING)
15+
add_subdirectory(test)
16+
# Ensure invoking the test target compiles the app first
17+
if(TARGET test)
18+
add_dependencies(test masp)
19+
endif()
20+
# Provide a convenient 'check' target that builds and runs tests
21+
add_custom_target(check
22+
COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> --output-on-failure
23+
DEPENDS masp
24+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
25+
)
26+
endif()
1427

cmake/RunMaspAndCompare.cmake

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/CMakeLists.txt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,4 @@ if(MINGW)
6262
target_link_libraries(masp PRIVATE gnurx)
6363
endif()
6464

65-
# Tests
66-
if(BUILD_TESTING)
67-
add_test(NAME masp_vu1Triangle
68-
COMMAND ${CMAKE_COMMAND}
69-
-Dmasp_exe=$<TARGET_FILE:masp>
70-
-Dsrc=${CMAKE_SOURCE_DIR}/test/vu1Triangle.vcl
71-
-Dexpected=${CMAKE_SOURCE_DIR}/test/vu1Triangle.vcl_masp
72-
-Dout=${CMAKE_BINARY_DIR}/test_outputs/vu1Triangle.vcl_masp
73-
-P ${CMAKE_SOURCE_DIR}/cmake/RunMaspAndCompare.cmake)
74-
endif()
65+
# Tests are defined in test/CMakeLists.txt

test/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# C unit tests
2+
add_subdirectory(unit)
3+
4+

test/unit/CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Minimal C unit test that calls masp's main in-process to avoid CLI parsing issues
2+
3+
add_executable(test_masp_cli
4+
${CMAKE_SOURCE_DIR}/test/unit/test_masp_cli.c
5+
${CMAKE_SOURCE_DIR}/src/hash.c
6+
${CMAKE_SOURCE_DIR}/src/macro.c
7+
${CMAKE_SOURCE_DIR}/src/sb.c
8+
${CMAKE_SOURCE_DIR}/src/compat.c
9+
)
10+
11+
target_include_directories(test_masp_cli PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src)
12+
13+
target_compile_definitions(test_masp_cli PRIVATE
14+
SRC_DIR="${CMAKE_SOURCE_DIR}"
15+
BUILD_DIR="${CMAKE_BINARY_DIR}"
16+
PACKAGE_VERSION="test"
17+
REPORT_BUGS_TO="ci"
18+
LOCALEDIR=""
19+
)
20+
21+
add_test(NAME masp_cli_unit COMMAND test_masp_cli)
22+
23+
# Windows (MinGW) needs POSIX regex library (libgnurx)
24+
if(MINGW)
25+
target_link_libraries(test_masp_cli PRIVATE gnurx)
26+
endif()
27+
28+

test/unit/test_masp_cli.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <sys/stat.h>
5+
#include <errno.h>
6+
#ifdef _WIN32
7+
#include <direct.h>
8+
#include <process.h>
9+
#define MKDIR(p) _mkdir(p)
10+
#else
11+
#include <sys/types.h>
12+
#include <unistd.h>
13+
#include <sys/wait.h>
14+
#define MKDIR(p) mkdir((p), 0777)
15+
#endif
16+
17+
#ifndef SRC_DIR
18+
#define SRC_DIR "."
19+
#endif
20+
#ifndef BUILD_DIR
21+
#define BUILD_DIR "."
22+
#endif
23+
24+
// Avoid symbol clash: rename masp main before including the implementation
25+
#define main masp_program_main
26+
#include "../../src/masp.c"
27+
#undef main
28+
29+
static int files_equal(const char *a, const char *b) {
30+
FILE *fa = fopen(a, "rb");
31+
FILE *fb = fopen(b, "rb");
32+
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return 0; }
33+
int eq = 1;
34+
const size_t BUFSZ = 8192;
35+
unsigned char *ba = (unsigned char*)malloc(BUFSZ);
36+
unsigned char *bb = (unsigned char*)malloc(BUFSZ);
37+
if (!ba || !bb) { eq = 0; goto done; }
38+
size_t ra, rb;
39+
do {
40+
ra = fread(ba, 1, BUFSZ, fa);
41+
rb = fread(bb, 1, BUFSZ, fb);
42+
if (ra != rb || memcmp(ba, bb, ra) != 0) { eq = 0; break; }
43+
} while (ra > 0 && rb > 0);
44+
done:
45+
if (ba) free(ba);
46+
if (bb) free(bb);
47+
fclose(fa);
48+
fclose(fb);
49+
return eq;
50+
}
51+
52+
static int run_vu1Triangle(void) {
53+
char out_path[1024];
54+
char src_path[1024];
55+
char expected_path[1024];
56+
char out_dir[1024];
57+
snprintf(out_path, sizeof(out_path), "%s/test_outputs/masp_vu1Triangle.unit.out", BUILD_DIR);
58+
snprintf(src_path, sizeof(src_path), "%s/test/vu1Triangle.vcl", SRC_DIR);
59+
snprintf(expected_path, sizeof(expected_path), "%s/test/vu1Triangle.vcl_masp", SRC_DIR);
60+
61+
// Ensure output directory exists (CI may not have it yet)
62+
snprintf(out_dir, sizeof(out_dir), "%s/test_outputs", BUILD_DIR);
63+
if (MKDIR(out_dir) != 0 && errno != EEXIST) {
64+
fprintf(stderr, "Failed to create output dir: %s (errno=%d)\n", out_dir, errno);
65+
return 1;
66+
}
67+
68+
int rc = 0;
69+
#if defined(_WIN32)
70+
if (1) {
71+
char masp_path[1024];
72+
snprintf(masp_path, sizeof(masp_path), "%s\\src\\masp.exe", BUILD_DIR);
73+
const char *argvp[] = { masp_path, "-p", "-s", "-c", ";", "-o", out_path, "--", src_path, NULL };
74+
int sprc = _spawnv(_P_WAIT, masp_path, (const char* const*)argvp);
75+
if (sprc == -1) {
76+
fprintf(stderr, "spawn failed for %s (errno=%d)\n", masp_path, errno);
77+
return 1;
78+
}
79+
rc = sprc;
80+
} else
81+
#elif defined(__unix__)
82+
{
83+
// Prefer a clean subprocess exec on UNIX to avoid sanitizer/locale clashes on CI
84+
char masp_path[1024];
85+
snprintf(masp_path, sizeof(masp_path), "%s/src/masp", BUILD_DIR);
86+
pid_t pid = fork();
87+
if (pid == 0) {
88+
const char *argvp[] = { masp_path, "-p", "-s", "-c", ";", "-o", out_path, "--", src_path, NULL };
89+
execv(masp_path, (char* const*)argvp);
90+
_exit(127);
91+
} else if (pid > 0) {
92+
int status = 0;
93+
if (waitpid(pid, &status, 0) < 0) {
94+
perror("waitpid");
95+
return 1;
96+
}
97+
if (WIFEXITED(status)) {
98+
rc = WEXITSTATUS(status);
99+
} else if (WIFSIGNALED(status)) {
100+
int sig = WTERMSIG(status);
101+
fprintf(stderr, "masp subprocess terminated by signal %d\n", sig);
102+
// Fallback to in-process to surface better diagnostics
103+
const char *argv0[] = { "masp", "-p", "-s", "-c", ";", "-o", out_path, "--", src_path, NULL };
104+
int argc = 9;
105+
rc = masp_program_main(argc, (char**)argv0);
106+
} else {
107+
fprintf(stderr, "masp subprocess terminated abnormally\n");
108+
return 1;
109+
}
110+
} else {
111+
perror("fork");
112+
return 1;
113+
}
114+
}
115+
#else
116+
{
117+
const char *argv0[] = { "masp", "-p", "-s", "-c", ";", "-o", out_path, "--", src_path, NULL };
118+
int argc = 9;
119+
rc = masp_program_main(argc, (char**)argv0);
120+
}
121+
#endif
122+
if (rc != 0) {
123+
fprintf(stderr, "masp_program_main returned %d\n", rc);
124+
return 1;
125+
}
126+
struct stat st;
127+
if (stat(out_path, &st) != 0) {
128+
fprintf(stderr, "Output file not created: %s\n", out_path);
129+
return 1;
130+
}
131+
if (!files_equal(out_path, expected_path)) {
132+
fprintf(stderr, "Output differs from expected: %s\n", out_path);
133+
return 1;
134+
}
135+
return 0;
136+
}
137+
138+
int main(void) {
139+
int failures = 0;
140+
failures += run_vu1Triangle();
141+
if (failures) {
142+
fprintf(stderr, "Unit tests failed: %d\n", failures);
143+
return 1;
144+
}
145+
printf("All unit tests passed.\n");
146+
return 0;
147+
}
148+
149+

0 commit comments

Comments
 (0)