diff --git a/cms/envs/test.py b/cms/envs/test.py index 9abe6e0e07..cb2fc8c72c 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -21,7 +21,6 @@ sessions. Assumes structure: from .common import * import os from path import Path as path -from warnings import filterwarnings, simplefilter from uuid import uuid4 from util.db import NoOpMigrationModules from openedx.core.lib.derived import derive_settings @@ -175,15 +174,6 @@ CACHES = { }, } -# hide ratelimit warnings while running tests -filterwarnings('ignore', message='No request passed to the backend, unable to rate-limit') - -# Ignore deprecation warnings (so we don't clutter Jenkins builds/production) -# https://docs.python.org/2/library/warnings.html#the-warnings-filter -# Change to "default" to see the first instance of each hit -# or "error" to convert all into errors -simplefilter('ignore') - ################################# CELERY ###################################### CELERY_ALWAYS_EAGER = True diff --git a/cms/pytest.ini b/cms/pytest.ini index c12268a41f..043a960b8d 100644 --- a/cms/pytest.ini +++ b/cms/pytest.ini @@ -1,6 +1,11 @@ [pytest] DJANGO_SETTINGS_MODULE = cms.envs.test addopts = --nomigrations --reuse-db --durations=20 -p no:randomly +# Enable default handling for all warnings, including those that are ignored by default; +# but hide rate-limit warnings, because we deliberately don't throttle test user logins +filterwarnings = + default + ignore:No request passed to the backend, unable to rate-limit:UserWarning norecursedirs = envs python_classes = python_files = tests.py test_*.py *_tests.py diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 309cb495eb..c46a1e7f5d 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -161,7 +161,7 @@ REGISTER_USER = Signal(providing_args=["user", "registration"]) # TODO: Remove Django 1.11 upgrade shim # SHIM: Compensate for behavior change of default authentication backend in 1.10 -if django.VERSION[0] == 1 and django.VERSION[1] < 10: +if django.VERSION < (1, 10): NEW_USER_AUTH_BACKEND = 'django.contrib.auth.backends.ModelBackend' else: # We want to allow inactive users to log in only when their account is first created @@ -171,6 +171,18 @@ else: # pylint: disable=logging-format-interpolation +def authenticate_new_user(request, username, password): + """ + Immediately after a user creates an account, we log them in. They are only + logged in until they close the browser. They can't log in again until they click + the activation link from the email. + """ + backend = load_backend(NEW_USER_AUTH_BACKEND) + user = backend.authenticate(request=request, username=username, password=password) + user.backend = NEW_USER_AUTH_BACKEND + return user + + def csrf_token(context): """A csrf token that can be included in a form.""" token = context.get('csrf_token', '') @@ -2126,12 +2138,7 @@ def create_account_with_params(request, params): else: compose_and_send_activation_email(user, profile, registration) - # Immediately after a user creates an account, we log them in. They are only - # logged in until they close the browser. They can't log in again until they click - # the activation link from the email. - backend = load_backend(NEW_USER_AUTH_BACKEND) - new_user = backend.authenticate(request=request, username=user.username, password=params['password']) - new_user.backend = NEW_USER_AUTH_BACKEND + new_user = authenticate_new_user(request, user.username, params['password']) login(request, new_user) request.session.set_expiry(0) @@ -2447,7 +2454,7 @@ def auto_auth(request): # Log in as the user if login_when_done: - user = authenticate(username=username, password=password) + user = authenticate_new_user(request, username, password) login(request, user) create_comments_service_user(user) diff --git a/common/lib/pytest.ini b/common/lib/pytest.ini index 349b1dff15..b83ce7cd70 100644 --- a/common/lib/pytest.ini +++ b/common/lib/pytest.ini @@ -1,6 +1,11 @@ [pytest] DJANGO_SETTINGS_MODULE = openedx.tests.settings addopts = --nomigrations --reuse-db --durations=20 +# Enable default handling for all warnings, including those that are ignored by default; +# but hide rate-limit warnings, because we deliberately don't throttle test user logins +filterwarnings = + default + ignore:No request passed to the backend, unable to rate-limit:UserWarning norecursedirs = .cache python_classes = python_files = tests.py test_*.py tests_*.py *_tests.py __init__.py diff --git a/common/test/pytest.ini b/common/test/pytest.ini index d8172d2726..2bbdadfc0f 100644 --- a/common/test/pytest.ini +++ b/common/test/pytest.ini @@ -1,3 +1,8 @@ [pytest] addopts = -p no:randomly --durations=20 +# Enable default handling for all warnings, including those that are ignored by default; +# but hide rate-limit warnings, because we deliberately don't throttle test user logins +filterwarnings = + default + ignore:No request passed to the backend, unable to rate-limit:UserWarning norecursedirs = .cache diff --git a/lms/envs/test.py b/lms/envs/test.py index f32d50b6e7..2ee5c4ddcf 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -22,7 +22,6 @@ from .common import * import os from path import Path as path from uuid import uuid4 -from warnings import filterwarnings, simplefilter from util.db import NoOpMigrationModules from openedx.core.lib.derived import derive_settings @@ -232,15 +231,6 @@ CACHES = { # Dummy secret key for dev SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd' -# hide ratelimit warnings while running tests -filterwarnings('ignore', message='No request passed to the backend, unable to rate-limit') - -# Ignore deprecation warnings (so we don't clutter Jenkins builds/production) -# https://docs.python.org/2/library/warnings.html#the-warnings-filter -# Change to "default" to see the first instance of each hit -# or "error" to convert all into errors -simplefilter('ignore') - ############################# SECURITY SETTINGS ################################ # Default to advanced security in common.py, so tests can reset here to use # a simpler security model diff --git a/openedx/tests/settings.py b/openedx/tests/settings.py index 85e1df4f71..ccfc7217be 100644 --- a/openedx/tests/settings.py +++ b/openedx/tests/settings.py @@ -58,10 +58,12 @@ INSTALLED_APPS = ( 'djcelery', 'openedx.core.djangoapps.video_config', 'openedx.core.djangoapps.video_pipeline', + 'openedx.core.djangoapps.bookmarks.apps.BookmarksConfig', 'edxval', 'courseware', 'student', 'certificates.apps.CertificatesConfig', + 'openedx.core.djangoapps.user_api', 'course_modes.apps.CourseModesConfig', 'lms.djangoapps.verify_student.apps.VerifyStudentConfig', 'openedx.core.djangoapps.dark_lang', @@ -71,6 +73,7 @@ INSTALLED_APPS = ( 'openedx.core.djangoapps.self_paced', 'milestones', 'celery_utils', + 'lms.djangoapps.completion.apps.CompletionAppConfig', ) LMS_ROOT_URL = 'http://localhost:8000' diff --git a/openedx/tests/util/__init__.py b/openedx/tests/util/__init__.py index b99c3c88a6..0db3fb023f 100644 --- a/openedx/tests/util/__init__.py +++ b/openedx/tests/util/__init__.py @@ -12,6 +12,7 @@ def expected_redirect_url(relative_url, hostname='testserver'): """ Get the expected redirect URL for the current Django version and the given relative URL: + * Django 1.8 and earlier redirect URLs beginning with a slash to absolute URLs, later versions redirect to relative ones. * Django 1.8 and earlier leave URLs without a leading slash alone, later diff --git a/scripts/generic-ci-tests.sh b/scripts/generic-ci-tests.sh index fcc99748ad..bd08c59169 100755 --- a/scripts/generic-ci-tests.sh +++ b/scripts/generic-ci-tests.sh @@ -131,14 +131,8 @@ case "$TEST_SUITE" in "lms-unit") case "$SHARD" in - "all") - $TOX paver test_system -s lms --disable_capture $PAVER_ARGS $PARALLEL 2> lms-tests.log - ;; - [1-3]) - $TOX paver test_system -s lms --disable_capture --eval-attr="shard==$SHARD" $PAVER_ARGS $PARALLEL 2> lms-tests.$SHARD.log - ;; - 4|"noshard") - $TOX paver test_system -s lms --disable_capture --eval-attr='not shard' $PAVER_ARGS $PARALLEL 2> lms-tests.4.log + "all"|[1-4]|"noshard") + $TOX bash scripts/unit-tests.sh ;; *) # If no shard is specified, rather than running all tests, create an empty xunit file. This is a @@ -151,12 +145,8 @@ case "$TEST_SUITE" in esac ;; - "cms-unit") - $TOX paver test_system -s cms --disable_capture $PAVER_ARGS 2> cms-tests.log - ;; - - "commonlib-unit") - $TOX paver test_lib --disable_capture $PAVER_ARGS 2> common-tests.log + "cms-unit"|"commonlib-unit") + $TOX bash scripts/unit-tests.sh ;; "js-unit") diff --git a/scripts/unit-tests.sh b/scripts/unit-tests.sh new file mode 100755 index 0000000000..f4cb1164b7 --- /dev/null +++ b/scripts/unit-tests.sh @@ -0,0 +1,58 @@ +#!/bin/bash +set -e + +############################################################################### +# +# unit-tests.sh +# +# Execute Python unit tests for edx-platform. +# +# This script is typically called from generic-ci-tests.sh, which defines +# these environment variables: +# +# `TEST_SUITE` defines which kind of test to run. +# Possible values are: +# +# - "lms-unit": Run the LMS Python unit tests +# - "cms-unit": Run the CMS Python unit tests +# - "commonlib-unit": Run Python unit tests from the common/lib directory +# +# `SHARD` is a number indicating which subset of the tests to build. +# +# For "lms-unit", the tests are put into shard groups +# using the 'attr' decorator (e.g. "@attr(shard=1)"). Anything with +# the 'shard=n' attribute will run in the nth shard. If there isn't a +# shard explicitly assigned, the test will run in the last shard. +# +# This script is broken out so it can be run by tox and redirect stderr to +# the specified file before tox gets a chance to redirect it to stdout. +# +############################################################################### + +PAVER_ARGS="-v" +PARALLEL="--processes=-1" + +case "${TEST_SUITE}" in + + "lms-unit") + case "$SHARD" in + "all") + paver test_system -s lms --disable_capture ${PAVER_ARGS} ${PARALLEL} 2> lms-tests.log + ;; + [1-3]) + paver test_system -s lms --disable_capture --eval-attr="shard==$SHARD" ${PAVER_ARGS} ${PARALLEL} 2> lms-tests.${SHARD}.log + ;; + 4|"noshard") + paver test_system -s lms --disable_capture --eval-attr='not shard' ${PAVER_ARGS} ${PARALLEL} 2> lms-tests.4.log + ;; + esac + ;; + + "cms-unit") + paver test_system -s cms --disable_capture ${PAVER_ARGS} 2> cms-tests.log + ;; + + "commonlib-unit") + paver test_lib --disable_capture ${PAVER_ARGS} 2> common-tests.log + ;; +esac diff --git a/setup.cfg b/setup.cfg index 0c96005f23..2d773758c1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,6 +17,11 @@ process-timeout=300 [tool:pytest] DJANGO_SETTINGS_MODULE = lms.envs.test addopts = --nomigrations --reuse-db --durations=20 +# Enable default handling for all warnings, including those that are ignored by default; +# but hide rate-limit warnings, because we deliberately don't throttle test user logins +filterwarnings = + default + ignore:No request passed to the backend, unable to rate-limit:UserWarning norecursedirs = .* *.egg build conf dist node_modules test_root cms/envs lms/envs python_classes = python_files = tests.py test_*.py tests_*.py *_tests.py __init__.py diff --git a/tox.ini b/tox.ini index 5b3d2bfb66..f52278750d 100644 --- a/tox.ini +++ b/tox.ini @@ -42,6 +42,8 @@ passenv = SELENIUM_BROWSER SELENIUM_HOST SELENIUM_PORT + SHARD + TEST_SUITE deps = django18: Django>=1.8,<1.9 django19: Django>=1.9,<1.10