Skip to content

Commit edb686a

Browse files
authored
Move snapshot tests into workerd (#5205)
Gives us test coverage for memory snapshots in workerd
1 parent 45e6532 commit edb686a

File tree

12 files changed

+163
-36
lines changed

12 files changed

+163
-36
lines changed

build/wd_test.bzl

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,20 @@ SH_RUNTEST_NORMAL = """"$@" -dTEST_TMPDIR=$TEST_TMPDIR"""
113113
# invoke workerd twice for snapshot tests.
114114

115115
WINDOWS_RUNTEST_SNAPSHOT = """
116-
powershell -Command \"%* --python-save-snapshot\" `-dTEST_TMPDIR=$ENV:TEST_TMPDIR
116+
powershell -Command \"%* --python-save-snapshot $ENV:PYTHON_SAVE_SNAPSHOT_ARGS\" `-dTEST_TMPDIR=$ENV:TEST_TMPDIR
117117
set TEST_EXIT=!ERRORLEVEL!
118118
if !TEST_EXIT! EQU 0 (
119-
powershell -Command \"%* --python-load-snapshot\" `-dTEST_TMPDIR=$ENV:TEST_TMPDIR
119+
powershell -Command \"%* --python-load-snapshot snapshot.bin\" `-dTEST_TMPDIR=$ENV:TEST_TMPDIR
120120
set TEST_EXIT=!ERRORLEVEL!
121121
)
122122
"""
123123

124124
SH_RUNTEST_SNAPSHOT = """
125125
echo Creating Python Snapshot
126-
"$@" -dTEST_TMPDIR=$TEST_TMPDIR --python-save-snapshot
126+
"$@" -dTEST_TMPDIR=$TEST_TMPDIR --python-save-snapshot $PYTHON_SAVE_SNAPSHOT_ARGS
127127
echo ""
128128
echo Using Python Snapshot
129-
"$@" -dTEST_TMPDIR=$TEST_TMPDIR --python-load-snapshot
129+
"$@" -dTEST_TMPDIR=$TEST_TMPDIR --python-load-snapshot snapshot.bin
130130
"""
131131

132132
def _wd_test_impl(ctx):
@@ -191,12 +191,23 @@ def _wd_test_impl(ctx):
191191
# Include all file types that might contain testable code
192192
extensions = ["cc", "c++", "cpp", "cxx", "c", "h", "hh", "hpp", "hxx", "inc", "js", "ts", "mjs", "wd-test", "capnp"],
193193
)
194+
environment = {}
195+
if ctx.attr.python_snapshot_test:
196+
environment["PYTHON_SAVE_SNAPSHOT_ARGS"] = ""
197+
if ctx.attr.load_snapshot:
198+
if ctx.attr.python_snapshot_test:
199+
environment["PYTHON_SAVE_SNAPSHOT_ARGS"] = "--python-load-snapshot load_snapshot.bin"
200+
f = ctx.attr.load_snapshot.files.to_list()[0]
201+
runfiles = runfiles.merge(ctx.runfiles(symlinks = {"load_snapshot.bin": f}))
194202

195203
return [
196204
DefaultInfo(
197205
executable = executable,
198206
runfiles = runfiles,
199207
),
208+
RunEnvironmentInfo(
209+
environment = environment,
210+
),
200211
instrumented_files_info,
201212
]
202213

@@ -259,6 +270,7 @@ _wd_test = rule(
259270
default = "//src/workerd/api/node/tests:sidecar-supervisor",
260271
),
261272
"python_snapshot_test": attr.bool(),
273+
"load_snapshot": attr.label(allow_single_file = True),
262274
# A reference to the Windows platform label, needed for the implementation of wd_test
263275
"_platforms_os_windows": attr.label(default = "@platforms//os:windows"),
264276
},

src/workerd/api/pyodide/pyodide.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ struct PythonConfig {
6161
const PyodidePackageManager pyodidePackageManager;
6262
bool createSnapshot;
6363
bool createBaselineSnapshot;
64-
bool loadSnapshotFromDisk;
64+
kj::Maybe<kj::String> loadSnapshotFromDisk;
6565
};
6666

6767
// A function to read a segment of the tar file into a buffer

