From d1ebf24c719298961339c434358fbd987b6ccfa7 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Wed, 27 Nov 2024 13:32:12 +0100 Subject: [PATCH] Use fixtures to control testing output paths and cleanup --- .github/workflows/ci.yml | 14 +- pytests/conftest.py | 26 ++- pytests/test_demo_scenes.py | 314 +++++++++++++++++-------------- pytests/test_gpsStartTimeFlag.py | 88 +++------ pytests/test_pyhelios.py | 57 +++--- 5 files changed, 254 insertions(+), 245 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb4cbb627..6937aa6b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,10 +63,16 @@ jobs: env: SETUPTOOLS_SCM_SUBPROCESS_TIMEOUT: "120" - - name: Run tests - # Disable MacOS for now - we do not yet officially support it and we need to invest a bit - # more efforts into investigating broken LAZ files written by Helios on MacOS. - if: runner.os != 'macOS' + # Do not run on MacOS for now - we do not yet officially support it and we need to invest a bit + # more efforts into investigating broken LAZ files written by Helios on MacOS. + + - name: Run tests (incl. regression tests) + if: runner.os == 'Windows' + run: | + python -m pytest --regression-tests + + - name: Run tests (excl. regression tests) + if: runner.os == 'Linux' run: | python -m pytest diff --git a/pytests/conftest.py b/pytests/conftest.py index 5ac0bc52c..2ca0ac1aa 100644 --- a/pytests/conftest.py +++ b/pytests/conftest.py @@ -1,5 +1,8 @@ +import os +import pathlib import pooch import pytest +import shutil TEST_DATA_ARCHIVE = "https://github.com/dokempf/helios-test-data/releases/download/2024-08-29/data.tar.gz" @@ -7,12 +10,16 @@ @pytest.fixture -def regression_data(): +def regression_data(request): """ A fixture that ensures the existence of regression data Returns the Path to where that data is located after (cached) download from GitHub. """ + + if not request.config.getoption("--regression-tests"): + return None + # Define the cache location cache = pooch.os_cache("helios") @@ -28,6 +35,16 @@ def regression_data(): return cache +@pytest.fixture(scope="session") +def output_dir(request): + dir = pathlib.Path(os.getcwd()) / "pytest-output" + + yield dir + + if request.config.getoption("--delete-output"): + shutil.rmtree(dir) + + def pytest_addoption(parser): parser.addoption( "--regression-tests", @@ -35,3 +52,10 @@ def pytest_addoption(parser): default=False, help="run regression tests", ) + + parser.addoption( + "--delete-output", + action="store_true", + default=False, + help="run regression tests", + ) diff --git a/pytests/test_demo_scenes.py b/pytests/test_demo_scenes.py index 86c8dd0c2..552908786 100644 --- a/pytests/test_demo_scenes.py +++ b/pytests/test_demo_scenes.py @@ -1,7 +1,6 @@ from pyhelios.__main__ import helios_exec import os -import shutil import sys from pathlib import Path import numpy as np @@ -16,13 +15,9 @@ except ImportError: pass -DELETE_FILES_AFTER = False -WORKING_DIR = os.getcwd() - -def find_playback_dir(survey_path): - playback = Path(WORKING_DIR) / 'output' - with open(Path(WORKING_DIR) / survey_path, 'r') as sf: +def find_playback_dir(survey_path, playback): + with open(survey_path, 'r') as sf: for line in sf: if ' Path: +def run_helios_executable(survey_path: Path, output_dir: Path, options=None) -> Path: if options is None: options = list() - helios_exec([str(survey_path)] + options + ['--rebuildScene', - '--seed', '43', - '-vt', - '-j', '1']) + helios_exec([str(survey_path)] + options + ['--output', str(output_dir), + '--rebuildScene', + '--seed', '43', + '-vt', + '-j', '1']) - return find_playback_dir(survey_path) + return find_playback_dir(survey_path, output_dir) -def run_helios_pyhelios(survey_path: Path, las_output: bool = True, zip_output: bool = False, +def run_helios_pyhelios(survey_path: Path, output: Path, las_output: bool = True, zip_output: bool = False, start_time: str = None, split_by_channel: bool = False, las10: bool = False) -> Path: pyhelios.setDefaultRandomnessGeneratorSeed("43") simB = pyhelios.SimulationBuilder( surveyPath=str(survey_path.absolute()), assetsDir=[str(Path("assets"))], - outputDir=str(Path("output")), + outputDir=str(output), ) simB.setLasOutput(las_output) simB.setLas10(las10) @@ -65,7 +61,7 @@ def run_helios_pyhelios(survey_path: Path, las_output: bool = True, zip_output: sim.start() sim.join() - return find_playback_dir(survey_path) + return find_playback_dir(survey_path, output) def speed_from_traj(trajectory_file): @@ -98,142 +94,156 @@ def mode(arr): @pytest.mark.exe -def test_arbaro_tls_exe(regression_data): +def test_arbaro_tls_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output_dir, options=['--lasOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00', + ]) eval_arbaro_tls(regression_data, dirname_exe) @pytest.mark.pyh -def test_arbaro_tls_pyh(regression_data): +def test_arbaro_tls_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output=output_dir, start_time='2022-01-01 00:00:00') eval_arbaro_tls(regression_data, dirname_pyh) def eval_arbaro_tls(regression_data, dirname): assert (dirname / 'leg000_points.las').exists() - pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'arbaro_tls_leg000_points.las') - pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') - pcloud0.assert_equals(pcloud0_ref) assert (dirname / 'leg001_points.las').exists() - pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'arbaro_tls_leg001_points.las') - pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.las') - pcloud1.assert_equals(pcloud1_ref) + # ToDo: same approach for trajectory? with open(dirname / 'leg000_trajectory.txt', 'r') as f: line = f.readline() assert line.startswith('1.0000 25.5000 0.0000') - # clean up - if DELETE_FILES_AFTER: - shutil.rmtree(dirname) + + if regression_data: + pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'arbaro_tls_leg000_points.las') + pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') + pcloud0.assert_equals(pcloud0_ref) + + pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'arbaro_tls_leg001_points.las') + pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.las') + pcloud1.assert_equals(pcloud1_ref) @pytest.mark.exe -def test_tiffloader_als_exe(regression_data): +def test_tiffloader_als_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'test' / 'als_hd_demo_tiff_min.xml', + output_dir, options=['--lasOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00', + ]) eval_tiffloader_als(regression_data, dirname_exe) @pytest.mark.pyh -def test_tiffloader_als_pyh(regression_data): +def test_tiffloader_als_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'test' / 'als_hd_demo_tiff_min.xml', + output=output_dir, start_time='2022-01-01 00:00:00') eval_tiffloader_als(regression_data, dirname_pyh) def eval_tiffloader_als(regression_data, dirname): assert (dirname / 'leg000_points.las').exists() - pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg000_points.las') - pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') - pcloud0.assert_equals(pcloud0_ref) assert (dirname / 'leg001_points.las').exists() - pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg001_points.las') - pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.las') - pcloud1.assert_equals(pcloud1_ref) assert (dirname / 'leg002_points.las').exists() - pcloud2_ref = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg002_points.las') - pcloud2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.las') - pcloud2.assert_equals(pcloud2_ref) + + if regression_data: + pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg000_points.las') + pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') + pcloud0.assert_equals(pcloud0_ref) + pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg001_points.las') + pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.las') + pcloud1.assert_equals(pcloud1_ref) + pcloud2_ref = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg002_points.las') + pcloud2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.las') + pcloud2.assert_equals(pcloud2_ref) + with open(dirname / 'leg000_trajectory.txt', 'r') as f: next(f) line = f.readline() assert line.startswith('474500.7500 5474500.0000 1500.0000') - # clean up - if DELETE_FILES_AFTER: - shutil.rmtree(dirname) @pytest.mark.exe -def test_detailedVoxels_uls_exe(regression_data): +def test_detailedVoxels_uls_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'test' / 'uls_detailedVoxels_mode_comparison_min.xml', + output_dir, options=['--lasOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00' + ]) eval_detailedVoxels_uls(regression_data, dirname_exe) @pytest.mark.pyh -def test_detailedVoxels_uls_pyh(regression_data): +def test_detailedVoxels_uls_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'test' / 'uls_detailedVoxels_mode_comparison_min.xml', + output=output_dir, start_time='2022-01-01 00:00:00') eval_detailedVoxels_uls(regression_data, dirname_pyh) def eval_detailedVoxels_uls(regression_data, dirname): assert (dirname / 'leg000_points.las').exists() - pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'detailedVoxels_uls_leg000_points.las') - pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') - pcloud0.assert_equals(pcloud0_ref) with open(dirname / 'leg000_trajectory.txt', 'r') as f: for _ in range(6): next(f) line = f.readline() assert line.startswith('-3.0000 -2.1000 50.0000') - # clean up - if DELETE_FILES_AFTER: - shutil.rmtree(dirname) + + if regression_data: + pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'detailedVoxels_uls_leg000_points.las') + pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') + pcloud0.assert_equals(pcloud0_ref) @pytest.mark.exe -def test_xyzVoxels_tls_exe(regression_data): +def test_xyzVoxels_tls_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'voxels' / 'tls_sphere_xyzloader_normals.xml', + output_dir, options=['--lasOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00' + ]) eval_xyzVoxels_tls(regression_data, dirname_exe) @pytest.mark.pyh -def test_xyzVoxels_tls_pyh(regression_data): +def test_xyzVoxels_tls_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'voxels' / 'tls_sphere_xyzloader_normals.xml', + output=output_dir, start_time='2022-01-01 00:00:00') eval_xyzVoxels_tls(regression_data, dirname_pyh) def eval_xyzVoxels_tls(regression_data, dirname): assert (dirname / 'leg000_points.las').exists() - pcloud_ref = pcu.PointCloud.from_las_file(regression_data / 'xyzVoxels_tls_leg000_points.las') - pcloud = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') - pcloud.assert_equals(pcloud_ref) - # clean up - if DELETE_FILES_AFTER: - shutil.rmtree(dirname) + + if regression_data: + pcloud_ref = pcu.PointCloud.from_las_file(regression_data / 'xyzVoxels_tls_leg000_points.las') + pcloud = pcu.PointCloud.from_las_file(dirname / 'leg000_points.las') + pcloud.assert_equals(pcloud_ref) @pytest.mark.exe -def test_interpolated_traj_exe(regression_data): +def test_interpolated_traj_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'als_interpolated_trajectory.xml', + output_dir, options=['--lasOutput', '--zipOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00' + ]) eval_interpolated_traj(regression_data, dirname_exe) @pytest.mark.pyh -def test_interpolated_traj_pyh(regression_data): +def test_interpolated_traj_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'als_interpolated_trajectory.xml', + output=output_dir, zip_output=True, start_time='2022-01-01 00:00:00') eval_interpolated_traj(regression_data, dirname_pyh) @@ -241,47 +251,50 @@ def test_interpolated_traj_pyh(regression_data): def eval_interpolated_traj(regression_data, dirname): assert (dirname / 'leg000_points.laz').exists() - pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg000_points.laz') - pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') - pcloud0.assert_equals(pcloud0_ref) assert (dirname / 'leg001_points.laz').exists() - pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg001_points.laz') - pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.laz') - pcloud1.assert_equals(pcloud1_ref) assert (dirname / 'leg002_points.laz').exists() - pcloud2_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg002_points.laz') - pcloud2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.laz') - pcloud2.assert_equals(pcloud2_ref) assert (dirname / 'leg003_points.laz').exists() - pcloud3_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg003_points.laz') - pcloud3 = pcu.PointCloud.from_las_file(dirname / 'leg003_points.laz') - pcloud3.assert_equals(pcloud3_ref) + with open(dirname / 'leg000_trajectory.txt', 'r') as f: for _ in range(3): next(f) line = f.readline() assert line.startswith('13.4766 1.7424 400.0000') - # clean up - if DELETE_FILES_AFTER: - shutil.rmtree(dirname) + + if regression_data: + pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg000_points.laz') + pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') + pcloud0.assert_equals(pcloud0_ref) + pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg001_points.laz') + pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.laz') + pcloud2_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg002_points.laz') + pcloud2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.laz') + pcloud2.assert_equals(pcloud2_ref) + pcloud1.assert_equals(pcloud1_ref) + pcloud3_ref = pcu.PointCloud.from_las_file(regression_data / 'interpolated_traj_leg003_points.laz') + pcloud3 = pcu.PointCloud.from_las_file(dirname / 'leg003_points.laz') + pcloud3.assert_equals(pcloud3_ref) @pytest.mark.skipif("laspy" not in sys.modules, reason="requires the laspy library") @pytest.mark.exe -def test_quadcopter_exe(regression_data): +def test_quadcopter_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'toyblocks' / 'uls_toyblocks_survey_scene_combo.xml', + output_dir, options=['--lasOutput', '--zipOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00', + ]) eval_quadcopter(regression_data, dirname_exe) @pytest.mark.skipif("laspy" not in sys.modules, reason="requires the laspy library") @pytest.mark.pyh -def test_quadcopter_pyh(regression_data): +def test_quadcopter_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'toyblocks' / 'uls_toyblocks_survey_scene_combo.xml', + output=output_dir, zip_output=True, start_time='2022-01-01 00:00:00') eval_quadcopter(regression_data, dirname_pyh) @@ -289,21 +302,9 @@ def test_quadcopter_pyh(regression_data): def eval_quadcopter(regression_data, dirname): assert (dirname / 'leg000_points.laz').exists() - pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg000_points.laz') - pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') - pcloud0.assert_equals(pcloud0_ref) assert (dirname / 'leg001_points.laz').exists() - pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg001_points.laz') - pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.laz') - pcloud1.assert_equals(pcloud1_ref) assert (dirname / 'leg002_points.laz').exists() - pcloud2_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg002_points.laz') - pcloud2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.laz') - pcloud2.assert_equals(pcloud2_ref) assert (dirname / 'leg004_points.laz').exists() - pcloud3_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg004_points.laz') - pcloud3 = pcu.PointCloud.from_las_file(dirname / 'leg004_points.laz') - pcloud3.assert_equals(pcloud3_ref) assert speed_from_traj(dirname / 'leg000_trajectory.txt') == pytest.approx(10.0, 0.001) assert speed_from_traj(dirname / 'leg002_trajectory.txt') == pytest.approx(7.0, 0.001) assert speed_from_traj(dirname / 'leg004_trajectory.txt') == pytest.approx(4.0, 0.001) @@ -312,85 +313,106 @@ def eval_quadcopter(regression_data, dirname): next(f) line = f.readline() assert line.startswith('-69.9983 -60.0000 80.0002') - # clean up - if DELETE_FILES_AFTER: - shutil.rmtree(dirname) + + if regression_data: + pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg000_points.laz') + pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') + pcloud0.assert_equals(pcloud0_ref) + pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg001_points.laz') + pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg001_points.laz') + pcloud1.assert_equals(pcloud1_ref) + pcloud2_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg002_points.laz') + pcloud2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.laz') + pcloud2.assert_equals(pcloud2_ref) + pcloud3_ref = pcu.PointCloud.from_las_file(regression_data / 'quadcopter_leg004_points.laz') + pcloud3 = pcu.PointCloud.from_las_file(dirname / 'leg004_points.laz') + pcloud3.assert_equals(pcloud3_ref) @pytest.mark.pyh -def test_als_multichannel_pyh(regression_data): +def test_als_multichannel_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'light_als_toyblocks_multiscanner.xml', + output=output_dir, zip_output=True, start_time='2022-01-01 00:00:00') eval_als_multichannel(regression_data, dirname_pyh) @pytest.mark.pyh -def test_als_multichannel_split_pyh(regression_data): +def test_als_multichannel_split_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'light_als_toyblocks_multiscanner.xml', + output=output_dir, zip_output=True, split_by_channel=True, start_time='2022-01-01 00:00:00') eval_als_multichannel_split(regression_data, dirname_pyh) @pytest.mark.exe -def test_als_multichannel_exe(regression_data): +def test_als_multichannel_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'light_als_toyblocks_multiscanner.xml', + output_dir, options=['--lasOutput', '--zipOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00', + ]) eval_als_multichannel(regression_data, dirname_exe) @pytest.mark.exe -def test_als_multichannel_split_exe(regression_data): +def test_als_multichannel_split_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'light_als_toyblocks_multiscanner.xml', + output_dir, options=['--lasOutput', '--zipOutput', '--gpsStartTime', '2022-01-01 00:00:00', - '--splitByChannel']) + '--splitByChannel', + ]) eval_als_multichannel_split(regression_data, dirname_exe) def eval_als_multichannel(regression_data, dirname): assert len(fnmatch.filter(os.listdir(dirname), '*.laz')) == 2 assert (dirname / 'leg000_points.laz').exists() - pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_leg000_points.laz') - pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') - pcloud0.assert_equals(pcloud0_ref) assert (dirname / 'leg002_points.laz').exists() - pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_leg002_points.laz') - pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.laz') - pcloud1.assert_equals(pcloud1_ref) + + if regression_data: + pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_leg000_points.laz') + pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') + pcloud0.assert_equals(pcloud0_ref) + pcloud1_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_leg002_points.laz') + pcloud1 = pcu.PointCloud.from_las_file(dirname / 'leg002_points.laz') + pcloud1.assert_equals(pcloud1_ref) def eval_als_multichannel_split(regression_data, dirname): # 2 legs, Livox Mid-100 has 3 channels, so we expect 6 point clouds assert len(fnmatch.filter(os.listdir(dirname), '*.laz')) == 6 assert (dirname / 'leg000_points_dev0.laz').exists() - pcloud0_0_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg000_points_dev0.laz') - pcloud0_0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points_dev0.laz') - pcloud0_0.assert_equals(pcloud0_0_ref) assert (dirname / 'leg000_points_dev1.laz').exists() - pcloud0_1_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg000_points_dev1.laz') - pcloud0_1 = pcu.PointCloud.from_las_file(dirname / 'leg000_points_dev1.laz') - pcloud0_1.assert_equals(pcloud0_1_ref) assert (dirname / 'leg000_points_dev2.laz').exists() - pcloud0_2_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg000_points_dev2.laz') - pcloud0_2 = pcu.PointCloud.from_las_file(dirname / 'leg000_points_dev2.laz') - pcloud0_2.assert_equals(pcloud0_2_ref) assert (dirname / 'leg002_points_dev0.laz').exists() - pcloud1_0_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg002_points_dev0.laz') - pcloud1_0 = pcu.PointCloud.from_las_file(dirname / 'leg002_points_dev0.laz') - pcloud1_0.assert_equals(pcloud1_0_ref) assert (dirname / 'leg002_points_dev1.laz').exists() - pcloud1_1_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg002_points_dev1.laz') - pcloud1_1 = pcu.PointCloud.from_las_file(dirname / 'leg002_points_dev1.laz') - pcloud1_1.assert_equals(pcloud1_1_ref) assert (dirname / 'leg002_points_dev2.laz').exists() - pcloud1_2_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg002_points_dev2.laz') - pcloud1_2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points_dev2.laz') - pcloud1_2.assert_equals(pcloud1_2_ref) + + if regression_data: + pcloud0_0_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg000_points_dev0.laz') + pcloud0_0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points_dev0.laz') + pcloud0_0.assert_equals(pcloud0_0_ref) + pcloud0_1_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg000_points_dev1.laz') + pcloud0_1 = pcu.PointCloud.from_las_file(dirname / 'leg000_points_dev1.laz') + pcloud0_1.assert_equals(pcloud0_1_ref) + pcloud0_2_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg000_points_dev2.laz') + pcloud0_2 = pcu.PointCloud.from_las_file(dirname / 'leg000_points_dev2.laz') + pcloud0_2.assert_equals(pcloud0_2_ref) + pcloud1_0_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg002_points_dev0.laz') + pcloud1_0 = pcu.PointCloud.from_las_file(dirname / 'leg002_points_dev0.laz') + pcloud1_0.assert_equals(pcloud1_0_ref) + pcloud1_1_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg002_points_dev1.laz') + pcloud1_1 = pcu.PointCloud.from_las_file(dirname / 'leg002_points_dev1.laz') + pcloud1_1.assert_equals(pcloud1_1_ref) + pcloud1_2_ref = pcu.PointCloud.from_las_file(regression_data / 'als_multichannel_split_leg002_points_dev2.laz') + pcloud1_2 = pcu.PointCloud.from_las_file(dirname / 'leg002_points_dev2.laz') + pcloud1_2.assert_equals(pcloud1_2_ref) @pytest.mark.skipif("laspy" not in sys.modules, @@ -405,10 +427,10 @@ def eval_als_multichannel_split(regression_data, dirname): pytest.param(True, True, id="LAZ v1.0"), ] ) -def test_las_pyh(zip_flag: bool, las10_flag: bool): +def test_las_pyh(zip_flag: bool, las10_flag: bool, output_dir): """""" dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'light_als_toyblocks_multiscanner.xml', - las_output=True, zip_output=zip_flag, las10=las10_flag) + output=output_dir, las_output=True, zip_output=zip_flag, las10=las10_flag) las_version = "1.0" if las10_flag else "1.4" eval_las(dirname_pyh, las_version) @@ -425,7 +447,7 @@ def test_las_pyh(zip_flag: bool, las10_flag: bool): pytest.param(True, True, id="LAZ v1.0"), ] ) -def test_las_exe(zip_flag: bool, las10_flag: bool): +def test_las_exe(zip_flag: bool, las10_flag: bool, output_dir): options = ["--lasOutput"] if zip_flag: options.append("--zipOutput") @@ -433,7 +455,9 @@ def test_las_exe(zip_flag: bool, las10_flag: bool): options.append("--las10") dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'light_als_toyblocks_multiscanner.xml', - options=options) + output_dir, + options=options, + ) las_version = "1.0" if las10_flag else "1.4" eval_las(dirname_exe, las_version) @@ -462,10 +486,10 @@ def eval_las(dirname, las_version, check_empty=False): @pytest.mark.skipif("laspy" not in sys.modules, reason="requires the laspy library") @pytest.mark.pyh -def test_strip_id_pyh(): +def test_strip_id_pyh(output_dir): """""" dirname_pyh = run_helios_pyhelios(Path('data') / 'test' / 'als_hd_height_above_ground_stripid_light.xml', - las_output=True, zip_output=True) + output=output_dir, las_output=True, zip_output=True) las_version = "1.4" eval_las(dirname_pyh, las_version, check_empty=False) @@ -473,33 +497,35 @@ def test_strip_id_pyh(): @pytest.mark.skipif("laspy" not in sys.modules, reason="requires the laspy library") @pytest.mark.exe -def test_strip_id_exe(): +def test_strip_id_exe(output_dir): dirname_exe = run_helios_executable(Path('data') / 'test' / 'als_hd_height_above_ground_stripid_light.xml', + output_dir, options=['--lasOutput', '--zipOutput']) las_version = "1.4" eval_las(dirname_exe, las_version, check_empty=True) @pytest.mark.pyh -def test_dyn_pyh(regression_data): +def test_dyn_pyh(regression_data, output_dir): dirname_pyh = run_helios_pyhelios(Path('data') / 'surveys' / 'dyn' / 'tls_dyn_cube.xml', - las_output=True, zip_output=True, + output=output_dir, las_output=True, zip_output=True, start_time='2022-01-01 00:00:00') eval_dyn(regression_data, dirname_pyh) @pytest.mark.exe -def test_dyn_exe(regression_data): +def test_dyn_exe(regression_data, output_dir): dirname_exe = run_helios_executable(Path('data') / 'surveys' / 'dyn' / 'tls_dyn_cube.xml', + output_dir, options=['--lasOutput', '--zipOutput', - '--gpsStartTime', '2022-01-01 00:00:00']) + '--gpsStartTime', '2022-01-01 00:00:00' + ]) eval_dyn(regression_data, dirname_exe) def eval_dyn(regression_data, dirname): assert (dirname / 'leg000_points.laz').exists() - pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'dyn_leg000_points.laz') - pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') - pcloud0.assert_equals(pcloud0_ref) - # clean up - if DELETE_FILES_AFTER: - shutil.rmtree(dirname) + + if regression_data: + pcloud0_ref = pcu.PointCloud.from_las_file(regression_data / 'dyn_leg000_points.laz') + pcloud0 = pcu.PointCloud.from_las_file(dirname / 'leg000_points.laz') + pcloud0.assert_equals(pcloud0_ref) diff --git a/pytests/test_gpsStartTimeFlag.py b/pytests/test_gpsStartTimeFlag.py index 1a4ff3ca0..5b7cd2be1 100644 --- a/pytests/test_gpsStartTimeFlag.py +++ b/pytests/test_gpsStartTimeFlag.py @@ -1,35 +1,17 @@ -import os -import shutil from .test_demo_scenes import run_helios_executable, find_playback_dir from pathlib import Path -import sys import datetime -import hashlib -MAX_DIFFERENCE_BYTES = 1024 -DELETE_FILES_AFTER = True -WORKING_DIR = os.getcwd() -def sha256sum(filename): - h = hashlib.sha256() - b = bytearray(128*1024) - mv = memoryview(b) - with open(filename, 'rb', buffering=0) as f: - for n in iter(lambda : f.readinto(mv), 0): - h.update(mv[:n]) - return h.hexdigest() - - -def run_helios_pyhelios(survey_path: Path, options=None) -> Path: - sys.path.append(WORKING_DIR) +def run_helios_pyhelios(survey_path: Path, output_dir: Path, options=None) -> Path: import pyhelios pyhelios.setDefaultRandomnessGeneratorSeed("43") from pyhelios import SimulationBuilder simB = SimulationBuilder( surveyPath=str(survey_path.absolute()), - assetsDir=WORKING_DIR + os.sep + 'assets' + os.sep, - outputDir=WORKING_DIR + os.sep + 'output' + os.sep, + assetsDir=[str(Path("assets"))], + outputDir=str(output_dir), ) simB.setLasOutput(False) simB.setRebuildScene(True) @@ -41,64 +23,44 @@ def run_helios_pyhelios(survey_path: Path, options=None) -> Path: sim.start() output = sim.join() sim = None - return find_playback_dir(survey_path) + return find_playback_dir(survey_path, output_dir) + -def test_gpsStartTimeFlag_exe(): +def test_gpsStartTimeFlag_exe(output_dir): now = datetime.datetime(year=1994, month=1, day=18, hour=1, minute=45, second=22) unixtime = datetime.datetime.timestamp(now) stringtime = now.strftime('%Y-%m-%d %H:%M:%S') # first, run 'as is': - r1 = run_helios_executable(Path(WORKING_DIR) / 'data' / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', - options=['--gpsStartTime', '']) + r1 = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output_dir, + options=['--gpsStartTime', '']) # run with posix ts: - r2 = run_helios_executable(Path(WORKING_DIR) / 'data' / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', - options=['--gpsStartTime', f'{unixtime:.3f}']) + r2 = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output_dir, + options=['--gpsStartTime', f'{unixtime:.3f}']) # run with string ts: - r3 = run_helios_executable(Path(WORKING_DIR) / 'data' / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', - options=['--gpsStartTime', stringtime]) + r3 = run_helios_executable(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output_dir, + options=['--gpsStartTime', stringtime]) assert (r1 / 'leg000_points.xyz').exists() - r1_sum = sha256sum(r1 / 'leg000_points.xyz') - r2_sum = sha256sum(r2 / 'leg000_points.xyz') - r3_sum = sha256sum(r3 / 'leg000_points.xyz') - # assert r2_sum == r3_sum - # assert r2_sum == '41313dfe46ed34fcb9733af03a4d5e52487fd4579014f13dc00c609b53813229' or \ - # r2_sum == '984cfbbc5a54ab10a566ea901363218f35da569dbab5cd102424ab27794074ae' # linux checksum - # assert r1_sum != r2_sum - if DELETE_FILES_AFTER: - shutil.rmtree(r1) - shutil.rmtree(r2) - shutil.rmtree(r3) - -def test_gpsStartTimeFlag_pyh(): +def test_gpsStartTimeFlag_pyh(output_dir): now = datetime.datetime(year=1994, month=1, day=18, hour=1, minute=45, second=22) unixtime = datetime.datetime.timestamp(now) stringtime = now.strftime('%Y-%m-%d %H:%M:%S') # first, run 'as is': - r1 = run_helios_pyhelios(Path(WORKING_DIR) / 'data' / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', - options={'gpsStartTime': ''}) + r1 = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output_dir, + options={'gpsStartTime': ''}) # run with posix ts: - r2 = run_helios_pyhelios(Path(WORKING_DIR) / 'data' / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', - options={'gpsStartTime': f'{unixtime:.0f}'}) + r2 = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output_dir, + options={'gpsStartTime': f'{unixtime:.0f}'}) # run with string ts: - r3 = run_helios_pyhelios(Path(WORKING_DIR) / 'data' / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', - options={'gpsStartTime': stringtime}) + r3 = run_helios_pyhelios(Path('data') / 'surveys' / 'demo' / 'tls_arbaro_demo.xml', + output_dir, + options={'gpsStartTime': stringtime}) assert (r1 / 'leg000_points.xyz').exists() - r1_sum = sha256sum(r1 / 'leg000_points.xyz') - r2_sum = sha256sum(r2 / 'leg000_points.xyz') - r3_sum = sha256sum(r3 / 'leg000_points.xyz') - # assert r2_sum == r3_sum - # assert r2_sum == '41313dfe46ed34fcb9733af03a4d5e52487fd4579014f13dc00c609b53813229' or \ - # r2_sum == '984cfbbc5a54ab10a566ea901363218f35da569dbab5cd102424ab27794074ae' # linux checksum - # assert r1_sum != r2_sum - - if DELETE_FILES_AFTER: - try: - shutil.rmtree(r1) - shutil.rmtree(r2) - shutil.rmtree(r3) - except Exception as e: - print(f"Error cleaning up: {e}") diff --git a/pytests/test_pyhelios.py b/pytests/test_pyhelios.py index 732accd2b..7f5f54f20 100644 --- a/pytests/test_pyhelios.py +++ b/pytests/test_pyhelios.py @@ -12,15 +12,11 @@ import time import struct import xml.etree.ElementTree as ET -import shutil import laspy - -DELETE_FILES_AFTER = False -WORKING_DIR = os.getcwd() import pyhelios +from .test_demo_scenes import find_playback_dir from . import pcloud_utils as pcu -from pyhelios.util import xmldisplayer def find_scene(survey_file): @@ -49,7 +45,7 @@ def get_las_version(las_filename): @pytest.fixture(scope="session") -def test_sim(): +def test_sim(output_dir): """ Fixture which returns a simulation object for a given survey path """ @@ -59,8 +55,8 @@ def create_test_sim(survey_path, zip_output=True, las_output=True, las10=False): from pyhelios import SimulationBuilder simB = SimulationBuilder( surveyPath=str(survey_path.absolute()), - assetsDir=WORKING_DIR + os.sep + 'assets' + os.sep, - outputDir=WORKING_DIR + os.sep + 'output' + os.sep, + assetsDir=[str(Path('assets'))], + outputDir=str(output_dir), ) simB.setLasOutput(las_output) simB.setLas10(las10) @@ -187,7 +183,6 @@ def test_survey_characteristics(test_sim): """Test accessing survey characteristics (name, length)""" path_to_survey = Path('data') / 'surveys' / 'toyblocks' / 'als_toyblocks.xml' sim = test_sim(path_to_survey) - assert Path(sim.sim.getSurveyPath()) == Path(WORKING_DIR) / path_to_survey survey = sim.sim.getSurvey() assert survey.name == 'toyblocks_als' assert survey.getLength() == 0.0 @@ -199,7 +194,7 @@ def test_scene(): pass -def test_create_survey(): +def test_create_survey(output_dir): """Test creating/configuring a survey with pyhelios""" pyhelios.setDefaultRandomnessGeneratorSeed("7") test_survey_path = 'data/surveys/test_survey.xml' @@ -219,7 +214,7 @@ def test_create_survey(): simBuilder = pyhelios.SimulationBuilder( test_survey_path, 'assets/', - 'output/' + str(output_dir) ) simBuilder.setFinalOutput(True) simBuilder.setLasOutput(True) @@ -282,12 +277,6 @@ def test_create_survey(): np.testing.assert_allclose(meas[100, :3], np.array([83.32, -66.44204, 0.03114649])) np.testing.assert_allclose(traj[0, :3], np.array([waypoints[0][0], waypoints[0][1], altitude])) - # cleanup - os.remove(test_survey_path) - if DELETE_FILES_AFTER: - print(f"Deleting files in {Path(output.outpath).parent.as_posix()}") - shutil.rmtree(Path(output.outpath).parent) - def test_material(test_sim): """Test accessing material properties of a primitive in a scene""" @@ -347,15 +336,15 @@ def test_detector(test_sim): [pytest.param(True, id="setExportToFile(True)"), pytest.param(False, id="setExportToFile(False)")] ) -def test_output(regression_data, export_to_file): +def test_output(output_dir, regression_data, export_to_file): """Validating the output of a survey started with pyhelios""" from pyhelios import SimulationBuilder survey_path = Path('data') / 'test' / 'als_hd_demo_tiff_min.xml' pyhelios.setDefaultRandomnessGeneratorSeed("43") simB = SimulationBuilder( surveyPath=str(survey_path.absolute()), - assetsDir=WORKING_DIR + os.sep + 'assets' + os.sep, - outputDir=WORKING_DIR + os.sep + 'output' + os.sep, + assetsDir=[str(Path('assets'))], + outputDir=str(output_dir), ) simB.setFinalOutput(True) simB.setExportToFile(export_to_file) @@ -369,20 +358,22 @@ def test_output(regression_data, export_to_file): sim.start() output = sim.join() - measurements_array, trajectory_array = pyhelios.outputToNumpy(output) - time = measurements_array[:, 16] - pcloud = pcu.PointCloud(measurements_array[:, :3], fnames=['gps_time'], F=time.reshape(time.shape[0], 1)) - las = laspy.read(regression_data / 'tiffloader_als_merged.las') - pcloud_ref = pcu.PointCloud.from_las(las, fnames=['gps_time']) - pcloud.assert_equals(pcloud_ref, eps=0.00011) # larger tolerance due to las scale factor of 0.0001 + + if regression_data: + measurements_array, trajectory_array = pyhelios.outputToNumpy(output) + time = measurements_array[:, 16] + pcloud = pcu.PointCloud(measurements_array[:, :3], fnames=['gps_time'], F=time.reshape(time.shape[0], 1)) + las = laspy.read(regression_data / 'tiffloader_als_merged.las') + pcloud_ref = pcu.PointCloud.from_las(las, fnames=['gps_time']) + pcloud.assert_equals(pcloud_ref, eps=0.00011) # larger tolerance due to las scale factor of 0.0001 + if export_to_file: - dirname = xmldisplayer.find_playback_dir(survey_path, helios_root=WORKING_DIR) + dirname = find_playback_dir(survey_path, output_dir) assert (Path(dirname) / 'leg000_points.xyz').exists() - pcloud0 = pcu.PointCloud.from_xyz_file(Path(dirname) / 'leg000_points.xyz', cols=(0, 1, 2), names=['x', 'y', 'z']) - pcloud_ref0 = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg000_points.las') - pcloud0.assert_equals(pcloud_ref0, eps=0.00011) # larger tolerance due to las scale factor of 0.0001 assert (Path(dirname) / 'leg001_points.xyz').exists() assert (Path(dirname) / 'leg002_points.xyz').exists() - if DELETE_FILES_AFTER: - print(f"Deleting files in {Path(output.outpath).parent.as_posix()}") - shutil.rmtree(Path(output.outpath).parent) + + if regression_data: + pcloud0 = pcu.PointCloud.from_xyz_file(Path(dirname) / 'leg000_points.xyz', cols=(0, 1, 2), names=['x', 'y', 'z']) + pcloud_ref0 = pcu.PointCloud.from_las_file(regression_data / 'tiffloader_als_leg000_points.las') + pcloud0.assert_equals(pcloud_ref0, eps=0.00011) # larger tolerance due to las scale factor of 0.0001