Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu26.04
# install needed packages
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install \
catch2 \
clang \
clang-tidy \
clang-format \
Expand All @@ -19,7 +20,6 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
python3 \
openmpi-bin \
libboost-dev \
libboost-test-dev \
libhwloc-dev \
libmsgpack-cxx-dev \
libopenmpi-dev \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Install dependencies from APT
run: |
sudo apt-get update
sudo apt-get install -y just libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev libhwloc-dev
sudo apt-get install -y just libopenmpi-dev openmpi-bin libboost-dev libmsgpack-cxx-dev libhwloc-dev catch2

- name: Install the latest version of uv
uses: astral-sh/setup-uv@v10.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docpages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
- name: Install dependencies from APT
run: |
sudo apt-get update
sudo apt-get install -y just libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev libhwloc-dev
sudo apt-get install -y just libopenmpi-dev openmpi-bin libboost-dev libmsgpack-cxx-dev libhwloc-dev catch2

- name: Install the latest version of uv
uses: astral-sh/setup-uv@v10.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/qa-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev libhwloc-dev
sudo apt-get install -y libopenmpi-dev openmpi-bin libboost-dev libmsgpack-cxx-dev libhwloc-dev catch2

- name: Install the latest version of uv
uses: astral-sh/setup-uv@v10.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
brew install boost open-mpi msgpack-cxx hwloc
else
sudo apt-get update
packages="libopenmpi-dev openmpi-bin libboost-dev libboost-test-dev libmsgpack-cxx-dev libhwloc-dev"
packages="libopenmpi-dev openmpi-bin libboost-dev libmsgpack-cxx-dev libhwloc-dev catch2"
if [[ "${{ matrix.compiler }}" == "clang++-18" ]]; then
packages="$packages clang-18"
fi
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ mp = MajoranaPropagator(operator, initial_state, cutoff=4)
- **nanobind**: Modern Python-C++ binding (prefer over pybind11)
- **scikit-build-core**: Modern build system replacing setuptools
- **uv**: Package management
- **Boost**: Used for various utilities (unordered_map, unit tests)
- **Boost**: Used for production C++ utilities such as unordered containers
- **Catch2 v3**: C++ unit-test framework
- **msgpack**: Serialization of the test-data fixtures only (`tests/data/*.msgpack`); consumed by the Python test loaders and the C++ test suite, not by the shipped library
- **hwloc**: CPU topology discovery and thread binding for partition placement (`CpuTopology.cpp`). Required system library (`libhwloc-dev` on Debian/Ubuntu, `hwloc` on Homebrew). Requires `pkg-config` so CMake can locate `hwloc`. Bundled into wheels automatically by auditwheel/delocate.
- **MPI**: For distributed parallelization
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,16 @@ uv sync --all-extras -v
uv sync --all-extras -v --config-settings=cmake.define.monoprop_ENABLE_MPI=ON
```

C++ unit-test build:
C++ unit-test build (Catch2 v3, registered through CTest):

```bash
uv sync --all-extras -v
ctest --test-dir build/editable/Release
```

Release builds compile the test sources at `-O1` to reduce template-heavy build
time while keeping the library at `-O3`.

Full instructions — prerequisites, MPI options, and running the example
executable — are in the [building guide](https://docs.monoprop.algorithmiq.tech/building).
In particular, from-source builds require `hwloc` and `pkg-config` so CMake can
Expand Down
108 changes: 89 additions & 19 deletions cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
find_package(Boost 1.85 COMPONENTS unit_test_framework REQUIRED)

include(${PROJECT_SOURCE_DIR}/cmake/CPM.cmake)

cpmfindpackage(
NAME Catch2
VERSION 3
GIT_REPOSITORY "https://github.com/catchorg/Catch2.git"
GIT_TAG "v3.15.3"
OPTIONS "CATCH_INSTALL_DOCS OFF"
SYSTEM YES
EXCLUDE_FROM_ALL YES
)

cpmaddpackage(
NAME "msgpack-cxx"
GIT_REPOSITORY "https://github.com/msgpack/msgpack-c"
Expand All @@ -26,12 +34,7 @@ file(

add_executable(monoprop_unit_tests.x ${_tests_cpps})

target_compile_definitions(
monoprop_unit_tests.x
PUBLIC
BOOST_TEST_DYN_LINK
BOOST_TEST_NO_MAIN
)
target_compile_options(monoprop_unit_tests.x PRIVATE $<$<CONFIG:Release>:-O1>)

target_include_directories(
monoprop_unit_tests.x
Expand All @@ -44,14 +47,19 @@ target_link_libraries(
monoprop_unit_tests.x
PRIVATE
monoprop-objs
Boost::unit_test_framework
Catch2::Catch2
msgpack-cxx
PkgConfig::HWLOC
)

include(${CMAKE_CURRENT_LIST_DIR}/boost-test.cmake)
if(Catch2_SOURCE_DIR)
list(APPEND CMAKE_MODULE_PATH "${Catch2_SOURCE_DIR}/extras")
else()
list(APPEND CMAKE_MODULE_PATH "${Catch2_DIR}")
endif()
include(Catch)

# CTest launches each Boost case as a world-size-1 process, so excluding fabric components cuts
# CTest launches each Catch2 case as a world-size-1 process, so excluding fabric components cuts
# MPI_Init from 2.03 s to 0.61 s without affecting communication; real MPI tests must keep the
# full component set.
#
Expand All @@ -71,14 +79,76 @@ if(monoprop_TEST_EXCLUDE_MPI_FABRIC AND monoprop_ENABLE_MPI)
)
endif()

# Automatic discovery of unit tests.
# Default CTest run includes per-case serial tests plus suite-level MPI variants
# for monoprop_MPI_TEST_PROCS.
discover_tests(
catch_discover_tests(
monoprop_unit_tests.x
PROPERTIES
LABELS
"unit"
SERIAL_ENVIRONMENT
${_monoprop_serial_env_entries}
LABELS
unit
)

configure_file(
${CMAKE_CURRENT_LIST_DIR}/catch-properties.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/catch-properties.cmake
@ONLY
)
set_property(
DIRECTORY
APPEND
PROPERTY
TEST_INCLUDE_FILES
${CMAKE_CURRENT_BINARY_DIR}/catch-properties.cmake
)

set(
monoprop_MPI_TEST_PROCS
"2"
CACHE STRING
"Semicolon-separated list of ranks for MPI test variants"
)

set(_monoprop_mpiexec "${MPIEXEC_EXECUTABLE}")
if(NOT _monoprop_mpiexec)
find_program(
_monoprop_mpiexec
NAMES
mpiexec
mpirun
)
endif()

set(_monoprop_mpiexec_numproc_flag "${MPIEXEC_NUMPROC_FLAG}")
if(NOT _monoprop_mpiexec_numproc_flag)
set(_monoprop_mpiexec_numproc_flag "-n")
endif()

if(monoprop_ENABLE_MPI AND _monoprop_mpiexec)
set(_monoprop_mpi_ranks ${monoprop_MPI_TEST_PROCS})
list(REMOVE_DUPLICATES _monoprop_mpi_ranks)
foreach(_monoprop_mpi_rank IN LISTS _monoprop_mpi_ranks)
if(NOT _monoprop_mpi_rank MATCHES "^[1-9][0-9]*$")
message(
FATAL_ERROR
"Invalid MPI rank '${_monoprop_mpi_rank}' in monoprop_MPI_TEST_PROCS='${monoprop_MPI_TEST_PROCS}'"
)
endif()

add_test(
NAME "monoprop_unit_tests.x_mpi_${_monoprop_mpi_rank}"
COMMAND
"${_monoprop_mpiexec}" "${_monoprop_mpiexec_numproc_flag}"
"${_monoprop_mpi_rank}" ${MPIEXEC_PREFLAGS}
$<TARGET_FILE:monoprop_unit_tests.x> --order lex --rng-seed 1
${MPIEXEC_POSTFLAGS}
)
set_tests_properties(
"monoprop_unit_tests.x_mpi_${_monoprop_mpi_rank}"
PROPERTIES
WORKING_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}"
LABELS
"unit;cxx;mpi;mpi-${_monoprop_mpi_rank}"
ENVIRONMENT
"OMPI_ALLOW_RUN_AS_ROOT=1;OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1"
)
endforeach()
endif()
27 changes: 15 additions & 12 deletions cpp/tests/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# C++ Test Suite

This directory contains the C++ test suite for monoprop, built using Boost.Test.
This directory contains the C++ test suite for monoprop, built using Catch2 v3.
Every `*.cpp` here is globbed into a single executable, `monoprop_unit_tests.x`.

## Test Organization

Tests carry no labels of their own. The CTest harness (`boostAddTests.cmake`)
discovers every Boost.Test case and registers it twice:
Catch2's CMake integration discovers every `TEST_CASE` and registers it as a
`serial` CTest test. The project also registers suite-level MPI variants:

- **`serial`**: the case run in-process with `MPI_COMM_SELF`.
- **`mpi`** (+ rank-specific `mpi-<n>`): the whole suite wrapped in
Expand All @@ -30,6 +30,9 @@ uv sync --all-extras -v
ctest --test-dir build/editable/Release
```

