Files
ortools-clone/tools/testing/binary_test.py
2025-12-02 15:33:12 +01:00

132 lines
4.7 KiB
Python

#!/usr/bin/env python3
# Copyright 2010-2025 Google LLC
# 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.
"""Base class for binary tests."""
from collections.abc import Sequence
import re
import subprocess
from absl.testing import absltest
from tools.testing import bintest_matchers
from tools.testing import bintest_run_utils
class BinaryTestCase(absltest.TestCase):
"""Base class for testing binaries.
Provides utility methods for running binaries, getting paths to data
dependencies (defined in the `named_data` attribute of the build rule),
and extracting information from logs.
"""
def assert_binary_succeeds(self, binary: str, *args: str) -> str:
"""Runs a command, expanding $(label) expressions, and returns the output.
`binary` and `args` can contain expressions of the form `$(label)`. These
expressions are replaced by the absolute path of the data dependency
associated with `label` in the `named_data` attribute of the build rule.
See `bintest_run_utils.run` for more details.
Args:
binary: The path to the binary to execute.
*args: Arguments to pass to the command.
Returns:
The standard output of the command execution.
Raises:
ValueError: If any label expansion fails.
"""
try:
return bintest_run_utils.run(binary, *args)
except subprocess.CalledProcessError as e:
self.fail(
f"Expected {binary!r} to succeed but it failed with a return code"
f" {e.returncode}.\n"
f"The expanded command line was: {e.cmd!r}.\n"
f"The standard error was: {e.stderr!r}.\n"
f"The standard output was: {e.stdout!r}.\n"
)
def assert_binary_fails(
self, binary: str, *args: str
) -> subprocess.CalledProcessError:
"""Runs a command, expanding $(label) expressions, and asserts that it fails.
`binary` and `args` can contain expressions of the form `$(label)`. These
expressions are replaced by the absolute path of the data dependency
associated with `label` in the `named_data` attribute of the build rule.
See `bintest_run_utils.run` for more details.
Args:
binary: The path to the binary to execute.
*args: Arguments to pass to the command.
Returns:
The subprocess.CalledProcessError raised by the command execution.
Raises:
ValueError: If any label expansion fails.
"""
with self.assertRaises(subprocess.CalledProcessError) as e:
bintest_run_utils.run(binary, *args)
return e.exception
def assert_extract(self, log: str, *patterns: str) -> Sequence[float]:
"""Extracts floating point numbers from a log string based on patterns.
Patterns are applied sequentially. Each pattern can contain "@num()"
placeholders, which are replaced by a regex to capture floating-point
numbers.
See `bintest_matchers.extract` for more details.
Args:
log: The log string to search within.
*patterns: Pattern strings to search for sequentially.
Returns:
A tuple of floating point numbers extracted from the log, in the order of
their `@num()` matchers in `patterns`.
Raises:
AssertionError: If any pattern is not found or if number extraction fails.
"""
try:
return bintest_matchers.extract(log, *patterns, check_matcher_specs=False)
except bintest_matchers.MatchError as e:
self.fail(e)
def assert_has_line_with_prefixed_number(self, prefix: str, log: str) -> float:
"""Extracts a floating point number from a log line starting with a prefix.
Args:
prefix: The prefix of the line to search for.
log: The log string to search within.
Returns:
The floating point number extracted from the line.
"""
regex = re.escape(prefix) + " *" + bintest_matchers.Re.NUMBER
if match := re.search(regex, log):
return float(match[1])
self.fail(f"No line starting with {prefix!r} found in {log!r}")
def main() -> None:
absltest.main()