Skip to content

Commit 2a49ca0

Browse files
committed
Add tests to verify our version updates are working
1 parent 3389d47 commit 2a49ca0

File tree

4 files changed

+199
-107
lines changed

4 files changed

+199
-107
lines changed

relenv/build/darwin.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,35 @@ def update_expat(dirs: Dirs, env: MutableMapping[str, str]) -> None:
8484

8585
# Copy source files to Modules/expat/
8686
expat_source_dir = tmpbuild / f"expat-{version}" / "lib"
87+
updated_files = []
8788
for source_file in ["*.h", "*.c"]:
8889
for file_path in glob.glob(str(expat_source_dir / source_file)):
8990
target_file = expat_dir / pathlib.Path(file_path).name
9091
# Remove old file if it exists
9192
if target_file.exists():
9293
target_file.unlink()
9394
shutil.copy2(file_path, str(expat_dir))
95+
updated_files.append(target_file)
96+
97+
# Touch all updated files to ensure make rebuilds them
98+
# (The tarball may contain files with newer timestamps)
99+
import time
100+
import os
101+
102+
now = time.time()
103+
for target_file in updated_files:
104+
os.utime(target_file, (now, now))
105+
106+
# Update SBOM with correct checksums for updated expat files
107+
from relenv.build.common import update_sbom_checksums
108+
109+
files_to_update = {}
110+
for target_file in updated_files:
111+
# SBOM uses relative paths from Python source root
112+
relative_path = f"Modules/expat/{target_file.name}"
113+
files_to_update[relative_path] = target_file
114+
115+
update_sbom_checksums(dirs.source, files_to_update)
94116

95117

96118
def build_python(env: MutableMapping[str, str], dirs: Dirs, logfp: IO[str]) -> None:

relenv/build/linux.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,34 @@ def update_expat(dirs: Dirs, env: EnvMapping) -> None:
400400

401401
# Copy source files to Modules/expat/
402402
expat_source_dir = tmpbuild / f"expat-{version}" / "lib"
403+
updated_files = []
403404
for source_file in ["*.h", "*.c"]:
404405
for file_path in glob.glob(str(expat_source_dir / source_file)):
405406
target_file = expat_dir / pathlib.Path(file_path).name
406407
# Remove old file if it exists
407408
if target_file.exists():
408409
target_file.unlink()
409410
shutil.copy2(file_path, str(expat_dir))
411+
updated_files.append(target_file)
412+
413+
# Touch all updated files to ensure make rebuilds them
414+
# (The tarball may contain files with newer timestamps)
415+
import time
416+
417+
now = time.time()
418+
for target_file in updated_files:
419+
os.utime(target_file, (now, now))
420+
421+
# Update SBOM with correct checksums for updated expat files
422+
from relenv.build.common import update_sbom_checksums
423+
424+
files_to_update = {}
425+
for target_file in updated_files:
426+
# SBOM uses relative paths from Python source root
427+
relative_path = f"Modules/expat/{target_file.name}"
428+
files_to_update[relative_path] = target_file
429+
430+
update_sbom_checksums(dirs.source, files_to_update)
410431

411432

