Skip to content

Commit f34bd23

Browse files
committed
Adding more unit tests
1 parent bc25015 commit f34bd23

2 files changed

Lines changed: 172 additions & 11 deletions

File tree

GNU-gasp.pdf

211 KB
Binary file not shown.

test/unit/test_masp_cli.c

Lines changed: 172 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ static void print_diff_snippet(const char *out_path, const char *exp_path) {
7171
break;
7272
}
7373
if (strcmp(le, lo) != 0) {
74-
// Trim trailing newlines for nicer printing
7574
size_t len_e = strlen(le); if (len_e && (le[len_e-1] == '\n' || le[len_e-1] == '\r')) le[len_e-1] = '\0';
7675
size_t len_o = strlen(lo); if (len_o && (lo[len_o-1] == '\n' || lo[len_o-1] == '\r')) lo[len_o-1] = '\0';
7776
fprintf(stderr, "L%05d | EXPECTED: %s\n", line, le);
@@ -81,9 +80,7 @@ static void print_diff_snippet(const char *out_path, const char *exp_path) {
8180
}
8281
line++;
8382
}
84-
// If actual has extra lines
8583
if (shown == 0) {
86-
// Check for extra lines when content so far matched
8784
if (fgets(lo, sizeof(lo), fo)) {
8885
size_t len_o = strlen(lo); if (len_o && (lo[len_o-1] == '\n' || lo[len_o-1] == '\r')) lo[len_o-1] = '\0';
8986
fprintf(stderr, "L%05d | EXPECTED: <EOF>\n", line);
@@ -94,6 +91,96 @@ static void print_diff_snippet(const char *out_path, const char *exp_path) {
9491
fclose(fe);
9592
}
9693

94+
static int write_text_file(const char *path, const char *text) {
95+
FILE *f = fopen(path, "wb");
96+
if (!f) return -1;
97+
size_t n = fwrite(text, 1, strlen(text), f);
98+
fclose(f);
99+
return (n == strlen(text)) ? 0 : -1;
100+
}
101+
102+
static int read_file_to_buf(const char *path, char **out, size_t *out_len) {
103+
*out = NULL; *out_len = 0;
104+
FILE *f = fopen(path, "rb");
105+
if (!f) return -1;
106+
fseek(f, 0, SEEK_END);
107+
long sz = ftell(f);
108+
fseek(f, 0, SEEK_SET);
109+
if (sz < 0) { fclose(f); return -1; }
110+
char *buf = (char*)malloc((size_t)sz + 1);
111+
if (!buf) { fclose(f); return -1; }
112+
size_t n = fread(buf, 1, (size_t)sz, f);
113+
fclose(f);
114+
buf[n] = '\0';
115+
*out = buf; *out_len = n;
116+
return 0;
117+
}
118+
119+
static int run_case(const char *name, const char *src_text, const char *must_contain) {
120+
char src_path[1024];
121+
char out_path[1024];
122+
#ifdef _WIN32
123+
snprintf(src_path, sizeof(src_path), "%s\\test_outputs\\%s.vcl", BUILD_DIR, name);
124+
snprintf(out_path, sizeof(out_path), "%s\\test_outputs\\%s.out", BUILD_DIR, name);
125+
#else
126+
snprintf(src_path, sizeof(src_path), "%s/test_outputs/%s.vcl", BUILD_DIR, name);
127+
snprintf(out_path, sizeof(out_path), "%s/test_outputs/%s.out", BUILD_DIR, name);
128+
#endif
129+
if (write_text_file(src_path, src_text) != 0) {
130+
fprintf(stderr, "Failed to write source %s\n", src_path);
131+
return 1;
132+
}
133+
134+
// Build argv
135+
int rc;
136+
#if defined(_WIN32)
137+
char masp_path[1024];
138+
snprintf(masp_path, sizeof(masp_path), "%s\\src\\masp.exe", BUILD_DIR);
139+
const char *argvp[] = { masp_path, "-p", "-s", "-c", ";", "-o", out_path, "--", src_path, NULL };
140+
rc = _spawnv(_P_WAIT, masp_path, (const char* const*)argvp);
141+
if (rc == -1) {
142+
fprintf(stderr, "spawn failed for %s (errno=%d)\n", masp_path, errno);
143+
return 1;
144+
}
145+
#else
146+
{
147+
char masp_path[1024];
148+
snprintf(masp_path, sizeof(masp_path), "%s/src/masp", BUILD_DIR);
149+
pid_t pid = fork();
150+
if (pid == 0) {
151+
const char *argvp[] = { masp_path, "-p", "-s", "-c", ";", "-o", out_path, "--", src_path, NULL };
152+
execv(masp_path, (char* const*)argvp);
153+
_exit(127);
154+
} else if (pid > 0) {
155+
int status = 0;
156+
if (waitpid(pid, &status, 0) < 0) { perror("waitpid"); return 1; }
157+
if (WIFEXITED(status)) rc = WEXITSTATUS(status);
158+
else { fprintf(stderr, "subprocess terminated abnormally\n"); return 1; }
159+
} else { perror("fork"); return 1; }
160+
}
161+
#endif
162+
if (rc != 0) {
163+
fprintf(stderr, "masp returned %d for %s\n", rc, name);
164+
return 1;
165+
}
166+
struct stat st; if (stat(out_path, &st) != 0 || st.st_size <= 0) {
167+
fprintf(stderr, "Output empty for %s: %s\n", name, out_path);
168+
return 1;
169+
}
170+
if (must_contain && *must_contain) {
171+
char *buf = NULL; size_t blen = 0;
172+
if (read_file_to_buf(out_path, &buf, &blen) != 0) { fprintf(stderr, "Read failed %s\n", out_path); return 1; }
173+
int ok = strstr(buf, must_contain) != NULL;
174+
if (!ok) {
175+
fprintf(stderr, "Expected substring not found for %s: '%s'\n", name, must_contain);
176+
free(buf);
177+
return 1;
178+
}
179+
free(buf);
180+
}
181+
return 0;
182+
}
183+
97184
static int run_vu1Triangle(void) {
98185
char out_path[1024];
99186
char src_path[1024];
@@ -103,7 +190,6 @@ static int run_vu1Triangle(void) {
103190
snprintf(src_path, sizeof(src_path), "%s/test/vu1Triangle.vcl", SRC_DIR);
104191
snprintf(expected_path, sizeof(expected_path), "%s/test/vu1Triangle.vcl_masp", SRC_DIR);
105192
#ifdef _WIN32
106-
// Normalize to backslashes for Windows APIs
107193
for (char *p = out_path; *p; ++p) if (*p == '/') *p = '\\';
108194
for (char *p = src_path; *p; ++p) if (*p == '/') *p = '\\';
109195
for (char *p = expected_path; *p; ++p) if (*p == '/') *p = '\\';
@@ -131,7 +217,6 @@ static int run_vu1Triangle(void) {
131217
} else
132218
#elif defined(__unix__)
133219
{
134-
// Prefer a clean subprocess exec on UNIX to avoid sanitizer/locale clashes on CI
135220
char masp_path[1024];
136221
snprintf(masp_path, sizeof(masp_path), "%s/src/masp", BUILD_DIR);
137222
pid_t pid = fork();
@@ -150,7 +235,6 @@ static int run_vu1Triangle(void) {
150235
} else if (WIFSIGNALED(status)) {
151236
int sig = WTERMSIG(status);
152237
fprintf(stderr, "masp subprocess terminated by signal %d\n", sig);
153-
// Fallback to in-process to surface better diagnostics
154238
const char *argv0[] = { "masp", "-p", "-s", "-c", ";", "-o", out_path, "--", src_path, NULL };
155239
int argc = 9;
156240
rc = masp_program_main(argc, (char**)argv0);
@@ -174,11 +258,7 @@ static int run_vu1Triangle(void) {
174258
fprintf(stderr, "masp_program_main returned %d\n", rc);
175259
return 1;
176260
}
177-
struct stat st;
178-
if (stat(out_path, &st) != 0) {
179-
fprintf(stderr, "Output file not created: %s\n", out_path);
180-
return 1;
181-
}
261+
struct stat st; if (stat(out_path, &st) != 0) { fprintf(stderr, "Output file not created: %s\n", out_path); return 1; }
182262
if (!files_equal(out_path, expected_path)) {
183263
struct stat st2; st2.st_size = -1; (void)stat(out_path, &st2);
184264
fprintf(stderr, "Output differs from expected: %s (size=%ld)\n", out_path, (long)st2.st_size);
@@ -188,9 +268,90 @@ static int run_vu1Triangle(void) {
188268
return 0;
189269
}
190270

271+
static int run_basic_suite(void) {
272+
int failed = 0;
273+
// Ensure output dir exists
274+
char out_dir[1024];
275+
#ifdef _WIN32
276+
snprintf(out_dir, sizeof(out_dir), "%s\\test_outputs", BUILD_DIR);
277+
#else
278+
snprintf(out_dir, sizeof(out_dir), "%s/test_outputs", BUILD_DIR);
279+
#endif
280+
if (MKDIR(out_dir) != 0 && errno != EEXIST) {
281+
fprintf(stderr, "Failed to create: %s (errno=%d)\n", out_dir, errno);
282+
return 1;
283+
}
284+
285+
// 1) .db outputs .byte
286+
failed += run_case("db_bytes", ".db 1,2,3\n", ".byte\t1,2,3");
287+
// 2) .dw outputs .short
288+
failed += run_case("dw_short", ".dw 258\n", ".short\t258");
289+
// 3) .dl outputs .long
290+
failed += run_case("dl_long", ".dl 65539\n", ".long\t65539");
291+
// 4) .assign substitution in data line
292+
failed += run_case("assign_subst", "X .assign 3\n.db X\n", ".byte\t3");
293+
// 5) trivial line is copied with comment when -s is used (first char ';')
294+
failed += run_case("copysource", ".db 7\n", ";");
295+
// 6) Macro define and expand: emits byte 5
296+
failed += run_case(
297+
"macro_expand",
298+
".macro M x\n .db \\x\n .endm\n M 5\n",
299+
".byte\t5");
300+
// 7) Include file: create an included file that emits .db 42
301+
{
302+
char inc_path[1024];
303+
#ifdef _WIN32
304+
snprintf(inc_path, sizeof(inc_path), "%s\\test_outputs\\inc_simple.vcl", BUILD_DIR);
305+
#else
306+
snprintf(inc_path, sizeof(inc_path), "%s/test_outputs/inc_simple.vcl", BUILD_DIR);
307+
#endif
308+
if (write_text_file(inc_path, ".db 42\n") != 0) {
309+
fprintf(stderr, "Failed to write include file: %s\n", inc_path);
310+
return failed + 1;
311+
}
312+
char src_text[1400];
313+
snprintf(src_text, sizeof(src_text), ".include \"%s\"\n", inc_path);
314+
failed += run_case("include_simple", src_text, ".byte\t42");
315+
}
316+
// 8) Multiple data on one line
317+
failed += run_case("db_multi", ".db 10,11,12,13\n", ".byte\t10,11,12,13");
318+
// 9) Base conversions: binary, hex, octal, decimal
319+
failed += run_case("base_bin", ".db b'1010'\n", ".byte\t10");
320+
failed += run_case("base_hex", ".db h'FF'\n", ".byte\t255");
321+
failed += run_case("base_oct", ".db q'10'\n", ".byte\t8");
322+
failed += run_case("base_dec", ".db d'12'\n", ".byte\t12");
323+
// 10) Align directive
324+
failed += run_case("align4", ".align 4\n", ".align\t4");
325+
// 11) Conditional true branch
326+
failed += run_case("aif_true", ".AIF 1\n.DB 9\n.AENDI\n", ".byte\t9");
327+
// 12) Conditional else branch
328+
failed += run_case("aif_false_else", ".AIF 0\n.DB 1\n.AELSE\n.DB 2\n.AENDI\n", ".byte\t2");
329+
// 13) PRINT outputs string literal
330+
failed += run_case("print_str", ".PRINT \"HELLO\"\n", "HELLO");
331+
// 14) HEADING emits a .title line with the string
332+
failed += run_case("heading", ".HEADING \"TITLE\"\n", ".title\t\"TITLE\"");
333+
// 15) String in data
334+
failed += run_case("db_string", ".db \"ABC\"\n", "ABC");
335+
// 16) Page eject
336+
failed += run_case("page", ".PAGE\n", ".eject");
337+
// 17) Export emits .global
338+
failed += run_case("export_global", ".EXPORT foo\n", ".global\tfoo");
339+
// 18) Repeat block
340+
failed += run_case("arepeat", ".AREPEAT 3\n.DB 1\n.AENDR\n", ".byte\t1");
341+
// 19) Reserve space
342+
failed += run_case("res_space", ".RES 4\n", ".space");
343+
failed += run_case("sres_space", ".SRES 2\n", ".space");
344+
// 20) Print listing toggles
345+
failed += run_case("print_list_toggle", ".PRINT LIST\n", ".list");
346+
failed += run_case("print_nolist_toggle", ".PRINT NOLIST\n", ".nolist");
347+
348+
return failed;
349+
}
350+
191351
int main(void) {
192352
int failures = 0;
193353
failures += run_vu1Triangle();
354+
failures += run_basic_suite();
194355
if (failures) {
195356
fprintf(stderr, "Unit tests failed: %d\n", failures);
196357
return 1;

0 commit comments

Comments
 (0)