[libc] Implement lit-based test execution for Libc (#178746)
This provides optional lit-based test execution for the LLVM Libc tests, alongside the existing CMake-based test execution. Usage: ninja -C build check-libc-lit cd build && bin/llvm-lit libc/test/src/ Partially addresses [#118694](https://github.com/llvm/llvm-project/issues/118694). A future PR once this lands will flip the default (per suggestion in the RFC)
This commit is contained in:
@@ -344,6 +344,10 @@ function(create_libc_unittest fq_target_name)
|
||||
)
|
||||
endif()
|
||||
add_dependencies(libc-unit-tests ${fq_target_name})
|
||||
# Also add dependency to build-only target for lit
|
||||
if(TARGET libc-unit-tests-build)
|
||||
add_dependencies(libc-unit-tests-build ${fq_build_target_name})
|
||||
endif()
|
||||
endfunction(create_libc_unittest)
|
||||
|
||||
function(add_libc_unittest target_name)
|
||||
@@ -883,6 +887,10 @@ function(add_libc_hermetic test_name)
|
||||
# If it is a benchmark, it will already have been added to the
|
||||
# gpu-benchmark target
|
||||
add_dependencies(libc-hermetic-tests ${fq_target_name})
|
||||
# Also add dependency to build-only target for lit
|
||||
if(TARGET libc-hermetic-tests-build)
|
||||
add_dependencies(libc-hermetic-tests-build ${fq_build_target_name})
|
||||
endif()
|
||||
endif()
|
||||
endfunction(add_libc_hermetic)
|
||||
|
||||
|
||||
@@ -6,6 +6,31 @@ add_dependencies(check-libc libc-unit-tests libc-hermetic-tests)
|
||||
add_custom_target(exhaustive-check-libc)
|
||||
add_custom_target(libc-long-running-tests)
|
||||
|
||||
# Build-only targets for lit (don't run tests, just build executables)
|
||||
add_custom_target(libc-unit-tests-build)
|
||||
add_custom_target(libc-hermetic-tests-build)
|
||||
|
||||
# Configure the site config file for lit
|
||||
configure_lit_site_cfg(
|
||||
${LIBC_SOURCE_DIR}/test/lit.site.cfg.py.in
|
||||
${LIBC_BUILD_DIR}/test/lit.site.cfg.py
|
||||
MAIN_CONFIG
|
||||
${LIBC_SOURCE_DIR}/test/lit.cfg.py
|
||||
PATHS
|
||||
"LLVM_SOURCE_DIR"
|
||||
"LLVM_BINARY_DIR"
|
||||
"LLVM_TOOLS_DIR"
|
||||
"LLVM_LIBS_DIR"
|
||||
"LIBC_SOURCE_DIR"
|
||||
"LIBC_BUILD_DIR"
|
||||
)
|
||||
|
||||
add_lit_testsuite(check-libc-lit
|
||||
"Running libc tests via lit"
|
||||
${LIBC_BUILD_DIR}/test
|
||||
DEPENDS libc-unit-tests-build libc-hermetic-tests-build
|
||||
)
|
||||
|
||||
add_subdirectory(UnitTest)
|
||||
|
||||
if(LIBC_TARGET_OS_IS_GPU)
|
||||
|
||||
17
libc/test/lit.cfg.py
Normal file
17
libc/test/lit.cfg.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- Python -*-
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# All the Lit configuration is handled in the site config -- this file is only
|
||||
# left as a canary to catch invocations of Lit that do not go through llvm-lit.
|
||||
#
|
||||
# Invocations that go through llvm-lit will automatically use the right Lit
|
||||
# site configuration inside the build directory.
|
||||
|
||||
lit_config.fatal(
|
||||
"You seem to be running Lit directly -- you should be running Lit through "
|
||||
"<build>/bin/llvm-lit, which will ensure that the right Lit configuration "
|
||||
"file is used."
|
||||
)
|
||||
37
libc/test/lit.site.cfg.py.in
Normal file
37
libc/test/lit.site.cfg.py.in
Normal file
@@ -0,0 +1,37 @@
|
||||
@LIT_SITE_CFG_IN_HEADER@
|
||||
|
||||
import os
|
||||
import site
|
||||
|
||||
# Configuration values from CMake
|
||||
config.llvm_tools_dir = lit_config.substitute(path(r"@LLVM_TOOLS_DIR@"))
|
||||
config.libc_src_root = path(r"@LIBC_SOURCE_DIR@")
|
||||
config.libc_obj_root = path(r"@LIBC_BUILD_DIR@")
|
||||
config.libc_test_cmd = "@LIBC_TEST_CMD@"
|
||||
|
||||
# Add libc's utils directory to the path so we can import the test format.
|
||||
site.addsitedir(os.path.join(config.libc_src_root, "utils"))
|
||||
import libctest
|
||||
|
||||
# name: The name of this test suite.
|
||||
config.name = "libc"
|
||||
|
||||
# testFormat: Use libc's custom test format that discovers pre-built
|
||||
# test executables (Libc*Tests) in the build directory.
|
||||
config.test_format = libctest.LibcTest()
|
||||
|
||||
# excludes: A list of directories to exclude from the testsuite.
|
||||
config.excludes = ["Inputs", "CMakeLists.txt", "README.txt", "LICENSE.txt", "UnitTest"]
|
||||
|
||||
# test_source_root: The root path where tests are located.
|
||||
# test_exec_root: The root path where test executables are built.
|
||||
# Set both to the build directory so ExecutableTest finds executables correctly.
|
||||
config.test_exec_root = os.path.join(config.libc_obj_root, "test")
|
||||
config.test_source_root = config.test_exec_root
|
||||
|
||||
# Add tool directories to PATH (in case we add FileCheck tests later).
|
||||
if hasattr(config, "llvm_tools_dir") and config.llvm_tools_dir:
|
||||
config.environment["PATH"] = os.path.pathsep.join(
|
||||
[config.llvm_tools_dir, config.environment.get("PATH", "")]
|
||||
)
|
||||
|
||||
21
libc/utils/libctest/__init__.py
Normal file
21
libc/utils/libctest/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# ===----------------------------------------------------------------------===##
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===----------------------------------------------------------------------===##
|
||||
|
||||
"""
|
||||
Lit test format for LLVM libc unit tests.
|
||||
|
||||
This format extends lit.formats.ExecutableTest to discover pre-built test
|
||||
executables in the build directory. Test executables are expected to follow
|
||||
the naming pattern used by add_libc_test():
|
||||
libc.test.src.<category>.<test_name>.__unit__.__build__
|
||||
libc.test.src.<category>.<test_name>.__hermetic__.__build__
|
||||
"""
|
||||
|
||||
from .format import LibcTest
|
||||
|
||||
__all__ = ["LibcTest"]
|
||||
106
libc/utils/libctest/format.py
Normal file
106
libc/utils/libctest/format.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# ===----------------------------------------------------------------------===##
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===----------------------------------------------------------------------===##
|
||||
|
||||
"""
|
||||
Lit test format for LLVM libc tests.
|
||||
|
||||
This format discovers pre-built test executables in the build directory
|
||||
and runs them. It extends lit's ExecutableTest format.
|
||||
|
||||
The lit config sets test_source_root == test_exec_root (both to the build
|
||||
directory), following the pattern used by llvm/test/Unit/lit.cfg.py.
|
||||
|
||||
Test executables are discovered by looking for files matching:
|
||||
libc.test.src.<category>.<test_name>.__unit__.__build__
|
||||
libc.test.src.<category>.<test_name>.__hermetic__.__build__
|
||||
|
||||
These are created by the add_libc_test() infrastructure.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shlex
|
||||
|
||||
import lit.formats
|
||||
import lit.Test
|
||||
import lit.util
|
||||
|
||||
|
||||
class LibcTest(lit.formats.ExecutableTest):
|
||||
"""
|
||||
Test format for libc unit tests.
|
||||
|
||||
Extends ExecutableTest to discover tests from the build directory
|
||||
rather than the source directory. Test executables are named like:
|
||||
libc.test.src.ctype.isalnum_test.__unit__.__build__
|
||||
and return 0 on success.
|
||||
"""
|
||||
|
||||
def getTestsInDirectory(self, testSuite, path_in_suite, litConfig, localConfig):
|
||||
"""
|
||||
Discover test executables in the build directory.
|
||||
|
||||
Since test_source_root == test_exec_root (both point to build dir),
|
||||
we use getSourcePath() to find test executables.
|
||||
"""
|
||||
source_path = testSuite.getSourcePath(path_in_suite)
|
||||
|
||||
# Look for test executables in the build directory
|
||||
if not os.path.isdir(source_path):
|
||||
return
|
||||
|
||||
# Sort for deterministic test discovery/output ordering.
|
||||
for filename in sorted(os.listdir(source_path)):
|
||||
filepath = os.path.join(source_path, filename)
|
||||
|
||||
# Match our test executable pattern
|
||||
if self._isTestExecutable(filename, filepath):
|
||||
# Create a test with the executable name
|
||||
yield lit.Test.Test(testSuite, path_in_suite + (filename,), localConfig)
|
||||
|
||||
def _isTestExecutable(self, filename, filepath):
|
||||
"""Check if a file is a test executable we should run."""
|
||||
# Pattern: libc.test.src.*.__unit__.__build__ or .__hermetic__.__build__
|
||||
if not filename.startswith("libc.test."):
|
||||
return False
|
||||
if not (
|
||||
filename.endswith(".__unit__.__build__")
|
||||
or filename.endswith(".__hermetic__.__build__")
|
||||
):
|
||||
return False
|
||||
# Must be executable
|
||||
if not os.path.isfile(filepath):
|
||||
return False
|
||||
if not os.access(filepath, os.X_OK):
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, test, litConfig):
|
||||
"""
|
||||
Execute a test by running the test executable.
|
||||
|
||||
Runs from the executable's directory so relative paths (like
|
||||
testdata/test.txt) work correctly.
|
||||
"""
|
||||
|
||||
test_path = test.getSourcePath()
|
||||
exec_dir = os.path.dirname(test_path)
|
||||
|
||||
test_cmd_template = getattr(test.config, "libc_test_cmd", "")
|
||||
if test_cmd_template:
|
||||
test_cmd = test_cmd_template.replace("@BINARY@", test_path)
|
||||
cmd_args = shlex.split(test_cmd)
|
||||
if not cmd_args:
|
||||
cmd_args = [test_path]
|
||||
out, err, exit_code = lit.util.executeCommand(cmd_args, cwd=exec_dir)
|
||||
else:
|
||||
out, err, exit_code = lit.util.executeCommand([test_path], cwd=exec_dir)
|
||||
|
||||
if not exit_code:
|
||||
return lit.Test.PASS, ""
|
||||
|
||||
return lit.Test.FAIL, out + err
|
||||
Reference in New Issue
Block a user