blob: 0f27f350597c82528fff0c0132d28a71e356b498 [file] [log] [blame]
László Csomorbe3b1fc2019-04-12 19:35:29 +02001# Copyright 2019 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""A test rule that compares two binary files.
16
17The rule uses a Bash command (diff) on Linux/macOS/non-Windows, and a cmd.exe
18command (fc.exe) on Windows (no Bash is required).
19"""
20
Alexandre Rostovtsevde3035d2022-04-06 15:16:14 -040021load("//lib:shell.bzl", "shell")
22
László Csomorbe3b1fc2019-04-12 19:35:29 +020023def _runfiles_path(f):
24 if f.root.path:
25 return f.path[len(f.root.path) + 1:] # generated file
26 else:
27 return f.path # source file
28
29def _diff_test_impl(ctx):
30 if ctx.attr.is_windows:
31 test_bin = ctx.actions.declare_file(ctx.label.name + "-test.bat")
32 ctx.actions.write(
33 output = test_bin,
László Csomor58068fe2019-09-17 14:03:22 +020034 content = """@rem Generated by diff_test.bzl, do not edit.
35@echo off
László Csomorbe3b1fc2019-04-12 19:35:29 +020036SETLOCAL ENABLEEXTENSIONS
37SETLOCAL ENABLEDELAYEDEXPANSION
László Csomor58068fe2019-09-17 14:03:22 +020038set MF=%RUNFILES_MANIFEST_FILE:/=\\%
39set PATH=%SYSTEMROOT%\\system32
László Csomorbe3b1fc2019-04-12 19:35:29 +020040set F1={file1}
41set F2={file2}
42if "!F1:~0,9!" equ "external/" (set F1=!F1:~9!) else (set F1=!TEST_WORKSPACE!/!F1!)
43if "!F2:~0,9!" equ "external/" (set F2=!F2:~9!) else (set F2=!TEST_WORKSPACE!/!F2!)
44for /F "tokens=2* usebackq" %%i in (`findstr.exe /l /c:"!F1! " "%MF%"`) do (
45 set RF1=%%i
László Csomor58068fe2019-09-17 14:03:22 +020046 set RF1=!RF1:/=\\!
László Csomorbe3b1fc2019-04-12 19:35:29 +020047)
48if "!RF1!" equ "" (
Alexandre Rostovtsev872e9b02022-07-15 13:07:09 -040049 if "%RUNFILES_MANIFEST_ONLY%" neq "1" if exist "%RUNFILES_DIR%\\%F1%" (
50 set RF1="%RUNFILES_DIR%\\%F1%"
51 ) else (
52 if exist "{file1}" (
53 set RF1="{file1}"
54 )
55 )
56 if "!RF1!" neq "" (
Thaler Benedek43a545a2021-09-24 16:49:07 +020057 set RF1=!RF1:/=\\!
58 ) else (
59 echo>&2 ERROR: !F1! not found
60 exit /b 1
61 )
László Csomorbe3b1fc2019-04-12 19:35:29 +020062)
63for /F "tokens=2* usebackq" %%i in (`findstr.exe /l /c:"!F2! " "%MF%"`) do (
64 set RF2=%%i
László Csomor58068fe2019-09-17 14:03:22 +020065 set RF2=!RF2:/=\\!
László Csomorbe3b1fc2019-04-12 19:35:29 +020066)
67if "!RF2!" equ "" (
Alexandre Rostovtsev872e9b02022-07-15 13:07:09 -040068 if "%RUNFILES_MANIFEST_ONLY%" neq "1" if exist "%RUNFILES_DIR%\\%F2%" (
69 set RF2="%RUNFILES_DIR%\\%F2%"
70 ) else (
71 if exist "{file2}" (
72 set RF2="{file2}"
73 )
74 )
75 if "!RF2!" neq "" (
Thaler Benedek43a545a2021-09-24 16:49:07 +020076 set RF2=!RF2:/=\\!
77 ) else (
78 echo>&2 ERROR: !F2! not found
79 exit /b 1
80 )
László Csomorbe3b1fc2019-04-12 19:35:29 +020081)
82fc.exe 2>NUL 1>NUL /B "!RF1!" "!RF2!"
83if %ERRORLEVEL% neq 0 (
84 if %ERRORLEVEL% equ 1 (
Alex Eagledf3c9e22021-08-18 08:23:43 -070085 echo>&2 FAIL: files "{file1}" and "{file2}" differ. {fail_msg}
László Csomorbe3b1fc2019-04-12 19:35:29 +020086 exit /b 1
87 ) else (
88 fc.exe /B "!RF1!" "!RF2!"
89 exit /b %errorlevel%
90 )
91)
92""".format(
Alexandre Rostovtsevde3035d2022-04-06 15:16:14 -040093 # TODO(arostovtsev): use shell.escape_for_bat when https://github.com/bazelbuild/bazel-skylib/pull/363 is merged
Alex Eagledf3c9e22021-08-18 08:23:43 -070094 fail_msg = ctx.attr.failure_message,
László Csomorbe3b1fc2019-04-12 19:35:29 +020095 file1 = _runfiles_path(ctx.file.file1),
96 file2 = _runfiles_path(ctx.file.file2),
97 ),
98 is_executable = True,
99 )
100 else:
101 test_bin = ctx.actions.declare_file(ctx.label.name + "-test.sh")
102 ctx.actions.write(
103 output = test_bin,
Yesudeep Mangalapilly8e923ca2021-10-25 06:12:41 -0700104 content = r"""#!/usr/bin/env bash
László Csomorbe3b1fc2019-04-12 19:35:29 +0200105set -euo pipefail
106F1="{file1}"
107F2="{file2}"
Robbert van Ginkelfeb52962020-04-14 10:04:34 -0700108[[ "$F1" =~ ^external/* ]] && F1="${{F1#external/}}" || F1="$TEST_WORKSPACE/$F1"
109[[ "$F2" =~ ^external/* ]] && F2="${{F2#external/}}" || F2="$TEST_WORKSPACE/$F2"
László Csomorbe3b1fc2019-04-12 19:35:29 +0200110if [[ -d "${{RUNFILES_DIR:-/dev/null}}" && "${{RUNFILES_MANIFEST_ONLY:-}}" != 1 ]]; then
111 RF1="$RUNFILES_DIR/$F1"
112 RF2="$RUNFILES_DIR/$F2"
113elif [[ -f "${{RUNFILES_MANIFEST_FILE:-/dev/null}}" ]]; then
114 RF1="$(grep -F -m1 "$F1 " "$RUNFILES_MANIFEST_FILE" | sed 's/^[^ ]* //')"
115 RF2="$(grep -F -m1 "$F2 " "$RUNFILES_MANIFEST_FILE" | sed 's/^[^ ]* //')"
c-parsons84a12d12019-05-07 16:25:43 -0400116elif [[ -f "$TEST_SRCDIR/$F1" && -f "$TEST_SRCDIR/$F2" ]]; then
117 RF1="$TEST_SRCDIR/$F1"
118 RF2="$TEST_SRCDIR/$F2"
László Csomorbe3b1fc2019-04-12 19:35:29 +0200119else
120 echo >&2 "ERROR: could not find \"{file1}\" and \"{file2}\""
121 exit 1
122fi
123if ! diff "$RF1" "$RF2"; then
Alexandre Rostovtsevde3035d2022-04-06 15:16:14 -0400124 echo >&2 "FAIL: files \"{file1}\" and \"{file2}\" differ. "{fail_msg}
László Csomorbe3b1fc2019-04-12 19:35:29 +0200125 exit 1
126fi
127""".format(
Alexandre Rostovtsevde3035d2022-04-06 15:16:14 -0400128 fail_msg = shell.quote(ctx.attr.failure_message),
László Csomorbe3b1fc2019-04-12 19:35:29 +0200129 file1 = _runfiles_path(ctx.file.file1),
130 file2 = _runfiles_path(ctx.file.file2),
131 ),
132 is_executable = True,
133 )
134 return DefaultInfo(
135 executable = test_bin,
136 files = depset(direct = [test_bin]),
137 runfiles = ctx.runfiles(files = [test_bin, ctx.file.file1, ctx.file.file2]),
138 )
139
140_diff_test = rule(
141 attrs = {
Alex Eagledf3c9e22021-08-18 08:23:43 -0700142 "failure_message": attr.string(),
László Csomorbe3b1fc2019-04-12 19:35:29 +0200143 "file1": attr.label(
144 allow_single_file = True,
145 mandatory = True,
146 ),
147 "file2": attr.label(
148 allow_single_file = True,
149 mandatory = True,
150 ),
151 "is_windows": attr.bool(mandatory = True),
152 },
153 test = True,
154 implementation = _diff_test_impl,
155)
156
Alexandre Rostovtsevde3035d2022-04-06 15:16:14 -0400157def diff_test(name, file1, file2, failure_message = None, **kwargs):
László Csomorbe3b1fc2019-04-12 19:35:29 +0200158 """A test that compares two files.
159
160 The test succeeds if the files' contents match.
161
162 Args:
163 name: The name of the test rule.
Alexandre Rostovtsevbc112d42022-10-03 05:13:46 -0400164 file1: Label of the file to compare to `file2`.
165 file2: Label of the file to compare to `file1`.
Alexandre Rostovtsevde3035d2022-04-06 15:16:14 -0400166 failure_message: Additional message to log if the files' contents do not match.
Alexandre Rostovtsevbc112d42022-10-03 05:13:46 -0400167 **kwargs: The [common attributes for tests](https://bazel.build/reference/be/common-definitions#common-attributes-tests).
László Csomorbe3b1fc2019-04-12 19:35:29 +0200168 """
169 _diff_test(
170 name = name,
171 file1 = file1,
172 file2 = file2,
Alexandre Rostovtsevde3035d2022-04-06 15:16:14 -0400173 failure_message = failure_message,
László Csomorbe3b1fc2019-04-12 19:35:29 +0200174 is_windows = select({
175 "@bazel_tools//src/conditions:host_windows": True,
176 "//conditions:default": False,
177 }),
178 **kwargs
179 )