412433
def build_python(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:

relenv/build/windows.py

Lines changed: 34 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -225,109 +225,44 @@ def update_expat(dirs: Dirs, env: EnvMapping) -> None:
225225
# Copy *.h and *.c to expat directory
226226
expat_lib_dir = dirs.source / "Modules" / "expat" / f"expat-{version}" / "lib"
227227
expat_dir = dirs.source / "Modules" / "expat"
228+
updated_files = []
228229
for file in glob.glob(str(expat_lib_dir / "*.h")):
229-
if expat_dir / os.path.basename(file):
230-
(expat_dir / os.path.basename(file)).unlink()
230+
target = expat_dir / os.path.basename(file)
231+
if target.exists():
232+
target.unlink()
231233
shutil.move(file, str(expat_dir))
234+
updated_files.append(target)
232235
for file in glob.glob(str(expat_lib_dir / "*.c")):
233-
if expat_dir / os.path.basename(file):
234-
(expat_dir / os.path.basename(file)).unlink()
236+
target = expat_dir / os.path.basename(file)
237+
if target.exists():
238+
target.unlink()
235239
shutil.move(file, str(expat_dir))
236-
# Update sbom.spdx.json with the correct hashes. This became a thing in 3.12
237-
# python Tools/build/generate_sbom.py doesn't work because it requires a git
238-
# repository, so we have to do it manually.
239-
if env["RELENV_PY_MAJOR_VERSION"] in ["3.12", "3.13", "3.14"]:
240-
checksums = {
241-
"Modules/expat/expat.h": [
242-
{
243-
"algorithm": "SHA1",
244-
"checksumValue": "a4395dd0589a97aab0904f7a5f5dc5781a086aa2",
245-
},
246-
{
247-
"algorithm": "SHA256",
248-
"checksumValue": "610b844bbfa3ec955772cc825db4d4db470827d57adcb214ad372d0eaf00e591",
249-
},
250-
],
251-
"Modules/expat/expat_external.h": [
252-
{
253-
"algorithm": "SHA1",
254-
"checksumValue": "8fdf2e79a7ab46a3c76c74ed7e5fe641cbef308d",
255-
},
256-
{
257-
"algorithm": "SHA256",
258-
"checksumValue": "ffb960af48b80935f3856a16e87023524b104f7fc1e58104f01db88ba7bfbcc9",
259-
},
260-
],
261-
"Modules/expat/internal.h": [
262-
{
263-
"algorithm": "SHA1",
264-
"checksumValue": "7dce7d98943c5db33ae05e54801dcafb4547b9dd",
265-
},
266-
{
267-
"algorithm": "SHA256",
268-
"checksumValue": "6bfe307d52e7e4c71dbc30d3bd902a4905cdd83bbe4226a7e8dfa8e4c462a157",
269-
},
270-
],
271-
"Modules/expat/refresh.sh": [
272-
{
273-
"algorithm": "SHA1",
274-
"checksumValue": "71812ca27328697a8dcae1949cd638717538321a",
275-
},
276-
{
277-
"algorithm": "SHA256",
278-
"checksumValue": "64fd1368de41e4ebc14593c65f5a676558aed44bd7d71c43ae05d06f9086d3b0",
279-
},
280-
],
281-
"Modules/expat/xmlparse.c": [
282-
{
283-
"algorithm": "SHA1",
284-
"checksumValue": "4c81a1f04fc653877c63c834145c18f93cd95f3e",
285-
},
286-
{
287-
"algorithm": "SHA256",
288-
"checksumValue": "04a379615f476d55f95ca1853107e20627b48ca4afe8d0fd5981ac77188bf0a6",
289-
},
290-
],
291-
"Modules/expat/xmlrole.h": [
292-
{
293-
"algorithm": "SHA1",
294-
"checksumValue": "ac2964cca107f62dd133bfd4736a9a17defbc401",
295-
},
296-
{
297-
"algorithm": "SHA256",
298-
"checksumValue": "92e41f373b67f6e0dcd7735faef3c3f1e2c17fe59e007e6b74beef6a2e70fa88",
299-
},
300-
],
301-
"Modules/expat/xmltok.c": [
302-
{
303-
"algorithm": "SHA1",
304-
"checksumValue": "1e2d35d90a1c269217f83d3bdf3c71cc22cb4c3f",
305-
},
306-
{
307-
"algorithm": "SHA256",
308-
"checksumValue": "98d0fc735041956cc2e7bbbe2fb8f03130859410e0aee5e8015f406a37c02a3c",
309-
},
310-
],
311-
"Modules/expat/xmltok.h": [
312-
{
313-
"algorithm": "SHA1",
314-
"checksumValue": "d126831eaa5158cff187a8c93f4bc1c8118f3b17",
315-
},
316-
{
317-
"algorithm": "SHA256",
318-
"checksumValue": "91bf003a725a675761ea8d92cebc299a76fd28c3a950572f41bc7ce5327ee7b5",
319-
},
320-
],
321-
}
322-
spdx_json = dirs.source / "Misc" / "sbom.spdx.json"
323-
with open(str(spdx_json), "r") as f:
324-
data = json.load(f)
325-
for file in data["files"]:
326-
if file["fileName"] in checksums.keys():
327-
print(file["fileName"])
328-
file["checksums"] = checksums[file["fileName"]]
329-
with open(str(spdx_json), "w") as f:
330-
json.dump(data, f, indent=2)
240+
updated_files.append(target)
241+
242+
# Touch all updated files to ensure MSBuild rebuilds them
243+
# (The original files may have newer timestamps)
244+
import time
245+
246+
now = time.time()
247+
for target_file in updated_files:
248+
os.utime(target_file, (now, now))
249+
250+
# Update SBOM with correct checksums for updated expat files
251+
# Map SBOM file names to actual file paths
252+
from relenv.build.common import update_sbom_checksums
253+
254+
files_to_update = {}
255+
for target_file in updated_files:
256+
# SBOM uses relative paths from Python source root
257+
relative_path = f"Modules/expat/{target_file.name}"
258+
files_to_update[relative_path] = target_file
259+
260+
# Also include refresh.sh which was patched
261+
bash_refresh = dirs.source / "Modules" / "expat" / "refresh.sh"
262+
if bash_refresh.exists():
263+
files_to_update["Modules/expat/refresh.sh"] = bash_refresh
264+
265+
update_sbom_checksums(dirs.source, files_to_update)
331266

332267

333268
def build_python(env: EnvMapping, dirs: Dirs, logfp: IO[str]) -> None:

tests/test_verify_build.py

Lines changed: 122 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,20 +2098,31 @@ def test_install_setuptools_25_2_to_25_3(pipexec, build, minor_version, pip_vers
20982098
)
20992099

21002100

2101-
@pytest.mark.skip_unless_on_windows
2102-
def test_expat_version_windows(pyexec):
2101+
def test_expat_version(pyexec):
21032102
"""
2104-
Verify that the Windows build contains the correct expat version.
2103+
Verify that the build contains the correct expat version.
2104+
2105+
This validates that update_expat() successfully updated the bundled
2106+
expat library to match the version in python-versions.json.
21052107
2106-
This validates that update_expat() in windows.py successfully updated
2107-
the bundled expat library to match the version in python-versions.json.
2108+
Works on all platforms: Linux, Darwin (macOS), and Windows.
21082109
"""
21092110
from relenv.build.common import get_dependency_version
21102111

2112+
# Map sys.platform to relenv platform names
2113+
platform_map = {
2114+
"linux": "linux",
2115+
"darwin": "darwin",
2116+
"win32": "win32",
2117+
}
2118+
platform = platform_map.get(sys.platform)
2119+
if not platform:
2120+
pytest.skip(f"Unknown platform: {sys.platform}")
2121+
21112122
# Get expected version from python-versions.json
2112-
expat_info = get_dependency_version("expat", "win32")
2123+
expat_info = get_dependency_version("expat", platform)
21132124
if not expat_info:
2114-
pytest.skip("No expat version defined in python-versions.json for win32")
2125+
pytest.skip(f"No expat version defined in python-versions.json for {platform}")
21152126

21162127
expected_version = expat_info["version"]
21172128

@@ -2132,6 +2143,109 @@ def test_expat_version_windows(pyexec):
21322143
actual_version = actual_version_str.replace("expat_", "").replace("_", ".")
21332144

21342145
assert actual_version == expected_version, (
2135-
f"Expat version mismatch: expected {expected_version}, "
2146+
f"Expat version mismatch on {platform}: expected {expected_version}, "
2147+
f"found {actual_version} (from {actual_version_str})"
2148+
)
2149+
2150+
2151+
def test_sqlite_version(pyexec):
2152+
"""
2153+
Verify that the build contains the correct SQLite version.
2154+
2155+
This validates that SQLite was built with the version specified
2156+
in python-versions.json.
2157+
2158+
Works on all platforms: Linux, Darwin (macOS), and Windows.
2159+
"""
2160+
from relenv.build.common import get_dependency_version
2161+
2162+
# Map sys.platform to relenv platform names
2163+
platform_map = {
2164+
"linux": "linux",
2165+
"darwin": "darwin",
2166+
"win32": "win32",
2167+
}
2168+
platform = platform_map.get(sys.platform)
2169+
if not platform:
2170+
pytest.skip(f"Unknown platform: {sys.platform}")
2171+
2172+
# Get expected version from python-versions.json
2173+
sqlite_info = get_dependency_version("sqlite", platform)
2174+
if not sqlite_info:
2175+
pytest.skip(f"No sqlite version defined in python-versions.json for {platform}")
2176+
2177+
expected_version = sqlite_info["version"]
2178+
2179+
# Get actual version from the build
2180+
proc = subprocess.run(
2181+
[str(pyexec), "-c", "import sqlite3; print(sqlite3.sqlite_version)"],
2182+
capture_output=True,
2183+
check=True,
2184+
)
2185+
2186+
actual_version = proc.stdout.decode().strip()
2187+
2188+
# SQLite version in JSON is like "3.50.4.0" but runtime shows "3.50.4"
2189+
# So we need to handle both formats
2190+
if expected_version.count(".") == 3:
2191+
# Remove trailing .0 for comparison
2192+
expected_version = ".".join(expected_version.split(".")[:3])
2193+
2194+
assert actual_version == expected_version, (
2195+
f"SQLite version mismatch on {platform}: expected {expected_version}, "
2196+
f"found {actual_version}"
2197+
)
2198+
2199+
2200+
def test_openssl_version(pyexec):
2201+
"""
2202+
Verify that the build contains the correct OpenSSL version.
2203+
2204+
This validates that OpenSSL was built with the version specified
2205+
in python-versions.json.
2206+
2207+
Works on all platforms: Linux, Darwin (macOS), and Windows.
2208+
"""
2209+
import re
2210+
2211+
from relenv.build.common import get_dependency_version
2212+
2213+
# Map sys.platform to relenv platform names
2214+
platform_map = {
2215+
"linux": "linux",
2216+
"darwin": "darwin",
2217+
"win32": "win32",
2218+
}
2219+
platform = platform_map.get(sys.platform)
2220+
if not platform:
2221+
pytest.skip(f"Unknown platform: {sys.platform}")
2222+
2223+
# Get expected version from python-versions.json
2224+
openssl_info = get_dependency_version("openssl", platform)
2225+
if not openssl_info:
2226+
pytest.skip(
2227+
f"No openssl version defined in python-versions.json for {platform}"
2228+
)
2229+
2230+
expected_version = openssl_info["version"]
2231+
2232+
# Get actual version from the build
2233+
proc = subprocess.run(
2234+
[str(pyexec), "-c", "import ssl; print(ssl.OPENSSL_VERSION)"],
2235+
capture_output=True,
2236+
check=True,
2237+
)
2238+
2239+
actual_version_str = proc.stdout.decode().strip()
2240+
# Format is "OpenSSL 3.5.4 30 Sep 2025"
2241+
# Extract version number
2242+
match = re.search(r"OpenSSL (\d+\.\d+\.\d+)", actual_version_str)
2243+
if not match:
2244+
pytest.fail(f"Could not parse OpenSSL version from: {actual_version_str}")
2245+
2246+
actual_version = match.group(1)
2247+
2248+
assert actual_version == expected_version, (
2249+
f"OpenSSL version mismatch on {platform}: expected {expected_version}, "
21362250
f"found {actual_version} (from {actual_version_str})"
21372251
)

0 commit comments

Comments
 (0)