Skip to content

Commit 33d0fee

Browse files
committed
test sdk: increase coverage on AASXWriter
The adapter.aasx.AASXWriter class had a low branch coverage especially on failing conditions. These changes introduce unittests that cover cases in which either warnings are emitted or execptions raise.
1 parent 4360660 commit 33d0fee

1 file changed

Lines changed: 244 additions & 0 deletions

File tree

sdk/test/adapter/aasx/test_aasx.py

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,250 @@ def test_supplementary_file_container(self) -> None:
9191

9292

9393
class AASXWriterTest(unittest.TestCase):
94+
def test_write_missing_aas_objects(self):
95+
with tempfile.TemporaryDirectory() as tmpdir:
96+
tmpdir_path = Path(tmpdir)
97+
98+
# ---- Arange ----
99+
data = example_aas.create_full_example()
100+
101+
# ---- Act & Assert -----
102+
with self.assertLogs(level="WARNING") as log:
103+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
104+
# try to write non-existing object
105+
writer.write_aas_objects(
106+
"/aasx/selection.xml",
107+
["https://acplt.org/Test_AssetAdministrationShell",
108+
"http://false-identifier.org/",
109+
"http://acplt.org/Submodels/Assets/TestAsset/Identification"],
110+
data, aasx.DictSupplementaryFileContainer()
111+
)
112+
113+
self.assertIn("Could not find identifiable http://false-identifier.org/ in IdentifiableStore", log.output[0])
114+
115+
# assert only the two existing objects have been written to aasx file
116+
object_store = model.DictIdentifiableStore()
117+
with aasx.AASXReader(tmpdir_path / "tmp.aasx") as reader:
118+
reader.read_into(object_store, aasx.DictSupplementaryFileContainer())
119+
self.assertEqual(len(object_store), 2)
120+
121+
def test_writing_with_missing_file(self) -> None:
122+
with tempfile.TemporaryDirectory() as tmpdir:
123+
tmpdir_path = Path(tmpdir)
124+
125+
# ---- Arange ----
126+
# data contains a submodel with a File submodel_element
127+
# the empty_file_store does not contain the referenced file
128+
data = example_aas.create_full_example()
129+
empty_file_store = aasx.DictSupplementaryFileContainer()
130+
131+
# ---- Act & Assert ----
132+
# assert warning is present in failsafe mode
133+
with self.assertLogs(level="WARNING") as log:
134+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
135+
writer.write_all_aas_objects("/aasx/data.xml", data, empty_file_store)
136+
self.assertIn("Could not find file", log.output[0])
137+
138+
# assert exception is rose in non-failsafe mode
139+
with self.assertRaises(KeyError) as cm:
140+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
141+
writer.write_all_aas_objects("/aasx/data.xml", data, empty_file_store)
142+
self.assertIn("Could not find file", cm.exception.args[0])
143+
144+
def test_writing_file_twice(self) -> None:
145+
with tempfile.TemporaryDirectory() as tmpdir:
146+
tmpdir_path = Path(tmpdir)
147+
148+
# ---- Arange ----
149+
file_store = aasx.DictSupplementaryFileContainer()
150+
with open(Path(__file__).parent / "TestFile.pdf", "rb") as pdf:
151+
resulting_file_name = file_store.add_file("/TestFile.pdf", pdf, "application/pdf")
152+
153+
# create two submodels that reference the same file in file_store
154+
first_submodel = model.Submodel(
155+
id_="http://example.org/First_Submodel",
156+
submodel_element=[model.File(
157+
id_short="ExampleFile",
158+
content_type="application/pdf",
159+
value=resulting_file_name
160+
)]
161+
)
162+
second_submodel = model.Submodel(
163+
id_="http://example.org/SecondSubmodel",
164+
submodel_element=[model.File(
165+
id_short="ExampleFile",
166+
content_type="application/pdf",
167+
value=resulting_file_name
168+
)]
169+
)
170+
data = model.DictIdentifiableStore([first_submodel, second_submodel])
171+
172+
# ---- Act & Assert ----
173+
with self.assertNoLogs(level="WARNING"):
174+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx") as writer:
175+
writer.write_all_aas_objects("/aasx/data.xml", data, file_store)
176+
177+
def test_write_non_aas(self) -> None:
178+
with tempfile.TemporaryDirectory() as tmpdir:
179+
tmpdir_path = Path(tmpdir)
180+
181+
# ---- Arange ----
182+
data = example_aas.create_full_example()
183+
file_store = aasx.DictSupplementaryFileContainer()
184+
with open(Path(__file__).parent / "TestFile.pdf", "rb") as pdf:
185+
file_store.add_file("/TestFile.pdf", pdf, "application/pdf")
186+
187+
188+
# ---- Act & Assert ----
189+
# assert warning is present in failsafe mode
190+
with self.assertLogs(level="WARNING") as log:
191+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
192+
# try to write a non AAS object
193+
writer.write_aas("https://acplt.org/Test_Submodel", data, file_store)
194+
self.assertIn("Skipping AAS https://acplt.org/Test_Submodel", log.output[0])
195+
196+
# assert exception is rose in non-failsafe mode
197+
with self.assertRaises(TypeError) as cm:
198+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
199+
# try to write a non AAS object
200+
writer.write_aas("https://acplt.org/Test_Submodel", data, file_store)
201+
self.assertIn("Identifier https://acplt.org/Test_Submodel does not belong "
202+
"to an AssetAdministrationShell",cm.exception.args[0])
203+
204+
def test_write_aas_missing_submodel(self) -> None:
205+
with tempfile.TemporaryDirectory() as tmpdir:
206+
tmpdir_path = Path(tmpdir)
207+
208+
# ---- Arange ----
209+
# leave example_submodel out of object store
210+
data = model.DictIdentifiableStore([
211+
example_aas.create_example_asset_administration_shell(),
212+
example_aas.create_example_asset_identification_submodel(),
213+
example_aas.create_example_bill_of_material_submodel()
214+
])
215+
empty_file_store = aasx.DictSupplementaryFileContainer()
216+
217+
# ---- Act & Assert ----
218+
# assert warning is present in failsafe mode
219+
with self.assertLogs(level="WARNING") as log:
220+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
221+
writer.write_aas("https://acplt.org/Test_AssetAdministrationShell", data, empty_file_store)
222+
self.assertIn("Could not find Submodel", log.output[0])
223+
224+
# assert exception is rose in non-failsafe mode
225+
with self.assertRaises(KeyError) as cm:
226+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
227+
writer.write_aas("https://acplt.org/Test_AssetAdministrationShell", data, empty_file_store)
228+
self.assertIn("Could not find Submodel", cm.exception.args[0])
229+
230+
def test_write_aas_missing_concept_description(self) -> None:
231+
with tempfile.TemporaryDirectory() as tmpdir:
232+
tmpdir_path = Path(tmpdir)
233+
234+
# ---- Arange ----
235+
# leave example_concept_description out of object store
236+
data = model.DictIdentifiableStore([
237+
example_aas.create_example_asset_administration_shell(),
238+
example_aas.create_example_submodel(),
239+
example_aas.create_example_asset_identification_submodel(),
240+
example_aas.create_example_bill_of_material_submodel()
241+
])
242+
file_store = aasx.DictSupplementaryFileContainer()
243+
with open(Path(__file__).parent / "TestFile.pdf", "rb") as pdf:
244+
file_store.add_file("/TestFile.pdf", pdf, "application/pdf")
245+
246+
# ---- Act & Assert ----
247+
# assert warning is present in failsafe mode
248+
with self.assertLogs(level="WARNING") as log:
249+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
250+
writer.write_aas("https://acplt.org/Test_AssetAdministrationShell", data, file_store)
251+
self.assertIn("https://acplt.org/Test_ConceptDescription", log.output[0])
252+
self.assertRegex(log.output[0], "ConceptDescription .* not found")
253+
254+
# assert exception is rose in non-failsafe mode
255+
with self.assertRaises(KeyError) as cm:
256+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
257+
writer.write_aas("https://acplt.org/Test_AssetAdministrationShell", data, file_store)
258+
self.assertIn("https://acplt.org/Test_ConceptDescription", cm.exception.args[0])
259+
self.assertRegex(cm.exception.args[0], "ConceptDescription .* not found")
260+
261+
def test_write_aas_false_semantic_id(self) -> None:
262+
with tempfile.TemporaryDirectory() as tmpdir:
263+
tmpdir_path = Path(tmpdir)
264+
265+
# ---- Arange ----
266+
# semanticId of submodel holds reference to an object
267+
# that is no ContentDescription
268+
second_submodel = model.Submodel(
269+
id_="https://acplt.org/Second_Submodel"
270+
)
271+
submodel = model.Submodel(
272+
id_="https://acplt.org/Test_Submodel",
273+
semantic_id=model.ModelReference(
274+
key=(model.Key(type_=model.KeyTypes.SUBMODEL, value="https://acplt.org/Second_Submodel"),),
275+
type_=model.ConceptDescription
276+
)
277+
)
278+
data = model.DictIdentifiableStore([
279+
example_aas.create_example_asset_administration_shell(),
280+
example_aas.create_example_asset_identification_submodel(),
281+
example_aas.create_example_bill_of_material_submodel(),
282+
submodel, second_submodel
283+
])
284+
empty_file_store = aasx.DictSupplementaryFileContainer()
285+
286+
# ---- Act & Assert ----
287+
# assert warning is present in failsafe mode
288+
with self.assertLogs(level="WARNING") as log:
289+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
290+
writer.write_aas("https://acplt.org/Test_AssetAdministrationShell", data, empty_file_store)
291+
self.assertIn("which is not a ConceptDescription", log.output[0])
292+
293+
# assert exception is rose in non-failsafe mode
294+
with self.assertRaises(TypeError) as cm:
295+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
296+
writer.write_aas("https://acplt.org/Test_AssetAdministrationShell", data, empty_file_store)
297+
self.assertIn("which is not a ConceptDescription", cm.exception.args[0])
298+
299+
def test_write_core_properties_twice(self) -> None:
300+
with tempfile.TemporaryDirectory() as tmpdir:
301+
tmpdir_path = Path(tmpdir)
302+
303+
# ---- Arrange ----
304+
cp = pyecma376_2.OPCCoreProperties()
305+
cp.created = datetime.datetime.now()
306+
cp.creator = "Eclipse BaSyx Python Testing Framework"
307+
308+
# ---- Act & Assert ----
309+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx") as writer:
310+
writer.write_core_properties(cp)
311+
312+
# expect RuntimeError on second write
313+
with self.assertRaises(RuntimeError) as cm:
314+
writer.write_core_properties(cp)
315+
316+
self.assertIn("Core Properties have already been written", cm.exception.args[0])
317+
318+
319+
def test_write_thumbnail_twice(self) -> None:
320+
with tempfile.TemporaryDirectory() as tmpdir:
321+
tmpdir_path = Path(tmpdir)
322+
323+
# ---- Arrange ----
324+
with open(Path(__file__).parent / "test.png", "rb") as png:
325+
thumbnail = png.read()
326+
327+
# ---- Act & Assert ----
328+
with aasx.AASXWriter(tmpdir_path / "tmp.aasx") as writer:
329+
writer.write_thumbnail("/aasx/thumbnail.png", bytearray(thumbnail), "image/png")
330+
331+
# expect RuntimeError on second write
332+
with self.assertRaises(RuntimeError) as cm:
333+
writer.write_thumbnail("/aasx/thumbnail.png", bytearray(thumbnail), "image/png")
334+
335+
self.assertIn("package thumbnail has already been written", cm.exception.args[0])
336+
337+
94338
def test_writing_reading_example_aas(self) -> None:
95339
# Create example data and file_store
96340
data = example_aas.create_full_example() # creates a complete, valid example AAS

0 commit comments

Comments
 (0)