From ab1b436c11f077190f4496dbeec06c31968978aa Mon Sep 17 00:00:00 2001 From: arettig Date: Thu, 4 Jun 2026 15:24:12 +0000 Subject: [PATCH 1/3] Retry flaky test in vpe_circuits_test.py The built in @cirq.testing.retry_once_with_later_random_values decorator will rerun the test with a new random seed. --- src/openfermion/circuits/vpe_circuits_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openfermion/circuits/vpe_circuits_test.py b/src/openfermion/circuits/vpe_circuits_test.py index 95b04a747..881c1edcc 100644 --- a/src/openfermion/circuits/vpe_circuits_test.py +++ b/src/openfermion/circuits/vpe_circuits_test.py @@ -35,6 +35,7 @@ def test_single_circuit(): assert data_counts[1] == 100 +@cirq.testing.retry_once_with_later_random_values def test_single_timestep(): q0 = cirq.GridQubit(0, 0) q1 = cirq.GridQubit(0, 1) From cc30ee24c6dfee0ecd8a493f4150102d01b39397 Mon Sep 17 00:00:00 2001 From: arettig Date: Tue, 16 Jun 2026 23:39:48 +0000 Subject: [PATCH 2/3] Add fallback for flaky decorator when Cirq version < 1.5.0 The flaky decorator was added to Cirq in version 1.5.0, so the updated flaky test would fail if a previous version of Cirq was used. A wrapper for the decorator was added to wrapped.py, which will now define it manually if an old Cirq is used. --- src/openfermion/circuits/vpe_circuits_test.py | 3 ++- src/openfermion/testing/__init__.py | 6 ++++- src/openfermion/testing/wrapped.py | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/openfermion/circuits/vpe_circuits_test.py b/src/openfermion/circuits/vpe_circuits_test.py index 881c1edcc..781af140e 100644 --- a/src/openfermion/circuits/vpe_circuits_test.py +++ b/src/openfermion/circuits/vpe_circuits_test.py @@ -15,6 +15,7 @@ import cirq from openfermion.measurements import get_phase_function +from openfermion.testing import retry_once_with_later_random_values from .vpe_circuits import vpe_single_circuit, vpe_circuits_single_timestep @@ -35,7 +36,7 @@ def test_single_circuit(): assert data_counts[1] == 100 -@cirq.testing.retry_once_with_later_random_values +@retry_once_with_later_random_values def test_single_timestep(): q0 = cirq.GridQubit(0, 0) q1 = cirq.GridQubit(0, 1) diff --git a/src/openfermion/testing/__init__.py b/src/openfermion/testing/__init__.py index 344e8add9..92b655a01 100644 --- a/src/openfermion/testing/__init__.py +++ b/src/openfermion/testing/__init__.py @@ -26,4 +26,8 @@ module_importable, ) -from .wrapped import assert_equivalent_repr, assert_implements_consistent_protocols +from .wrapped import ( + assert_equivalent_repr, + assert_implements_consistent_protocols, + retry_once_with_later_random_values, +) diff --git a/src/openfermion/testing/wrapped.py b/src/openfermion/testing/wrapped.py index 7fd8f2a19..6c5784b7e 100644 --- a/src/openfermion/testing/wrapped.py +++ b/src/openfermion/testing/wrapped.py @@ -65,3 +65,28 @@ def assert_implements_consistent_protocols( global_vals=global_vals, # coverage: ignore local_vals=local_vals, # coverage: ignore ) # coverage: ignore + + +def retry_once_with_later_random_values(testfunc: Any) -> Any: + """Marks a test function for one retry with later random values. + + This decorator is intended for test functions which occasionally fail + for specific random seeds from pytest-randomly. + """ + try: + return cirq.testing.retry_once_with_later_random_values(testfunc) + except AttributeError: + # decorator not available in cirq < 1.5.0 + import functools + import warnings + + @functools.wraps(testfunc) + def wrapped_func(*args, **kwargs) -> Any: + try: + return testfunc(*args, **kwargs) + except AssertionError: + pass + warnings.warn("Retrying in case we got a failing seed from pytest-randomly.") + return testfunc(*args, **kwargs) + + return wrapped_func From b3e4a19a1393b700a4e8e9cfbdf3b122ab4c6e36 Mon Sep 17 00:00:00 2001 From: mhucka Date: Wed, 17 Jun 2026 02:21:53 +0000 Subject: [PATCH 3/3] Add coverage tests for retry_once_with_later_random_values --- src/openfermion/testing/wrapped_test.py | 83 +++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/openfermion/testing/wrapped_test.py diff --git a/src/openfermion/testing/wrapped_test.py b/src/openfermion/testing/wrapped_test.py new file mode 100644 index 000000000..fb340af23 --- /dev/null +++ b/src/openfermion/testing/wrapped_test.py @@ -0,0 +1,83 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from unittest import mock +import openfermion.testing.wrapped as wrapped + + +class MockCirqTesting: + @property + def retry_once_with_later_random_values(self): + raise AttributeError("No such attribute") + + +class MockCirq: + testing = MockCirqTesting() + + +def test_retry_once_fallback_success(): + with mock.patch('openfermion.testing.wrapped.cirq', MockCirq()): + + call_count = 0 + + @wrapped.retry_once_with_later_random_values + def successful_test(): + nonlocal call_count + call_count += 1 + return "Success" + + assert successful_test() == "Success" + assert call_count == 1 + + +def test_retry_once_fallback_flaky(): + with mock.patch('openfermion.testing.wrapped.cirq', MockCirq()): + + call_count = 0 + + @wrapped.retry_once_with_later_random_values + def flaky_test(): + nonlocal call_count + call_count += 1 + if call_count == 1: + raise AssertionError("Failed first time") + return "Success" + + with pytest.warns( + UserWarning, match="Retrying in case we got a failing seed from pytest-randomly." + ): + assert flaky_test() == "Success" + + assert call_count == 2 + + +def test_retry_once_fallback_failure(): + with mock.patch('openfermion.testing.wrapped.cirq', MockCirq()): + + call_count = 0 + + @wrapped.retry_once_with_later_random_values + def failing_test(): + nonlocal call_count + call_count += 1 + raise AssertionError("Failed both times") + + with pytest.warns( + UserWarning, match="Retrying in case we got a failing seed from pytest-randomly." + ): + with pytest.raises(AssertionError, match="Failed both times"): + failing_test() + + assert call_count == 2