Skip to content

Add flaky pytest infrastructure and weekend runners #1799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bb4634e
Add flaky test infra in `make pytest`
paul0403 Jun 10, 2025
931db6a
5 runs
paul0403 Jun 10, 2025
d61ea62
add weekly flaky test runner script
paul0403 Jun 10, 2025
cc61117
try skipping cuda
paul0403 Jun 10, 2025
298a9ab
don't ask when pip uninstall
paul0403 Jun 10, 2025
41ca911
testpypi catalyst follows dep versions already
paul0403 Jun 10, 2025
76eab9d
Merge remote-tracking branch 'origin/main' into paul0403/add_flaky_runs
paul0403 Jun 11, 2025
ec82572
Merge remote-tracking branch 'origin/main' into paul0403/add_flaky_runs
paul0403 Jun 11, 2025
fdcd8f3
set read-only permission on job
paul0403 Jun 11, 2025
b61f2db
popping capabilities should be per custom device class, NOT per insta…
paul0403 Jun 11, 2025
a981a4a
remove unused flaky import
paul0403 Jun 11, 2025
91f9c47
Merge remote-tracking branch 'origin/main' into paul0403/add_flaky_runs
paul0403 Jun 11, 2025
38b8753
add reset fixture to autograph `Failing` test util class
paul0403 Jun 11, 2025
35feb0f
add failure notif
paul0403 Jun 11, 2025
4d78c8e
codefactor
paul0403 Jun 11, 2025
a804c32
test failure notif
paul0403 Jun 11, 2025
29e82b8
remove temporary PR testings
paul0403 Jun 11, 2025
d2f87e1
Merge remote-tracking branch 'origin/main' into paul0403/add_flaky_runs
paul0403 Jun 12, 2025
86755fb
remove unnecessary line
paul0403 Jun 12, 2025
05421ef
add docstring for the mysterious `Failing` class in autograph test
paul0403 Jun 12, 2025
6b6a564
test_whileloop_no_warning passes
paul0403 Jun 13, 2025
de5e9ab
remove unused argument
paul0403 Jun 13, 2025
b6f27f9
Merge remote-tracking branch 'origin/main' into paul0403/add_flaky_runs
paul0403 Jun 13, 2025
e4f5a79
Update frontend/test/pytest/test_autograph.py
paul0403 Jun 13, 2025
c8f7956
line too long
paul0403 Jun 13, 2025
d26d043
remove test_whileloop_no_warning
paul0403 Jun 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/check-weekly-flaky.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Run Catalyst pytests for weekly flaky test checking

permissions:
contents: read

on:
schedule:
# Run every weekend on Saturday at 23:40 EDT (cron is in UTC)
# https://crontab.cronhub.io/
- cron: "40 23 * * SAT"

jobs:
weekly-flaky-test-run:
name: Run weekly flaky tests
runs-on: ubuntu-24.04

steps:
- name: Checkout Catalyst repo
uses: actions/checkout@v4

- name: Install Deps
run: |
python3 -m pip install -r requirements.txt
pip install --pre -U --extra-index-url https://test.pypi.org/simple/ PennyLane-Catalyst

- name: Run Python Pytest Tests with flaky Checks
run: |
make pytest ENABLE_FLAKY=ON
1 change: 1 addition & 0 deletions .github/workflows/notify-failed-jobs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- Build Catalyst Wheel on Linux (x86_64)
- Build Catalyst Wheel on macOS (arm64)
- Build nightly Catalyst releases for TestPyPI
- Run Catalyst pytests for weekly flaky test checking

jobs:
on-failure:
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ TEST_BACKEND ?= "lightning.qubit"
TEST_BRAKET ?= NONE
ENABLE_ASAN ?= OFF
TOML_SPECS ?= $(shell find ./runtime ./frontend -name '*.toml' -not -name 'pyproject.toml')
ENABLE_FLAKY ?= OFF

PLATFORM := $(shell uname -s)
ifeq ($(PLATFORM),Linux)
Expand Down Expand Up @@ -54,7 +55,11 @@ endif
# with the ASAN runtime. Since we don't exert much control over the "user" compiler, skip them.
TEST_EXCLUDES := -k "not test_executable_generation"
endif
PYTEST_FLAGS := $(PARALLELIZE) $(TEST_EXCLUDES)
FLAKY :=
ifeq ($(ENABLE_FLAKY),ON)
FLAKY := --force-flaky --max-runs=5 --min-passes=5
endif
PYTEST_FLAGS := $(PARALLELIZE) $(TEST_EXCLUDES) $(FLAKY)

