2929import pathlib
3030import platform
3131import typing
32+ import math
3233
3334import invoke .context
3435import pytest
3536
3637test_data_path = pathlib .Path (__file__ ).resolve ().parent .joinpath ("testdata" )
38+ size_comparison_tolerance = 0.03 # Maximum allowed archive size difference ratio
3739
3840
3941def test_all (run_command , working_dir ):
@@ -77,6 +79,7 @@ def test_all(run_command, working_dir):
7779 golden_logs_parent_path = test_data_path .joinpath ("test_all" , "golden" , "logs" , "generate" ),
7880 logs_subpath = pathlib .Path ("github.com" , "arduino-libraries" , "SpacebrewYun" , "index.html" ),
7981 )
82+ check_db (configuration = configuration )
8083 check_index (configuration = configuration )
8184
8285 # Run the engine again
@@ -95,6 +98,7 @@ def test_all(run_command, working_dir):
9598 golden_logs_parent_path = test_data_path .joinpath ("test_all" , "golden" , "logs" , "update" ),
9699 logs_subpath = pathlib .Path ("github.com" , "arduino-libraries" , "SpacebrewYun" , "index.html" ),
97100 )
101+ check_db (configuration = configuration )
98102 check_index (configuration = configuration )
99103
100104
@@ -104,9 +108,9 @@ def check_libraries(configuration):
104108 Keyword arguments:
105109 configuration -- dictionary defining the libraries-repository-engine configuration
106110 """
111+ # Check against the index
107112 with pathlib .Path (configuration ["LibrariesIndex" ]).open (mode = "r" , encoding = "utf-8" ) as libraries_index_file :
108113 libraries_index = json .load (fp = libraries_index_file )
109-
110114 for release in libraries_index ["libraries" ]:
111115 release_archive_path = pathlib .Path (
112116 configuration ["LibrariesFolder" ],
@@ -119,6 +123,21 @@ def check_libraries(configuration):
119123
120124 assert release ["checksum" ] == "SHA-256:" + hashlib .sha256 (release_archive_path .read_bytes ()).hexdigest ()
121125
126+ # Check against the db
127+ with pathlib .Path (configuration ["LibrariesDB" ]).open (mode = "r" , encoding = "utf-8" ) as library_db_file :
128+ library_db = json .load (fp = library_db_file )
129+ for release in library_db ["Releases" ]:
130+ release_archive_path = pathlib .Path (
131+ configuration ["LibrariesFolder" ],
132+ release ["URL" ].removeprefix (configuration ["BaseDownloadUrl" ]),
133+ )
134+
135+ assert release_archive_path .exists ()
136+
137+ assert release ["Size" ] == release_archive_path .stat ().st_size
138+
139+ assert release ["Checksum" ] == "SHA-256:" + hashlib .sha256 (release_archive_path .read_bytes ()).hexdigest ()
140+
122141
123142def check_logs (configuration , golden_logs_parent_path , logs_subpath ):
124143 """Run tests to determine whether the engine's logs are as expected.
@@ -145,34 +164,111 @@ def check_logs(configuration, golden_logs_parent_path, logs_subpath):
145164 assert logs == golden_logs
146165
147166
167+ def check_db (configuration ):
168+ """Run tests to determine whether the generated library database is as expected.
169+
170+ Keyword arguments:
171+ configuration -- dictionary defining the libraries-repository-engine configuration
172+ """
173+ checksum_placeholder = "CHECKSUM_PLACEHOLDER"
174+
175+ # Load generated db
176+ with pathlib .Path (configuration ["LibrariesDB" ]).open (mode = "r" , encoding = "utf-8" ) as db_file :
177+ db = json .load (fp = db_file )
178+ for release in db ["Releases" ]:
179+ # The checksum values in the db will be different on every run, so it's necessary to replace them with a
180+ # placeholder before comparing to the golden master
181+ release ["Checksum" ] = checksum_placeholder
182+
183+ # Load golden index
184+ golden_db_template = test_data_path .joinpath ("test_all" , "golden" , "db.json" ).read_text (encoding = "utf-8" )
185+ # Fill in mutable content
186+ golden_db_string = string .Template (template = golden_db_template ).substitute (
187+ base_download_url = configuration ["BaseDownloadUrl" ],
188+ checksum_placeholder = checksum_placeholder ,
189+ git_clones_folder = configuration ["GitClonesFolder" ],
190+ )
191+ golden_db = json .loads (golden_db_string )
192+
193+ # Compare db against golden master
194+ # Order of entries in the db is arbitrary so a simply equality assertion is not possible
195+ assert len (db ["Libraries" ]) == len (golden_db ["Libraries" ])
196+ for library in db ["Libraries" ]:
197+ assert library in golden_db ["Libraries" ]
198+
199+ assert len (db ["Releases" ]) == len (golden_db ["Releases" ])
200+ for release in db ["Releases" ]:
201+ # Find the golden master for the release
202+ golden_release = None
203+ for golden_release_candidate in golden_db ["Releases" ]:
204+ if (
205+ golden_release_candidate ["LibraryName" ] == release ["LibraryName" ]
206+ and golden_release_candidate ["Version" ] == release ["Version" ]
207+ ):
208+ golden_release = golden_release_candidate
209+ break
210+
211+ assert golden_release is not None # Matching golden release was found
212+
213+ # Small variation in size could result from compression algorithm changes, so we allow a tolerance
214+ assert "Size" in release
215+ assert math .isclose (release ["Size" ], golden_release ["Size" ], rel_tol = size_comparison_tolerance )
216+ # Remove size data so a direct comparison of the remaining data can be made against the golden master
217+ del release ["Size" ]
218+ del golden_release ["Size" ]
219+
220+ assert release == golden_release
221+
222+
148223def check_index (configuration ):
149224 """Run tests to determine whether the generated library index is as expected.
150225
151226 Keyword arguments:
152227 configuration -- dictionary defining the libraries-repository-engine configuration
153228 """
229+ checksum_placeholder = "CHECKSUM_PLACEHOLDER"
230+
154231 # Load generated index
155232 with pathlib .Path (configuration ["LibrariesIndex" ]).open (mode = "r" , encoding = "utf-8" ) as library_index_file :
156233 library_index = json .load (fp = library_index_file )
157234 for release in library_index ["libraries" ]:
158- # The checksum values in the index will be different on every run, so it's necessary to remove them before
159- # comparing to the golden index
160- del release ["checksum" ]
235+ # The checksum values in the index will be different on every run, so it's necessary to replace them with a
236+ # placeholder before comparing to the golden index
237+ release ["checksum" ] = checksum_placeholder
161238
162239 # Load golden index
163240 golden_library_index_template = test_data_path .joinpath ("test_all" , "golden" , "library_index.json" ).read_text (
164241 encoding = "utf-8"
165242 )
166243 # Fill in mutable content
167244 golden_library_index_string = string .Template (template = golden_library_index_template ).substitute (
168- base_download_url = configuration ["BaseDownloadUrl" ]
245+ base_download_url = configuration ["BaseDownloadUrl" ], checksum_placeholder = checksum_placeholder
169246 )
170247 golden_library_index = json .loads (golden_library_index_string )
171248
172249 # Order of releases in the index is arbitrary so a simply equality assertion is not possible
173250 assert len (library_index ["libraries" ]) == len (golden_library_index ["libraries" ])
174251 for release in library_index ["libraries" ]:
175- assert release in golden_library_index ["libraries" ]
252+ # Find the golden master for the release
253+ golden_release = None
254+ for golden_release_candidate in golden_library_index ["libraries" ]:
255+ if (
256+ golden_release_candidate ["name" ] == release ["name" ]
257+ and golden_release_candidate ["version" ] == release ["version" ]
258+ ):
259+ golden_release = golden_release_candidate
260+ break
261+
262+ assert golden_release is not None # Matching golden release was found
263+
264+ # Small variation in size could result from compression algorithm changes, so we allow a tolerance
265+ assert "size" in release
266+ assert math .isclose (release ["size" ], golden_release ["size" ], rel_tol = size_comparison_tolerance )
267+ # Remove size data so a direct comparison of the remaining data can be made against the golden master
268+ del release ["size" ]
269+ del golden_release ["size" ]
270+
271+ assert release == golden_release
176272
177273
178274# The engine's Git code struggles to get a clean checkout of releases under some circumstances.
0 commit comments