# Copyright 2019 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A test rule that compares two binary files or two directories. Similar to `bazel-skylib`'s [`diff_test`](https://github.com/bazelbuild/bazel-skylib/blob/main/rules/diff_test.bzl) but also supports comparing directories. The rule uses a Bash command (diff) on Linux/macOS/non-Windows, and a cmd.exe command (fc.exe) on Windows (no Bash is required). """ load("//lib:utils.bzl", "default_timeout") load(":directory_path.bzl", "DirectoryPathInfo") def _runfiles_path(f): if f.root.path: return f.path[len(f.root.path) + 1:] # generated file else: return f.path # source file def _diff_test_impl(ctx): is_windows = ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo]) if DirectoryPathInfo in ctx.attr.file1: file1 = ctx.attr.file1[DirectoryPathInfo].directory file1_path = "/".join([_runfiles_path(file1), ctx.attr.file1[DirectoryPathInfo].path]) else: if len(ctx.files.file1) != 1: fail("file1 must be a single file or a target that provides a DirectoryPathInfo") file1 = ctx.files.file1[0] file1_path = _runfiles_path(file1) if DirectoryPathInfo in ctx.attr.file2: file2 = ctx.attr.file2[DirectoryPathInfo].directory file2_path = "/".join([_runfiles_path(file2), ctx.attr.file2[DirectoryPathInfo].path]) else: if len(ctx.files.file2) != 1: fail("file2 must be a single file or a target that provides a DirectoryPathInfo") file2 = ctx.files.file2[0] file2_path = _runfiles_path(file2) if file1 == file2: msg = "diff_test comparing the same file %s" % file1 fail(msg) if is_windows: test_suffix = "-test.bat" template = ctx.file._diff_test_tmpl_bat else: test_suffix = "-test.sh" template = ctx.file._diff_test_tmpl_sh test_bin = ctx.actions.declare_file(ctx.label.name + test_suffix) ctx.actions.expand_template( template = template, output = test_bin, substitutions = { "{name}": ctx.attr.name, "{fail_msg}": ctx.attr.failure_message, "{file1}": file1_path, "{file2}": file2_path, "{build_file_path}": ctx.build_file_path, }, is_executable = True, ) return DefaultInfo( executable = test_bin, files = depset(direct = [test_bin]), runfiles = ctx.runfiles(files = [test_bin, file1, file2]), ) _diff_test = rule( attrs = { "failure_message": attr.string(), "file1": attr.label( allow_files = True, mandatory = True, ), "file2": attr.label( allow_files = True, mandatory = True, ), "_windows_constraint": attr.label(default = "@platforms//os:windows"), "_diff_test_tmpl_sh": attr.label( default = ":diff_test_tmpl.sh", allow_single_file = True, ), "_diff_test_tmpl_bat": attr.label( default = ":diff_test_tmpl.bat", allow_single_file = True, ), }, test = True, implementation = _diff_test_impl, ) def diff_test(name, file1, file2, size = None, timeout = None, **kwargs): """A test that compares two files. The test succeeds if the files' contents match. Args: name: The name of the test rule. file1: Label of the file to compare to file2. file2: Label of the file to compare to file1. size: standard attribute for tests timeout: standard attribute for tests. Defaults to "short" if both timeout and size are unspecified. **kwargs: The common attributes for tests. """ _diff_test( name = name, file1 = file1, file2 = file2, size = size, timeout = default_timeout(size, timeout), **kwargs )