# TODO: Find out why we have container overflow on macOS.
ASAN_OPTIONS := ASAN_OPTIONS="detect_leaks=0,detect_container_overflow=0"
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/pytest/device/test_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ class NoUnitaryDevice(qml.devices.Device):

def __init__(self, shots=None, wires=None):
super().__init__(wires=wires, shots=shots)
self.capabilities.operations.pop("QubitUnitary")
self.qjit_capabilities = self.capabilities

def apply(self, operations, **kwargs):
Expand All @@ -113,6 +112,9 @@ def execute(self, circuits, execution_config):
return circuits, execution_config


NoUnitaryDevice.capabilities.operations.pop("QubitUnitary")


class TestControlledDecomposition:
"""Test behaviour around the decomposition of the `Controlled` class."""

Expand Down
51 changes: 35 additions & 16 deletions frontend/test/pytest/test_autograph.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,34 @@


class Failing:
"""Test class that emulates failures in user-code"""
"""
Test class that emulates failures in user-code.

When autograph fails to convert, it will fall back to native python control flow execution.
In such cases autograph would raise a warning.

We want to test that this warning is properly raised, but if we use an actual error to trigger
the autograph fallback, the fallback would also fail. Therefore, we raise an exception in a
controlled fashion.

This Failing class has a class-level dictionary to keep track of what labels it has already
seen. When it sees a new label for the first time, it raises an exception. In all other cases,
it just silently lets the input value flow through.

For example, consider this code
@qjit(autograph=True)
def f1():
acc = 0
while acc < 5:
acc = Failing(acc, "while").val + 1
return acc

When tracing, the first Failing instance will encounter an unseen label "while".
This raises an exception and fails autograph, causing it to fallback.
Then in the actual python while loop, future Failing instances will encounter the same "while"
label. Since the label is not new, no new exception is raised, and the test finishes without
errors.
"""

triggered = defaultdict(bool)

Expand All @@ -73,6 +100,13 @@ def val(self):
return self.ref


@pytest.fixture(autouse=True)
def reset_Failing():
"""Reset class variable on `Failing` class before each test"""
Failing.triggered = defaultdict(bool)
yield


class TestSourceCodeInfo:
"""Unit tests for exception utilities that retrieves traceback information for the original
source code."""
Expand Down Expand Up @@ -1426,21 +1460,6 @@ def f1():

assert f1() == sum([1, 1, 2, 2])

@pytest.mark.xfail(reason="this won't run warning-free until we fix the resource warning issue")
@pytest.mark.filterwarnings("error")
def test_whileloop_no_warning(self, monkeypatch):
"""Test the absence of warnings if fallbacks are ignored."""
monkeypatch.setattr("catalyst.autograph_ignore_fallbacks", True)

@qjit(autograph=True)
def f():
acc = 0
while Failing(acc).val < 5:
acc = acc + 1
return acc

assert f() == 5

def test_whileloop_exception(self, monkeypatch):
"""Test for-loop error if strict-conversion is enabled."""
monkeypatch.setattr("catalyst.autograph_strict_conversion", True)
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/pytest/test_measurement_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ class CustomDevice(Device):

def __init__(self, wires, shots=1024):
super().__init__(wires=wires, shots=shots)
self.capabilities.operations.pop("BlockEncode")

@staticmethod
def get_c_interface():
Expand All @@ -69,6 +68,9 @@ def execute(self, circuits, execution_config):
return circuits, execution_config


CustomDevice.capabilities.operations.pop("BlockEncode")


class CustomDeviceLimitedMPs(Device):
"""A Custom Device from the device API without wires."""

Expand Down
4 changes: 3 additions & 1 deletion frontend/test/pytest/test_preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ class CustomDevice(Device):

def __init__(self, wires, shots=1024):
super().__init__(wires=wires, shots=shots)
self.capabilities.operations.pop("BlockEncode")
self.qjit_capabilities = self.capabilities

@staticmethod
Expand All @@ -106,6 +105,9 @@ def execute(self, circuits, execution_config):
raise NotImplementedError


CustomDevice.capabilities.operations.pop("BlockEncode")


class TestDecomposition:
"""Test the preprocessing transforms implemented in Catalyst."""

Expand Down