Source code for test_backup_zip

"""Test the integrity of the crowd data saved in a zip file."""

# Copyright  2025  Institute of Light and Matter, CNRS UMR 5306, University Claude Bernard Lyon 1
# Contributors: Oscar DUFOUR, Maxime STAPELLE, Alexandre NICOLAS

# This software is a computer program designed to generate a realistic crowd from anthropometric data and
# simulate the mechanical interactions that occur within it and with obstacles.

# This software is governed by the CeCILL-B license under French law and abiding by the rules of distribution
# of free software.  You can  use, modify and/ or redistribute the software under the terms of the CeCILL-B
# license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".

# As a counterpart to the access to the source code and  rights to copy, modify and redistribute granted by
# the license, users are provided only with a limited warranty  and the software's author,  the holder of the
# economic rights,  and the successive licensors  have only  limited liability.

# In this respect, the user's attention is drawn to the risks associated with loading,  using,  modifying
# and/or developing or reproducing the software by the user in light of its specific status of free software,
# that may mean  that it is complicated to manipulate,  and  that  also therefore means  that it is reserved
# for developers  and  experienced professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their requirements in conditions enabling
# the security of their systems and/or data to be ensured and,  more generally, to use and operate it in the
# same conditions as regards security.

# The fact that you are presently reading this means that you have had knowledge of the CeCILL-B license and that
# you accept its terms.

import zipfile
from pathlib import Path
from typing import Callable, Optional

import pytest

import configuration.backup.crowd_to_dict as fun_dict
import configuration.backup.crowd_to_zip_and_reverse as fun_zip
import configuration.backup.dict_to_xml_and_reverse as fun_xml
from configuration.models.crowd import Crowd
from configuration.utils.typing_custom import (
    DynamicCrowdDataType,
    GeometryDataType,
    InteractionsDataType,
    MaterialsDataType,
    StaticCrowdDataType,
)


[docs] @pytest.fixture(name="crowd_fixture") def crowd_fixture() -> Crowd: """ Initialize and set up the Crowd object. This fixture creates a `Crowd` instance, initializes agents, and packs them with forces. Returns ------- Crowd An initialized `Crowd` object. """ crowd_instance = Crowd() crowd_instance.create_agents() crowd_instance.pack_agents_with_forces() return crowd_instance
[docs] @pytest.fixture(name="output_zip_path_fixture") def output_zip_path_fixture(crowd_fixture: Crowd) -> Path: """ Save crowd data to a zip file and return the path. Parameters ---------- crowd_fixture : Crowd The `Crowd` object containing simulation data. Returns ------- Path The path to the saved zip file. """ output_file_path = Path.cwd().parent.parent / "data" / "tutorial_configuration_files" / "crowd_ANSURII.zip" fun_zip.save_crowd_data_to_zip(crowd_fixture, output_file_path) return output_file_path
[docs] @pytest.fixture(name="original_data_dicts_fixture") def original_data_dicts_fixture( crowd_fixture: Crowd, ) -> dict[str, StaticCrowdDataType | MaterialsDataType | DynamicCrowdDataType | GeometryDataType | InteractionsDataType]: """ Retrieve original data dictionaries from the Crowd object. This fixture collects static, materials, dynamic, and geometry parameters from the `Crowd` object. Parameters ---------- crowd_fixture : Crowd The `Crowd` object containing simulation data. Returns ------- dict[str, StaticCrowdDataType | MaterialsDataType | DynamicCrowdDataType | GeometryDataType | InteractionsDataType] A dictionary containing original simulation data categorized by type. Keys include "static", "materials", "dynamic", "geometry" and "interactions". """ return { "static": fun_dict.get_static_params(crowd_fixture), "materials": fun_dict.get_materials_params(), "dynamic": fun_dict.get_dynamic_params(crowd_fixture), "geometry": fun_dict.get_geometry_params(crowd_fixture), }
[docs] @pytest.fixture(name="loaded_xml_data_fixture") def loaded_xml_data_fixture(output_zip_path_fixture: Path) -> dict[str, Optional[bytes]]: """ Extract XML content from the zip file. This fixture reads XML files from the zip archive and categorizes them based on their content type. Parameters ---------- output_zip_path_fixture : Path The path to the zip file containing XML data. Returns ------- dict[str, Optional[bytes]] A dictionary containing XML content categorized by type. Keys include "static", "materials", "dynamic", and "geometry". Values are the raw XML content as bytes or None if not found. """ xml_data: dict[str, Optional[bytes]] = { "static": None, "materials": None, "dynamic": None, "geometry": None, } with zipfile.ZipFile(output_zip_path_fixture, "r") as zip_file: # List all files in the ZIP archive file_names = zip_file.namelist() # Filter only XML files xml_files = [file for file in file_names if file.endswith(".xml")] for xml_file in xml_files: with zip_file.open(xml_file, "r") as file: xml_content = file.read() # Assign each XML file to its respective variable based on naming conventions. if "agents" in xml_file.lower(): xml_data["static"] = xml_content elif "materials" in xml_file.lower(): xml_data["materials"] = xml_content elif "dynamics" in xml_file.lower(): xml_data["dynamic"] = xml_content elif "geometry" in xml_file.lower(): xml_data["geometry"] = xml_content return xml_data
[docs] @pytest.mark.parametrize( "key, parse_function", [ ("static", fun_xml.static_xml_to_dict), ("materials", fun_xml.materials_xml_to_dict), ("dynamic", fun_xml.dynamic_xml_to_dict), ("geometry", fun_xml.geometry_xml_to_dict), ], ) def test_crowd_data_integrity( key: str, parse_function: Callable[ [Optional[bytes]], dict[str, StaticCrowdDataType | MaterialsDataType | DynamicCrowdDataType | GeometryDataType], ], original_data_dicts_fixture: dict[str, StaticCrowdDataType | MaterialsDataType | DynamicCrowdDataType | GeometryDataType], loaded_xml_data_fixture: dict[str, Optional[bytes]], ) -> None: """ Test that the parsed XML data matches the original data dictionaries. Parameters ---------- key : str The key representing a specific type of data (e.g., static, materials). parse_function : Callable[[Optional[bytes]], dict[str, StaticCrowdDataType | MaterialsDataType | DynamicCrowdDataType | GeometryDataType]] The function used to parse the XML data into a dictionary. original_data_dicts_fixture : dict[str, StaticCrowdDataType | MaterialsDataType | DynamicCrowdDataType | GeometryDataType ] The original data dictionaries categorized by type. loaded_xml_data_fixture : dict[str, Optional[bytes]] The XML data loaded from the zip file categorized by type. """ assert loaded_xml_data_fixture[key] is not None, f"{key} XML data is missing!" parsed_data = parse_function(loaded_xml_data_fixture[key]) print(f"Parsed {key} data: {parsed_data}") print(f"Original {key} data: {original_data_dicts_fixture[key]}") assert parsed_data == original_data_dicts_fixture[key], f"{key} data mismatch!" assert parsed_data == original_data_dicts_fixture[key], f"{key} data mismatch!"