Skip to content

Commit 5a784b9

Browse files
committed
[GR-66789] Move downstream tests to GitHub actions
PullRequest: graalpython/3874
2 parents 8fa427e + 59fb1f7 commit 5a784b9

File tree

5 files changed

+222
-116
lines changed

5 files changed

+222
-116
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Weekly Downstream Tests
2+
on:
3+
schedule:
4+
- cron: '0 0 * * 1'
5+
workflow_dispatch:
6+
7+
jobs:
8+
downstream-tests:
9+
strategy:
10+
fail-fast: false
11+
matrix:
12+
name:
13+
- pybind11
14+
- virtualenv
15+
# Currently fails
16+
# - pyo3
17+
os:
18+
- id: ubuntu-latest
19+
graalpy_platform: linux-amd64
20+
- id: macos-latest
21+
graalpy_platform: darwin-aarch64
22+
23+
runs-on: ${{ matrix.os.id }}
24+
25+
steps:
26+
- name: Install CMake
27+
if: ${{ matrix.name == 'pybind11' }}
28+
uses: lukka/get-cmake@latest
29+
with:
30+
cmakeVersion: 3.30.8
31+
ninjaVersion: 1.12.1
32+
33+
- name: Install Rust toolchain
34+
if: ${{ matrix.name == 'pyo3' }}
35+
uses: actions-rs/toolchain@v1
36+
with:
37+
toolchain: stable
38+
39+
- name: Checkout main repository
40+
uses: actions/checkout@v4
41+
42+
- name: Get GraalPy EA build
43+
run: |
44+
tarball="$(curl -sfL https://raw.githubusercontent.com/graalvm/graal-languages-ea-builds/refs/heads/main/graalpy/versions/latest-native-${{ matrix.os.graalpy_platform }}.url)"
45+
curl -sfL "$tarball" | tar xz
46+
47+
- name: Run downstream tests for ${{ matrix.name }}
48+
run: python mx.graalpython/downstream_tests.py graalpy-*/bin/python ${{ matrix.name }}

ci.jsonnet

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@
7272
local cov_jacoco_base = self.cov_jacoco_gate_base,
7373
local cov_truffle = self.cov_truffle_gate,
7474
local watchdog = self.watchdog,
75-
local downstream_tests_gate = self.downstream_tests_gate,
7675
local bench_task(bench=null, benchmarks=BENCHMARKS) = super.bench_task(bench=bench, benchmarks=benchmarks),
7776
local bisect_bench_task = self.bisect_bench_task,
7877

