Björn Tillenius has proposed merging ~bjornt/maas:pytest-django-setup into maas:master.
Commit message: Add a pytest DB fixture to set up the database for the tests. You now no longer have to run the pytest tests that require a DB using bin/database. Instead you run bin/pytest and it will set up the DB automatically. It still uses the bin/database infrastructure under the hood, and it will re-use the DB by default. So the time it takes to run the tests is the same as before. For the performance tests, it's possible to pass in a DB dump to use as well. I removed pytest-django, since we want to control how the DB is created and cleaned up. Requested reviews: MAAS Maintainers (maas-maintainers) For more details, see: https://code.launchpad.net/~bjornt/maas/+git/maas/+merge/433712 -- Your team MAAS Maintainers is requested to review the proposed merge of ~bjornt/maas:pytest-django-setup into maas:master.
diff --git a/pyproject.toml b/pyproject.toml index abafc45..edf51b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,6 @@ snippets order_by_type = false [tool.pytest.ini_options] -DJANGO_SETTINGS_MODULE = "maasserver.djangosettings.development" filterwarnings = "error::BytesWarning" testpaths = [ "src/apiclient", @@ -54,7 +53,3 @@ testpaths = [ "src/provisioningserver/utils/pytest_tests", # [[[end]]] ] -markers = [ - "perftest: marks tests for performance testing" -] -addopts = "--reuse-db" diff --git a/requirements-dev.txt b/requirements-dev.txt index 1dbc728..0b72d0f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,6 @@ ipdb==0.13.9 junitxml==0.7 postgresfixture==0.4.2 pytest==7.1.2 -pytest-django==4.5.2 pytest-mock==3.7.0 pytest-xdist==2.5.0 python-subunit==1.4.0 diff --git a/setup.cfg b/setup.cfg index 402ce94..cb3e5db 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,10 @@ console_scripts = test.region.legacy = maastesting.scripts:run_region_legacy test.rack = maastesting.scripts:run_rack test.parallel = maastesting.scripts:run_parallel +pytest11 = + maas-django = maastesting.pytest.django + maas-seeds = maastesting.pytest.seeds + maas-perftest = maastesting.pytest.perftest [options.packages.find] where = src diff --git a/src/maasperf/tests/cli/conftest.py b/src/maasperf/tests/cli/conftest.py index 450955d..79aeba8 100644 --- a/src/maasperf/tests/cli/conftest.py +++ b/src/maasperf/tests/cli/conftest.py @@ -7,19 +7,6 @@ from django.core.serializers import serialize from django.http import HttpResponse from pytest import fixture -from maasserver.testing.factory import factory as maasserver_factory - - -# override pytest-django's db setup -@fixture(scope="session") -def django_db_setup(): - pass - - -@fixture(scope="session") -def factory(): - return maasserver_factory - @fixture() def maas_user(factory): diff --git a/src/maasperf/tests/cli/test_machines.py b/src/maasperf/tests/cli/test_machines.py index 95d09bc..94c3af3 100644 --- a/src/maasperf/tests/cli/test_machines.py +++ b/src/maasperf/tests/cli/test_machines.py @@ -11,7 +11,7 @@ from maascli.config import ProfileConfig from maascli.parser import get_deepest_subparser, prepare_parser [email protected]_db [email protected]("maasdb") def test_perf_list_machines_CLI( perf, cli_profile, monkeypatch, cli_machines_api_response ): diff --git a/src/maasperf/tests/conftest.py b/src/maasperf/tests/conftest.py deleted file mode 100644 index 3850845..0000000 --- a/src/maasperf/tests/conftest.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2022 Canonical Ltd. This software is licensed under the -# GNU Affero General Public License version 3 (see the file LICENSE). - -from pytest import fixture - -__all__ = [ - "admin_api_client", - "api_client", - "django_db_setup", - "factory", - "maas_user", -] - - -pytest_plugins = "maastesting.pytest.perftest,maastesting.pytest.seeds" - - -# override pytest-django's db setup -@fixture(scope="session") -def django_db_setup(): - pass - - -@fixture(scope="session") -def factory(): - # Local imports from maasserver so that pytest --help works - from maasserver.testing.factory import factory as maasserver_factory - - return maasserver_factory - - -@fixture() -def admin(factory): - return factory.make_admin() - - -@fixture() -def maas_user(factory): - return factory.make_User() - - -@fixture() -def api_client(maas_user): - # Local imports from maasserver so that pytest --help works - from maasserver.models.user import get_auth_tokens - from maasserver.testing.testclient import MAASSensibleOAuthClient - - return MAASSensibleOAuthClient( - user=maas_user, token=get_auth_tokens(maas_user)[0] - ) - - -@fixture() -def admin_api_client(admin): - # Local imports from maasserver so that pytest --help works - from maasserver.models.user import get_auth_tokens - from maasserver.testing.testclient import MAASSensibleOAuthClient - - return MAASSensibleOAuthClient(user=admin, token=get_auth_tokens(admin)[0]) diff --git a/src/maasperf/tests/maasserver/api/test_machines.py b/src/maasperf/tests/maasserver/api/test_machines.py index a823f68..157acc7 100644 --- a/src/maasperf/tests/maasserver/api/test_machines.py +++ b/src/maasperf/tests/maasserver/api/test_machines.py @@ -4,7 +4,6 @@ from django.urls import reverse from piston3.emitters import Emitter from piston3.handler import typemapper -import pytest from maasserver.api.machines import MachinesHandler from maastesting.http import make_HttpRequest @@ -15,7 +14,6 @@ class DummyEmitter(Emitter): self.construct() [email protected]_db def test_perf_list_machines_MachineHandler_api_endpoint( perf, admin_api_client ): @@ -23,7 +21,6 @@ def test_perf_list_machines_MachineHandler_api_endpoint( admin_api_client.get(reverse("machines_handler")) [email protected]_db def test_perf_list_machines_MachinesHander_direct_call(perf, admin): handler = MachinesHandler() request = make_HttpRequest() @@ -40,7 +37,6 @@ def test_perf_list_machines_MachinesHander_direct_call(perf, admin): emitter.render(request) [email protected]_db def test_perf_list_machines_MachinesHander_only_objects(perf, admin): handler = MachinesHandler() request = make_HttpRequest() diff --git a/src/maasperf/tests/maasserver/models/conftest.py b/src/maasperf/tests/maasserver/models/conftest.py deleted file mode 100644 index f3830e8..0000000 --- a/src/maasperf/tests/maasserver/models/conftest.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2022 Canonical Ltd. This software is licensed under the -# GNU Affero General Public License version 3 (see the file LICENSE). - -from pytest import fixture - -from maasserver.testing.factory import factory as maasserver_factory - - -# override pytest-django's db setup -@fixture(scope="session") -def django_db_setup(): - pass - - -@fixture(scope="session") -def factory(): - return maasserver_factory diff --git a/src/maasperf/tests/maasserver/models/test_machine.py b/src/maasperf/tests/maasserver/models/test_machine.py index 2273294..41b2248 100644 --- a/src/maasperf/tests/maasserver/models/test_machine.py +++ b/src/maasperf/tests/maasserver/models/test_machine.py @@ -7,16 +7,16 @@ import pytest from maasserver.models import Machine [email protected]_db [email protected]_transactions def test_perf_create_machines(perf, factory): # TODO use create machines script with perf.record("test_perf_create_machines"): - with transaction.atomic(): - for _ in range(30): - factory.make_Machine() + for _ in range(30): + factory.make_Machine() + transaction.commit() [email protected]_db [email protected]("maasdb") def test_perf_list_machines(perf): with perf.record("test_perf_list_machines"): list(Machine.objects.all()) diff --git a/src/maasperf/tests/maasserver/websockets/test_machines.py b/src/maasperf/tests/maasserver/websockets/test_machines.py index a182a22..f91ecc9 100644 --- a/src/maasperf/tests/maasserver/websockets/test_machines.py +++ b/src/maasperf/tests/maasserver/websockets/test_machines.py @@ -1,13 +1,10 @@ # Copyright 2022 Canonical Ltd. This software is licensed under the # GNU Affero General Public License version 3 (see the file LICENSE). -import pytest - from maasserver.models import Machine from maasserver.websockets.handlers.machine import MachineHandler [email protected]_db def test_perf_list_machines_Websocket_endpoint(perf, admin): # This should test the websocket calls that are used to load # the machine listing page on the initial page load. @@ -27,7 +24,6 @@ def test_perf_list_machines_Websocket_endpoint(perf, admin): ws_handler.list(params) [email protected]_db def test_perf_list_machines_Websocket_endpoint_all(perf, admin): # How long would it take to list all the machines using the # websocket without any pagination. diff --git a/src/maasserver/conftest.py b/src/maasserver/conftest.py index b50be82..063bfe9 100644 --- a/src/maasserver/conftest.py +++ b/src/maasserver/conftest.py @@ -9,7 +9,6 @@ import pytest from maasserver import vault from maasserver.config import RegionConfiguration -from maasserver.testing.factory import factory as maasserver_factory from maasserver.vault import ( get_region_vault_client, get_region_vault_client_if_enabled, @@ -23,11 +22,6 @@ def clean_globals(clean_globals): yield [email protected](scope="session") -def factory(): - return maasserver_factory - - @pytest.fixture def vault_regionconfig(mocker): store = {} diff --git a/src/maasserver/management/commands/pytest_tests/test_config_vault.py b/src/maasserver/management/commands/pytest_tests/test_config_vault.py index 23333ea..f75908c 100644 --- a/src/maasserver/management/commands/pytest_tests/test_config_vault.py +++ b/src/maasserver/management/commands/pytest_tests/test_config_vault.py @@ -25,6 +25,7 @@ def configure_mock(mocker): yield mocker.patch.object(config_vault, "configure_region_with_vault") [email protected]("maasdb") class TestConfigVaultConfigurateCommand: def _configure_kwargs( self, @@ -125,7 +126,7 @@ class TestConfigVaultConfigurateCommand: ) [email protected]_db [email protected]("maasdb") class TestSetVaultConfiguredDbCommand: def test_does_nothing_when_no_maas_id(self): assert MAAS_ID.get() is None @@ -149,7 +150,7 @@ class TestSetVaultConfiguredDbCommand: assert ControllerInfo.objects.get(node_id=node.id).vault_configured [email protected]_db [email protected]("maasdb") class TestConfigVaultMigrateCommand: def test_raises_when_vault_already_enabled(self): Config.objects.set_config("vault_enabled", True) @@ -198,7 +199,7 @@ class TestConfigVaultMigrateCommand: migrate_mock.assert_called_once_with(client) [email protected]_db [email protected]("maasdb") class TestMigrateSecrets: def test_migrate_secrets_enables_vault(self, mocker): assert not Config.objects.get_config("vault_enabled", False) @@ -334,7 +335,7 @@ class TestMigrateSecrets: assert notify_mock.call_count == 1 [email protected]_db [email protected]("maasdb") class TestStatus: def test_status_not_enabled(self, capsys): region_one = factory.make_RegionController(hostname="one") diff --git a/src/maasserver/models/pytest_tests/test_node.py b/src/maasserver/models/pytest_tests/test_node.py index ca5fb9f..72f89b2 100644 --- a/src/maasserver/models/pytest_tests/test_node.py +++ b/src/maasserver/models/pytest_tests/test_node.py @@ -1,11 +1,8 @@ import logging -import pytest - from maasserver.enum import NODE_STATUS [email protected]_db def test_node_mark_failed_deployment_logs_failure(factory, caplog): node = factory.make_Node( status=NODE_STATUS.DEPLOYING, with_boot_disk=False diff --git a/src/maasserver/pytest_tests/test_certificates.py b/src/maasserver/pytest_tests/test_certificates.py index 2e59d3d..2ff100d 100644 --- a/src/maasserver/pytest_tests/test_certificates.py +++ b/src/maasserver/pytest_tests/test_certificates.py @@ -7,7 +7,7 @@ from provisioningserver.testing.certificates import ( ) [email protected]_db [email protected]("maasdb") class TestGetMAASCertificate: def test_no_secret(self): assert ( diff --git a/src/maasserver/pytest_tests/test_secrets.py b/src/maasserver/pytest_tests/test_secrets.py index 4428151..16964f8 100644 --- a/src/maasserver/pytest_tests/test_secrets.py +++ b/src/maasserver/pytest_tests/test_secrets.py @@ -15,8 +15,8 @@ def vault_client(request): yield None [email protected]_db @pytest.mark.parametrize("vault_client", [True, False], indirect=True) [email protected]("maasdb") class TestSecretManager: def set_secret(self, vault_client, path, value): if vault_client: diff --git a/src/maasserver/pytest_tests/test_vault.py b/src/maasserver/pytest_tests/test_vault.py index d050999..c6cc302 100644 --- a/src/maasserver/pytest_tests/test_vault.py +++ b/src/maasserver/pytest_tests/test_vault.py @@ -141,7 +141,6 @@ class TestVaultClient: ensure_auth.assert_called_once() [email protected]_db class TestGetRegionVaultClient: def test_cached(self, mocker): mock_get_client = mocker.patch.object( diff --git a/src/maasserver/testing/resources.py b/src/maasserver/testing/resources.py index d628ba9..e2ba9f9 100644 --- a/src/maasserver/testing/resources.py +++ b/src/maasserver/testing/resources.py @@ -59,6 +59,28 @@ def connect_no_transaction(cluster): conn.close() +def create_postgres_cluster(): + cluster = ClusterFixture("db", preserve=True) + cluster.create() + postgres_path = Path(cluster.datadir) + postgres_conf = postgres_path / "postgresql.conf" + postgres_speed_conf = postgres_path / "postgresql.conf.speed" + if "postgresql.conf.speed" not in postgres_conf.read_text(): + with postgres_conf.open("a") as fh: + fh.write("include = 'postgresql.conf.speed'\n") + with postgres_speed_conf.open("w") as fh: + fh.write( + dedent( + """\ + fsync = off + full_page_writes = off + synchronous_commit = off + """ + ) + ) + return cluster + + class DatabaseClusterManager(TestResourceManager): """Resource manager for a PostgreSQL cluster.""" @@ -66,24 +88,7 @@ class DatabaseClusterManager(TestResourceManager): testDownCost = 2 def make(self, dependencies): - cluster = ClusterFixture("db", preserve=True) - cluster.create() - postgres_path = Path(cluster.datadir) - postgres_conf = postgres_path / "postgresql.conf" - postgres_speed_conf = postgres_path / "postgresql.conf.speed" - if "postgresql.conf.speed" not in postgres_conf.read_text(): - with postgres_conf.open("a") as fh: - fh.write("include = 'postgresql.conf.speed'\n") - with postgres_speed_conf.open("w") as fh: - fh.write( - dedent( - """\ - fsync = off - full_page_writes = off - synchronous_commit = off - """ - ) - ) + cluster = create_postgres_cluster() cluster.setUp() return cluster diff --git a/src/maastesting/pytest/__init__.py b/src/maastesting/pytest/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/maastesting/pytest/__init__.py diff --git a/src/maastesting/pytest/django.py b/src/maastesting/pytest/django.py new file mode 100644 index 0000000..6129d15 --- /dev/null +++ b/src/maastesting/pytest/django.py @@ -0,0 +1,245 @@ +from contextlib import contextmanager +import os +from pathlib import Path + +from django.db import transaction +from postgresfixture import ClusterFixture +import pytest + +from maasserver.djangosettings import development +import maasserver.testing +from maasserver.testing.resources import ( + close_all_connections, + create_postgres_cluster, +) +from maasserver.utils.orm import enable_all_database_connections + +cluster_stash = pytest.StashKey[ClusterFixture] +db_template_stash = pytest.StashKey[str] + + +def pytest_addoption(parser): + default_initial_db = ( + Path(maasserver.testing.__file__).parent / "initial.maas_test.sql" + ) + maas_parser = parser.getgroup("maas", description="MAAS") + maas_parser.addoption( + "--maas-recreate-initial-db", + help="Recreate the DB template that's used to speed up tests", + action="store_true", + ) + maas_parser.addoption( + "--maas-initial-db", + help="The initial DB dump that's used to create the DB template.", + default=str(default_initial_db), + ) + + +def load_initial_db_file(cluster, template_name, path): + if path.suffix == ".sql": + with connect(cluster) as conn: + with conn.cursor() as cursor: + cursor.execute(f'CREATE DATABASE "{template_name}"') + cluster.execute( + "psql", + "--quiet", + "--single-transaction", + "--set=ON_ERROR_STOP=1", + "--dbname", + template_name, + "--output", + os.devnull, + "--file", + str(path), + ) + else: + # Assume it's a DB dump of the "maas" database. + cluster.execute( + "pg_restore", + "-O", + "-x", + "--disable-triggers", + "--create", + "--clean", + "--if-exists", + "-d", + "postgres", + str(path), + ) + with connect(cluster) as conn: + with conn.cursor() as cursor: + cursor.execute( + f'ALTER DATABASE "maas" RENAME TO "{template_name}"' + ) + + +@contextmanager +def connect(cluster): + conn = cluster.connect() + conn.autocommit = True + yield conn + conn.close() + + [email protected](tryfirst=False) +def pytest_configure(config): + config.addinivalue_line( + "markers", + "allow_transactions: Allow a test to use transaction.commit()", + ) + + [email protected](tryfirst=True) +def pytest_load_initial_conftests(early_config, parser, args): + cluster = create_postgres_cluster() + cluster.setUp() + early_config.stash[cluster_stash] = cluster + os.environ[ + "DJANGO_SETTINGS_MODULE" + ] = "maasserver.djangosettings.development" + import django + + from maasserver.djangosettings import development + + database = development.DATABASES["default"] + template = f"{database['NAME']}_test" + early_config.stash[db_template_stash] = template + database["NAME"] = "no_such_db" + django.setup() + + [email protected] +def pytest_unconfigure(config): + cluster = config.stash[cluster_stash] + cluster.cleanUp() + + +def _set_up_template_db( + cluster, template_name, template_path, force_recreate=False +): + if force_recreate: + with connect(cluster) as conn: + with conn.cursor() as cursor: + cursor.execute(f'DROP DATABASE IF EXISTS "{template_name}"') + + if template_name not in cluster.databases: + load_initial_db_file(cluster, template_name, template_path) + + from django.core.management import call_command + + from maasserver import dbviews, triggers + from maasserver.djangosettings import development + + old_name = development.DATABASES["default"]["NAME"] + development.DATABASES["default"]["NAME"] = template_name + + import django + + django.setup() + enable_all_database_connections() + dbviews.drop_all_views() + call_command("migrate", interactive=False) + + triggers.register_all_triggers() + dbviews.register_all_views() + + close_all_connections() + development.DATABASES["default"]["NAME"] = old_name + + [email protected](scope="session") +def templatemaasdb(pytestconfig): + + cluster = pytestconfig.stash[cluster_stash] + force_recreate = pytestconfig.option.maas_recreate_initial_db + template_path = Path(pytestconfig.option.maas_initial_db) + with cluster.lock.exclusive: + template_name = pytestconfig.stash[db_template_stash] + _set_up_template_db( + cluster, template_name, template_path, force_recreate + ) + + [email protected] +def ensuremaasdb(templatemaasdb, pytestconfig, worker_id): + from maasserver.djangosettings import development + + template = pytestconfig.stash[db_template_stash] + dbname = f"{template}_{worker_id}" + database = development.DATABASES["default"] + database["NAME"] = dbname + cluster = pytestconfig.stash[cluster_stash] + if dbname not in cluster.databases: + template = pytestconfig.stash[db_template_stash] + with cluster.lock.exclusive: + with connect(cluster) as conn: + with conn.cursor() as cursor: + cursor.execute( + f'CREATE DATABASE "{dbname}" WITH TEMPLATE "{template}"' + ) + yield + database["NAME"] = "no_such_db" + + [email protected] +def maasdb(ensuremaasdb, request, pytestconfig): + enable_all_database_connections() + # Start a transaction. + transaction.set_autocommit(False) + allow_transactions = ( + request.node.get_closest_marker("allow_transactions") is not None + ) + if allow_transactions: + yield + close_all_connections() + # Since transactions are allowed, we assume a commit has been + # made, so we can't simply do rollback to clean up the DB. + dbname = development.DATABASES["default"]["NAME"] + cluster = pytestconfig.stash[cluster_stash] + cluster.dropdb(dbname) + else: + # Wrap the test in an atomic() block in order to prevent commits. + with transaction.atomic(): + yield + # Since we don't allow commits, we can safely rollback and don't + # have to recreate the DB. + transaction.rollback() + close_all_connections() + + [email protected] +def factory(maasdb): + # Local imports from maasserver so that pytest --help works + from maasserver.testing.factory import factory as maasserver_factory + + return maasserver_factory + + [email protected] +def admin(factory): + return factory.make_admin() + + [email protected] +def maas_user(factory): + return factory.make_User() + + [email protected] +def api_client(maas_user): + # Local imports from maasserver so that pytest --help works + from maasserver.models.user import get_auth_tokens + from maasserver.testing.testclient import MAASSensibleOAuthClient + + return MAASSensibleOAuthClient( + user=maas_user, token=get_auth_tokens(maas_user)[0] + ) + + [email protected] +def admin_api_client(admin): + # Local imports from maasserver so that pytest --help works + from maasserver.models.user import get_auth_tokens + from maasserver.testing.testclient import MAASSensibleOAuthClient + + return MAASSensibleOAuthClient(user=admin, token=get_auth_tokens(admin)[0]) diff --git a/src/maastesting/pytest/tests/__init__.py b/src/maastesting/pytest/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/maastesting/pytest/tests/__init__.py diff --git a/src/provisioningserver/conftest.py b/src/provisioningserver/conftest.py deleted file mode 100644 index 57b9e3a..0000000 --- a/src/provisioningserver/conftest.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2022 Canonical Ltd. This software is licensed under the -# GNU Affero General Public License version 3 (see the file LICENSE). - -import pytest - -from maastesting.factory import factory as maastesting_factory - - [email protected](scope="session") -def factory(): - return maastesting_factory diff --git a/utilities/check-imports b/utilities/check-imports index 2e3cf92..fb59c92 100755 --- a/utilities/check-imports +++ b/utilities/check-imports @@ -341,6 +341,7 @@ TestHelpers = ( TestHelpersRule = Rule( Allow("crochet|crochet.**"), Allow("django|django.**"), + Allow("maasserver|maasserver.**"), Allow("netaddr|netaddr.**"), Allow("twisted.**"), Allow("wrapt"), diff --git a/utilities/run-perf-tests-ci b/utilities/run-perf-tests-ci index b312abb..8dbd6cd 100755 --- a/utilities/run-perf-tests-ci +++ b/utilities/run-perf-tests-ci @@ -13,20 +13,28 @@ GIT_HASH="$(git rev-parse HEAD)" PYTHONHASHSEED="${PYTHONHASHSEED:-$(shuf -i 0-4294967295 -n 1)}" MAAS_RAND_SEED="${MAAS_RAND_SEED:-$(od -vAn -N8 -tx8 < /dev/urandom | tr -d ' ')}" +DB_DUMP=$1 OUTPUT_FILE="${OUTPUT_FILE:-maas-perf-results.json}" +if [ -z "$1" ] +then + echo "Usage: $0 <maas_db_dump>" + exit 1 +fi + export MAAS_RAND_SEED PYTHONHASHSEED GIT_HASH GIT_BRANCH echo "MAAS_RAND_SEED=${MAAS_RAND_SEED}" echo "PYTHONHASHSEED=${PYTHONHASHSEED}" -bin/database --preserve run make syncdb || exit 1 -exec bin/database --preserve run -- bin/pytest \ +exec bin/pytest \ -q \ --disable-warnings \ --show-capture=no \ --no-header \ --no-summary \ --junit-xml=junit-perf.xml \ + --maas-recreate-initial-db \ + --maas-initial-db ${DB_DUMP} \ ./src/maasperf/ \ --perf-output-file ${OUTPUT_FILE} diff --git a/utilities/run-py-tests-ci b/utilities/run-py-tests-ci index 67c9f96..5113d55 100755 --- a/utilities/run-py-tests-ci +++ b/utilities/run-py-tests-ci @@ -19,7 +19,6 @@ bin/test.parallel --emit-subunit | \ bin/subunit2junitxml --no-passthrough -f -o junit.xml | \ bin/subunit2pyunit --no-passthrough res1=$? -DBUPGRADE_ARGS='-v 0' bin/database --preserve run -- make syncdb -bin/database run -- bin/pytest -n auto --maxprocesses=6 --dist=loadscope --junit-xml=junit-pytest.xml +bin/pytest -n auto --maxprocesses=6 --dist=loadscope --junit-xml=junit-pytest.xml res2=$? exit $((res1 + res2))
-- Mailing list: https://launchpad.net/~sts-sponsors Post to : [email protected] Unsubscribe : https://launchpad.net/~sts-sponsors More help : https://help.launchpad.net/ListHelp

