From ea1a75db5b906e31646d0e41adad7b8ad8a63381 Mon Sep 17 00:00:00 2001 From: Matthew Clarkson Date: Thu, 7 Dec 2023 22:25:19 +0000 Subject: [PATCH 1/2] feat(test): add Batch script for Windows --- e2e/MODULE.bazel.lock | 2 +- e2e/toolchain/echo/echo.bat | 17 --- toolchain/test/BUILD.bazel | 14 ++- toolchain/test/nt.tmpl.bat | 215 ++++++++++++++++++++++++++++++++++++ toolchain/test/rule.bzl | 16 +-- 5 files changed, 238 insertions(+), 26 deletions(-) create mode 100644 toolchain/test/nt.tmpl.bat diff --git a/e2e/MODULE.bazel.lock b/e2e/MODULE.bazel.lock index 009e98d..a3b486a 100644 --- a/e2e/MODULE.bazel.lock +++ b/e2e/MODULE.bazel.lock @@ -835,7 +835,7 @@ "moduleExtensions": { "//:MODULE.bazel%_repo_rules": { "general": { - "bzlTransitiveDigest": "9JxXrmfNb78Xi25kLIofG+wYfaaT/4WtRWFscwj7N9s=", + "bzlTransitiveDigest": "D+LVccvjhC6Fe6jgT0CX0WPaNhyl1RcBJygqKAExA8o=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { diff --git a/e2e/toolchain/echo/echo.bat b/e2e/toolchain/echo/echo.bat index 187afc3..2da4198 100644 --- a/e2e/toolchain/echo/echo.bat +++ b/e2e/toolchain/echo/echo.bat @@ -1,18 +1 @@ -:: Enable Batch extensions -@verify other 2>nul -@setlocal EnableExtensions -@if errorlevel 1 ( - echo "Failed to enable extensions" - exit /b 120 -) - -:: Enable delayed expansion of variables with `!VAR!` -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - echo "Failed to enable extensions" - exit /b 120 -) - -:: Execute! @echo.%* \ No newline at end of file diff --git a/toolchain/test/BUILD.bazel b/toolchain/test/BUILD.bazel index 5a9399a..19450e2 100644 --- a/toolchain/test/BUILD.bazel +++ b/toolchain/test/BUILD.bazel @@ -1,4 +1,7 @@ -exports_files(["posix.tmpl.sh"]) +exports_files([ + "posix.tmpl.sh", + "nt.tmpl.bat", +]) exports_files( [ @@ -8,3 +11,12 @@ exports_files( ], visibility = ["//visibility:public"], ) + +alias( + name = "template", + actual = select({ + "//toolchain/constraint/os:windows": ":nt.tmpl.bat", + "//conditions:default": ":posix.tmpl.sh", + }), + visibility = ["//visibility:public"], +) diff --git a/toolchain/test/nt.tmpl.bat b/toolchain/test/nt.tmpl.bat new file mode 100644 index 0000000..fca3626 --- /dev/null +++ b/toolchain/test/nt.tmpl.bat @@ -0,0 +1,215 @@ +@echo off + +:: Enable Batch extensions +verify other 2>nul +setlocal EnableExtensions +if errorlevel 1 ( + echo "Failed to enable extensions" + exit /b 120 +) + +:: Check for delayed expansion of variables with `!VAR!` +verify other 2>nul +setlocal EnableDelayedExpansion +if errorlevel 1 ( + echo "Failed to enable extensions" + exit /b 120 +) +setlocal DisableDelayedExpansion + +:: Bazel substitutions +set "EXECUTABLE={{executable}}" +set "STDOUT={{stdout}}" +set "STDERR={{stderr}}" + +:: Runfiles +if [%RUNFILES_MANIFEST_ONLY%] neq [1] ( + echo>&2.Only runfile manifests are supported + exit /b 2 +) +setlocal EnableDelayedExpansion +for %%v in (EXECUTABLE,STDOUT,STDERR) do ( + for /f "tokens=1,2* usebackq" %%a in ("%RUNFILES_MANIFEST_FILE%") do ( + if "_main/!%%v!" == "%%a" ( + set "%%v=%%~fb" + ) + if "!%%v!" == "../%%a" ( + set "%%v=%%~fb" + ) + ) +) +setlocal DisableDelayedExpansion + +:: Execute! +for /f %%a in ("%EXECUTABLE%") do set EXTENSION=%%~xa +if "%EXTENSION%" == ".bat" set LAUNCHER=call +%LAUNCHER% "%EXECUTABLE%" %* >stdout.txt 2>stderr.txt +set "CODE=%ERRORLEVEL%" +if %CODE% neq 0 ( + >&2 echo.Failed to run: %EXECUTABLE% %* + >&2 echo.stdout: + >&2 type stdout.txt + >&2 echo.stderr: + >&2 type stderr.txt + exit /b %CODE% +) + +:: Compare +set "JUNIT=junit.xml" +if not [%XML_OUTPUT_FILE%] == [] set "JUNIT=%XML_OUTPUT_FILE%" +call :junit CODE stdout.txt "%STDOUT%" stderr.txt "%STDERR%" >"%JUNIT%" +exit /b %CODE% + +:junit - creates JUnit XML output from comparing files +:: %1 - return code variable +:: %* - pairs of files to compare +setlocal + +:: Output TAP/JUnit headers +set COUNT=0 +for %%a in (%*) do set /a "COUNT+=1" +set /a "TESTS=COUNT/2" +echo.^ +>&2 echo.1..%TESTS% +set SUM=0 + +:: Loop through each file pairing +set INDEX=0 +:loop +set /a "INDEX+=1" +call :compare CODE %INDEX% "%~2" "%~3" +set /a "SUM+=CODE" +shift /2 +shift /2 +if exist "%~3" goto :loop + +:: Output the JUnit footer +echo.^ + +endlocal & set "%~1=%SUM%" +goto :eof + +:compare - compare two files +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +if "%EXPECTED:~-19%" == "\toolchain\test\any" ( + call :any CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) else if "%EXPECTED:~-21%" == "\toolchain\test\empty" ( + call :empty CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) else if "%EXPECTED:~-25%" == "\toolchain\test\non-empty" ( + call :non-empty CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) else ( + call :diff CODE %INDEX% "%FILEPATH%" "%EXPECTED%" +) +endlocal & set %~1=%CODE% +goto :eof + +:any - a file can have any content +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +>&2 echo.ok %INDEX% - %FILEPATH% contained any content +echo.^ +endlocal & set %~1=0 +goto :eof + +:empty - a file must have zero content +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +for /f %%a in ("%FILEPATH%") do set "SIZE=%%~Za" +if %SIZE% equ 0 ( + >&2 echo.ok %INDEX% - %FILEPATH% was an empty file + echo.^ + set "CODE=0" +) else ( + >&2 echo.not ok %INDEX% - %FILEPATH% contained content when an empty file was expected + echo. ^ + echo. ^%FILEPATH% contained unexpected content: + type "%FILEPATH%" + echo. ^ + echo. ^ + set "CODE=1" +) +endlocal & set %~1=%CODE% +goto :eof + +:non-empty - a file must have some content +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +for /f %%a in ("%FILEPATH%") do set "SIZE=%%~Za" +if %SIZE% neq 0 ( + >&2 echo.ok %INDEX% - %FILEPATH% was a non-empty file + echo. ^ + set "CODE=0" +) else ( + >&2 echo.not ok %INDEX% - %FILEPATH% was an empty file when content was expected + echo. ^ + echo. ^%FILEPATH% was an empty file when content was expected^ + echo. ^ + set "CODE=1" +) +endlocal & set %~1=%CODE% +goto :eof + +:diff - compare the two passed files for content equivalence +:: %1 - return code variable +:: %2 - test index +:: %3 - file under test +:: %4 - expected output +setlocal +set "INDEX=%~2" +set "FILEPATH=%~3" +set "EXPECTED=%~4" +set "DIFF=%SYSTEMROOT%\\system32\\fc.exe" +if not exist "%DIFF%" ( + >&2 echo.not ok %INDEX% - missing file compare executable: %DIFF% + echo. ^ + echo. ^Missing file compare executable: %DIFF%^ + echo. ^ + set CODE=1 + goto :fail +) +set "CODE=%ERRORLEVEL%" +if %CODE% equ 0 ( + >&2 echo.ok %INDEX% - %FILEPATH% was identical + echo.^ +) else if %CODE% equ 1 ( + >&2 echo.not ok %INDEX% - %FILEPATH% had different content to %EXPECTED% + echo. ^ + echo. ^%FILEPATH% contained different content: + "%DIFF%" "%FILEPATH%" "%EXPECTED%" + echo. ^ + echo. ^ +) else ( + >&2 echo.not ok %INDEX% - unknown exit code from %DIFF%: %CODE% + echo. ^ + echo. ^Unknown exit code from %DIFF%: %CODE%^ + echo. ^ + set CODE=1 +) +:fail +endlocal & set %~1=%CODE% +goto :eof diff --git a/toolchain/test/rule.bzl b/toolchain/test/rule.bzl index 3c33f6d..45ea29f 100644 --- a/toolchain/test/rule.bzl +++ b/toolchain/test/rule.bzl @@ -32,7 +32,7 @@ Can be overridden to a custom script that receives the following replacements: - `{{stdout}}`: the expected standard output - `{{stderr}}`: the expected standard error """, - default = ":posix.tmpl.sh", + default = ":template", allow_single_file = True, ), } @@ -42,15 +42,17 @@ def implementation(ctx): fail("Only one toolchain can be provided") toolchain = ctx.attr.toolchains[0][platform_common.ToolchainInfo] - executable = ctx.actions.declare_file("{}.executable".format(ctx.label.name)) + executable = ctx.actions.declare_file("{}.{}".format(ctx.label.name, ctx.file.template.extension)) + + substitutions = ctx.actions.template_dict() + substitutions.add("{{executable}}", str(toolchain.executable.short_path)) + substitutions.add("{{stdout}}", str(ctx.file.stdout.short_path)) + substitutions.add("{{stderr}}", str(ctx.file.stderr.short_path)) + ctx.actions.expand_template( template = ctx.file.template, output = executable, - substitutions = { - "{{executable}}": str(toolchain.executable.short_path), - "{{stdout}}": str(ctx.file.stdout.short_path), - "{{stderr}}": str(ctx.file.stderr.short_path), - }, + computed_substitutions = substitutions, is_executable = True, ) -- GitLab From 82a4ecaa71dbd06c98f24ef5fb4dc40ad58a40a6 Mon Sep 17 00:00:00 2001 From: Matthew Clarkson Date: Fri, 8 Dec 2023 17:59:25 +0000 Subject: [PATCH 2/2] fix(test): simplify POSIX variable substitution No need to use arithmetic substitution. --- toolchain/test/posix.tmpl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain/test/posix.tmpl.sh b/toolchain/test/posix.tmpl.sh index d17d4c4..66da711 100644 --- a/toolchain/test/posix.tmpl.sh +++ b/toolchain/test/posix.tmpl.sh @@ -152,7 +152,7 @@ junit() ( TESTS=$((COUNT / 2)) readonly COUNT TESTS printf '\n' "${TESTS}" - printf >&2 '1..%i\n' $((TESTS)) + printf >&2 '1..%i\n' "${TESTS}" INDEX=1 while ! test -z ${2+x}; do FILEPATH="${1}" -- GitLab