diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..cf002457c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,128 @@ +name: main + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-20.04 + continue-on-error: ${{ matrix.allow_failure }} + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Setup mysql + if: contains(matrix.name, 'mysql') + run: | + sudo systemctl start mysql.service + echo "TEST_DB_USER=root" >> $GITHUB_ENV + echo "TEST_DB_PASSWORD=root" >> $GITHUB_ENV + + - name: Setup postgresql + if: contains(matrix.name, 'postgres') + run: | + sudo systemctl start postgresql.service + sudo -u postgres createuser --createdb $USER + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox==3.20.0 + + - name: Run tox + run: tox -e ${{ matrix.name }} + + - name: Report coverage + if: contains(matrix.name, 'coverage') + run: | + bash <(curl -s https://codecov.io/bash) -Z -X gcov -X xcode -X gcovout + + strategy: + fail-fast: false + matrix: + include: + - name: checkqa,docs + python: 3.8 + allow_failure: false + + - name: py38-dj31-postgres-xdist-coverage + python: 3.8 + allow_failure: false + + - name: py37-dj30-mysql_innodb-coverage + python: 3.7 + allow_failure: false + + - name: py36-dj22-sqlite-xdist-coverage + python: 3.6 + allow_failure: false + + - name: py37-dj22-sqlite-xdist-coverage + python: 3.7 + allow_failure: false + + - name: py38-dj30-sqlite-xdist-coverage + python: 3.8 + allow_failure: false + + - name: py38-dj31-sqlite-xdist-coverage + python: 3.8 + allow_failure: false + + - name: py38-djmaster-sqlite-coverage + python: 3.8 + allow_failure: true + + # Explicitly test (older) pytest 5.4. + - name: py35-dj22-postgres-pytest54-coverage + python: 3.5 + allow_failure: false + + - name: py35-dj22-sqlite_file-coverage + python: 3.5 + allow_failure: false + + - name: py36-dj31-mysql_myisam-coverage + python: 3.6 + allow_failure: false + + # pypy3: not included with coverage reports (much slower then). + - name: pypy3-dj22-postgres + python: pypy3 + allow_failure: false + + deploy: + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pytest-django' + runs-on: ubuntu-20.04 + needs: [test] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade wheel setuptools tox + + - name: Build package + run: python setup.py sdist bdist_wheel + + - name: Publish package + uses: pypa/gh-action-pypi-publish@1.4.1 + with: + user: __token__ + password: ${{ secrets.pypi_token }} diff --git a/.travis.yml b/.travis.yml index 60ecef3d8..28a8f4600 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,24 +53,24 @@ jobs: services: - postgresql - - stage: test_release - python: 3.8 - env: TOXENV=py38-dj31-postgres - services: - - postgresql +# - stage: test_release +# python: 3.8 +# env: TOXENV=py38-dj31-postgres +# services: +# - postgresql - - stage: release - script: skip - install: skip - after_success: true - deploy: - provider: pypi - user: blueyed - password: - secure: "FY7qbX/N0XRcH8hVk00SsQWvNIkuxKvY7Br4ghRnHvleHG3YulJ7WbJnik+9eoBGeMfJeNyzBfVjpeo1ZIq9IZBiyTdNfG/sZFsC5LOoG/CPxPH3nD9JktI2HoBMnlSbGg/MMHjY+wXuOY647U/3qNedcnQmGztYt6QWi5DRxu8=" - on: - tags: true - distributions: "sdist bdist_wheel" +# - stage: release +# script: skip +# install: skip +# after_success: true +# deploy: +# provider: pypi +# user: blueyed +# password: +# secure: "FY7qbX/N0XRcH8hVk00SsQWvNIkuxKvY7Br4ghRnHvleHG3YulJ7WbJnik+9eoBGeMfJeNyzBfVjpeo1ZIq9IZBiyTdNfG/sZFsC5LOoG/CPxPH3nD9JktI2HoBMnlSbGg/MMHjY+wXuOY647U/3qNedcnQmGztYt6QWi5DRxu8=" +# on: +# tags: true +# distributions: "sdist bdist_wheel" # NOTE: does not show up in "allowed failures" section, but is allowed to # fail (for the "test" stage). @@ -82,10 +82,10 @@ stages: if: tag IS NOT present - name: test if: tag IS NOT present - - name: test_release - if: tag IS present - - name: release - if: tag IS present + # - name: test_release + # if: tag IS present + # - name: release + # if: tag IS present install: - pip install tox==3.20.0 @@ -93,10 +93,10 @@ install: script: - tox -after_success: - - | - set -ex - if [[ "${TOXENV%-coverage}" != "$TOXENV" ]]; then - bash <(curl -s https://codecov.io/bash) -Z -X gcov -X xcode -X gcovout - fi - set +ex +# after_success: +# - | +# set -ex +# if [[ "${TOXENV%-coverage}" != "$TOXENV" ]]; then +# bash <(curl -s https://codecov.io/bash) -Z -X gcov -X xcode -X gcovout +# fi +# set +ex diff --git a/pytest_django_test/db_helpers.py b/pytest_django_test/db_helpers.py index 5e927df47..d3ec63764 100644 --- a/pytest_django_test/db_helpers.py +++ b/pytest_django_test/db_helpers.py @@ -38,19 +38,44 @@ def __init__(self, status_code, std_out, std_err): self.std_err = std_err -def run_cmd(*args): - r = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +def run_cmd(*args, env=None): + r = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env={**os.environ, **(env or {})}, + ) stdoutdata, stderrdata = r.communicate() ret = r.wait() return CmdResult(ret, stdoutdata, stderrdata) +def run_psql(*args): + env = {} + user = _settings.get("USER") + if user: # pragma: no branch + args = ("-U", user, *args) + password = _settings.get("PASSWORD") + if password: # pragma: no branch + env["PGPASSWORD"] = password + host = _settings.get("HOST") + if host: # pragma: no branch + args = ("-h", host, *args) + return run_cmd("psql", *args, env=env) + + def run_mysql(*args): - user = _settings.get("USER", None) + user = _settings.get("USER") if user: # pragma: no branch - args = ("-u", user) + tuple(args) - args = ("mysql",) + tuple(args) - return run_cmd(*args) + args = ("-u", user, *args) + password = _settings.get("PASSWORD") + if password: # pragma: no branch + # Note: "-ppassword" must be a single argument. + args = ("-p" + password, *args) + host = _settings.get("HOST") + if host: # pragma: no branch + args = ("-h", host, *args) + return run_cmd("mysql", *args) def skip_if_sqlite_in_memory(): @@ -73,7 +98,7 @@ def drop_database(db_suffix=None): db_engine = get_db_engine() if db_engine == "postgresql": - r = run_cmd("psql", "postgres", "-c", "DROP DATABASE %s" % name) + r = run_psql("postgres", "-c", "DROP DATABASE %s" % name) assert "DROP DATABASE" in force_str( r.std_out ) or "does not exist" in force_str(r.std_err) @@ -95,7 +120,7 @@ def db_exists(db_suffix=None): db_engine = get_db_engine() if db_engine == "postgresql": - r = run_cmd("psql", name, "-c", "SELECT 1") + r = run_psql(name, "-c", "SELECT 1") return r.status_code == 0 if db_engine == "mysql": @@ -112,7 +137,7 @@ def mark_database(): db_engine = get_db_engine() if db_engine == "postgresql": - r = run_cmd("psql", TEST_DB_NAME, "-c", "CREATE TABLE mark_table();") + r = run_psql(TEST_DB_NAME, "-c", "CREATE TABLE mark_table();") assert r.status_code == 0 return @@ -137,7 +162,7 @@ def mark_exists(): db_engine = get_db_engine() if db_engine == "postgresql": - r = run_cmd("psql", TEST_DB_NAME, "-c", "SELECT 1 FROM mark_table") + r = run_psql(TEST_DB_NAME, "-c", "SELECT 1 FROM mark_table") # When something pops out on std_out, we are good return bool(r.std_out) diff --git a/pytest_django_test/settings_mysql_innodb.py b/pytest_django_test/settings_mysql_innodb.py index 1fa08885a..5adc36526 100644 --- a/pytest_django_test/settings_mysql_innodb.py +++ b/pytest_django_test/settings_mysql_innodb.py @@ -1,11 +1,14 @@ +from os import environ + from .settings_base import * # noqa: F401 F403 DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": "pytest_django_should_never_get_accessed", - "HOST": "localhost", - "USER": "root", + "USER": environ.get("TEST_DB_USER", "root"), + "PASSWORD": environ.get("TEST_DB_PASSWORD", ""), + "HOST": environ.get("TEST_DB_HOST", "localhost"), "OPTIONS": {"init_command": "SET default_storage_engine=InnoDB"}, } } diff --git a/pytest_django_test/settings_mysql_myisam.py b/pytest_django_test/settings_mysql_myisam.py index d0a89afac..8e9106935 100644 --- a/pytest_django_test/settings_mysql_myisam.py +++ b/pytest_django_test/settings_mysql_myisam.py @@ -1,11 +1,14 @@ +from os import environ + from .settings_base import * # noqa: F401 F403 DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": "pytest_django_should_never_get_accessed", - "HOST": "localhost", - "USER": "root", + "USER": environ.get("TEST_DB_USER", "root"), + "PASSWORD": environ.get("TEST_DB_PASSWORD", ""), + "HOST": environ.get("TEST_DB_HOST", "localhost"), "OPTIONS": {"init_command": "SET default_storage_engine=MyISAM"}, } } diff --git a/pytest_django_test/settings_postgres.py b/pytest_django_test/settings_postgres.py index a926438b6..5c387ef7b 100644 --- a/pytest_django_test/settings_postgres.py +++ b/pytest_django_test/settings_postgres.py @@ -1,3 +1,5 @@ +from os import environ + from .settings_base import * # noqa: F401 F403 # PyPy compatibility @@ -13,5 +15,8 @@ "default": { "ENGINE": "django.db.backends.postgresql", "NAME": "pytest_django_should_never_get_accessed", - } + "USER": environ.get("TEST_DB_USER", ""), + "PASSWORD": environ.get("TEST_DB_PASSWORD", ""), + "HOST": environ.get("TEST_DB_HOST", ""), + }, } diff --git a/tests/test_db_access_in_repr.py b/tests/test_db_access_in_repr.py index 89158c40e..c8511cf17 100644 --- a/tests/test_db_access_in_repr.py +++ b/tests/test_db_access_in_repr.py @@ -21,7 +21,7 @@ def test_via_db_fixture(db): "tpkg/test_the_test.py:8: ", 'self = *RuntimeError*Database access not allowed*', "E *DoesNotExist: Item matching query does not exist.", - "* 2 failed in *", + "* 2 failed*", ]) assert "INTERNALERROR" not in str(result.stdout) + str(result.stderr) assert result.ret == 1 diff --git a/tests/test_db_setup.py b/tests/test_db_setup.py index 6499b1019..21e065948 100644 --- a/tests/test_db_setup.py +++ b/tests/test_db_setup.py @@ -480,7 +480,7 @@ def test_inner_migrations(): ) assert result.ret == 0 assert "Operations to perform:" not in result.stdout.str() - result.stdout.fnmatch_lines(["*= 1 passed in *"]) + result.stdout.fnmatch_lines(["*= 1 passed*"]) def test_migrations_run(self, django_testdir): testdir = django_testdir diff --git a/tests/test_django_configurations.py b/tests/test_django_configurations.py index 11d9c0ffd..70c0126ab 100644 --- a/tests/test_django_configurations.py +++ b/tests/test_django_configurations.py @@ -42,7 +42,7 @@ def test_settings(): result = testdir.runpytest_subprocess() result.stdout.fnmatch_lines([ 'django: settings: tpkg.settings_env (from env), configuration: MySettings (from env)', - "* 1 passed in*", + "* 1 passed*", ]) assert result.ret == 0 @@ -73,7 +73,7 @@ def test_ds(): result = testdir.runpytest_subprocess() result.stdout.fnmatch_lines([ 'django: settings: tpkg.settings_env (from env), configuration: MySettings (from env)', - "* 1 passed in*", + "* 1 passed*", ]) assert result.ret == 0 @@ -103,7 +103,7 @@ def test_ds(): result = testdir.runpytest_subprocess() result.stdout.fnmatch_lines([ 'django: settings: tpkg.settings_ini (from ini), configuration: MySettings (from ini)', - "* 1 passed in*", + "* 1 passed*", ]) assert result.ret == 0 @@ -135,6 +135,6 @@ def test_ds(): result.stdout.fnmatch_lines([ 'django: settings: tpkg.settings_opt (from option),' ' configuration: MySettings (from option)', - "* 1 passed in*", + "* 1 passed*", ]) assert result.ret == 0 diff --git a/tests/test_django_settings_module.py b/tests/test_django_settings_module.py index 685937cce..da16ff543 100644 --- a/tests/test_django_settings_module.py +++ b/tests/test_django_settings_module.py @@ -39,7 +39,7 @@ def test_ds(): result = testdir.runpytest_subprocess() result.stdout.fnmatch_lines([ "django: settings: tpkg.settings_ini (from ini)", - "*= 1 passed in *", + "*= 1 passed*", ]) assert result.ret == 0 @@ -60,7 +60,7 @@ def test_settings(): result = testdir.runpytest_subprocess() result.stdout.fnmatch_lines([ "django: settings: tpkg.settings_env (from env)", - "*= 1 passed in *", + "*= 1 passed*", ]) @@ -86,7 +86,7 @@ def test_ds(): result = testdir.runpytest_subprocess("--ds=tpkg.settings_opt") result.stdout.fnmatch_lines([ "django: settings: tpkg.settings_opt (from option)", - "*= 1 passed in *", + "*= 1 passed*", ]) @@ -137,7 +137,7 @@ def test_ds_after_user_conftest(testdir, monkeypatch): testdir.makepyfile(settings_after_conftest="SECRET_KEY='secret'") # testdir.makeconftest("import sys; print(sys.path)") result = testdir.runpytest_subprocess("-v") - result.stdout.fnmatch_lines(["* 1 passed in*"]) + result.stdout.fnmatch_lines(["* 1 passed*"]) assert result.ret == 0 @@ -225,7 +225,7 @@ def test_user_count(): """ ) result = testdir.runpython(p) - result.stdout.fnmatch_lines(["* 4 passed in*"]) + result.stdout.fnmatch_lines(["* 4 passed*"]) def test_settings_in_hook(testdir, monkeypatch): @@ -274,7 +274,7 @@ def test_settings(): """ ) result = testdir.runpytest_subprocess() - result.stdout.fnmatch_lines(["* 1 passed in*"]) + result.stdout.fnmatch_lines(["* 1 passed*"]) assert result.ret == 0 diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 0a25f771f..6a8e4208a 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -564,7 +564,7 @@ class Migration(migrations.Migration): ) result = django_testdir.runpytest_subprocess("-s") - result.stdout.fnmatch_lines(["* 1 passed in*"]) + result.stdout.fnmatch_lines(["* 1 passed*"]) assert result.ret == 0 diff --git a/tests/test_initialization.py b/tests/test_initialization.py index 33544bd26..b30c46f51 100644 --- a/tests/test_initialization.py +++ b/tests/test_initialization.py @@ -54,7 +54,7 @@ def test_ds(): "conftest", "pytest_configure: conftest", "pytest_configure: plugin", - "* 1 passed in*", + "* 1 passed*", ] ) assert result.ret == 0 diff --git a/tests/test_unittest.py b/tests/test_unittest.py index 95ea3eb3c..f9c01d9ed 100644 --- a/tests/test_unittest.py +++ b/tests/test_unittest.py @@ -97,7 +97,7 @@ def test_bar(self): '> assert 0, "trigger_error"', "E AssertionError: trigger_error", "E assert 0", - "*= 1 failed, 1 passed in *", + "*= 1 failed, 1 passed*", ] ) assert result.ret == 1 @@ -399,7 +399,7 @@ def test_noop(self): result = django_testdir.runpytest_subprocess("-q", "-s") result.stdout.fnmatch_lines( - ["*FooBarTestCase.setUpClass*", "*test_noop*", "1 passed in*"] + ["*FooBarTestCase.setUpClass*", "*test_noop*", "1 passed*"] ) assert result.ret == 0 @@ -484,5 +484,5 @@ def test_method(self): ) result = django_testdir.runpytest_subprocess("--pdb") - result.stdout.fnmatch_lines(["*= 1 passed in *"]) + result.stdout.fnmatch_lines(["*= 1 passed*"]) assert result.ret == 0 diff --git a/tox.ini b/tox.ini index c7ffafaf5..c24da8e9e 100644 --- a/tox.ini +++ b/tox.ini @@ -17,8 +17,8 @@ deps = mysql_myisam: mysqlclient==1.4.2.post1 mysql_innodb: mysqlclient==1.4.2.post1 - postgres: psycopg2-binary - postgres: psycopg2cffi + !pypy3-postgres: psycopg2-binary + pypy3-postgres: psycopg2cffi coverage: coverage-enable-subprocess pytest54: pytest>=5.4,<5.5 @@ -38,7 +38,7 @@ setenv = coverage: COVERAGE_FILE={toxinidir}/.coverage coverage: PYTESTDJANGO_COVERAGE_SRC={toxinidir}/ -passenv = PYTEST_ADDOPTS TERM +passenv = PYTEST_ADDOPTS TERM TEST_DB_USER TEST_DB_PASSWORD TEST_DB_HOST usedevelop = True commands = coverage: coverage erase