diff --git a/django/contrib/gis/db/backends/mysql/features.py b/django/contrib/gis/db/backends/mysql/features.py index 63e2fa1dd3..72e59d2278 100644 --- a/django/contrib/gis/db/backends/mysql/features.py +++ b/django/contrib/gis/db/backends/mysql/features.py @@ -24,3 +24,17 @@ class DatabaseFeatures(BaseSpatialFeatures, MySQLDatabaseFeatures): def supports_geometry_field_unique_index(self): # Not supported in MySQL since https://dev.mysql.com/worklog/task/?id=11808 return self.connection.mysql_is_mariadb + + @cached_property + def django_test_skips(self): + skips = super().django_test_skips + if ( + not self.connection.mysql_is_mariadb and + self.connection.mysql_version < (8, 0, 0) + ): + skips.update({ + 'MySQL < 8 gives different results.': { + 'gis_tests.geoapp.tests.GeoLookupTest.test_disjoint_lookup', + }, + }) + return skips diff --git a/django/contrib/gis/db/backends/spatialite/features.py b/django/contrib/gis/db/backends/spatialite/features.py index 6e5bd95179..947d874b30 100644 --- a/django/contrib/gis/db/backends/spatialite/features.py +++ b/django/contrib/gis/db/backends/spatialite/features.py @@ -12,3 +12,13 @@ class DatabaseFeatures(BaseSpatialFeatures, SQLiteDatabaseFeatures): @cached_property def supports_area_geodetic(self): return bool(self.connection.ops.lwgeom_version()) + + @cached_property + def django_test_skips(self): + skips = super().django_test_skips + skips.update({ + "SpatiaLite doesn't support distance lookups with Distance objects.": { + 'gis_tests.geogapp.tests.GeographyTest.test02_distance_lookup', + }, + }) + return skips diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py index 0099dd18bb..81cb34bd9f 100644 --- a/django/db/backends/base/creation.py +++ b/django/db/backends/base/creation.py @@ -1,12 +1,14 @@ import os import sys from io import StringIO +from unittest import expectedFailure, skip from django.apps import apps from django.conf import settings from django.core import serializers from django.db import router from django.db.transaction import atomic +from django.utils.module_loading import import_string # The prefix to put on the default database name when creating # the test database. @@ -92,6 +94,9 @@ class BaseDatabaseCreation: # Ensure a connection for the side effect of initializing the test database. self.connection.ensure_connection() + if os.environ.get('RUNNING_DJANGOS_TEST_SUITE') == 'true': + self.mark_expected_failures_and_skips() + return test_database_name def set_as_test_mirror(self, primary_settings_dict): @@ -293,6 +298,29 @@ class BaseDatabaseCreation: cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name)) + def mark_expected_failures_and_skips(self): + """ + Mark tests in Django's test suite which are expected failures on this + database and test which should be skipped on this database. + """ + for test_name in self.connection.features.django_test_expected_failures: + test_case_name, _, test_method_name = test_name.rpartition('.') + test_app = test_name.split('.')[0] + # Importing a test app that isn't installed raises RuntimeError. + if test_app in settings.INSTALLED_APPS: + test_case = import_string(test_case_name) + test_method = getattr(test_case, test_method_name) + setattr(test_case, test_method_name, expectedFailure(test_method)) + for reason, tests in self.connection.features.django_test_skips.items(): + for test_name in tests: + test_case_name, _, test_method_name = test_name.rpartition('.') + test_app = test_name.split('.')[0] + # Importing a test app that isn't installed raises RuntimeError. + if test_app in settings.INSTALLED_APPS: + test_case = import_string(test_case_name) + test_method = getattr(test_case, test_method_name) + setattr(test_case, test_method_name, skip(reason)(test_method)) + def sql_table_creation_suffix(self): """ SQL to append to the end of the test table creation statements. diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 125d52a21f..885033361a 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -323,6 +323,13 @@ class BaseDatabaseFeatures: 'swedish_ci': None # Swedish case-insensitive. } + # A set of dotted paths to tests in Django's test suite that are expected + # to fail on this database. + django_test_expected_failures = {} + # A map of reasons to sets of dotted paths to tests in Django's test suite + # that should be skipped for this database. + django_test_skips = {} + def __init__(self, connection): self.connection = connection diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index 3713df549b..72fff72648 100644 --- a/django/db/backends/mysql/features.py +++ b/django/db/backends/mysql/features.py @@ -50,6 +50,52 @@ class DatabaseFeatures(BaseDatabaseFeatures): 'swedish_ci': 'utf8_swedish_ci', } + @cached_property + def django_test_skips(self): + skips = { + "This doesn't work on MySQL.": { + 'db_functions.comparison.test_greatest.GreatestTests.test_coalesce_workaround', + 'db_functions.comparison.test_least.LeastTests.test_coalesce_workaround', + }, + 'Running on MySQL requires utf8mb4 encoding (#18392).': { + 'model_fields.test_textfield.TextFieldTests.test_emoji', + 'model_fields.test_charfield.TestCharField.test_emoji', + }, + } + if 'ONLY_FULL_GROUP_BY' in self.connection.sql_mode: + skips.update({ + 'GROUP BY optimization does not work properly when ' + 'ONLY_FULL_GROUP_BY mode is enabled on MySQL, see #31331.': { + 'aggregation.tests.AggregateTestCase.test_aggregation_subquery_annotation_multivalued', + 'annotations.tests.NonAggregateAnnotationTestCase.test_annotation_aggregate_with_m2o', + }, + }) + if ( + self.connection.mysql_is_mariadb and + (10, 4, 3) < self.connection.mysql_version < (10, 5, 2) + ): + skips.update({ + 'https://jira.mariadb.org/browse/MDEV-19598': { + 'schema.tests.SchemaTests.test_alter_not_unique_field_to_primary_key', + }, + }) + if ( + self.connection.mysql_is_mariadb and + (10, 4, 12) < self.connection.mysql_version < (10, 5) + ): + skips.update({ + 'https://jira.mariadb.org/browse/MDEV-22775': { + 'schema.tests.SchemaTests.test_alter_pk_with_self_referential_field', + }, + }) + if not self.supports_explain_analyze: + skips.update({ + 'MariaDB and MySQL >= 8.0.18 specific.': { + 'queries.test_explain.ExplainTests.test_mysql_analyze', + }, + }) + return skips + @cached_property def _mysql_storage_engine(self): "Internal method used in Django tests. Don't rely on this from your code" diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index e66c5da09f..2570675809 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -72,6 +72,28 @@ class DatabaseFeatures(BaseDatabaseFeatures): 'swedish_ci': 'SWEDISH_CI', } + django_test_skips = { + "Oracle doesn't support SHA224.": { + 'db_functions.text.test_sha224.SHA224Tests.test_basic', + 'db_functions.text.test_sha224.SHA224Tests.test_transform', + }, + "Oracle doesn't support bitwise XOR.": { + 'expressions.tests.ExpressionOperatorTests.test_lefthand_bitwise_xor', + 'expressions.tests.ExpressionOperatorTests.test_lefthand_bitwise_xor_null', + }, + "Oracle requires ORDER BY in row_number, ANSI:SQL doesn't.": { + 'expressions_window.tests.WindowFunctionTests.test_row_number_no_ordering', + }, + 'Raises ORA-00600: internal error code on Oracle 18.': { + 'model_fields.test_jsonfield.TestQuerying.test_usage_in_subquery', + }, + } + django_test_expected_failures = { + # A bug in Django/cx_Oracle with respect to string handling (#23843). + 'annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions', + 'annotations.tests.NonAggregateAnnotationTestCase.test_custom_functions_can_ref_other_functions', + } + @cached_property def introspected_field_types(self): return { diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index f5f844ca42..84259c0c19 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -59,6 +59,12 @@ class DatabaseFeatures(BaseDatabaseFeatures): has_json_operators = True json_key_contains_list_matching_requires_list = True + django_test_skips = { + 'opclasses are PostgreSQL only.': { + 'indexes.tests.SchemaIndexesNotPostgreSQLTests.test_create_index_ignores_opclasses', + }, + } + @cached_property def test_collations(self): # PostgreSQL < 10 doesn't support ICU collations. diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py index 880d2e50dd..3348256c74 100644 --- a/django/db/backends/sqlite3/features.py +++ b/django/db/backends/sqlite3/features.py @@ -50,6 +50,37 @@ class DatabaseFeatures(BaseDatabaseFeatures): 'non_default': 'nocase', } + @cached_property + def django_test_skips(self): + skips = { + 'SQLite stores values rounded to 15 significant digits.': { + 'model_fields.test_decimalfield.DecimalFieldTests.test_fetch_from_db_without_float_rounding', + }, + 'SQLite naively remakes the table on field alteration.': { + 'schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops', + 'schema.tests.SchemaTests.test_unique_and_reverse_m2m', + 'schema.tests.SchemaTests.test_alter_field_default_doesnt_perform_queries', + 'schema.tests.SchemaTests.test_rename_column_renames_deferred_sql_references', + }, + "SQLite doesn't have a constraint.": { + 'model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values', + }, + } + if Database.sqlite_version_info < (3, 27): + skips.update({ + 'Nondeterministic failure on SQLite < 3.27.': { + 'expressions_window.tests.WindowFunctionTests.test_subquery_row_range_rank', + }, + }) + if self.connection.is_in_memory_db(): + skips.update({ + "the sqlite backend's close() method is a no-op when using an " + "in-memory database": { + 'servers.test_liveserverthread.LiveServerThreadTest.test_closes_connections', + }, + }) + return skips + @cached_property def supports_atomic_references_rename(self): # SQLite 3.28.0 bundled with MacOS 10.15 does not support renaming diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt index d5047dfe60..b155f37e00 100644 --- a/docs/releases/3.2.txt +++ b/docs/releases/3.2.txt @@ -197,6 +197,14 @@ CSRF * ... +Database backends +~~~~~~~~~~~~~~~~~ + +* Third-party database backends can now skip or mark as expected failures + tests in Django's test suite using the new + ``DatabaseFeatures.django_test_skips`` and + ``django_test_expected_failures`` attributes. + Decorators ~~~~~~~~~~ diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index ad4b94a7f9..0bced06ce9 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -1218,11 +1218,6 @@ class AggregateTestCase(TestCase): Subquery annotations must be included in the GROUP BY if they use potentially multivalued relations (contain the LOOKUP_SEP). """ - if connection.vendor == 'mysql' and 'ONLY_FULL_GROUP_BY' in connection.sql_mode: - self.skipTest( - 'GROUP BY optimization does not work properly when ' - 'ONLY_FULL_GROUP_BY mode is enabled on MySQL, see #31331.' - ) subquery_qs = Author.objects.filter( pk=OuterRef('pk'), book__name=OuterRef('book__name'), diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index cc9bd82656..8337f344ed 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -2,7 +2,6 @@ import datetime from decimal import Decimal from django.core.exceptions import FieldDoesNotExist, FieldError -from django.db import connection from django.db.models import ( BooleanField, Case, CharField, Count, DateTimeField, DecimalField, Exists, ExpressionWrapper, F, FloatField, Func, IntegerField, Max, @@ -20,19 +19,6 @@ from .models import ( ) -def cxOracle_py3_bug(func): - """ - There's a bug in Django/cx_Oracle with respect to string handling under - Python 3 (essentially, they treat Python 3 strings as Python 2 strings - rather than unicode). This makes some tests here fail under Python 3, so - we mark them as expected failures until someone fixes them in #23843. - """ - from unittest import expectedFailure - - from django.db import connection - return expectedFailure(func) if connection.vendor == 'oracle' else func - - class NonAggregateAnnotationTestCase(TestCase): @classmethod @@ -590,7 +576,6 @@ class NonAggregateAnnotationTestCase(TestCase): e.id, e.first_name, e.manager, e.random_value, e.last_name, e.age, e.salary, e.store.name, e.annotated_value)) - @cxOracle_py3_bug def test_custom_functions(self): Company(name='Apple', motto=None, ticker_name='APPL', description='Beautiful Devices').save() Company(name='Django Software Foundation', motto=None, ticker_name=None, description=None).save() @@ -617,7 +602,6 @@ class NonAggregateAnnotationTestCase(TestCase): lambda c: (c.name, c.tagline) ) - @cxOracle_py3_bug def test_custom_functions_can_ref_other_functions(self): Company(name='Apple', motto=None, ticker_name='APPL', description='Beautiful Devices').save() Company(name='Django Software Foundation', motto=None, ticker_name=None, description=None).save() @@ -770,11 +754,6 @@ class NonAggregateAnnotationTestCase(TestCase): ]) def test_annotation_aggregate_with_m2o(self): - if connection.vendor == 'mysql' and 'ONLY_FULL_GROUP_BY' in connection.sql_mode: - self.skipTest( - 'GROUP BY optimization does not work properly when ' - 'ONLY_FULL_GROUP_BY mode is enabled on MySQL, see #31331.' - ) qs = Author.objects.filter(age__lt=30).annotate( max_pages=Case( When(book_contact_set__isnull=True, then=Value(0)), diff --git a/tests/backends/base/test_creation.py b/tests/backends/base/test_creation.py index 3e29961d21..6c98556f1e 100644 --- a/tests/backends/base/test_creation.py +++ b/tests/backends/base/test_creation.py @@ -1,5 +1,6 @@ import copy import datetime +import os from unittest import mock from django.db import DEFAULT_DB_ALIAS, connection, connections @@ -107,6 +108,22 @@ class TestDbCreationTests(SimpleTestCase): with mock.patch.object(creation, '_destroy_test_db'): creation.destroy_test_db(old_database_name, verbosity=0) + @mock.patch.dict(os.environ, {'RUNNING_DJANGOS_TEST_SUITE': ''}) + @mock.patch.object(BaseDatabaseCreation, 'mark_expected_failures_and_skips') + def test_mark_expected_failures_and_skips_call(self, mark_expected_failures_and_skips, *mocked_objects): + """ + mark_expected_failures_and_skips() isn't called unless + RUNNING_DJANGOS_TEST_SUITE is 'true'. + """ + test_connection = get_connection_copy() + creation = test_connection.creation_class(test_connection) + if connection.vendor == 'oracle': + # Don't close connection on Oracle. + creation.connection.close = mock.Mock() + with mock.patch.object(creation, '_create_test_db'): + creation.create_test_db(verbosity=0, autoclobber=True, serialize=False) + self.assertIs(mark_expected_failures_and_skips.called, False) + class TestDeserializeDbFromString(TransactionTestCase): available_apps = ['backends'] @@ -188,3 +205,48 @@ class TestDeserializeDbFromString(TransactionTestCase): data = connection.creation.serialize_db_to_string() self.assertIn('"model": "backends.schoolclass"', data) self.assertIn('"year": 1000', data) + + +class SkipTestClass: + def skip_function(self): + pass + + +def skip_test_function(): + pass + + +def expected_failure_test_function(): + pass + + +class TestMarkTests(SimpleTestCase): + def test_mark_expected_failures_and_skips(self): + test_connection = get_connection_copy() + creation = BaseDatabaseCreation(test_connection) + creation.connection.features.django_test_expected_failures = { + 'backends.base.test_creation.expected_failure_test_function', + } + creation.connection.features.django_test_skips = { + 'skip test class': { + 'backends.base.test_creation.SkipTestClass', + }, + 'skip test function': { + 'backends.base.test_creation.skip_test_function', + }, + } + creation.mark_expected_failures_and_skips() + self.assertIs( + expected_failure_test_function.__unittest_expecting_failure__, + True, + ) + self.assertIs(SkipTestClass.__unittest_skip__, True) + self.assertEqual( + SkipTestClass.__unittest_skip_why__, + 'skip test class', + ) + self.assertIs(skip_test_function.__unittest_skip__, True) + self.assertEqual( + skip_test_function.__unittest_skip_why__, + 'skip test function', + ) diff --git a/tests/db_functions/comparison/test_greatest.py b/tests/db_functions/comparison/test_greatest.py index ef93d808c2..f11e7b824c 100644 --- a/tests/db_functions/comparison/test_greatest.py +++ b/tests/db_functions/comparison/test_greatest.py @@ -1,6 +1,6 @@ from datetime import datetime, timedelta from decimal import Decimal -from unittest import skipIf, skipUnless +from unittest import skipUnless from django.db import connection from django.db.models.expressions import RawSQL @@ -33,7 +33,6 @@ class GreatestTests(TestCase): articles = Article.objects.annotate(last_updated=Greatest('written', 'published')) self.assertIsNone(articles.first().last_updated) - @skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL") def test_coalesce_workaround(self): past = datetime(1900, 1, 1) now = timezone.now() diff --git a/tests/db_functions/comparison/test_least.py b/tests/db_functions/comparison/test_least.py index de2c543f0b..e0318e25c6 100644 --- a/tests/db_functions/comparison/test_least.py +++ b/tests/db_functions/comparison/test_least.py @@ -1,6 +1,6 @@ from datetime import datetime, timedelta from decimal import Decimal -from unittest import skipIf, skipUnless +from unittest import skipUnless from django.db import connection from django.db.models.expressions import RawSQL @@ -35,7 +35,6 @@ class LeastTests(TestCase): articles = Article.objects.annotate(first_updated=Least('written', 'published')) self.assertIsNone(articles.first().first_updated) - @skipIf(connection.vendor == 'mysql', "This doesn't work on MySQL") def test_coalesce_workaround(self): future = datetime(2100, 1, 1) now = timezone.now() diff --git a/tests/db_functions/text/test_sha224.py b/tests/db_functions/text/test_sha224.py index a8291d416c..d2f1d538dc 100644 --- a/tests/db_functions/text/test_sha224.py +++ b/tests/db_functions/text/test_sha224.py @@ -20,7 +20,6 @@ class SHA224Tests(TestCase): Author(alias=None), ]) - @unittest.skipIf(connection.vendor == 'oracle', "Oracle doesn't support SHA224.") def test_basic(self): authors = Author.objects.annotate( sha224_alias=SHA224('alias'), @@ -37,7 +36,6 @@ class SHA224Tests(TestCase): ], ) - @unittest.skipIf(connection.vendor == 'oracle', "Oracle doesn't support SHA224.") def test_transform(self): with register_lookup(CharField, SHA224): authors = Author.objects.filter( diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index d8d8370cb3..463150f8a1 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -1230,13 +1230,11 @@ class ExpressionOperatorTests(TestCase): self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 1764) self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(61.02, places=2)) - @unittest.skipIf(connection.vendor == 'oracle', "Oracle doesn't support bitwise XOR.") def test_lefthand_bitwise_xor(self): Number.objects.update(integer=F('integer').bitxor(48)) self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 26) self.assertEqual(Number.objects.get(pk=self.n1.pk).integer, -26) - @unittest.skipIf(connection.vendor == 'oracle', "Oracle doesn't support bitwise XOR.") def test_lefthand_bitwise_xor_null(self): employee = Employee.objects.create(firstname='John', lastname='Doe') Employee.objects.update(salary=F('salary').bitxor(48)) diff --git a/tests/expressions_window/tests.py b/tests/expressions_window/tests.py index 08f215215b..8b1b4a8a3f 100644 --- a/tests/expressions_window/tests.py +++ b/tests/expressions_window/tests.py @@ -1,6 +1,6 @@ import datetime from decimal import Decimal -from unittest import mock, skipIf +from unittest import mock from django.core.exceptions import FieldError from django.db import NotSupportedError, connection @@ -150,7 +150,6 @@ class WindowFunctionTests(TestCase): ('Johnson', 'Management', 12), ], lambda entry: (entry.name, entry.department, entry.row_number)) - @skipIf(connection.vendor == 'oracle', "Oracle requires ORDER BY in row_number, ANSI:SQL doesn't") def test_row_number_no_ordering(self): """ The row number window function computes the number based on the order @@ -633,10 +632,6 @@ class WindowFunctionTests(TestCase): ('Brown', 'Sales', 53000, datetime.date(2009, 9, 1), 148000) ], transform=lambda row: (row.name, row.department, row.salary, row.hire_date, row.sum)) - @skipIf( - connection.vendor == 'sqlite' and connection.Database.sqlite_version_info < (3, 27), - 'Nondeterministic failure on SQLite < 3.27.' - ) def test_subquery_row_range_rank(self): qs = Employee.objects.annotate( highest_avg_salary_date=Subquery( diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index 350bd04629..94cb4b10de 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -1,5 +1,4 @@ import tempfile -import unittest from io import StringIO from django.contrib.gis import gdal @@ -229,8 +228,6 @@ class GeoLookupTest(TestCase): def test_disjoint_lookup(self): "Testing the `disjoint` lookup type." - if mysql and not mariadb and connection.mysql_version < (8, 0, 0): - raise unittest.SkipTest('MySQL < 8 gives different results.') ptown = City.objects.get(name='Pueblo') qs1 = City.objects.filter(point__disjoint=ptown.point) self.assertEqual(7, qs1.count()) diff --git a/tests/gis_tests/geogapp/tests.py b/tests/gis_tests/geogapp/tests.py index 507d8a970e..0faf749e14 100644 --- a/tests/gis_tests/geogapp/tests.py +++ b/tests/gis_tests/geogapp/tests.py @@ -2,7 +2,7 @@ Tests for geography support in PostGIS """ import os -from unittest import skipIf, skipUnless +from unittest import skipUnless from django.contrib.gis.db import models from django.contrib.gis.db.models.functions import Area, Distance @@ -22,7 +22,6 @@ class GeographyTest(TestCase): "Ensure geography features loaded properly." self.assertEqual(8, City.objects.count()) - @skipIf(spatialite, "SpatiaLite doesn't support distance lookups with Distance objects.") @skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic") def test02_distance_lookup(self): "Testing distance lookup support on non-point geography fields." diff --git a/tests/indexes/tests.py b/tests/indexes/tests.py index 3c4541f684..6d01e3b52f 100644 --- a/tests/indexes/tests.py +++ b/tests/indexes/tests.py @@ -1,5 +1,5 @@ import datetime -from unittest import skipIf, skipUnless +from unittest import skipUnless from django.db import connection from django.db.models import CASCADE, ForeignKey, Index, Q @@ -89,7 +89,6 @@ class SchemaIndexesTests(TestCase): ) -@skipIf(connection.vendor == 'postgresql', 'opclasses are PostgreSQL only') class SchemaIndexesNotPostgreSQLTests(TransactionTestCase): available_apps = ['indexes'] diff --git a/tests/model_fields/test_charfield.py b/tests/model_fields/test_charfield.py index 0e6109c860..17c230c776 100644 --- a/tests/model_fields/test_charfield.py +++ b/tests/model_fields/test_charfield.py @@ -1,7 +1,5 @@ -from unittest import skipIf - from django.core.exceptions import ValidationError -from django.db import connection, models +from django.db import models from django.test import SimpleTestCase, TestCase from .models import Post @@ -22,7 +20,6 @@ class TestCharField(TestCase): def test_lookup_integer_in_charfield(self): self.assertEqual(Post.objects.filter(title=9).count(), 0) - @skipIf(connection.vendor == 'mysql', 'Running on MySQL requires utf8mb4 encoding (#18392)') def test_emoji(self): p = Post.objects.create(title='Smile 😀', body='Whatever.') p.refresh_from_db() diff --git a/tests/model_fields/test_decimalfield.py b/tests/model_fields/test_decimalfield.py index 729b5e570a..2d96cfd437 100644 --- a/tests/model_fields/test_decimalfield.py +++ b/tests/model_fields/test_decimalfield.py @@ -1,9 +1,8 @@ -import unittest from decimal import Decimal from django.core import validators from django.core.exceptions import ValidationError -from django.db import connection, models +from django.db import models from django.test import TestCase from .models import BigD, Foo @@ -66,7 +65,6 @@ class DecimalFieldTests(TestCase): bd = BigD.objects.get(pk=bd.pk) self.assertEqual(bd.d, Decimal('12.9')) - @unittest.skipIf(connection.vendor == 'sqlite', 'SQLite stores values rounded to 15 significant digits.') def test_fetch_from_db_without_float_rounding(self): big_decimal = BigD.objects.create(d=Decimal('.100000000000000000000000000005')) big_decimal.refresh_from_db() diff --git a/tests/model_fields/test_integerfield.py b/tests/model_fields/test_integerfield.py index 13b18d967c..af5a9f2e35 100644 --- a/tests/model_fields/test_integerfield.py +++ b/tests/model_fields/test_integerfield.py @@ -1,5 +1,3 @@ -import unittest - from django.core import validators from django.core.exceptions import ValidationError from django.db import IntegrityError, connection, models @@ -192,7 +190,6 @@ class PositiveIntegerFieldTests(IntegerFieldTests): else models.IntegerField ) - @unittest.skipIf(connection.vendor == 'sqlite', "SQLite doesn't have a constraint.") def test_negative_values(self): p = PositiveIntegerModel.objects.create(value=0) p.value = models.F('value') - 1 diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index 321826814b..f71efcff61 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -1,6 +1,6 @@ import operator import uuid -from unittest import mock, skipIf +from unittest import mock from django import forms from django.core import serializers @@ -719,10 +719,6 @@ class TestQuerying(TestCase): objs_with_value, ) - @skipIf( - connection.vendor == 'oracle', - 'Raises ORA-00600: internal error code on Oracle 18.', - ) def test_usage_in_subquery(self): self.assertSequenceEqual( NullableJSONModel.objects.filter( diff --git a/tests/model_fields/test_textfield.py b/tests/model_fields/test_textfield.py index f0bce822a4..dd5f5a5280 100644 --- a/tests/model_fields/test_textfield.py +++ b/tests/model_fields/test_textfield.py @@ -1,7 +1,5 @@ -from unittest import skipIf - from django import forms -from django.db import connection, models +from django.db import models from django.test import SimpleTestCase, TestCase from .models import Post @@ -32,7 +30,6 @@ class TextFieldTests(TestCase): def test_lookup_integer_in_textfield(self): self.assertEqual(Post.objects.filter(body=24).count(), 0) - @skipIf(connection.vendor == 'mysql', 'Running on MySQL requires utf8mb4 encoding (#18392)') def test_emoji(self): p = Post.objects.create(title='Whatever', body='Smile 😀.') p.refresh_from_db() diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py index a8ecd752cd..ba835afb74 100644 --- a/tests/queries/test_explain.py +++ b/tests/queries/test_explain.py @@ -80,9 +80,6 @@ class ExplainTests(TestCase): @unittest.skipUnless(connection.vendor == 'mysql', 'MariaDB and MySQL >= 8.0.18 specific.') def test_mysql_analyze(self): - # Inner skip to avoid module level query for MySQL version. - if not connection.features.supports_explain_analyze: - raise unittest.SkipTest('MariaDB and MySQL >= 8.0.18 specific.') qs = Tag.objects.filter(name='test') with CaptureQueriesContext(connection) as captured_queries: qs.explain(analyze=True) diff --git a/tests/runtests.py b/tests/runtests.py index ad282ce782..ba7c163bf6 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -254,6 +254,10 @@ def setup(verbosity, test_labels, parallel, start_at, start_after): apps.set_installed_apps(settings.INSTALLED_APPS) + # Set an environment variable that other code may consult to see if + # Django's own test suite is running. + os.environ['RUNNING_DJANGOS_TEST_SUITE'] = 'true' + return state @@ -267,6 +271,7 @@ def teardown(state): # FileNotFoundError at the end of a test run (#27890). from multiprocessing.util import _finalizer_registry _finalizer_registry.pop((-100, 0), None) + del os.environ['RUNNING_DJANGOS_TEST_SUITE'] def actual_test_processes(parallel): diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 9b8e27e207..7740065d31 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -729,12 +729,6 @@ class SchemaTests(TransactionTestCase): Foo.objects.create() def test_alter_not_unique_field_to_primary_key(self): - if ( - connection.vendor == 'mysql' and - connection.mysql_is_mariadb and - (10, 4, 3) < connection.mysql_version < (10, 5, 2) - ): - self.skipTest('https://jira.mariadb.org/browse/MDEV-19598') # Create the table. with connection.schema_editor() as editor: editor.create_model(Author) @@ -1957,7 +1951,6 @@ class SchemaTests(TransactionTestCase): TagUniqueRename._meta.db_table = old_table_name @isolate_apps('schema') - @unittest.skipIf(connection.vendor == 'sqlite', 'SQLite naively remakes the table on field alteration.') @skipUnlessDBFeature('supports_foreign_keys') def test_unique_no_unnecessary_fk_drops(self): """ @@ -1991,7 +1984,6 @@ class SchemaTests(TransactionTestCase): self.assertEqual(len(cm.records), 1) @isolate_apps('schema') - @unittest.skipIf(connection.vendor == 'sqlite', 'SQLite remakes the table on field alteration.') def test_unique_and_reverse_m2m(self): """ AlterField can modify a unique field when there's a reverse M2M @@ -2759,7 +2751,6 @@ class SchemaTests(TransactionTestCase): if connection.features.can_introspect_default: self.assertIsNone(field.default) - @unittest.skipIf(connection.vendor == 'sqlite', 'SQLite naively remakes the table on field alteration.') def test_alter_field_default_doesnt_perform_queries(self): """ No queries are performed if a field default changes and the field's @@ -3041,12 +3032,6 @@ class SchemaTests(TransactionTestCase): Changing the primary key field name of a model with a self-referential foreign key (#26384). """ - if ( - connection.vendor == 'mysql' and - connection.mysql_is_mariadb and - (10, 4, 12) < connection.mysql_version < (10, 5) - ): - self.skipTest('https://jira.mariadb.org/browse/MDEV-22775') with connection.schema_editor() as editor: editor.create_model(Node) old_field = Node._meta.get_field('node_id') @@ -3203,7 +3188,6 @@ class SchemaTests(TransactionTestCase): self.assertIs(statement.references_table('schema_author'), False) self.assertIs(statement.references_table('schema_book'), False) - @unittest.skipIf(connection.vendor == 'sqlite', 'SQLite naively remakes the table on field alteration.') def test_rename_column_renames_deferred_sql_references(self): with connection.schema_editor() as editor: editor.create_model(Author) diff --git a/tests/servers/test_liveserverthread.py b/tests/servers/test_liveserverthread.py index 9762b53791..a2af459edc 100644 --- a/tests/servers/test_liveserverthread.py +++ b/tests/servers/test_liveserverthread.py @@ -13,8 +13,6 @@ class LiveServerThreadTest(TestCase): def test_closes_connections(self): conn = connections[DEFAULT_DB_ALIAS] - if conn.vendor == 'sqlite' and conn.is_in_memory_db(): - self.skipTest("the sqlite backend's close() method is a no-op when using an in-memory database") # Pass a connection to the thread to check they are being closed. connections_override = {DEFAULT_DB_ALIAS: conn}