Release builds compile these test sources at `-O1`; linked production objects
remain at `-O3`.

For an MPI-enabled tree, rerun `uv sync` with
`--config-settings=cmake.define.monoprop_ENABLE_MPI=ON`.
## Running Tests
Expand All @@ -44,15 +47,14 @@ ctest --test-dir build/editable/Release -L mpi-2 # only the 2-rank run
Or drive the binary directly:

```bash
build/editable/Release/bin/monoprop_unit_tests.x --list_content
build/editable/Release/bin/monoprop_unit_tests.x --run_test=pauli_algebra_*
build/editable/Release/bin/monoprop_unit_tests.x --list-tests
build/editable/Release/bin/monoprop_unit_tests.x "pauli_algebra_*"
mpirun -n 2 build/editable/Release/bin/monoprop_unit_tests.x
```

Because CTest discovery treats each `--list_content` line as a top-level test
name and cannot address suite-nested cases, tests use flat
`BOOST_AUTO_TEST_CASE`s with a shared name prefix (e.g. `pauli_algebra_*`,
`inverted_index_*`) rather than `BOOST_AUTO_TEST_SUITE`.
Tests use stable, flat names with shared prefixes (for example,
`pauli_algebra_*` and `inverted_index_*`) so direct Catch2 test specs remain
predictable.

