From 0670c03bc2b654ab57943a7624e179c3946664d2 Mon Sep 17 00:00:00 2001 From: Rangi42 Date: Tue, 23 Sep 2025 15:51:05 -0400 Subject: [PATCH] Add CLI tests for RGBASM --- CONTRIBUTING.md | 20 +++++++++++ include/platform.hpp | 2 +- src/asm/main.cpp | 46 ++++++++++++------------ src/fix/main.cpp | 4 ++- src/gfx/main.cpp | 4 ++- src/link/main.cpp | 12 ++++--- src/usage.cpp | 50 ++++++++++++++++----------- test/asm/cli/bad-dep-file.err | 1 + test/asm/cli/bad-dep-file.flags | 1 + test/asm/cli/invalid-B.err | 1 + test/asm/cli/invalid-B.flags | 1 + test/asm/cli/invalid-Q.err | 1 + test/asm/cli/invalid-Q.flags | 1 + test/asm/cli/invalid-X.err | 1 + test/asm/cli/invalid-X.flags | 1 + test/asm/cli/invalid-b-digits.err | 1 + test/asm/cli/invalid-b-digits.flags | 1 + test/asm/cli/invalid-color.err | 1 + test/asm/cli/invalid-color.flags | 1 + test/asm/cli/invalid-g-digits.err | 1 + test/asm/cli/invalid-g-digits.flags | 1 + test/asm/cli/invalid-p.err | 1 + test/asm/cli/invalid-p.flags | 1 + test/asm/cli/invalid-r.err | 1 + test/asm/cli/invalid-r.flags | 1 + test/asm/cli/invalid-s.err | 1 + test/asm/cli/invalid-s.flags | 1 + test/asm/cli/multiple-inputs.err | 17 +++++++++ test/asm/cli/multiple-inputs.flags | 1 + test/asm/cli/out-of-range-Q.err | 1 + test/asm/cli/out-of-range-Q.flags | 1 + test/asm/cli/out-of-range-p.err | 1 + test/asm/cli/out-of-range-p.flags | 1 + test/asm/cli/out-of-range-r.err | 1 + test/asm/cli/out-of-range-r.flags | 1 + test/asm/cli/override-filenames.err | 20 +++++++++++ test/asm/cli/override-filenames.flags | 1 + test/asm/test.sh | 28 +++++++++++++++ 38 files changed, 180 insertions(+), 51 deletions(-) create mode 100644 test/asm/cli/bad-dep-file.err create mode 100644 test/asm/cli/bad-dep-file.flags create mode 100644 test/asm/cli/invalid-B.err create mode 100644 test/asm/cli/invalid-B.flags create mode 100644 test/asm/cli/invalid-Q.err create mode 100644 test/asm/cli/invalid-Q.flags create mode 100644 test/asm/cli/invalid-X.err create mode 100644 test/asm/cli/invalid-X.flags create mode 100644 test/asm/cli/invalid-b-digits.err create mode 100644 test/asm/cli/invalid-b-digits.flags create mode 100644 test/asm/cli/invalid-color.err create mode 100644 test/asm/cli/invalid-color.flags create mode 100644 test/asm/cli/invalid-g-digits.err create mode 100644 test/asm/cli/invalid-g-digits.flags create mode 100644 test/asm/cli/invalid-p.err create mode 100644 test/asm/cli/invalid-p.flags create mode 100644 test/asm/cli/invalid-r.err create mode 100644 test/asm/cli/invalid-r.flags create mode 100644 test/asm/cli/invalid-s.err create mode 100644 test/asm/cli/invalid-s.flags create mode 100644 test/asm/cli/multiple-inputs.err create mode 100644 test/asm/cli/multiple-inputs.flags create mode 100644 test/asm/cli/out-of-range-Q.err create mode 100644 test/asm/cli/out-of-range-Q.flags create mode 100644 test/asm/cli/out-of-range-p.err create mode 100644 test/asm/cli/out-of-range-p.flags create mode 100644 test/asm/cli/out-of-range-r.err create mode 100644 test/asm/cli/out-of-range-r.flags create mode 100644 test/asm/cli/override-filenames.err create mode 100644 test/asm/cli/override-filenames.flags diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cfd7e995..c171ee14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,9 +88,17 @@ doesn't fit the existing scheme(s). ### RGBASM +There are two kinds of test. + +#### Simple tests + Each `.asm` file corresponds to one test. RGBASM will be invoked on the `.asm` file with all warnings enabled. +If a `.flags` file exists, its first line contains flags to pass to RGBASM. +(There may be more lines, which will be ignored; they can serve as comments to +explain what the test is about.) + If a `.out` file exists, RGBASM's output (`print`, `println`, etc.) must match its contents. If a `.err` file exists, RGBASM's error output (`warn`, errors, etc.) must match @@ -100,6 +108,18 @@ If a `.out.bin` file exists, the object file will be linked, and the generated ROM truncated to the length of the `.out.bin` file. After that, the ROM must match the `.out.bin` file. +#### CLI tests + +Each `.flags` file in `cli/` corresponds to one test. +RGBASM will be invoked, passing it the first line of the `.flags` file. +(There may be more lines, which will be ignored; they can serve as comments to +explain what the test is about.) + +If a `.out` file exists, RGBASM's output (`print`, `println`, etc.) must match +its contents. +If a `.err` file exists, RGBASM's error output (`warn`, errors, etc.) must match +its contents. + ### RGBLINK Each `.asm` file corresponds to one test, or one *set* of tests. diff --git a/include/platform.hpp b/include/platform.hpp index d9979f65..5b64a572 100644 --- a/include/platform.hpp +++ b/include/platform.hpp @@ -55,7 +55,7 @@ #define setmode(fd, mode) (0) #endif -// MingGW and Cygwin need POSIX functions which are not standard C explicitly enabled +// MingGW and Cygwin need POSIX functions which are not standard C explicitly enabled, #if defined(__MINGW32__) || defined(__CYGWIN__) #define _POSIX_C_SOURCE 200809L #endif diff --git a/src/asm/main.cpp b/src/asm/main.cpp index 303e8386..4de58abc 100644 --- a/src/asm/main.cpp +++ b/src/asm/main.cpp @@ -308,7 +308,7 @@ int main(int argc, char *argv[]) { // Maximum of 100 errors only applies if rgbasm is printing errors to a terminal if (isatty(STDERR_FILENO)) { - options.maxErrors = 100; + options.maxErrors = 100; // LCOV_EXCL_LINE } // Parse CLI options @@ -497,9 +497,10 @@ int main(int argc, char *argv[]) { } break; - // Unrecognized options + // LCOV_EXCL_START default: - usage.printAndExit(1); // LCOV_EXCL_LINE + usage.printAndExit(1); + // LCOV_EXCL_STOP } } @@ -520,6 +521,11 @@ int main(int argc, char *argv[]) { verbosePrint(VERB_NOTICE, "Assembling \"%s\"\n", mainFileName.c_str()); // LCOV_EXCL_LINE if (dependFileName) { + if (options.targetFileName.empty()) { + fatal("Dependency files can only be created if a target file is specified with either " + "'-o', '-MQ' or '-MT'"); + } + if (strcmp("-", dependFileName)) { options.dependFile = fopen(dependFileName, "w"); if (options.dependFile == nullptr) { @@ -532,10 +538,6 @@ int main(int argc, char *argv[]) { } } - if (options.dependFile && options.targetFileName.empty()) { - fatal("Dependency files can only be created if a target file is specified with either " - "'-o', '-MQ' or '-MT'"); - } options.printDep(mainFileName); charmap_New(DEFAULT_CHARMAP_NAME, nullptr); @@ -545,28 +547,26 @@ int main(int argc, char *argv[]) { // Perform parse (`yy::parser` is auto-generated from `parser.y`) if (yy::parser parser; parser.parse() != 0) { - if (warnings.nbErrors == 0) { - warnings.nbErrors = 1; - } + // Exited due to YYABORT or YYNOMEM + fatal("Unrecoverable error while parsing"); // LCOV_EXCL_LINE } - if (!fstk_FailedOnMissingInclude()) { - sect_CheckUnionClosed(); - sect_CheckLoadClosed(); - sect_CheckSizes(); - - charmap_CheckStack(); - opt_CheckStack(); - sect_CheckStack(); - } - - requireZeroErrors(); - - // If parse aborted due to missing an include, and `-MG` was given, exit normally + // If parse aborted without errors due to a missing INCLUDE, and `-MG` was given, exit normally if (fstk_FailedOnMissingInclude()) { + requireZeroErrors(); return 0; } + sect_CheckUnionClosed(); + sect_CheckLoadClosed(); + sect_CheckSizes(); + + charmap_CheckStack(); + opt_CheckStack(); + sect_CheckStack(); + + requireZeroErrors(); + out_WriteObject(); for (auto const &[name, features] : stateFileSpecs) { diff --git a/src/fix/main.cpp b/src/fix/main.cpp index c8e74899..26195830 100644 --- a/src/fix/main.cpp +++ b/src/fix/main.cpp @@ -325,8 +325,10 @@ int main(int argc, char *argv[]) { } break; + // LCOV_EXCL_START default: - usage.printAndExit(1); // LCOV_EXCL_LINE + usage.printAndExit(1); + // LCOV_EXCL_STOP } } diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index 69c736bc..24959c0d 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -542,8 +542,10 @@ static char *parseArgv(int argc, char *argv[]) { } break; + // LCOV_EXCL_START default: - usage.printAndExit(1); // LCOV_EXCL_LINE + usage.printAndExit(1); + // LCOV_EXCL_STOP } } diff --git a/src/link/main.cpp b/src/link/main.cpp index a95ec15f..e4533b4b 100644 --- a/src/link/main.cpp +++ b/src/link/main.cpp @@ -400,8 +400,10 @@ int main(int argc, char *argv[]) { } break; + // LCOV_EXCL_START default: - usage.printAndExit(1); // LCOV_EXCL_LINE + usage.printAndExit(1); + // LCOV_EXCL_STOP } } @@ -435,10 +437,10 @@ int main(int argc, char *argv[]) { verbosePrint(VERB_NOTICE, "Reading linker script...\n"); if (lexer_Init(linkerScriptName)) { - yy::parser parser; - // We don't care about the return value, as any error increments the global error count, - // which is what `main` checks. - (void)parser.parse(); + if (yy::parser parser; parser.parse() != 0) { + // Exited due to YYABORT or YYNOMEM + fatal("Unrecoverable error while reading linker script"); // LCOV_EXCL_LINE + } } // If the linker script produced any errors, some sections may be in an invalid state diff --git a/src/usage.cpp b/src/usage.cpp index c90f58fb..666a4178 100644 --- a/src/usage.cpp +++ b/src/usage.cpp @@ -17,25 +17,37 @@ #include #endif -// Use the console window width minus 1 as the maximum line length for flags -static size_t maxLineLen = []() { -#if defined(_MSC_VER) || defined(__MINGW32__) - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); - return csbi.srWindow.Right > csbi.srWindow.Left - ? static_cast(csbi.srWindow.Right - csbi.srWindow.Left) - : 79; -#else - struct winsize winSize; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize); - return winSize.ws_col > 1 ? static_cast(winSize.ws_col - 1) : 79; -#endif -}(); - -// LCOV_EXCL_START - void Usage::printAndExit(int code) const { - FILE *file = code ? stderr : stdout; + FILE *file; + bool isTerminal; + if (code) { + file = stderr; + isTerminal = isatty(STDERR_FILENO); + } else { + file = stdout; + isTerminal = isatty(STDOUT_FILENO); + } + + // Use the console window width minus 1 as the maximum line length for flags, + // or the historically common 80 minus 1 if the output is not to a console TTY + size_t maxLineLen = 79; + if (isTerminal) { + // LCOV_EXCL_START +#if defined(_MSC_VER) || defined(__MINGW32__) + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + if (csbi.srWindow.Right > csbi.srWindow.Left) { + maxLineLen = static_cast(csbi.srWindow.Right - csbi.srWindow.Left); + } +#else + struct winsize winSize; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize); + if (winSize.ws_col > 1) { + maxLineLen = static_cast(winSize.ws_col - 1); + } +#endif + // LCOV_EXCL_STOP + } // Print "Usage: " style_Set(file, STYLE_GREEN, true); @@ -149,5 +161,3 @@ void Usage::printAndExit(char const *fmt, ...) const { printAndExit(1); } - -// LCOV_EXCL_STOP diff --git a/test/asm/cli/bad-dep-file.err b/test/asm/cli/bad-dep-file.err new file mode 100644 index 00000000..5aaa1736 --- /dev/null +++ b/test/asm/cli/bad-dep-file.err @@ -0,0 +1 @@ +FATAL: Dependency files can only be created if a target file is specified with either '-o', '-MQ' or '-MT' diff --git a/test/asm/cli/bad-dep-file.flags b/test/asm/cli/bad-dep-file.flags new file mode 100644 index 00000000..4a776805 --- /dev/null +++ b/test/asm/cli/bad-dep-file.flags @@ -0,0 +1 @@ +-M depfile inputfile diff --git a/test/asm/cli/invalid-B.err b/test/asm/cli/invalid-B.err new file mode 100644 index 00000000..ab99fac9 --- /dev/null +++ b/test/asm/cli/invalid-B.err @@ -0,0 +1 @@ +FATAL: Invalid argument for option '-B' diff --git a/test/asm/cli/invalid-B.flags b/test/asm/cli/invalid-B.flags new file mode 100644 index 00000000..4f2fac0e --- /dev/null +++ b/test/asm/cli/invalid-B.flags @@ -0,0 +1 @@ +-B nan diff --git a/test/asm/cli/invalid-Q.err b/test/asm/cli/invalid-Q.err new file mode 100644 index 00000000..b672e8a2 --- /dev/null +++ b/test/asm/cli/invalid-Q.err @@ -0,0 +1 @@ +FATAL: Invalid argument for option '-Q' diff --git a/test/asm/cli/invalid-Q.flags b/test/asm/cli/invalid-Q.flags new file mode 100644 index 00000000..f8c93d68 --- /dev/null +++ b/test/asm/cli/invalid-Q.flags @@ -0,0 +1 @@ +-Q invalid diff --git a/test/asm/cli/invalid-X.err b/test/asm/cli/invalid-X.err new file mode 100644 index 00000000..8b63086a --- /dev/null +++ b/test/asm/cli/invalid-X.err @@ -0,0 +1 @@ +FATAL: Invalid argument for option '-X' diff --git a/test/asm/cli/invalid-X.flags b/test/asm/cli/invalid-X.flags new file mode 100644 index 00000000..060ed23b --- /dev/null +++ b/test/asm/cli/invalid-X.flags @@ -0,0 +1 @@ +-X 0c777 diff --git a/test/asm/cli/invalid-b-digits.err b/test/asm/cli/invalid-b-digits.err new file mode 100644 index 00000000..3d0a42de --- /dev/null +++ b/test/asm/cli/invalid-b-digits.err @@ -0,0 +1 @@ +FATAL: Must specify exactly 2 characters for option '-b' diff --git a/test/asm/cli/invalid-b-digits.flags b/test/asm/cli/invalid-b-digits.flags new file mode 100644 index 00000000..fafe8ae4 --- /dev/null +++ b/test/asm/cli/invalid-b-digits.flags @@ -0,0 +1 @@ +-b 01X diff --git a/test/asm/cli/invalid-color.err b/test/asm/cli/invalid-color.err new file mode 100644 index 00000000..1a404429 --- /dev/null +++ b/test/asm/cli/invalid-color.err @@ -0,0 +1 @@ +FATAL: Invalid argument for option '--color' diff --git a/test/asm/cli/invalid-color.flags b/test/asm/cli/invalid-color.flags new file mode 100644 index 00000000..155f9e88 --- /dev/null +++ b/test/asm/cli/invalid-color.flags @@ -0,0 +1 @@ +--color yes diff --git a/test/asm/cli/invalid-g-digits.err b/test/asm/cli/invalid-g-digits.err new file mode 100644 index 00000000..9652cdd7 --- /dev/null +++ b/test/asm/cli/invalid-g-digits.err @@ -0,0 +1 @@ +FATAL: Must specify exactly 4 characters for option '-g' diff --git a/test/asm/cli/invalid-g-digits.flags b/test/asm/cli/invalid-g-digits.flags new file mode 100644 index 00000000..183dc929 --- /dev/null +++ b/test/asm/cli/invalid-g-digits.flags @@ -0,0 +1 @@ +-g 0123X diff --git a/test/asm/cli/invalid-p.err b/test/asm/cli/invalid-p.err new file mode 100644 index 00000000..7de44b3b --- /dev/null +++ b/test/asm/cli/invalid-p.err @@ -0,0 +1 @@ +FATAL: Invalid argument for option '-p' diff --git a/test/asm/cli/invalid-p.flags b/test/asm/cli/invalid-p.flags new file mode 100644 index 00000000..7770caf5 --- /dev/null +++ b/test/asm/cli/invalid-p.flags @@ -0,0 +1 @@ +-p 123abc diff --git a/test/asm/cli/invalid-r.err b/test/asm/cli/invalid-r.err new file mode 100644 index 00000000..afe82baa --- /dev/null +++ b/test/asm/cli/invalid-r.err @@ -0,0 +1 @@ +FATAL: Invalid argument for option '-r' diff --git a/test/asm/cli/invalid-r.flags b/test/asm/cli/invalid-r.flags new file mode 100644 index 00000000..8bcd056c --- /dev/null +++ b/test/asm/cli/invalid-r.flags @@ -0,0 +1 @@ +-r nan diff --git a/test/asm/cli/invalid-s.err b/test/asm/cli/invalid-s.err new file mode 100644 index 00000000..60356f1f --- /dev/null +++ b/test/asm/cli/invalid-s.err @@ -0,0 +1 @@ +FATAL: Invalid argument for option '-s' diff --git a/test/asm/cli/invalid-s.flags b/test/asm/cli/invalid-s.flags new file mode 100644 index 00000000..7d4bb5c7 --- /dev/null +++ b/test/asm/cli/invalid-s.flags @@ -0,0 +1 @@ +-s invalid diff --git a/test/asm/cli/multiple-inputs.err b/test/asm/cli/multiple-inputs.err new file mode 100644 index 00000000..b0a800e9 --- /dev/null +++ b/test/asm/cli/multiple-inputs.err @@ -0,0 +1,17 @@ +FATAL: More than one input file specified +Usage: rgbasm [-EhVvw] [-B depth] [-b chars] [-D name[=value]] [-g chars] + [-I path] [-M depend_file] [-MC] [-MG] [-MP] [-MT target_file] + [-MQ target_file] [-o out_file] [-P include_file] [-p pad_value] + [-Q precision] [-r depth] [-s features:state_file] [-W warning] + [-X max_errors] + +Useful options: + -E, --export-all export all labels + -M, --dependfile set the output dependency file + -o, --output set the output object file + -p, --pad-value set the value to use for `DS` + -s, --state : set an output state file + -V, --version print RGBASM version and exit + -W, --warning enable or disable warnings + +For more help, use "man rgbasm" or go to https://rgbds.gbdev.io/docs/ diff --git a/test/asm/cli/multiple-inputs.flags b/test/asm/cli/multiple-inputs.flags new file mode 100644 index 00000000..bd46cf2d --- /dev/null +++ b/test/asm/cli/multiple-inputs.flags @@ -0,0 +1 @@ +one two diff --git a/test/asm/cli/out-of-range-Q.err b/test/asm/cli/out-of-range-Q.err new file mode 100644 index 00000000..2e1da32a --- /dev/null +++ b/test/asm/cli/out-of-range-Q.err @@ -0,0 +1 @@ +FATAL: Argument for option '-Q' must be between 1 and 31 diff --git a/test/asm/cli/out-of-range-Q.flags b/test/asm/cli/out-of-range-Q.flags new file mode 100644 index 00000000..a5d473dd --- /dev/null +++ b/test/asm/cli/out-of-range-Q.flags @@ -0,0 +1 @@ +-Q .999 diff --git a/test/asm/cli/out-of-range-p.err b/test/asm/cli/out-of-range-p.err new file mode 100644 index 00000000..31b904ba --- /dev/null +++ b/test/asm/cli/out-of-range-p.err @@ -0,0 +1 @@ +FATAL: Argument for option '-p' must be between 0 and 0xFF diff --git a/test/asm/cli/out-of-range-p.flags b/test/asm/cli/out-of-range-p.flags new file mode 100644 index 00000000..92f9cf0e --- /dev/null +++ b/test/asm/cli/out-of-range-p.flags @@ -0,0 +1 @@ +-p 999 diff --git a/test/asm/cli/out-of-range-r.err b/test/asm/cli/out-of-range-r.err new file mode 100644 index 00000000..0571a2a8 --- /dev/null +++ b/test/asm/cli/out-of-range-r.err @@ -0,0 +1 @@ +FATAL: Argument for option '-r' is out of range diff --git a/test/asm/cli/out-of-range-r.flags b/test/asm/cli/out-of-range-r.flags new file mode 100644 index 00000000..d27b901c --- /dev/null +++ b/test/asm/cli/out-of-range-r.flags @@ -0,0 +1 @@ +-r 0x10000000000000000 diff --git a/test/asm/cli/override-filenames.err b/test/asm/cli/override-filenames.err new file mode 100644 index 00000000..2e4e8799 --- /dev/null +++ b/test/asm/cli/override-filenames.err @@ -0,0 +1,20 @@ +warning: Overriding dependency file "one" +warning: Overriding output file "one" +warning: Overriding state file "only" +FATAL: No input file specified (pass "-" to read from standard input) +Usage: rgbasm [-EhVvw] [-B depth] [-b chars] [-D name[=value]] [-g chars] + [-I path] [-M depend_file] [-MC] [-MG] [-MP] [-MT target_file] + [-MQ target_file] [-o out_file] [-P include_file] [-p pad_value] + [-Q precision] [-r depth] [-s features:state_file] [-W warning] + [-X max_errors] + +Useful options: + -E, --export-all export all labels + -M, --dependfile set the output dependency file + -o, --output set the output object file + -p, --pad-value set the value to use for `DS` + -s, --state : set an output state file + -V, --version print RGBASM version and exit + -W, --warning enable or disable warnings + +For more help, use "man rgbasm" or go to https://rgbds.gbdev.io/docs/ diff --git a/test/asm/cli/override-filenames.flags b/test/asm/cli/override-filenames.flags new file mode 100644 index 00000000..3e1d720a --- /dev/null +++ b/test/asm/cli/override-filenames.flags @@ -0,0 +1 @@ +-M one -M two -o one -o two -s equ:only -s var:only diff --git a/test/asm/test.sh b/test/asm/test.sh index c9ddbca5..edbaafac 100755 --- a/test/asm/test.sh +++ b/test/asm/test.sh @@ -132,6 +132,34 @@ for i in *.asm notexist.asm; do done done +for i in cli/*.flags; do + RGBASMFLAGS="$(head -n 1 "$i")" # Allow other lines to serve as comments + (( tests++ )) + echo "${bold}${green}${i%.flags}...${rescolors}${resbold}" + if [ -e "${i%.flags}.out" ]; then + desired_output=${i%.flags}.out + else + desired_output=/dev/null + fi + if [ -e "${i%.flags}.err" ]; then + desired_errput=${i%.flags}.err + else + desired_errput=/dev/null + fi + "$RGBASM" $RGBASMFLAGS >"$output" 2>"$errput" + + tryDiff "$desired_output" "$output" out + our_rc=$? + tryDiff "$desired_errput" "$errput" err + (( our_rc = our_rc || $? )) + + (( rc = rc || our_rc )) + if [[ $our_rc -ne 0 ]]; then + (( failed++ )) + break + fi +done + # These tests do their own thing i="continues-after-missing-include"