Coverage for tests/pex25d/artifact_test.py: 100%
57 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 19:08 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 19:08 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024-2026 Martin Jan Köhler and Harald Pretl
4# Johannes Kepler University, Institute for Integrated Circuits.
5#
6# This file is part of KPEX
7# (see https://github.com/iic-jku/klayout-pex).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21# SPDX-License-Identifier: GPL-3.0-or-later
22# --------------------------------------------------------------------------------
23#
25from __future__ import annotations
27import allure
28import os
29import tempfile
30import unittest
32from klayout_pex.pex25d.artifact import (
33 ArtifactFormat, ArtifactKind, ArtifactNamingError, STDIO_PATH,
34 infer_artifact_spec,
35)
36from klayout_pex.pex25d.codec import load_artifact, save_artifact
37from klayout_pex.pex25d.resolver import resolve
39from .pex25d_fixtures import MINIMAL, read
42@allure.parent_suite("Unit Tests")
43@allure.tag("PEX25D", "Artifact")
44class Pex25DArtifactTest(unittest.TestCase):
45 # ------------------------------------------------------------ inference
47 def test_the_naming_convention(self):
48 cases = {
49 'x.pex25d': (ArtifactKind.FILE, ArtifactFormat.TEXT, False),
50 'x.pex25d.pb': (ArtifactKind.FILE, ArtifactFormat.PB, False),
51 'x.pex25d.textpb': (ArtifactKind.FILE, ArtifactFormat.TEXTPB, False),
52 'x.pex25d.scene.pb': (ArtifactKind.SCENE, ArtifactFormat.PB, False),
53 'x.pex25d.scene.textpb': (ArtifactKind.SCENE, ArtifactFormat.TEXTPB, False),
54 'x.pex25d.gz': (ArtifactKind.FILE, ArtifactFormat.TEXT, True),
55 'x.pex25d.scene.pb.gz': (ArtifactKind.SCENE, ArtifactFormat.PB, True),
56 }
57 for path, (kind, format, gzipped) in cases.items():
58 with self.subTest(path=path):
59 spec = infer_artifact_spec(path)
60 assert (spec.kind, spec.format, spec.gzipped) == (kind, format, gzipped)
62 def test_an_unconventional_name_has_to_be_stated(self):
63 with self.assertRaises(ArtifactNamingError):
64 infer_artifact_spec('/tmp/whatever.dat')
65 spec = infer_artifact_spec('/tmp/whatever.dat',
66 kind=ArtifactKind.SCENE,
67 format=ArtifactFormat.PB)
68 assert (spec.kind, spec.format) == (ArtifactKind.SCENE, ArtifactFormat.PB)
70 def test_an_explicit_choice_beats_the_name(self):
71 spec = infer_artifact_spec('x.pex25d', format=ArtifactFormat.TEXTPB)
72 assert spec.format == ArtifactFormat.TEXTPB
73 assert spec.kind == ArtifactKind.FILE
75 def test_stdio_falls_back_to_the_defaults(self):
76 spec = infer_artifact_spec(STDIO_PATH)
77 assert spec.is_stdio
78 assert (spec.kind, spec.format) == (ArtifactKind.FILE, ArtifactFormat.TEXT)
80 def test_binary_formats_are_flagged(self):
81 assert infer_artifact_spec('x.pex25d.pb').is_binary
82 assert infer_artifact_spec('x.pex25d.gz').is_binary
83 assert not infer_artifact_spec('x.pex25d').is_binary
84 assert not infer_artifact_spec('x.pex25d.textpb').is_binary
86 # ----------------------------------------------------------- round trip
88 def round_trip(self, message, name: str):
89 with tempfile.TemporaryDirectory() as directory:
90 spec = infer_artifact_spec(os.path.join(directory, name))
91 save_artifact(message, spec)
92 reloaded = load_artifact(spec)
93 assert reloaded.SerializeToString(deterministic=True) == \
94 message.SerializeToString(deterministic=True)
96 def test_a_file_survives_every_encoding(self):
97 message = read(MINIMAL)
98 for name in ('a.pex25d', 'a.pex25d.pb', 'a.pex25d.textpb',
99 'a.pex25d.gz', 'a.pex25d.pb.gz'):
100 with self.subTest(name=name):
101 self.round_trip(message, name)
103 def test_a_scene_survives_the_protobuf_encodings(self):
104 scene = resolve(read(MINIMAL))
105 for name in ('a.pex25d.scene.pb', 'a.pex25d.scene.textpb',
106 'a.pex25d.scene.pb.gz'):
107 with self.subTest(name=name):
108 self.round_trip(scene, name)
110 def test_a_scene_has_no_text_spelling(self):
111 # Resolution is not reversible; asking for a text scene is refused when
112 # the spec is built, rather than silently lowered when it is written.
113 with self.assertRaises(ArtifactNamingError):
114 infer_artifact_spec('a.pex25d', kind=ArtifactKind.SCENE)
115 with self.assertRaises(ArtifactNamingError):
116 infer_artifact_spec('a.pex25d.scene.pb', format=ArtifactFormat.TEXT)