## Shared Test Utilities

Expand All @@ -71,7 +73,8 @@ name and cannot address suite-nested cases, tests use flat
(`core_with_gate`, `layer_with_gate`, `graph_with_gates`) for white-box
MPGraph transform tests.
- **`TestData.{h,cpp}`**: the `CaseData` struct and msgpack fixture loader.
- **`boost-test.cmake` / `boostAddTests.cmake`**: CMake test discovery.
- **`catch-properties.cmake.in`**: adds the project CTest labels and serial-only
MPI environment to Catch2's discovered tests.

## Test Files (by area)

Expand Down Expand Up @@ -118,11 +121,11 @@ CMake wraps the whole suite in `mpiexec -n <rank>` for each rank in
per case, because the ranks have to reach the same collectives. For exhaustive
rank coverage: `-Dmonoprop_MPI_TEST_PROCS='1;2;4'`. To run a single case under
MPI while debugging, invoke the binary directly:
`mpirun -n 2 build/editable/Release/bin/monoprop_unit_tests.x --run_test=<case>`.
`mpirun -n 2 build/editable/Release/bin/monoprop_unit_tests.x "<case>"`.

## Adding New Tests

1. Add a `*.cpp` with flat `BOOST_AUTO_TEST_CASE`s (shared name prefix).
1. Add a `*.cpp` with Catch2 `TEST_CASE`s and a shared name prefix.
2. Reuse the shared helpers above rather than copying oracle/harness code.
3. For MPI-required scenarios, check `monoprop::mpi::size(MPI_COMM_WORLD)` and
skip if `< 2`.
Expand Down
Loading
Loading