Before this patch, if Scylla crashes during some test in cql-pytest, all tests after it will fail because they can't connect to Scylla - and we can get a report on hundreds of failures without a clear sign of where the real problem was. This patch introduces an autouse fixture (i.e., a fixture automatically used by every test) which tries to run a do-nothing CQL command after each test. If this CQL command fails, we conclude that Scylla crashed and report the test in which this happened - and exist pytest instead of failing a hundred more tests. Fixes #8080 Signed-off-by: Nadav Har'El <nyh@scylladb.com> Message-Id: <20210304132804.1527977-1-nyh@scylladb.com>
116 lines
5.5 KiB
Python
116 lines
5.5 KiB
Python
# Copyright 2020 ScyllaDB
|
|
#
|
|
# This file is part of Scylla.
|
|
#
|
|
# Scylla is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Scylla is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# This file configures pytest for all tests in this directory, and also
|
|
# defines common test fixtures for all of them to use. A "fixture" is some
|
|
# setup which an invididual test requires to run; The fixture has setup code
|
|
# and teardown code, and if multiple tests require the same fixture, it can
|
|
# be set up only once - while still allowing the user to run individual tests
|
|
# and automatically setting up the fixtures they need.
|
|
|
|
import pytest
|
|
|
|
from cassandra.cluster import Cluster, ConsistencyLevel, ExecutionProfile, EXEC_PROFILE_DEFAULT
|
|
from cassandra.policies import RoundRobinPolicy
|
|
|
|
from util import unique_name, new_test_table
|
|
|
|
# By default, tests run against a CQL server (Scylla or Cassandra) listening
|
|
# on localhost:9042. Add the --host and --port options to allow overiding
|
|
# these defaults.
|
|
def pytest_addoption(parser):
|
|
parser.addoption('--host', action='store', default='localhost',
|
|
help='CQL server host to connect to')
|
|
parser.addoption('--port', action='store', default='9042',
|
|
help='CQL server port to connect to')
|
|
|
|
# "cql" fixture: set up client object for communicating with the CQL API.
|
|
# The host/port combination of the server are determined by the --host and
|
|
# --port options, and defaults to localhost and 9042, respectively.
|
|
# We use scope="session" so that all tests will reuse the same client object.
|
|
@pytest.fixture(scope="session")
|
|
def cql(request):
|
|
profile = ExecutionProfile(
|
|
load_balancing_policy=RoundRobinPolicy(),
|
|
consistency_level=ConsistencyLevel.LOCAL_QUORUM,
|
|
serial_consistency_level=ConsistencyLevel.LOCAL_SERIAL,
|
|
# The default timeout (in seconds) for execute() commands is 10, which
|
|
# should have been more than enough, but in some extreme cases with a
|
|
# very slow debug build running on a very busy machine and a very slow
|
|
# request (e.g., a DROP KEYSPACE needing to drop multiple tables)
|
|
# 10 seconds may not be enough, so let's increase it. See issue #7838.
|
|
request_timeout = 120)
|
|
cluster = Cluster(execution_profiles={EXEC_PROFILE_DEFAULT: profile},
|
|
contact_points=[request.config.getoption('host')],
|
|
port=request.config.getoption('port'),
|
|
# TODO: make the protocol version an option, to allow testing with
|
|
# different versions. If we drop this setting completely, it will
|
|
# mean pick the latest version supported by the client and the server.
|
|
protocol_version=4
|
|
)
|
|
return cluster.connect()
|
|
|
|
# A function-scoped autouse=True fixture allows us to test after every test
|
|
# that the CQL connection is still alive - and if not report the test which
|
|
# crashed Scylla and stop running any more tests.
|
|
@pytest.fixture(scope="function", autouse=True)
|
|
def cql_test_connection(cql, request):
|
|
yield
|
|
try:
|
|
# We want to run a do-nothing CQL command. "use system" is the
|
|
# closest to do-nothing I could find...
|
|
cql.execute("use system")
|
|
except:
|
|
pytest.exit(f"Scylla appears to have crashed in test {request.node.parent.name}::{request.node.name}")
|
|
|
|
# "test_keyspace" fixture: Creates and returns a temporary keyspace to be
|
|
# used in tests that need a keyspace. The keyspace is created with RF=1,
|
|
# and automatically deleted at the end. We use scope="session" so that all
|
|
# tests will reuse the same keyspace.
|
|
@pytest.fixture(scope="session")
|
|
def test_keyspace(cql):
|
|
name = unique_name()
|
|
cql.execute("CREATE KEYSPACE " + name + " WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }")
|
|
yield name
|
|
cql.execute("DROP KEYSPACE " + name)
|
|
|
|
# The "scylla_only" fixture can be used by tests for Scylla-only features,
|
|
# which do not exist on Apache Cassandra. A test using this fixture will be
|
|
# skipped if running with "run-cassandra".
|
|
@pytest.fixture(scope="session")
|
|
def scylla_only(cql):
|
|
# We recognize Scylla by checking if there is any system table whose name
|
|
# contains the word "scylla":
|
|
names = [row.table_name for row in cql.execute("SELECT * FROM system_schema.tables WHERE keyspace_name = 'system'")]
|
|
if not any('scylla' in name for name in names):
|
|
pytest.skip('Scylla-only test skipped')
|
|
|
|
# "cassandra_bug" is similar to "scylla_only", except instead of skipping
|
|
# the test, it is expected to fail (xfail) on Cassandra. It should be used
|
|
# in rare cases where we consider Scylla's behavior to be the correct one,
|
|
# and Cassandra's to be the bug.
|
|
@pytest.fixture(scope="session")
|
|
def cassandra_bug(cql):
|
|
# We recognize Scylla by checking if there is any system table whose name
|
|
# contains the word "scylla":
|
|
names = [row.table_name for row in cql.execute("SELECT * FROM system_schema.tables WHERE keyspace_name = 'system'")]
|
|
if not any('scylla' in name for name in names):
|
|
pytest.xfail('A known Cassandra bug')
|
|
|
|
# TODO: use new_test_table and "yield from" to make shared test_table
|
|
# fixtures with some common schemas.
|