@@ -341,14 +340,6 @@
341340
"tox-example": gpgate_ee + require(GPYEE_NATIVE_STANDALONE) + platform_spec(no_jobs) + platform_spec({
342341
"linux:amd64:jdk-latest" : tier3 + t("01:00:00"),
343342
}),
344-
"python-downstream-tests": gpgate + platform_spec(no_jobs) + downstream_tests_gate({
345-
"project:*": {
346-
"linux:amd64:jdk-latest": daily + require(GPY_NATIVE_STANDALONE) + t("01:00:00"),
347-
},
348-
"project:virtualenv": {
349-
"darwin:aarch64:jdk-latest": weekly + t("01:00:00"),
350-
},
351-
}),
352343
"build-wheels": base_gate + platform_spec(no_jobs) + platform_spec({
353344
"windows:amd64:jdk-latest" : on_demand + t("01:00:00"),
354345
"darwin:aarch64:jdk-latest" : on_demand + t("01:00:00"),

ci/python-gate.libsonnet

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -614,21 +614,6 @@
614614
style_gate:: base_style_gate + task_spec({
615615
tags +:: ",fullbuild,python-license",
616616
}),
617-
618-
local downstream_projects = ["pybind11", "virtualenv"],
619-
local downstream_tests_feature_map = {
620-
project: {
621-
[name]: $.no_jobs {"*" +: task_spec({downstream_project:: name})}
622-
for name in downstream_projects
623-
},
624-
},
625-
626-
downstream_tests_gate(downstream_spec):: run_spec.generate_variants(downstream_spec, downstream_tests_feature_map) + task_spec({
627-
name_suffix:: [self.downstream_project],
628-
run: [
629-
["mx"] + self.mx_parameters + self.dy + self.primary_suite + ["downstream-test", self.downstream_project],
630-
],
631-
}),
632617
}
633618

634619
// Local Variables:

mx.graalpython/downstream_tests.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
import argparse
41+
import os
42+
import shlex
43+
import shutil
44+
import subprocess
45+
from pathlib import Path
46+
47+
DIR = Path(__file__).parent.parent
48+
DOWNSTREAM_TESTS = {}
49+
50+
51+
def run(*args, check=True, **kwargs):
52+
return subprocess.run(*args, check=check, **kwargs)
53+
54+
55+
def run_in_venv(venv, cmd, **kwargs):
56+
return run(['sh', '-c', f". {venv}/bin/activate && {shlex.join(cmd)}"], **kwargs)
57+
58+
59+
def downstream_test(name):
60+
def decorator(fn):
61+
DOWNSTREAM_TESTS[name] = fn
62+
return fn
63+
64+
return decorator
65+
66+
67+
@downstream_test('hpy')
68+
def downstream_test_hpy(graalpy, args=None, env=None, check=True, timeout=None):
69+
testdir = Path('upstream-tests').absolute()
70+
shutil.rmtree(testdir, ignore_errors=True)
71+
testdir.mkdir(exist_ok=True)
72+
hpy_root = DIR / "graalpython" / "hpy"
73+
shutil.copytree(hpy_root, testdir / "hpy")
74+
hpy_root = testdir / "hpy"
75+
hpy_test_root = hpy_root / "test"
76+
venv = testdir / 'hpy_venv'
77+
run([graalpy, "-m", "venv", str(venv)])
78+
run_in_venv(venv, ["pip", "install", "pytest", "pytest-xdist", "pytest-rerunfailures", "filelock"])
79+
env = env or os.environ.copy()
80+
env["SETUPTOOLS_SCM_PRETEND_VERSION"] = "0.9.0"
81+
run_in_venv(venv, ["pip", "install", "-e", "."], cwd=str(hpy_root), env=env)
82+
parallelism = str(min(os.cpu_count(), int(os.cpu_count() / 4)))
83+
args = args or []
84+
args = [
85+
"python",
86+
"--vm.ea",
87+
"--experimental-options=true",
88+
"--python.EnableDebuggingBuiltins",
89+
*args,
90+
"-m", "pytest",
91+
"-v",
92+
# for those cases where testing invalid handles corrupts the process so
93+
# much that we crash - we don't recover gracefully in some cases :(
94+
"--reruns", "3",
95+
"-n", parallelism,
96+
str(hpy_test_root),
97+
# test_distutils is just slow and testing the build infrastructure
98+
"-k", "not test_distutils"
99+
]
100+
return run_in_venv(venv, args, env=env, cwd=str(hpy_root), check=check, timeout=timeout)
101+
102+
103+
@downstream_test('pybind11')
104+
def downstream_test_pybind11(graalpy):
105+
testdir = Path('upstream-tests').absolute()
106+
shutil.rmtree(testdir, ignore_errors=True)
107+
testdir.mkdir(exist_ok=True)
108+
run(['git', 'clone', 'https://github.com/pybind/pybind11.git'], cwd=testdir)
109+
src = testdir / 'pybind11'
110+
venv = src / 'venv'
111+
run([graalpy, '-m', 'venv', str(venv)])
112+
run_in_venv(venv, ['pip', 'install', 'pytest'])
113+
run_in_venv(venv, ['cmake', '-S', '.', '-B', 'build', '-DPYBIND11_WERROR=ON'], cwd=src)
114+
# GitHub actions tend to OOM here
115+
parallel_arg = ['--parallel'] if "GITHUB_ACTIONS" not in os.environ else []
116+
run_in_venv(venv, ['cmake', '--build', 'build', *parallel_arg], cwd=src)
117+
env = os.environ.copy()
118+
env['PYTHONPATH'] = 'build/tests'
119+
run_in_venv(venv, ['pytest', '-v', '--tb=short', 'tests'], cwd=src, env=env)
120+
121+
122+
@downstream_test('virtualenv')
123+
def downstream_test_virtualenv(graalpy):
124+
testdir = Path('upstream-tests').absolute()
125+
shutil.rmtree(testdir, ignore_errors=True)
126+
testdir.mkdir(exist_ok=True)
127+
run(['git', 'clone', 'https://github.com/pypa/virtualenv.git', '-b', 'main'], cwd=testdir)
128+
src = testdir / 'virtualenv'
129+
venv = src / 'venv'
130+
run([graalpy, '-m', 'venv', str(venv)])
131+
env = os.environ.copy()
132+
env.pop('VIRTUAL_ENV_DISABLE_PROMPT', None)
133+
env['CI_RUN'] = '1'
134+
# Need to avoid pulling in graalpy seeder
135+
env['PIP_GRAALPY_DISABLE_PATCHING'] = '1'
136+
run_in_venv(venv, ['pip', 'install', f'{src}[test]'], env=env)
137+
# Don't activate the venv, it interferes with the test
138+
run([
139+
str(venv / 'bin' / 'pytest'), '-v', '--tb=short', 'tests',
140+
'-k', 'not fish and not csh and not nushell and not powershell',
141+
], cwd=src, env=env)
142+
143+
144+
@downstream_test('pyo3')
145+
def downstream_test_pyo3(graalpy):
146+
testdir = Path('upstream-tests').absolute()
147+
shutil.rmtree(testdir, ignore_errors=True)
148+
testdir.mkdir(exist_ok=True)
149+
run(['git', 'clone', 'https://github.com/PyO3/pyo3.git', '-b', 'main'], cwd=testdir)
150+
src = testdir / 'pyo3'
151+
venv = src / 'venv'
152+
run([graalpy, '-m', 'venv', str(venv)])
153+
run_in_venv(venv, ['pip', 'install', 'nox'])
154+
run_in_venv(venv, ['nox', '-s', 'test-py'], cwd=src)
155+
156+
157+
def run_downstream_test(python, project):
158+
DOWNSTREAM_TESTS[project](python)
159+
160+
161+
def main():
162+
parser = argparse.ArgumentParser("Runs important upstream packages tests using their main branch")
163+
parser.add_argument("python")
164+
parser.add_argument("project", choices=sorted(DOWNSTREAM_TESTS))
165+
args = parser.parse_args()
166+
run_downstream_test(args.python, args.project)
167+
168+
169+
if __name__ == '__main__':
170+
main()

mx.graalpython/mx_graalpython.py

Lines changed: 4 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
from typing import cast
4545

46+
import downstream_tests
4647
import mx_graalpython_benchmark
4748
import mx_graalpython_gradleproject
4849
import mx_urlrewrites
@@ -1056,7 +1057,7 @@ def run_python_unittests(python_binary, args=None, paths=None, exclude=None, env
10561057

10571058
def run_hpy_unittests(python_binary, args=None, env=None, nonZeroIsFatal=True, timeout=None, report=False):
10581059
t0 = time.time()
1059-
result = downstream_test_hpy(python_binary, args=args, env=env, nonZeroIsFatal=nonZeroIsFatal, timeout=timeout)
1060+
result = downstream_tests.downstream_test_hpy(python_binary, args=args, env=env, check=nonZeroIsFatal, timeout=timeout)
10601061
if report:
10611062
mx_gate.make_test_report([{
10621063
"name": report.title,
@@ -2626,105 +2627,16 @@ def graalpy_jmh(args):
26262627
mx.run_java(vm_args + ['org.openjdk.jmh.Main'] + args)
26272628

26282629

2629-
def run_in_venv(venv, cmd, **kwargs):
2630-
return mx.run(['sh', '-c', f". {venv}/bin/activate && {shlex.join(cmd)}"], **kwargs)
2631-
2632-
2633-
DOWNSTREAM_TESTS = {}
2634-
2635-
def downstream_test(name):
2636-
def decorator(fn):
2637-
DOWNSTREAM_TESTS[name] = fn
2638-
return fn
2639-
return decorator
2640-
2641-
2642-
@downstream_test('hpy')
2643-
def downstream_test_hpy(graalpy, args=None, env=None, nonZeroIsFatal=True, timeout=None):
2644-
testdir = Path('upstream-tests').absolute()
2645-
shutil.rmtree(testdir, ignore_errors=True)
2646-
testdir.mkdir(exist_ok=True)
2647-
hpy_root = os.path.join(mx.dependency("hpy").dir)
2648-
shutil.copytree(hpy_root, testdir / "hpy")
2649-
hpy_root = testdir / "hpy"
2650-
hpy_test_root = hpy_root / "test"
2651-
venv = testdir / 'hpy_venv'
2652-
mx.run([graalpy, "-m", "venv", str(venv)])
2653-
run_in_venv(venv, ["pip", "install", "pytest", "pytest-xdist", "pytest-rerunfailures", "filelock"])
2654-
env = env or os.environ.copy()
2655-
env["SETUPTOOLS_SCM_PRETEND_VERSION"] = "0.9.0"
2656-
run_in_venv(venv, ["pip", "install", "-e", "."], cwd=str(hpy_root), env=env)
2657-
parallelism = str(min(os.cpu_count(), int(os.cpu_count() / 4)))
2658-
args = args or []
2659-
args = [
2660-
"python",
2661-
"--vm.ea",
2662-
"--experimental-options=true",
2663-
"--python.EnableDebuggingBuiltins",
2664-
*args,
2665-
"-m", "pytest",
2666-
"-v",
2667-
# for those cases where testing invalid handles corrupts the process so
2668-
# much that we crash - we don't recover gracefully in some cases :(
2669-
"--reruns", "3",
2670-
"-n", parallelism,
2671-
str(hpy_test_root),
2672-
# test_distutils is just slow and testing the build infrastructure
2673-
"-k", "not test_distutils"
2674-
]
2675-
mx.logv(shlex.join(args))
2676-
return run_in_venv(venv, args, env=env, cwd=str(hpy_root), nonZeroIsFatal=nonZeroIsFatal, timeout=timeout)
2677-
2678-
2679-
@downstream_test('pybind11')
2680-
def downstream_test_pybind11(graalpy):
2681-
testdir = Path('upstream-tests').absolute()
2682-
shutil.rmtree(testdir, ignore_errors=True)
2683-
testdir.mkdir(exist_ok=True)
2684-
mx.run(['git', 'clone', 'https://github.com/pybind/pybind11.git'], cwd=testdir)
2685-
src = testdir / 'pybind11'
2686-
venv = src / 'venv'
2687-
mx.run([graalpy, '-m', 'venv', str(venv)])
2688-
run_in_venv(venv, ['pip', 'install', 'pytest'])
2689-
run_in_venv(venv, ['cmake', '-S', '.', '-B', 'build', '-DPYBIND11_WERROR=ON'], cwd=src)
2690-
run_in_venv(venv, ['cmake', '--build', 'build', '--parallel'], cwd=src)
2691-
env = os.environ.copy()
2692-
env['PYTHONPATH'] = 'build/tests'
2693-
run_in_venv(venv, ['pytest', '-v', '--tb=short', 'tests'], cwd=src, env=env)
2694-
2695-
2696-
@downstream_test('virtualenv')
2697-
def downstream_test_virtualenv(graalpy):
2698-
testdir = Path('upstream-tests').absolute()
2699-
shutil.rmtree(testdir, ignore_errors=True)
2700-
testdir.mkdir(exist_ok=True)
2701-
mx.run(['git', 'clone', 'https://github.com/pypa/virtualenv.git', '-b', 'main'], cwd=testdir)
2702-
src = testdir / 'virtualenv'
2703-
venv = src / 'venv'
2704-
mx.run([graalpy, '-m', 'venv', str(venv)])
2705-
env = os.environ.copy()
2706-
env.pop('VIRTUAL_ENV_DISABLE_PROMPT', None)
2707-
env['CI_RUN'] = '1'
2708-
# Need to avoid pulling in graalpy seeder
2709-
env['PIP_GRAALPY_DISABLE_PATCHING'] = '1'
2710-
run_in_venv(venv, ['pip', 'install', f'{src}[test]'], env=env)
2711-
# Don't activate the venv, it interferes with the test
2712-
mx.run([
2713-
str(venv / 'bin' / 'pytest'), '-v', '--tb=short', 'tests',
2714-
'-k', 'not fish and not csh and not nushell and not powershell',
2715-
], cwd=src, env=env)
2716-
2717-
27182630
def run_downstream_test(args):
27192631
parser = ArgumentParser(description="Runs important upstream packages tests using their main branch")
2720-
parser.add_argument('project', choices=sorted(DOWNSTREAM_TESTS))
2632+
parser.add_argument('project')
27212633
parser.add_argument('--dev', action='store_true', help="Use JVM dev standalone")
27222634
args = parser.parse_args(args)
27232635
if args.dev:
27242636
graalpy = graalpy_standalone('jvm', dev=True)
27252637
else:
27262638
graalpy = graalpy_standalone_native()
2727-
DOWNSTREAM_TESTS[args.project](graalpy)
2639+
downstream_tests.run_downstream_test(graalpy, args.project)
27282640

27292641

27302642
# ----------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)