From 3567abce9ecf321a7300964c67ba310793f06a9c Mon Sep 17 00:00:00 2001 From: Arendelle <2381642961@qq.com> Date: Wed, 1 Jan 2025 11:24:06 +0800 Subject: [PATCH] add invalid data test --- .github/workflows/test.yml | 2 +- .gitignore | 1 + physicsLab/Experiment.py | 124 +++++++++++------- physicsLab/celestial/planets.py | 14 +- physicsLab/errors.py | 5 - test_pl/base.py | 1 + test_pl/{ => data}/All-Celestial-Elements.sav | 0 test_pl/{ => data}/All-Circuit-Elements.sav | 0 .../All-Electromagnetism-Elements.sav | 0 .../Export-All-Celestial-Elements.sav | 0 .../Export-All-Circuit-Elements.sav | 0 .../Export-All-Electromagnetism-Elements.sav | 0 test_pl/data/invalid.sav | 1 + test_pl/test_physicsLab.py | 41 ++++-- 14 files changed, 129 insertions(+), 60 deletions(-) rename test_pl/{ => data}/All-Celestial-Elements.sav (100%) rename test_pl/{ => data}/All-Circuit-Elements.sav (100%) rename test_pl/{ => data}/All-Electromagnetism-Elements.sav (100%) rename test_pl/{ => data}/Export-All-Celestial-Elements.sav (100%) rename test_pl/{ => data}/Export-All-Circuit-Elements.sav (100%) rename test_pl/{ => data}/Export-All-Electromagnetism-Elements.sav (100%) create mode 100644 test_pl/data/invalid.sav diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bbfc71c..ec074de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ on: jobs: build: - runs-on: ${{ matrix.os }} + runs-on: test on ${{ matrix.os }} with ${{ matrix.python-version }} strategy: fail-fast: false matrix: diff --git a/.gitignore b/.gitignore index 0f0dfce..da4f5ab 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ physicsLabSav/ # special file of physicsLab *.sav +!test_pl/data/*.sav *.mido.py *.pl.py *.test.* diff --git a/physicsLab/Experiment.py b/physicsLab/Experiment.py index 26ceada..751f1f5 100644 --- a/physicsLab/Experiment.py +++ b/physicsLab/Experiment.py @@ -92,8 +92,6 @@ class Experiment: else: SAV_PATH_DIR = "physicsLabSav" - SAV_PATH: str # 存档的完整路径 - @overload def __init__(self, open_mode: OpenMode, sav_name: str) -> None: ''' 根据存档名打开存档 @@ -138,25 +136,21 @@ def __init__(self, open_mode: OpenMode, *args) -> None: # 尽管读取存档时会将元件的字符串一并读入, 但只有在调用 load_elements 将元件的信息 # 导入self.Elements与self._element_position之后, 元件信息才被完全导入 - if open_mode == OpenMode.load_by_sav_name or open_mode == OpenMode.load_by_filepath: - sav_name, *rest = args - - if not isinstance(sav_name, str) or len(rest) != 0: - raise TypeError - + if open_mode == OpenMode.load_by_sav_name or open_mode == OpenMode.load_by_filepath or open_mode == OpenMode.load_by_plar_app: if open_mode == OpenMode.load_by_filepath: + sav_name, *rest = args + if not isinstance(sav_name, str) or len(rest) != 0: + raise TypeError + self.SAV_PATH = os.path.abspath(sav_name) if not os.path.exists(self.SAV_PATH): - raise errors.ExperimentNotExistError(f"{self.SAV_PATH} not found") + raise FileNotFoundError(f"\"{self.SAV_PATH}\" not found") if _ExperimentStack.inside(self): raise errors.ExperimentOpenedError - _ExperimentStack.push(self) _temp = _open_sav(self.SAV_PATH) - assert _temp is not None # ??? 万一就是用户的输入不对呢 - if "Experiment" in _temp.keys(): self.PlSav = _temp else: # 读取物实导出的存档只含有.sav的Experiment部分 @@ -170,7 +164,13 @@ def __init__(self, open_mode: OpenMode, *args) -> None: raise errors.InternalError self.PlSav["Experiment"] = _temp + + _ExperimentStack.push(self) elif open_mode == OpenMode.load_by_sav_name: + sav_name, *rest = args + if not isinstance(sav_name, str) or len(rest) != 0: + raise TypeError + filename = search_experiment(sav_name) if filename is None: raise errors.ExperimentNotExistError(f'No such experiment "{sav_name}"') @@ -179,9 +179,47 @@ def __init__(self, open_mode: OpenMode, *args) -> None: if _ExperimentStack.inside(self): raise errors.ExperimentOpenedError - _ExperimentStack.push(self) + _ExperimentStack.push(self) # TODO 所有的_Experiment.push都应该在构造函数快完成的时候才执行 self.PlSav = search_experiment.sav + elif open_mode == OpenMode.load_by_plar_app: + content_id, category, *rest = args + + if not isinstance(content_id, str) or not isinstance(category, Category): + raise TypeError + if len(rest) == 0: + user = User() + elif len(rest) == 1: + if not isinstance(rest[0], User): + raise TypeError + user = rest[0] + else: + raise TypeError + + self.SAV_PATH = os.path.join(Experiment.SAV_PATH_DIR, f"{content_id}.sav") + if _ExperimentStack.inside(self): + raise errors.ExperimentOpenedError + + _summary = user.get_summary(content_id, category)["Data"] + del _summary["$type"] + _experiment = user.get_experiment(_summary["ContentID"])["Data"] + del _experiment["$type"] + + if _experiment["Type"] == ExperimentType.Circuit.value: + self.experiment_type = ExperimentType.Circuit + self.PlSav = copy.deepcopy(savTemplate.Circuit) + elif _experiment["Type"] == ExperimentType.Celestial.value: + self.experiment_type = ExperimentType.Celestial + self.PlSav = copy.deepcopy(savTemplate.Celestial) + elif _experiment["Type"] == ExperimentType.Electromagnetism.value: + self.experiment_type = ExperimentType.Electromagnetism + self.PlSav = copy.deepcopy(savTemplate.Electromagnetism) + else: + assert False + + self.PlSav["Experiment"] = _experiment + self.PlSav["Summary"] = _summary + _ExperimentStack.push(self) else: raise errors.InternalError @@ -211,30 +249,6 @@ def __init__(self, open_mode: OpenMode, *args) -> None: self.StatusSave: dict = {"SimulationSpeed": 1.0, "Elements": []} else: raise errors.InternalError - elif open_mode == OpenMode.load_by_plar_app: - content_id, category, *rest = args - - if not isinstance(content_id, str) or not isinstance(category, Category): - raise TypeError - if len(rest) == 0: - user = User() - elif len(rest) == 1: - if not isinstance(rest[0], User): - raise TypeError - user = rest[0] - else: - raise TypeError - - self.SAV_PATH = os.path.join(Experiment.SAV_PATH_DIR, f"{content_id}.sav") - if _ExperimentStack.inside(self): - raise errors.ExperimentOpenedError - - _ExperimentStack.push(self) - - _summary = user.get_summary(content_id, category)["Data"] - del _summary["$type"] - _summary["Category"] = category.value - self.PlSav["Summary"] = _summary # 此处应该有问题, 存档不完整 elif open_mode == OpenMode.crt: sav_name, experiment_type, force_crt, *rest = args @@ -293,17 +307,31 @@ def __init__(self, open_mode: OpenMode, *args) -> None: self.VisionCenter: _tools.position = _tools.position(0, 0 ,0.88) self.TargetRotation: _tools.position = _tools.position(90, 0, 0) else: - raise errors.InternalError + assert False self.entitle(sav_name) else: - raise errors.InternalError + assert False + assert isinstance(self.open_mode, OpenMode) + assert isinstance(self._elements_position, dict) + assert isinstance(self.Elements, list) + assert isinstance(self.is_load_elements, bool) assert isinstance(self.SAV_PATH, str) + assert isinstance(self.PlSav, dict) + assert isinstance(self.StatusSave, dict) + assert isinstance(self.CameraSave, dict) + assert isinstance(self.VisionCenter, _tools.position) + assert isinstance(self.TargetRotation, _tools.position) + assert isinstance(self.experiment_type, ExperimentType) + if self.experiment_type == ExperimentType.Circuit: + assert isinstance(self.Wires, set) + assert isinstance(self.is_elementXYZ, bool) + assert isinstance(self.elementXYZ_origin_position, _tools.position) # TODO 将该函数放到elements.py中 def get_element_from_identifier(self, identifier: str): - ''' 通过 原件的["Identifier"]获取元件的引用 ''' + ''' 通过原件的id获取元件的引用 ''' for element in self.Elements: assert hasattr(element, "data") if element.data["Identifier"] == identifier: # type: ignore -> has attr .data @@ -311,6 +339,8 @@ def get_element_from_identifier(self, identifier: str): raise errors.ElementNotFound def _read_CameraSave(self, camera_save: str) -> None: + assert isinstance(camera_save, str) + self.CameraSave = json.loads(camera_save) temp = eval(f"({self.CameraSave['VisionCenter']})") self.VisionCenter: _tools.position = _tools.position(temp[0], temp[2], temp[1]) # x, z, y @@ -784,7 +814,8 @@ def _get_all_pl_sav() -> List[str]: savs = savs[savs.__len__() - 1] return [aSav for aSav in savs if aSav.endswith('sav')] -def _open_sav(sav_path) -> Optional[dict]: +# TODO 不再返回None, 而是直接走异常传播路径 +def _open_sav(sav_path) -> dict: ''' 打开一个存档, 返回存档对应的dict @param sav_path: 存档的绝对路径 ''' @@ -813,7 +844,11 @@ def encode_sav(path: str, encoding: str) -> Optional[dict]: else: with open(sav_path, "rb") as f: encoding = chardet.detect(f.read())["encoding"] - return encode_sav(sav_path, encoding) + res = encode_sav(sav_path, encoding) + if res is not None: + return res + + raise errors.InvalidSavError def search_experiment(sav_name: str) -> Optional[str]: ''' 检测实验是否存在 @@ -822,8 +857,9 @@ def search_experiment(sav_name: str) -> Optional[str]: 若存在则返回存档对应的文件名, 若不存在则返回None ''' for aSav in _get_all_pl_sav(): - sav = _open_sav(os.path.join(Experiment.SAV_PATH_DIR, aSav)) - if sav is None: + try: + sav = _open_sav(os.path.join(Experiment.SAV_PATH_DIR, aSav)) + except errors.InvalidSavError: continue if sav["InternalName"] == sav_name: search_experiment.sav = sav diff --git a/physicsLab/celestial/planets.py b/physicsLab/celestial/planets.py index 60d1ba5..f0c47d6 100644 --- a/physicsLab/celestial/planets.py +++ b/physicsLab/celestial/planets.py @@ -282,7 +282,19 @@ def __init__(self, x: numType, y: numType, z: numType) -> None: class Moon(PlanetBase): def __init__(self, x: numType, y: numType, z: numType) -> None: self.data = { - "Identifier": Generate, "Model": "Moon", "Override": None, "Name": "月球", "Parent": None, "Type": 2, "Changed": False, "Extras": {}, "Radius": 1737.1, "RadiusVisible": 0.0116118016, "RotationPeriod": 27.3, "RotationPhase": 0.0, "AxialTilt": 0.0, "Mass": 0.073477, "OrbitType": 0, "OrbitEstimation": 3, "Density": 3.346481005246245, "Gravity": 1.625149040802321, "Luminosity": 0.0, "Temperature": 0.0, "Albedo": 0.12300000339746475, "PowerAbsorbtion": 0.0, "PlanetariumBalance": 0.0, "Position": Generate, "Velocity": Generate, "Acceleration": Generate, "Period": 0.0, "Eccentricity": "NaN", "OmegaUC": 0.0, "OmegaLC": "NaN", "Inclination": "NaN", "Phase": 0.0, "PhaseCurrent": "NaN", "AxisSemi": "NaN", "Perihelion": "NaN", "Aphelion": "NaN", "LeavingKepler": False} + "Identifier": Generate, "Model": "Moon", "Override": None, + "Name": "月球", "Parent": None, "Type": 2, "Changed": False, + "Extras": {}, "Radius": 1737.1, "RadiusVisible": 0.0116118016, + "RotationPeriod": 27.3, "RotationPhase": 0.0, "AxialTilt": 0.0, + "Mass": 0.073477, "OrbitType": 0, "OrbitEstimation": 3, + "Density": 3.346481005246245, "Gravity": 1.625149040802321, + "Luminosity": 0.0, "Temperature": 0.0, "Albedo": 0.12300000339746475, + "PowerAbsorbtion": 0.0, "PlanetariumBalance": 0.0, "Position": Generate, + "Velocity": Generate, "Acceleration": Generate, "Period": 0.0, + "Eccentricity": "NaN", "OmegaUC": 0.0, "OmegaLC": "NaN", "Inclination": "NaN", + "Phase": 0.0, "PhaseCurrent": "NaN", "AxisSemi": "NaN", "Perihelion": "NaN", + "Aphelion": "NaN", "LeavingKepler": False + } class Chocolate_Ball(PlanetBase): def __init__(self, x: numType, y: numType, z: numType) -> None: diff --git a/physicsLab/errors.py b/physicsLab/errors.py index 569139b..62adf06 100644 --- a/physicsLab/errors.py +++ b/physicsLab/errors.py @@ -100,11 +100,6 @@ def __init__(self, err_msg: str = "Index out of range") -> None: def __str__(self): return self.err_msg -# 类实例化异常 基类无法被实例化 -class instantiateError(Exception): - def __str__(self): - return "This class cannot be instantiated" - class ExperimentError(Exception): def __init__(self, string: str = "") -> None: self.err_msg: str = string diff --git a/test_pl/base.py b/test_pl/base.py index 020b673..b6ba496 100644 --- a/test_pl/base.py +++ b/test_pl/base.py @@ -3,6 +3,7 @@ from physicsLab import * TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA_DIR = os.path.join(TEST_DIR, "data") USE_VIZTRACER: bool = False diff --git a/test_pl/All-Celestial-Elements.sav b/test_pl/data/All-Celestial-Elements.sav similarity index 100% rename from test_pl/All-Celestial-Elements.sav rename to test_pl/data/All-Celestial-Elements.sav diff --git a/test_pl/All-Circuit-Elements.sav b/test_pl/data/All-Circuit-Elements.sav similarity index 100% rename from test_pl/All-Circuit-Elements.sav rename to test_pl/data/All-Circuit-Elements.sav diff --git a/test_pl/All-Electromagnetism-Elements.sav b/test_pl/data/All-Electromagnetism-Elements.sav similarity index 100% rename from test_pl/All-Electromagnetism-Elements.sav rename to test_pl/data/All-Electromagnetism-Elements.sav diff --git a/test_pl/Export-All-Celestial-Elements.sav b/test_pl/data/Export-All-Celestial-Elements.sav similarity index 100% rename from test_pl/Export-All-Celestial-Elements.sav rename to test_pl/data/Export-All-Celestial-Elements.sav diff --git a/test_pl/Export-All-Circuit-Elements.sav b/test_pl/data/Export-All-Circuit-Elements.sav similarity index 100% rename from test_pl/Export-All-Circuit-Elements.sav rename to test_pl/data/Export-All-Circuit-Elements.sav diff --git a/test_pl/Export-All-Electromagnetism-Elements.sav b/test_pl/data/Export-All-Electromagnetism-Elements.sav similarity index 100% rename from test_pl/Export-All-Electromagnetism-Elements.sav rename to test_pl/data/Export-All-Electromagnetism-Elements.sav diff --git a/test_pl/data/invalid.sav b/test_pl/data/invalid.sav new file mode 100644 index 0000000..53b86a5 --- /dev/null +++ b/test_pl/data/invalid.sav @@ -0,0 +1 @@ +this is not Physics-Lab-AR's save file diff --git a/test_pl/test_physicsLab.py b/test_pl/test_physicsLab.py index d45ede9..17cc35d 100644 --- a/test_pl/test_physicsLab.py +++ b/test_pl/test_physicsLab.py @@ -30,36 +30,57 @@ def test_experiment_stack(self): @my_test_dec def test_load_all_elements(self): # 物实导出存档与保存到本地的格式不一样, 因此每种类型的实验都有两种格式的测试数据 - expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DIR, "All-Circuit-Elements.sav")) + expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "All-Circuit-Elements.sav")) load_elements(expe) self.assertTrue(count_elements(expe) == 91) expe.exit(delete=False) - expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DIR, "Export-All-Circuit-Elements.sav")) + expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "Export-All-Circuit-Elements.sav")) load_elements(expe) self.assertTrue(count_elements(expe) == 91) expe.exit() - expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DIR, "All-Celestial-Elements.sav")) + expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "All-Celestial-Elements.sav")) load_elements(expe) self.assertTrue(count_elements(expe) == 27) expe.exit() - expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DIR, "Export-All-Celestial-Elements.sav")) + expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "Export-All-Celestial-Elements.sav")) load_elements(expe) self.assertTrue(count_elements(expe) == 27) expe.exit() - expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DIR, "All-Electromagnetism-Elements.sav")) + expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "All-Electromagnetism-Elements.sav")) load_elements(expe) self.assertTrue(count_elements(expe) == 7) expe.exit() - expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DIR, "Export-All-Electromagnetism-Elements.sav")) + expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "Export-All-Electromagnetism-Elements.sav")) load_elements(expe) self.assertTrue(count_elements(expe) == 7) expe.exit() + @my_test_dec + def test_double_load_error(self): + expe = Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "All-Circuit-Elements.sav")) + try: + Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "All-Circuit-Elements.sav")) + except ExperimentOpenedError: + pass + else: + raise TestError + finally: + expe.exit() + + @my_test_dec + def test_load_invalid_sav(self): + try: + Experiment(OpenMode.load_by_filepath, os.path.join(TEST_DATA_DIR, "invalid.sav")) + except InvalidSavError: + pass + else: + raise TestError + @my_test_dec def test_normal_circuit_usage(self): expe: Experiment = Experiment(OpenMode.crt, "__test__", ExperimentType.Circuit, True) @@ -94,14 +115,16 @@ def test_read_Experiment(self): @my_test_dec def test_crt_Experiment(self): + exp: Experiment = Experiment(OpenMode.crt, "__test__", ExperimentType.Circuit, True) + exp.save() try: - exp: Experiment = Experiment(OpenMode.crt, "__test__", ExperimentType.Circuit, True) - exp.save() Experiment(OpenMode.crt, "__test__", ExperimentType.Circuit, False) # will fail except ExperimentExistError: - exp.exit(delete=True) + pass else: raise TestError + finally: + exp.exit(delete=True) @my_test_dec def test_crt_wire(self):