src/workerd/server/server.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ class Server final: private kj::TaskSet::ErrorHandler {
7878
void setPythonCreateBaselineSnapshot() {
7979
pythonConfig.createBaselineSnapshot = true;
8080
}
81-
void setPythonLoadSnapshot() {
82-
pythonConfig.loadSnapshotFromDisk = true;
81+
void setPythonLoadSnapshot(kj::String snapshot) {
82+
pythonConfig.loadSnapshotFromDisk = kj::mv(snapshot);
8383
}
8484

8585
// Runs the server using the given config.
@@ -126,7 +126,7 @@ class Server final: private kj::TaskSet::ErrorHandler {
126126
.pyodideDiskCacheRoot = kj::none,
127127
.createSnapshot = false,
128128
.createBaselineSnapshot = false,
129-
.loadSnapshotFromDisk = false};
129+
.loadSnapshotFromDisk = kj::none};
130130

131131
bool experimental = false;
132132

src/workerd/server/tests/python/BUILD.bazel

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
1-
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
21
load("//src/workerd/server/tests/python:import_tests.bzl", "gen_import_tests", "gen_rust_import_tests")
3-
load("//src/workerd/server/tests/python:py_wd_test.bzl", "py_wd_test")
4-
5-
# pyodide_dev.capnp.bin represents a custom pyodide version "dev" that is generated
6-
# at build time using the latest contents of the src/pyodide directory.
7-
# This is used to run tests to ensure that they are always run against the latest build of
8-
# the Pyodide bundle.
9-
copy_file(
10-
name = "pyodide_dev.capnp.bin@rule",
11-
src = "//src/pyodide:pyodide.capnp.bin_cross",
12-
out = "pyodide-bundle-cache/pyodide_dev.capnp.bin",
13-
visibility = ["//visibility:public"],
14-
)
2+
load("//src/workerd/server/tests/python:py_wd_test.bzl", "py_wd_test", "python_test_setup")
3+
4+
python_test_setup()
155

166
py_wd_test("hello")
177

@@ -70,3 +60,15 @@ py_wd_test("vendor_dir_compat_flag")
7060
py_wd_test("multiprocessing")
7161

7262
py_wd_test("default-class-with-legacy-global-handlers")
63+
64+
py_wd_test(
65+
"fastapi",
66+
make_snapshot = False,
67+
use_snapshot = "fastapi",
68+
)
69+
70+
py_wd_test(
71+
"numpy",
72+
make_snapshot = False,
73+
use_snapshot = "numpy",
74+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Workerd = import "/workerd/workerd.capnp";
2+
3+
const unitTests :Workerd.Config = (
4+
services = [
5+
( name = "fastapi",
6+
worker = (
7+
modules = [
8+
(name = "worker.py", pythonModule = embed "worker.py"),
9+
(name = "fastapi", pythonRequirement = "fastapi")
10+
],
11+
compatibilityDate = "2025-08-14",
12+
compatibilityFlags = [%PYTHON_FEATURE_FLAGS],
13+
)
14+
),
15+
],
16+
);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import fastapi
2+
from workers import WorkerEntrypoint
3+
4+
5+
class Default(WorkerEntrypoint):
6+
def test(self):
7+
assert fastapi.__version__ in {"0.110.0", "0.116.1"}

src/workerd/server/tests/python/import_tests.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def _test(name, directory, wd_test, py_file, python_version, **kwds):
4545
python_flags = [python_version],
4646
make_snapshot = False,
4747
args = ["--experimental", "--pyodide-package-disk-cache-dir", "../all_pyodide_wheels_%s" % pkg_tag],
48+
skip_default_data = True,
4849
data = [py_file, "@all_pyodide_wheels_%s//:whls" % pkg_tag],
4950
**kwds
5051
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Workerd = import "/workerd/workerd.capnp";
2+
3+
const unitTests :Workerd.Config = (
4+
services = [
5+
( name = "numpy",
6+
worker = (
7+
modules = [
8+
(name = "worker.py", pythonModule = embed "worker.py"),
9+
(name = "numpy", pythonRequirement = "numpy")
10+
],
11+
compatibilityDate = "2025-08-14",
12+
compatibilityFlags = [%PYTHON_FEATURE_FLAGS],
13+
)
14+
),
15+
],
16+
);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import numpy as np
2+
from workers import WorkerEntrypoint
3+
4+
5+
class Default(WorkerEntrypoint):
6+
def test(self):
7+
res = np.arange(12).reshape((3, -1))[::-2, ::-2]
8+
assert str(res) == "[[11 9]\n [ 3 1]]"

src/workerd/server/tests/python/py_wd_test.bzl

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
12
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
23
load("//:build/python_metadata.bzl", "BUNDLE_VERSION_INFO")
34
load("//:build/wd_test.bzl", "wd_test")
@@ -12,16 +13,29 @@ def _py_wd_test_helper(
1213
name,
1314
src,
1415
python_flag,
15-
make_snapshot,
1616
*,
17-
args = [],
17+
make_snapshot,
18+
use_snapshot,
19+
args,
20+
data = [],
1821
**kwargs):
1922
name_flag = name + "_" + python_flag
2023
templated_src = name_flag.replace("/", "-") + "@template"
2124
templated_src = "/".join(src.split("/")[:-1] + [templated_src])
2225
flags = _get_enable_flags(python_flag)
2326
feature_flags_txt = ",".join(['"{}"'.format(flag) for flag in flags])
2427

28+
load_snapshot = None
29+
if use_snapshot:
30+
version_info = BUNDLE_VERSION_INFO[python_flag]
31+
32+
snapshot = version_info[use_snapshot + "_snapshot"]
33+
data = data + [":python_snapshots"]
34+
load_snapshot = snapshot
35+
36+
if load_snapshot and not make_snapshot:
37+
args += ["--python-load-snapshot", "load_snapshot.bin"]
38+
2539
expand_template(
2640
name = name_flag + "@rule",
2741
out = templated_src,
@@ -34,9 +48,56 @@ def _py_wd_test_helper(
3448
name = name_flag + "@",
3549
args = args,
3650
python_snapshot_test = make_snapshot,
51+
data = data,
52+
load_snapshot = load_snapshot,
3753
**kwargs
3854
)
3955

56+
def _snapshot_file(snapshot):
57+
if not snapshot:
58+
return []
59+
copy_file(
60+
name = "pyodide-snapshot-%s@copy" % snapshot,
61+
src = "@pyodide-snapshot-%s//file" % snapshot,
62+
out = snapshot,
63+
visibility = ["//visibility:public"],
64+
)
65+
return [":" + snapshot]
66+
67+
def _snapshot_files(
68+
baseline_snapshot = None,
69+
numpy_snapshot = None,
70+
fastapi_snapshot = None,
71+
**_kwds):
72+
result = []
73+
result += _snapshot_file(baseline_snapshot)
74+
result += _snapshot_file(numpy_snapshot)
75+
result += _snapshot_file(fastapi_snapshot)
76+
return result
77+
78+
def python_test_setup():
79+
# pyodide_dev.capnp.bin represents a custom pyodide version "dev" that is generated
80+
# at build time using the latest contents of the src/pyodide directory.
81+
# This is used to run tests to ensure that they are always run against the latest build of
82+
# the Pyodide bundle.
83+
copy_file(
84+
name = "pyodide_dev.capnp.bin@rule",
85+
src = "//src/pyodide:pyodide.capnp.bin_cross",
86+
out = "pyodide-bundle-cache/pyodide_dev.capnp.bin",
87+
visibility = ["//visibility:public"],
88+
)
89+
data = []
90+
for x in BUNDLE_VERSION_INFO.values():
91+
if x["name"] == "development":
92+
continue
93+
data += _snapshot_files(**x)
94+
95+
native.filegroup(
96+
name = "python_snapshots",
97+
data = data,
98+
visibility = ["//visibility:public"],
99+
)
100+
40101
def py_wd_test(
41102
directory = None,
42103
*,
@@ -49,12 +110,16 @@ def py_wd_test(
49110
size = "enormous",
50111
tags = [],
51112
make_snapshot = True,
113+
use_snapshot = None,
114+
skip_default_data = False,
52115
**kwargs):
53116
if python_flags == "all":
54117
python_flags = BUNDLE_VERSION_INFO.keys()
55118
python_flags = [flag for flag in python_flags if flag not in skip_python_flags and flag in BUNDLE_VERSION_INFO]
56-
if data == None and directory != None:
57-
data = native.glob(
119+
if data == None:
120+
data = []
121+
if directory and not skip_default_data:
122+
data += native.glob(
58123
[
59124
directory + "/**",
60125
],
@@ -82,8 +147,10 @@ def py_wd_test(
82147
src,
83148
python_flag,
84149
make_snapshot = make_snapshot,
150+
use_snapshot = use_snapshot,
85151
data = data,
86152
args = args,
87153
size = size,
88154
tags = tags,
155+
**kwargs
89156
)

0 commit comments

Comments
 (0)