feat(test): enable parallel tests (#384)
- Set unique WORKING_DIRECTORY per test binary via cc_test()/cuda_test() to prevent filesystem path conflicts when running tests in parallel. Each test runs in ${CMAKE_BINARY_DIR}/test_tmp/${test_name}/.
- Enable parallel ctest execution in the unittest target with ProcessorCount-based --parallel flag (defaults to NPROC - 1).
- Set TEST_BINARY_DIR environment variable for crash recovery tests so they can locate helper binaries from isolated working directories.
- Update LocateDataGenerator() and LocateOptimizeGenerator() to search TEST_BINARY_DIR and TEST_BINARY_DIR/bin for helper executables.
This commit is contained in:
parent
d0489da401
commit
42958caef5
|
|
@ -93,6 +93,7 @@ jobs:
|
|||
cmake==3.30.0 \
|
||||
ninja==1.11.1 \
|
||||
pytest \
|
||||
pytest-xdist \
|
||||
scikit-build-core \
|
||||
setuptools_scm
|
||||
shell: bash
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ jobs:
|
|||
cmake==3.30.0 `
|
||||
ninja==1.11.1 `
|
||||
pytest `
|
||||
pytest-xdist `
|
||||
scikit-build-core `
|
||||
setuptools_scm
|
||||
shell: powershell
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ jobs:
|
|||
ninja==1.11.1 \
|
||||
pytest \
|
||||
pytest-cov \
|
||||
pytest-xdist \
|
||||
scikit-build-core \
|
||||
setuptools_scm
|
||||
shell: bash
|
||||
|
|
|
|||
|
|
@ -318,10 +318,20 @@ if(NOT TARGET unittest)
|
|||
# iOS: build-only target; tests are run on simulator separately
|
||||
add_custom_target(unittest)
|
||||
else()
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(NPROC)
|
||||
if(NPROC EQUAL 0)
|
||||
set(NPROC 1)
|
||||
endif()
|
||||
math(EXPR PARALLEL_JOBS "${NPROC} - 1")
|
||||
if(PARALLEL_JOBS LESS 1)
|
||||
set(PARALLEL_JOBS 1)
|
||||
endif()
|
||||
add_custom_target(
|
||||
unittest
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
|
||||
--build-config $<CONFIGURATION>
|
||||
--parallel ${PARALLEL_JOBS}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1110,16 +1120,18 @@ function(cc_test)
|
|||
"${CC_ARGS_UNPARSED_ARGUMENTS}"
|
||||
)
|
||||
add_dependencies(unittest ${CC_ARGS_NAME})
|
||||
set(TEST_WORKING_DIR "${CMAKE_BINARY_DIR}/test_tmp/${CC_ARGS_NAME}")
|
||||
file(MAKE_DIRECTORY "${TEST_WORKING_DIR}")
|
||||
add_custom_target(
|
||||
unittest.${CC_ARGS_NAME}
|
||||
COMMAND $<TARGET_FILE:${CC_ARGS_NAME}> "${CC_ARGS_ARGS}"
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${TEST_WORKING_DIR}
|
||||
DEPENDS ${CC_ARGS_NAME}
|
||||
)
|
||||
add_test(
|
||||
NAME ${CC_ARGS_NAME}
|
||||
COMMAND $<TARGET_FILE:${CC_ARGS_NAME}> "${CC_ARGS_ARGS}"
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${TEST_WORKING_DIR}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
|
|
@ -1925,16 +1937,18 @@ function(cuda_test)
|
|||
"${CUDA_ARGS_UNPARSED_ARGUMENTS}"
|
||||
)
|
||||
add_dependencies(unittest ${CUDA_ARGS_NAME})
|
||||
set(TEST_WORKING_DIR "${CMAKE_BINARY_DIR}/test_tmp/${CUDA_ARGS_NAME}")
|
||||
file(MAKE_DIRECTORY "${TEST_WORKING_DIR}")
|
||||
add_custom_target(
|
||||
unittest.${CUDA_ARGS_NAME}
|
||||
COMMAND $<TARGET_FILE:${CUDA_ARGS_NAME}> "${CUDA_ARGS_ARGS}"
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${TEST_WORKING_DIR}
|
||||
DEPENDS ${CUDA_ARGS_NAME}
|
||||
)
|
||||
add_test(
|
||||
NAME ${CUDA_ARGS_NAME}
|
||||
COMMAND $<TARGET_FILE:${CUDA_ARGS_NAME}> "${CUDA_ARGS_ARGS}"
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${TEST_WORKING_DIR}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ test = [
|
|||
"pytest >=8.0",
|
||||
"pytest-cov >=4.1",
|
||||
"pytest-mock >=3.12",
|
||||
"pytest-xdist >=3.5",
|
||||
"cibuildwheel == 3.4.0",
|
||||
]
|
||||
docs = [
|
||||
|
|
@ -142,6 +143,7 @@ addopts = [
|
|||
"--strict-markers",
|
||||
"--strict-config",
|
||||
"--tb=short",
|
||||
"-n=auto",
|
||||
]
|
||||
xfail_strict = true
|
||||
log_cli_level = "INFO"
|
||||
|
|
@ -169,7 +171,7 @@ build = [
|
|||
"cp314-*",
|
||||
]
|
||||
build-frontend = "build"
|
||||
test-requires = ["pytest", "numpy"]
|
||||
test-requires = ["pytest", "pytest-xdist", "numpy"]
|
||||
test-command = "cd {project} && pytest python/tests -v --tb=short"
|
||||
build-verbosity = 1
|
||||
|
||||
|
|
|
|||
|
|
@ -77,5 +77,8 @@ foreach(CC_SRCS ${ALL_TEST_SRCS})
|
|||
)
|
||||
add_dependencies(${CC_TARGET} data_generator)
|
||||
add_dependencies(${CC_TARGET} collection_optimizer)
|
||||
set_tests_properties(${CC_TARGET} PROPERTIES
|
||||
ENVIRONMENT "TEST_BINARY_DIR=${PROJECT_BINARY_DIR}"
|
||||
)
|
||||
cc_test_suite(zvec_crash_recovery ${CC_TARGET})
|
||||
endforeach()
|
||||
|
|
|
|||
|
|
@ -46,38 +46,7 @@ const int num_batches{1000};
|
|||
|
||||
|
||||
static std::string LocateOptimizeGenerator() {
|
||||
namespace fs = std::filesystem;
|
||||
std::cout << "Current path: " << fs::current_path() << std::endl;
|
||||
|
||||
const std::string base_name = "collection_optimizer";
|
||||
std::vector<std::string> candidates;
|
||||
const std::vector<std::string> search_paths = {"./", "./bin/"};
|
||||
|
||||
for (const auto &p : search_paths) {
|
||||
candidates.push_back(p);
|
||||
}
|
||||
|
||||
// TODO(windows): unify _WIN32/_WIN64/MSCV_VER
|
||||
#ifdef _WIN32
|
||||
for (const auto &p : search_paths) {
|
||||
candidates.push_back(p + "Debug/");
|
||||
candidates.push_back(p + "Release/");
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto &p : candidates) {
|
||||
p += base_name;
|
||||
#ifdef _WIN32
|
||||
p += ".exe";
|
||||
#endif
|
||||
}
|
||||
|
||||
for (const auto &p : candidates) {
|
||||
if (fs::exists(p)) {
|
||||
return fs::canonical(p).string();
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("collection_optimizer binary not found");
|
||||
return LocateBinary("collection_optimizer");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <zvec/db/collection.h>
|
||||
#include <zvec/db/doc.h>
|
||||
|
||||
|
|
@ -149,4 +156,50 @@ inline Doc CreateTestDoc(uint64_t doc_id, int version) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Locate a binary by name, searching common paths and TEST_BINARY_DIR.
|
||||
*
|
||||
* @param binary_name The base name of the binary (e.g. "data_generator")
|
||||
* @return std::string The canonical path to the found binary
|
||||
* @throws std::runtime_error if the binary is not found
|
||||
*/
|
||||
inline std::string LocateBinary(const std::string &binary_name) {
|
||||
namespace fs = std::filesystem;
|
||||
std::cout << "Current path: " << fs::current_path() << std::endl;
|
||||
|
||||
std::vector<std::string> candidates;
|
||||
const std::vector<std::string> search_paths = {"./", "./bin/"};
|
||||
|
||||
for (const auto &p : search_paths) {
|
||||
candidates.push_back(p);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
for (const auto &p : search_paths) {
|
||||
candidates.push_back(p + "Debug/");
|
||||
candidates.push_back(p + "Release/");
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *test_binary_dir = std::getenv("TEST_BINARY_DIR");
|
||||
if (test_binary_dir != nullptr) {
|
||||
candidates.push_back(std::string(test_binary_dir) + "/");
|
||||
candidates.push_back(std::string(test_binary_dir) + "/bin/");
|
||||
}
|
||||
|
||||
for (auto &p : candidates) {
|
||||
p += binary_name;
|
||||
#ifdef _WIN32
|
||||
p += ".exe";
|
||||
#endif
|
||||
}
|
||||
|
||||
for (const auto &p : candidates) {
|
||||
if (fs::exists(p)) {
|
||||
return fs::canonical(p).string();
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(binary_name + " binary not found");
|
||||
}
|
||||
|
||||
} // namespace zvec
|
||||
|
|
|
|||
|
|
@ -44,38 +44,7 @@ const zvec::CollectionOptions options_{false, true, 256 * 1024};
|
|||
|
||||
|
||||
static std::string LocateDataGenerator() {
|
||||
namespace fs = std::filesystem;
|
||||
std::cout << "Current path: " << fs::current_path() << std::endl;
|
||||
|
||||
const std::string base_name = "data_generator";
|
||||
std::vector<std::string> candidates;
|
||||
// Define potential search locations relative to the current working directory
|
||||
const std::vector<std::string> search_paths = {"./", "./bin/"};
|
||||
|
||||
for (const auto &p : search_paths) {
|
||||
candidates.push_back(p);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
for (const auto &p : search_paths) {
|
||||
candidates.push_back(p + "Debug/");
|
||||
candidates.push_back(p + "Release/");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
for (auto &p : candidates) {
|
||||
p += base_name;
|
||||
#ifdef _WIN32
|
||||
p += ".exe"; // Append .exe suffix for Windows compatibility
|
||||
#endif
|
||||
}
|
||||
|
||||
for (const auto &p : candidates) {
|
||||
if (fs::exists(p)) {
|
||||
return fs::canonical(p).string();
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("data_generator binary not found");
|
||||
return LocateBinary("data_generator");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue