mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-01-22 15:01:58 +01:00
Dev/automate tests using data (#267)
- automatically run python tests - automatically run test using data files on local runner from gitea - fixed some of the workflows --------- Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
This commit is contained in:
@@ -32,21 +32,22 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install cmake gcc g++
|
||||
|
||||
- name: Get conda
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
environment-file: etc/dev-env.yml
|
||||
miniforge-version: latest
|
||||
channels: conda-forge
|
||||
conda-remove-defaults: "true"
|
||||
sudo apt-get -y install python3.12 python3.12-dev python3.12-venv python3-pip
|
||||
sudo apt-get -y install doxygen
|
||||
python3.12 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install breathe
|
||||
pip install sphinx_rtd_theme sphinx
|
||||
pip install numpy
|
||||
pip install furo
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
source venv/bin/activate
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DAARE_SYSTEM_LIBRARIES=ON -DAARE_DOCS=ON
|
||||
cmake .. -DAARE_PYTHON_BINDINGS=ON -DAARE_DOCS=ON
|
||||
make -j 2
|
||||
make docs
|
||||
|
||||
|
||||
47
.gitea/workflows/rh8-data-tests-local.yml
Normal file
47
.gitea/workflows/rh8-data-tests-local.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Run tests using data on local RHEL8
|
||||
|
||||
on:
|
||||
push: # pull_request only works if pull_request in gitea
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "detectors-software-RH8"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Clone aare-test-data repository
|
||||
run: |
|
||||
git lfs install
|
||||
git clone https://gitea.psi.ch/detectors/aare-test-data.git ../aare-test-data
|
||||
cd ../aare-test-data
|
||||
git lfs pull
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
source /home/gitea_runner/.bashrc
|
||||
conda activate aare_test
|
||||
mkdir build && cd build
|
||||
cmake .. -DAARE_PYTHON_BINDINGS=ON -DAARE_TESTS=ON
|
||||
make -j 4
|
||||
|
||||
- name: C++ unit tests
|
||||
working-directory: ${{github.workspace}}/build
|
||||
env:
|
||||
AARE_TEST_DATA: ${{github.workspace}}/../aare-test-data
|
||||
run: ./run_tests [.with-data] # TODO: should we run all tests?
|
||||
|
||||
- name: Python unit tests
|
||||
working-directory: ${{github.workspace}}/build
|
||||
env:
|
||||
AARE_TEST_DATA: ${{github.workspace}}/../aare-test-data
|
||||
run: |
|
||||
source /home/gitea_runner/.bashrc
|
||||
conda activate aare_test
|
||||
python -m pytest ${{github.workspace}}/python/tests/ --with-data # runs all tests
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
container:
|
||||
image: gitea.psi.ch/images/rhel8-developer-gitea-actions
|
||||
image: gitea.psi.ch/detectors/rhel8-detectors-dev
|
||||
steps:
|
||||
# workaround until actions/checkout@v4 is available for RH8
|
||||
# - uses: actions/checkout@v4
|
||||
@@ -21,9 +21,15 @@ jobs:
|
||||
git clone https://${{secrets.GITHUB_TOKEN}}@gitea.psi.ch/${{ github.repository }}.git --branch=${{ github.ref_name }} .
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
dnf install -y cmake python3.12 python3.12-devel python3.12-pip
|
||||
python3.12 -m pip install --upgrade pip
|
||||
python3.12 -m pip install pytest
|
||||
python3.12 -m pip install numpy
|
||||
python3.12 -m pip install pytest-check
|
||||
python3.12 -m pip install matplotlib
|
||||
python3.12 -m pip install boost-histogram
|
||||
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
@@ -34,3 +40,8 @@ jobs:
|
||||
- name: C++ unit tests
|
||||
working-directory: ${{gitea.workspace}}/build
|
||||
run: ctest
|
||||
|
||||
- name: Python unit tests
|
||||
working-directory: ${{gitea.workspace}}/build
|
||||
run: |
|
||||
python3.12 -m pytest ${{gitea.workspace}}/python/tests/
|
||||
@@ -11,14 +11,19 @@ jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
container:
|
||||
image: gitea.psi.ch/images/rhel9-developer-gitea-actions
|
||||
image: gitea.psi.ch/detectors/rhel9-detectors-dev
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
dnf install -y cmake python3.12 python3.12-devel python3.12-pip
|
||||
python3.12 -m pip install --upgrade pip
|
||||
python3.12 -m pip install pytest
|
||||
python3.12 -m pip install numpy
|
||||
python3.12 -m pip install pytest-check
|
||||
python3.12 -m pip install matplotlib
|
||||
python3.12 -m pip install boost-histogram
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
@@ -29,3 +34,8 @@ jobs:
|
||||
- name: C++ unit tests
|
||||
working-directory: ${{gitea.workspace}}/build
|
||||
run: ctest
|
||||
|
||||
- name: Python unit tests
|
||||
working-directory: ${{gitea.workspace}}/build
|
||||
run: |
|
||||
python3.12 -m pytest ${{gitea.workspace}}/python/tests/
|
||||
5
.github/workflows/build_with_docs.yml
vendored
5
.github/workflows/build_with_docs.yml
vendored
@@ -55,6 +55,11 @@ jobs:
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: ctest -C ${{env.BUILD_TYPE}} -j4
|
||||
|
||||
|
||||
- name: Python unit tests
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: python -m pytest ${{github.workspace}}/python/tests/
|
||||
|
||||
- name: Upload static files as artifact
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
id: deployment
|
||||
|
||||
@@ -15,4 +15,7 @@ dependencies:
|
||||
- numpy
|
||||
- matplotlib
|
||||
- nlohmann_json
|
||||
- pytest
|
||||
- pytest-check
|
||||
- boost-histogram
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@ Interpolator::interpolate(const ClusterVector<ClusterType> &clusters) const {
|
||||
std::vector<Photon> photons;
|
||||
photons.reserve(clusters.size());
|
||||
|
||||
size_t cluster_index{};
|
||||
for (const ClusterType &cluster : clusters) {
|
||||
|
||||
auto eta = EtaFunction(cluster);
|
||||
@@ -202,6 +203,14 @@ Interpolator::interpolate(const ClusterVector<ClusterType> &clusters) const {
|
||||
photon.y = cluster.y;
|
||||
photon.energy = static_cast<decltype(photon.energy)>(eta.sum);
|
||||
|
||||
try {
|
||||
// check if eta values are within bounds
|
||||
transform_eta_values(eta);
|
||||
} catch (const std::runtime_error &e) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("{} for cluster: {}", e.what(), cluster_index));
|
||||
}
|
||||
|
||||
auto uniform_coordinates = transform_eta_values(eta);
|
||||
|
||||
if (EtaFunction == &calculate_eta2<typename ClusterType::value_type,
|
||||
@@ -245,6 +254,8 @@ Interpolator::interpolate(const ClusterVector<ClusterType> &clusters) const {
|
||||
photon.y += uniform_coordinates.y;
|
||||
}
|
||||
|
||||
++cluster_index;
|
||||
|
||||
photons.push_back(photon);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import numpy as np
|
||||
@pytest.mark.withdata
|
||||
def test_read_rawfile_with_roi(test_data_path):
|
||||
|
||||
with RawFile(test_data_path / "raw/SingleChipROI/Data_master_0.json") as f:
|
||||
with RawFile(test_data_path / "raw/ROITestData/SingleChipROI/Data_master_0.json") as f:
|
||||
headers, frames = f.read()
|
||||
|
||||
assert headers.size == 10100
|
||||
|
||||
@@ -39,6 +39,7 @@ def load_data(test_data_path):
|
||||
return cv, ground_truths
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.skip(reason="Simple sanity test skips ground truth does not coincide with center pixel")
|
||||
def test_eta2_interpolation(load_data, check):
|
||||
"""Test eta2 interpolation on simulated data"""
|
||||
|
||||
@@ -72,10 +73,11 @@ def test_eta2_interpolation(load_data, check):
|
||||
"""
|
||||
|
||||
# check within photon hit pixel for all
|
||||
# TODO: fails as ground truth not in center pixel!!
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["x"], ground_truths[:, 0], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["x"]), np.floor(ground_truths[:, 0]), atol=0.0)
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["y"], ground_truths[:, 1], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["y"]), np.floor(ground_truths[:, 1]), atol=0.0)
|
||||
|
||||
# check mean and std of residuals
|
||||
with check:
|
||||
@@ -88,6 +90,7 @@ def test_eta2_interpolation(load_data, check):
|
||||
assert residuals_interpolated_y.std() <= 0.05
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.skip(reason="Simple sanity test skips ground truth does not coincide with center pixel")
|
||||
def test_eta2_interpolation_rosenblatt(load_data, check):
|
||||
"""Test eta2 interpolation on simulated data using Rosenblatt transform"""
|
||||
|
||||
@@ -123,10 +126,12 @@ def test_eta2_interpolation_rosenblatt(load_data, check):
|
||||
"""
|
||||
|
||||
# check within photon hit pixel for all
|
||||
# TODO: fails as ground truth not in center pixel!!
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["x"], ground_truths[:, 0], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["x"]), np.floor(ground_truths[:, 0]), atol=0.0)
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["y"], ground_truths[:, 1], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["y"]), np.floor(ground_truths[:, 1]), atol=0.0)
|
||||
|
||||
|
||||
# check mean and std of residuals
|
||||
with check:
|
||||
@@ -140,6 +145,7 @@ def test_eta2_interpolation_rosenblatt(load_data, check):
|
||||
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.skip(reason="Simple sanity test skips ground truth does not coincide with center pixel")
|
||||
def test_cross_eta_interpolation(load_data, check):
|
||||
"""Test cross eta interpolation on simulated data"""
|
||||
|
||||
@@ -173,11 +179,11 @@ def test_cross_eta_interpolation(load_data, check):
|
||||
"""
|
||||
|
||||
# check within photon hit pixel for all
|
||||
# TODO: fails as eta_x = 0, eta_y = 0 is not leading to offset (0.5,0.5)
|
||||
# TODO: fails as ground truth not in center pixel!!
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["x"], ground_truths[:, 0], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["x"]), np.floor(ground_truths[:, 0]), atol=0.0)
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["y"], ground_truths[:, 1], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["y"]), np.floor(ground_truths[:, 1]), atol=0.0)
|
||||
|
||||
# check mean and std of residuals
|
||||
with check:
|
||||
@@ -190,13 +196,14 @@ def test_cross_eta_interpolation(load_data, check):
|
||||
assert residuals_interpolated_y.std() <= 0.05
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.skip(reason="Simple sanity test skips ground truth does not coincide with center pixel")
|
||||
def test_eta3_interpolation(load_data, check):
|
||||
"""Test eta3 interpolation on simulated data"""
|
||||
|
||||
cv, ground_truths = load_data
|
||||
|
||||
num_bins = 201
|
||||
eta_distribution = calculate_eta_distribution(cv, calculate_eta3, edges_x=[-0.5,0.5], edges_y=[-0.5,0.5], nbins=num_bins)
|
||||
eta_distribution = calculate_eta_distribution(cv, calculate_eta3, edges_x=[-0.6,0.6], edges_y=[-0.6,0.6], nbins=num_bins)
|
||||
|
||||
interpolator = Interpolator(eta_distribution, eta_distribution.axes[0].edges, eta_distribution.axes[1].edges, eta_distribution.axes[2].edges)
|
||||
|
||||
@@ -223,11 +230,11 @@ def test_eta3_interpolation(load_data, check):
|
||||
"""
|
||||
|
||||
# check within photon hit pixel for all
|
||||
# TODO: fails as eta_x = 0, eta_y = 0 is not leading to offset (0.5,0.5)
|
||||
# TODO: fails as ground truth not in center pixel!!
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["x"], ground_truths[:, 0], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["x"]), np.floor(ground_truths[:, 0]), atol=0.0)
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["y"], ground_truths[:, 1], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["y"]), np.floor(ground_truths[:, 1]), atol=0.0)
|
||||
|
||||
# check mean and std of residuals
|
||||
with check:
|
||||
@@ -240,6 +247,7 @@ def test_eta3_interpolation(load_data, check):
|
||||
assert residuals_interpolated_y.std() <= 0.05
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.skip(reason="Simple sanity test skips ground truth does not coincide with center pixel")
|
||||
def test_full_eta2_interpolation(load_data, check):
|
||||
"""Test full eta2 interpolation on simulated data"""
|
||||
|
||||
@@ -273,10 +281,11 @@ def test_full_eta2_interpolation(load_data, check):
|
||||
"""
|
||||
|
||||
# check within photon hit pixel for all
|
||||
# TODO: fails as ground truth not in center pixel!!
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["x"], ground_truths[:, 0], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["x"]), np.floor(ground_truths[:, 0]), atol=0.0)
|
||||
with check:
|
||||
assert np.allclose(interpolated_photons["y"], ground_truths[:, 1], atol=5e-1)
|
||||
assert np.allclose(np.floor(interpolated_photons["y"]), np.floor(ground_truths[:, 1]), atol=0.0)
|
||||
|
||||
# check mean and std of residuals
|
||||
with check:
|
||||
|
||||
@@ -292,7 +292,8 @@ TEST_CASE("check find_geometry", "[.with-data]") {
|
||||
|
||||
TEST_CASE("Open multi module file with ROI", "[.with-data]") {
|
||||
|
||||
auto fpath = test_data_path() / "raw/ROITestData/SingleChipROI/Data_master_0.json";
|
||||
auto fpath =
|
||||
test_data_path() / "raw/ROITestData/SingleChipROI/Data_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
RawFile f(fpath, "r");
|
||||
|
||||
@@ -229,7 +229,7 @@ TEST_CASE("Parse a master file in .raw format", "[.integration]") {
|
||||
}
|
||||
|
||||
TEST_CASE("Parse a master file in new .json format",
|
||||
"[.integration][.width-data]") {
|
||||
"[.integration][.with-data]") {
|
||||
|
||||
auto file_path =
|
||||
test_data_path() / "raw" / "newmythen03" / "run_87_master_0.json";
|
||||
@@ -488,7 +488,7 @@ TEST_CASE("Parse JUNGFRAU 7.2 master from string stream") {
|
||||
REQUIRE(f.udp_interfaces_per_module() == xy{2, 1});
|
||||
}
|
||||
|
||||
TEST_CASE("Parse a CTB file from stream"){
|
||||
TEST_CASE("Parse a CTB file from stream") {
|
||||
std::string master_content = R"({
|
||||
"Version": 8.0,
|
||||
"Timestamp": "Mon Dec 15 10:57:27 2025",
|
||||
@@ -552,16 +552,17 @@ TEST_CASE("Parse a CTB file from stream"){
|
||||
REQUIRE(f.quad() == 0);
|
||||
REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard);
|
||||
REQUIRE(f.frame_padding() == 1);
|
||||
REQUIRE(f.total_frames_expected() == 1); //This is Total Frames in the master file
|
||||
REQUIRE(f.total_frames_expected() ==
|
||||
1); // This is Total Frames in the master file
|
||||
REQUIRE(f.exptime() == std::chrono::milliseconds(250));
|
||||
REQUIRE(f.period() == std::chrono::milliseconds(10));
|
||||
REQUIRE(f.analog_samples() == std::nullopt); //Analog Flag is 0
|
||||
REQUIRE(f.digital_samples() == std::nullopt); //Digital Flag is 0
|
||||
REQUIRE(f.analog_samples() == std::nullopt); // Analog Flag is 0
|
||||
REQUIRE(f.digital_samples() == std::nullopt); // Digital Flag is 0
|
||||
REQUIRE(f.transceiver_samples() == 1152);
|
||||
REQUIRE(f.frames_in_file() == 40);
|
||||
}
|
||||
|
||||
TEST_CASE("Parse v8.0 MYTHEN3 from stream"){
|
||||
TEST_CASE("Parse v8.0 MYTHEN3 from stream") {
|
||||
std::string master_content = R"({
|
||||
"Version": 8.0,
|
||||
"Timestamp": "Wed Oct 1 14:37:26 2025",
|
||||
@@ -637,7 +638,8 @@ TEST_CASE("Parse v8.0 MYTHEN3 from stream"){
|
||||
REQUIRE(f.quad() == 0);
|
||||
REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard);
|
||||
REQUIRE(f.frame_padding() == 1);
|
||||
REQUIRE(f.total_frames_expected() == 1); //This is Total Frames in the master file
|
||||
REQUIRE(f.total_frames_expected() ==
|
||||
1); // This is Total Frames in the master file
|
||||
REQUIRE(f.counter_mask() == 4);
|
||||
REQUIRE(f.bitdepth() == 32);
|
||||
|
||||
@@ -648,7 +650,7 @@ TEST_CASE("Parse v8.0 MYTHEN3 from stream"){
|
||||
REQUIRE(f.period() == std::chrono::nanoseconds(0));
|
||||
}
|
||||
|
||||
TEST_CASE("Parse a v7.1 Mythen3 from stream"){
|
||||
TEST_CASE("Parse a v7.1 Mythen3 from stream") {
|
||||
std::string master_content = R"({
|
||||
"Version": 7.1,
|
||||
"Timestamp": "Wed Sep 21 13:48:10 2022",
|
||||
@@ -721,7 +723,8 @@ TEST_CASE("Parse a v7.1 Mythen3 from stream"){
|
||||
REQUIRE(f.quad() == 0);
|
||||
REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard);
|
||||
REQUIRE(f.frame_padding() == 1);
|
||||
REQUIRE(f.total_frames_expected() == 1); //This is Total Frames in the master file
|
||||
REQUIRE(f.total_frames_expected() ==
|
||||
1); // This is Total Frames in the master file
|
||||
REQUIRE(f.counter_mask() == 0x7);
|
||||
REQUIRE(f.bitdepth() == 32);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user