From e5590e94683517f5b708fced00fb88b4c5ee28e0 Mon Sep 17 00:00:00 2001 From: Chris Rossi Date: Fri, 7 Feb 2020 10:57:28 -0500 Subject: [PATCH] fix: attempt to have fewer transient errors in continuous integration We have been having more transient errors in the system tests run by Kokoro. This patch: 1) Increased timeout used by `eventually` fixture from 60 seconds to 120 seconds. 2) Randomizes "other namespace", like we were already doing with the primary namespace used by tests. This should make it vanishingly unlikely that a test can have its initial state polluted by a previously run test, as each test will be run in its own namespace. 3) Relaxes the checks for undeleted entities during test cleanup. Instead of a failing assertion, now, we'll just log a warning if there are any leftover entities at the end of a test. --- tests/pytest.ini | 2 ++ tests/system/__init__.py | 3 +-- tests/system/conftest.py | 40 +++++++++++++++++--------------------- tests/system/test_query.py | 10 +++++----- 4 files changed, 26 insertions(+), 29 deletions(-) create mode 100644 tests/pytest.ini diff --git a/tests/pytest.ini b/tests/pytest.ini new file mode 100644 index 00000000..15b7fe77 --- /dev/null +++ b/tests/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --log-cli-level=WARN diff --git a/tests/system/__init__.py b/tests/system/__init__.py index 648910e3..d65ddbba 100644 --- a/tests/system/__init__.py +++ b/tests/system/__init__.py @@ -16,10 +16,9 @@ KIND = "SomeKind" OTHER_KIND = "OtherKind" -OTHER_NAMESPACE = "other-namespace" -def eventually(f, predicate, timeout=60, interval=2): +def eventually(f, predicate, timeout=120, interval=2): """Runs `f` in a loop, hoping for eventual success. Some things we're trying to test in Datastore are eventually diff --git a/tests/system/conftest.py b/tests/system/conftest.py index e3d5ee69..02af2500 100644 --- a/tests/system/conftest.py +++ b/tests/system/conftest.py @@ -1,4 +1,5 @@ import itertools +import logging import os import uuid @@ -10,7 +11,9 @@ from google.cloud.ndb import global_cache as global_cache_module -from . import KIND, OTHER_KIND, OTHER_NAMESPACE +from . import KIND, OTHER_KIND + +log = logging.getLogger(__name__) def _make_ds_client(namespace): @@ -23,22 +26,14 @@ def _make_ds_client(namespace): return client -def all_entities(client): +def all_entities(client, other_namespace): return itertools.chain( client.query(kind=KIND).fetch(), client.query(kind=OTHER_KIND).fetch(), - client.query(namespace=OTHER_NAMESPACE).fetch(), + client.query(namespace=other_namespace).fetch(), ) -@pytest.fixture(scope="module", autouse=True) -def initial_clean(): - # Make sure database is in clean state at beginning of test run - client = _make_ds_client(None) - for entity in all_entities(client): - client.delete(entity.key) - - @pytest.fixture(scope="session") def deleted_keys(): return set() @@ -55,17 +50,10 @@ def ds_client(namespace): @pytest.fixture -def with_ds_client(ds_client, to_delete, deleted_keys): - # Make sure we're leaving database as clean as we found it after each test - results = [ - entity - for entity in all_entities(ds_client) - if entity.key not in deleted_keys - ] - assert not results - +def with_ds_client(ds_client, to_delete, deleted_keys, other_namespace): yield ds_client + # Clean up after ourselves while to_delete: batch = to_delete[:500] ds_client.delete_multi(batch) @@ -74,10 +62,13 @@ def with_ds_client(ds_client, to_delete, deleted_keys): not_deleted = [ entity - for entity in all_entities(ds_client) + for entity in all_entities(ds_client, other_namespace) if entity.key not in deleted_keys ] - assert not not_deleted + if not_deleted: + log.warning( + "CLEAN UP: Entities not deleted from test: {}".format(not_deleted) + ) @pytest.fixture @@ -125,6 +116,11 @@ def namespace(): return str(uuid.uuid4()) +@pytest.fixture +def other_namespace(): + return str(uuid.uuid4()) + + @pytest.fixture def client_context(namespace): client = ndb.Client(namespace=namespace) diff --git a/tests/system/test_query.py b/tests/system/test_query.py index 705d6372..f706ec0c 100644 --- a/tests/system/test_query.py +++ b/tests/system/test_query.py @@ -28,7 +28,7 @@ from google.cloud import ndb -from tests.system import KIND, OTHER_NAMESPACE, eventually +from tests.system import KIND, eventually def _length_equals(n): @@ -278,12 +278,12 @@ class SomeKind(ndb.Model): @pytest.mark.usefixtures("client_context") -def test_namespace(dispose_of): +def test_namespace(dispose_of, other_namespace): class SomeKind(ndb.Model): foo = ndb.IntegerProperty() bar = ndb.StringProperty() - entity1 = SomeKind(foo=1, bar="a", namespace=OTHER_NAMESPACE) + entity1 = SomeKind(foo=1, bar="a", namespace=other_namespace) entity1.put() dispose_of(entity1.key._key) @@ -293,12 +293,12 @@ class SomeKind(ndb.Model): eventually(SomeKind.query().fetch, _length_equals(1)) - query = SomeKind.query(namespace=OTHER_NAMESPACE) + query = SomeKind.query(namespace=other_namespace) results = eventually(query.fetch, _length_equals(1)) assert results[0].foo == 1 assert results[0].bar == "a" - assert results[0].key.namespace() == OTHER_NAMESPACE + assert results[0].key.namespace() == other_namespace @pytest.mark.usefixtures("client_context")