@@ -91,6 +91,250 @@ def test_supplementary_file_container(self) -> None:
9191
9292
9393class 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