Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #17365, #17366, #18727 -- Switched to discovery test runner.

Thanks to Preston Timmons for the bulk of the work on the patch, especially
updating Django's own test suite to comply with the requirements of the new
runner. Thanks also to Jannis Leidel and Mahdi Yusuf for earlier work on the
patch and the discovery runner.

Refs #11077, #17032, and #18670.
  • Loading branch information...
commit 9012833af857e081b515ce760685b157638efcef 1 parent c0d8932
Carl Meyer authored May 10, 2013

Showing 79 changed files with 959 additions and 1,019 deletions. Show diff stats Hide diff stats

  1. 2  .gitignore
  2. 2  django/conf/global_settings.py
  3. 1  django/contrib/admindocs/tests/__init__.py
  4. 15  django/contrib/auth/tests/__init__.py
  5. 2  django/contrib/auth/tests/test_handlers.py
  6. 2  django/contrib/auth/tests/test_management.py
  7. 6  django/contrib/flatpages/tests/__init__.py
  8. 2  django/contrib/formtools/tests/__init__.py
  9. 2  django/contrib/formtools/tests/urls.py
  10. 7  django/contrib/gis/gdal/__init__.py
  11. 28  django/contrib/gis/gdal/tests/__init__.py
  12. 19  django/contrib/gis/gdal/tests/test_driver.py
  13. 68  django/contrib/gis/gdal/tests/test_ds.py
  14. 18  django/contrib/gis/gdal/tests/test_envelope.py
  15. 19  django/contrib/gis/gdal/tests/test_geom.py
  16. 16  django/contrib/gis/gdal/tests/test_srs.py
  17. 26  django/contrib/gis/geoip/tests.py
  18. 24  django/contrib/gis/geos/__init__.py
  19. 28  django/contrib/gis/geos/tests/__init__.py
  20. 44  django/contrib/gis/geos/tests/test_geos.py
  21. 27  django/contrib/gis/geos/tests/test_geos_mutation.py
  22. 16  django/contrib/gis/geos/tests/test_io.py
  23. 12  django/contrib/gis/geos/tests/test_mutable_list.py
  24. 97  django/contrib/gis/tests/__init__.py
  25. 31  django/contrib/gis/tests/distapp/tests.py
  26. 19  django/contrib/gis/tests/geo3d/tests.py
  27. 12  django/contrib/gis/tests/geoadmin/tests.py
  28. 7  django/contrib/gis/tests/geoapp/test_feeds.py
  29. 7  django/contrib/gis/tests/geoapp/test_regress.py
  30. 7  django/contrib/gis/tests/geoapp/test_sitemaps.py
  31. 29  django/contrib/gis/tests/geoapp/tests.py
  32. 11  django/contrib/gis/tests/geogapp/tests.py
  33. 12  django/contrib/gis/tests/inspectapp/tests.py
  34. 20  django/contrib/gis/tests/layermap/tests.py
  35. 15  django/contrib/gis/tests/relatedapp/tests.py
  36. 1  django/contrib/gis/tests/test_spatialrefsys.py
  37. 9  django/contrib/gis/tests/utils.py
  38. 5  django/contrib/messages/tests/__init__.py
  39. 4  django/contrib/sitemaps/tests/__init__.py
  40. 7  django/test/_doctest.py
  41. 289  django/test/runner.py
  42. 215  django/test/simple.py
  43. 10  django/test/testcases.py
  44. 2  django/utils/unittest/loader.py
  45. 1  docs/index.txt
  46. 11  docs/internals/deprecation.txt
  47. 5  docs/intro/tutorial05.txt
  48. 57  docs/ref/contrib/gis/testing.txt
  49. 7  docs/ref/settings.txt
  50. 2  docs/releases/1.2.4.txt
  51. 2  docs/releases/1.3.txt
  52. 57  docs/releases/1.6.txt
  53. 77  docs/topics/testing/advanced.txt
  54. 81  docs/topics/testing/doctests.txt
  55. 77  docs/topics/testing/index.txt
  56. 94  docs/topics/testing/overview.txt
  57. 6  tests/admin_scripts/tests.py
  58. 11  tests/generic_views/tests.py
  59. 6  tests/i18n/tests.py
  60. 5  tests/model_fields/tests.py
  61. 97  tests/runtests.py
  62. 9  tests/template_tests/tests.py
  63. 0  tests/{test_runner/deprecation_app → test_discovery_sample}/__init__.py
  64. 7  tests/test_discovery_sample/pattern_tests.py
  65. 0  tests/{test_runner/invalid_app/models → test_discovery_sample/tests}/__init__.py
  66. 7  tests/test_discovery_sample/tests/tests.py
  67. 22  tests/test_discovery_sample/tests_sample.py
  68. 0  tests/test_discovery_sample2/__init__.py
  69. 7  tests/test_discovery_sample2/tests.py
  70. 68  tests/test_runner/test_discover_runner.py
  71. 25  tests/test_runner/tests.py
  72. 0  tests/test_runner_deprecation_app/__init__.py
  73. 0  tests/{test_runner/deprecation_app → test_runner_deprecation_app}/models.py
  74. 2  tests/{test_runner/deprecation_app → test_runner_deprecation_app}/tests.py
  75. 0  tests/{test_runner/invalid_app → test_runner_invalid_app}/__init__.py
  76. 0  tests/test_runner_invalid_app/models/__init__.py
  77. 0  tests/{test_runner/invalid_app → test_runner_invalid_app}/tests/__init__.py
  78. 34  tests/utils_tests/tests.py
  79. 6  tests/validation/tests.py
2  .gitignore
@@ -5,4 +5,4 @@ MANIFEST
5 5
 dist/
6 6
 docs/_build/
7 7
 tests/coverage_html/
8  
-tests/.coverage
  8
+tests/.coverage
2  django/conf/global_settings.py
@@ -576,7 +576,7 @@
576 576
 ###########
577 577
 
578 578
 # The name of the class to use to run the test suite
579  
-TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
  579
+TEST_RUNNER = 'django.test.runner.DiscoverRunner'
580 580
 
581 581
 ############
582 582
 # FIXTURES #
1  django/contrib/admindocs/tests/__init__.py
... ...
@@ -1 +0,0 @@
1  
-from .test_fields import TestFieldType
15  django/contrib/auth/tests/__init__.py
... ...
@@ -1,16 +1 @@
1  
-from django.contrib.auth.tests.test_custom_user import *
2  
-from django.contrib.auth.tests.test_auth_backends import *
3  
-from django.contrib.auth.tests.test_basic import *
4  
-from django.contrib.auth.tests.test_context_processors import *
5  
-from django.contrib.auth.tests.test_decorators import *
6  
-from django.contrib.auth.tests.test_forms import *
7  
-from django.contrib.auth.tests.test_remote_user import *
8  
-from django.contrib.auth.tests.test_management import *
9  
-from django.contrib.auth.tests.test_models import *
10  
-from django.contrib.auth.tests.test_handlers import *
11  
-from django.contrib.auth.tests.test_hashers import *
12  
-from django.contrib.auth.tests.test_signals import *
13  
-from django.contrib.auth.tests.test_tokens import *
14  
-from django.contrib.auth.tests.test_views import *
15  
-
16 1
 # The password for the fixture data users is 'password'
2  django/contrib/auth/tests/test_handlers.py
@@ -2,7 +2,7 @@
2 2
 
3 3
 from django.contrib.auth.handlers.modwsgi import check_password, groups_for_user
4 4
 from django.contrib.auth.models import User, Group
5  
-from django.contrib.auth.tests import CustomUser
  5
+from django.contrib.auth.tests.test_custom_user import CustomUser
6 6
 from django.contrib.auth.tests.utils import skipIfCustomUser
7 7
 from django.test import TransactionTestCase
8 8
 from django.test.utils import override_settings
2  django/contrib/auth/tests/test_management.py
@@ -5,7 +5,7 @@
5 5
 from django.contrib.auth.management import create_permissions
6 6
 from django.contrib.auth.management.commands import changepassword
7 7
 from django.contrib.auth.models import User
8  
-from django.contrib.auth.tests import CustomUser
  8
+from django.contrib.auth.tests.test_custom_user import CustomUser
9 9
 from django.contrib.auth.tests.utils import skipIfCustomUser
10 10
 from django.core.management import call_command
11 11
 from django.core.management.base import CommandError
6  django/contrib/flatpages/tests/__init__.py
... ...
@@ -1,6 +0,0 @@
1  
-from django.contrib.flatpages.tests.test_csrf import *
2  
-from django.contrib.flatpages.tests.test_forms import *
3  
-from django.contrib.flatpages.tests.test_models import *
4  
-from django.contrib.flatpages.tests.test_middleware import *
5  
-from django.contrib.flatpages.tests.test_templatetags import *
6  
-from django.contrib.flatpages.tests.test_views import *
2  django/contrib/formtools/tests/__init__.py
... ...
@@ -1,2 +0,0 @@
1  
-from django.contrib.formtools.tests.tests import *
2  
-from django.contrib.formtools.tests.wizard import *
2  django/contrib/formtools/tests/urls.py
@@ -5,7 +5,7 @@
5 5
 from __future__ import absolute_import
6 6
 
7 7
 from django.conf.urls import patterns, url
8  
-from django.contrib.formtools.tests import TestFormPreview
  8
+from django.contrib.formtools.tests.tests import TestFormPreview
9 9
 
10 10
 from django.contrib.formtools.tests.forms import TestForm
11 11
 
7  django/contrib/gis/gdal/__init__.py
@@ -31,6 +31,9 @@
31 31
  to a non-existant file location (e.g., `GDAL_LIBRARY_PATH='/null/path'`;
32 32
  setting to None/False/'' will not work as a string must be given).
33 33
 """
  34
+from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError, SRSException
  35
+from django.contrib.gis.gdal.geomtype import OGRGeomType
  36
+
34 37
 # Attempting to import objects that depend on the GDAL library.  The
35 38
 # HAS_GDAL flag will be set to True if the library is present on
36 39
 # the system.
@@ -41,7 +44,7 @@
41 44
     from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
42 45
     from django.contrib.gis.gdal.geometries import OGRGeometry
43 46
     HAS_GDAL = True
44  
-except Exception:
  47
+except OGRException:
45 48
     HAS_GDAL = False
46 49
 
47 50
 try:
@@ -50,5 +53,3 @@
50 53
     # No ctypes, but don't raise an exception.
51 54
     pass
52 55
 
53  
-from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError, SRSException
54  
-from django.contrib.gis.gdal.geomtype import OGRGeomType
28  django/contrib/gis/gdal/tests/__init__.py
... ...
@@ -1,28 +0,0 @@
1  
-"""
2  
-Module for executing all of the GDAL tests.  None
3  
-of these tests require the use of the database.
4  
-"""
5  
-from __future__ import absolute_import
6  
-
7  
-from django.utils.unittest import TestSuite, TextTestRunner
8  
-
9  
-# Importing the GDAL test modules.
10  
-from . import test_driver, test_ds, test_envelope, test_geom, test_srs
11  
-
12  
-test_suites = [test_driver.suite(),
13  
-               test_ds.suite(),
14  
-               test_envelope.suite(),
15  
-               test_geom.suite(),
16  
-               test_srs.suite(),
17  
-               ]
18  
-
19  
-def suite():
20  
-    "Builds a test suite for the GDAL tests."
21  
-    s = TestSuite()
22  
-    for test_suite in test_suites:
23  
-        s.addTest(test_suite)
24  
-    return s
25  
-
26  
-def run(verbosity=1):
27  
-    "Runs the GDAL tests."
28  
-    TextTestRunner(verbosity=verbosity).run(suite())
19  django/contrib/gis/gdal/tests/test_driver.py
... ...
@@ -1,5 +1,10 @@
1  
-import unittest
2  
-from django.contrib.gis.gdal import Driver, OGRException
  1
+from django.contrib.gis.gdal import HAS_GDAL
  2
+from django.utils import unittest
  3
+from django.utils.unittest import skipUnless
  4
+
  5
+if HAS_GDAL:
  6
+    from django.contrib.gis.gdal import Driver, OGRException
  7
+
3 8
 
4 9
 valid_drivers = ('ESRI Shapefile', 'MapInfo File', 'TIGER', 'S57', 'DGN',
5 10
                  'Memory', 'CSV', 'GML', 'KML')
@@ -12,6 +17,8 @@
12 17
            'sHp' : 'ESRI Shapefile',
13 18
            }
14 19
 
  20
+
  21
+@skipUnless(HAS_GDAL, "GDAL is required")
15 22
 class DriverTest(unittest.TestCase):
16 23
 
17 24
     def test01_valid_driver(self):
@@ -30,11 +37,3 @@ def test03_aliases(self):
30 37
         for alias, full_name in aliases.items():
31 38
             dr = Driver(alias)
32 39
             self.assertEqual(full_name, str(dr))
33  
-
34  
-def suite():
35  
-    s = unittest.TestSuite()
36  
-    s.addTest(unittest.makeSuite(DriverTest))
37  
-    return s
38  
-
39  
-def run(verbosity=2):
40  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
68  django/contrib/gis/gdal/tests/test_ds.py
... ...
@@ -1,32 +1,38 @@
1 1
 import os
2  
-import unittest
3  
-from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError, GDAL_VERSION
4  
-from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
5  
-from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA
6  
-
7  
-
8  
-# List of acceptable data sources.
9  
-ds_list = (TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile',
10  
-                  fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
11  
-                  extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
12  
-                  srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]',
13  
-                  field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : list(range(1, 6)), 'str' : [str(i) for i in range(1, 6)]},
14  
-                  fids=range(5)),
15  
-           TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT',
16  
-                  fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString.
17  
-                  extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV
18  
-                  field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']},
19  
-                  fids=range(1,4)),
20  
-           TestDS('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3,
21  
-                  driver='ESRI Shapefile',
22  
-                  fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
23  
-                  extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS
24  
-                  srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
25  
-           )
26  
-
27  
-bad_ds = (TestDS('foo'),
28  
-          )
29 2
 
  3
+from django.contrib.gis.gdal import HAS_GDAL
  4
+from django.contrib.gis.geometry.test_data import get_ds_file, TestDS, TEST_DATA
  5
+from django.utils import unittest
  6
+from django.utils.unittest import skipUnless
  7
+
  8
+if HAS_GDAL:
  9
+    from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError, GDAL_VERSION
  10
+    from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
  11
+
  12
+    # List of acceptable data sources.
  13
+    ds_list = (
  14
+        TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile',
  15
+            fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
  16
+            extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
  17
+            srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]',
  18
+            field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : list(range(1, 6)), 'str' : [str(i) for i in range(1, 6)]},
  19
+            fids=range(5)),
  20
+        TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT',
  21
+            fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString.
  22
+            extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV
  23
+            field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']},
  24
+            fids=range(1,4)),
  25
+        TestDS('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3,
  26
+            driver='ESRI Shapefile',
  27
+            fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
  28
+            extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS
  29
+            srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
  30
+    )
  31
+
  32
+bad_ds = (TestDS('foo'),)
  33
+
  34
+
  35
+@skipUnless(HAS_GDAL, "GDAL is required")
30 36
 class DataSourceTest(unittest.TestCase):
31 37
 
32 38
     def test01_valid_shp(self):
@@ -236,11 +242,3 @@ def test07_integer_overflow(self):
236 242
         feat = ds[0][0]
237 243
         # Reference value obtained using `ogrinfo`.
238 244
         self.assertEqual(676586997978, feat.get('ALAND10'))
239  
-
240  
-def suite():
241  
-    s = unittest.TestSuite()
242  
-    s.addTest(unittest.makeSuite(DataSourceTest))
243  
-    return s
244  
-
245  
-def run(verbosity=2):
246  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
18  django/contrib/gis/gdal/tests/test_envelope.py
... ...
@@ -1,5 +1,9 @@
1  
-from django.contrib.gis.gdal import Envelope, OGRException
  1
+from django.contrib.gis.gdal import HAS_GDAL
2 2
 from django.utils import unittest
  3
+from django.utils.unittest import skipUnless
  4
+
  5
+if HAS_GDAL:
  6
+    from django.contrib.gis.gdal import Envelope, OGRException
3 7
 
4 8
 
5 9
 class TestPoint(object):
@@ -7,11 +11,13 @@ def __init__(self, x, y):
7 11
         self.x = x
8 12
         self.y = y
9 13
 
  14
+
  15
+@skipUnless(HAS_GDAL, "GDAL is required")
10 16
 class EnvelopeTest(unittest.TestCase):
11 17
 
12 18
     def setUp(self):
13 19
         self.e = Envelope(0, 0, 5, 5)
14  
-
  20
+    
15 21
     def test01_init(self):
16 22
         "Testing Envelope initilization."
17 23
         e1 = Envelope((0, 0, 5, 5))
@@ -85,11 +91,3 @@ def test08_expand_to_include_point(self):
85 91
         self.assertEqual((-1, 0, 5, 5), self.e)
86 92
         self.e.expand_to_include(TestPoint(10, 10))
87 93
         self.assertEqual((-1, 0, 10, 10), self.e)
88  
-
89  
-def suite():
90  
-    s = unittest.TestSuite()
91  
-    s.addTest(unittest.makeSuite(EnvelopeTest))
92  
-    return s
93  
-
94  
-def run(verbosity=2):
95  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
19  django/contrib/gis/gdal/tests/test_geom.py
@@ -5,12 +5,19 @@
5 5
 except ImportError:
6 6
     import pickle
7 7
 
8  
-from django.contrib.gis.gdal import (OGRGeometry, OGRGeomType, OGRException,
9  
-    OGRIndexError, SpatialReference, CoordTransform, GDAL_VERSION)
  8
+from django.contrib.gis.gdal import HAS_GDAL
10 9
 from django.contrib.gis.geometry.test_data import TestDataMixin
11 10
 from django.utils.six.moves import xrange
12 11
 from django.utils import unittest
  12
+from django.utils.unittest import skipUnless
13 13
 
  14
+if HAS_GDAL:
  15
+    from django.contrib.gis.gdal import (OGRGeometry, OGRGeomType,
  16
+        OGRException, OGRIndexError, SpatialReference, CoordTransform,
  17
+        GDAL_VERSION)
  18
+
  19
+
  20
+@skipUnless(HAS_GDAL, "GDAL is required")
14 21
 class OGRGeomTest(unittest.TestCase, TestDataMixin):
15 22
     "This tests the OGR Geometry."
16 23
 
@@ -476,11 +483,3 @@ def test19_equivalence_regression(self):
476 483
         "Testing equivalence methods with non-OGRGeometry instances."
477 484
         self.assertNotEqual(None, OGRGeometry('POINT(0 0)'))
478 485
         self.assertEqual(False, OGRGeometry('LINESTRING(0 0, 1 1)') == 3)
479  
-
480  
-def suite():
481  
-    s = unittest.TestSuite()
482  
-    s.addTest(unittest.makeSuite(OGRGeomTest))
483  
-    return s
484  
-
485  
-def run(verbosity=2):
486  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
16  django/contrib/gis/gdal/tests/test_srs.py
... ...
@@ -1,5 +1,9 @@
1  
-from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException
  1
+from django.contrib.gis.gdal import HAS_GDAL
2 2
 from django.utils import unittest
  3
+from django.utils.unittest import skipUnless
  4
+
  5
+if HAS_GDAL:
  6
+    from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException
3 7
 
4 8
 
5 9
 class TestSRS:
@@ -46,6 +50,8 @@ def __init__(self, wkt, **kwargs):
46 50
 
47 51
 bad_srlist = ('Foobar', 'OOJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',)
48 52
 
  53
+
  54
+@skipUnless(HAS_GDAL, "GDAL is required")
49 55
 class SpatialRefTest(unittest.TestCase):
50 56
 
51 57
     def test01_wkt(self):
@@ -155,11 +161,3 @@ def test13_attr_value(self):
155 161
         self.assertEqual('EPSG', s1['AUTHORITY'])
156 162
         self.assertEqual(4326, int(s1['AUTHORITY', 1]))
157 163
         self.assertEqual(None, s1['FOOBAR'])
158  
-
159  
-def suite():
160  
-    s = unittest.TestSuite()
161  
-    s.addTest(unittest.makeSuite(SpatialRefTest))
162  
-    return s
163  
-
164  
-def run(verbosity=2):
165  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
26  django/contrib/gis/geoip/tests.py
@@ -3,16 +3,28 @@
3 3
 
4 4
 import os
5 5
 from django.conf import settings
6  
-from django.contrib.gis.geos import GEOSGeometry
7  
-from django.contrib.gis.geoip import GeoIP, GeoIPException
  6
+from django.contrib.gis.geos import HAS_GEOS
  7
+from django.contrib.gis.geoip import HAS_GEOIP
8 8
 from django.utils import unittest
  9
+from django.utils.unittest import skipUnless
9 10
 
10 11
 from django.utils import six
11 12
 
  13
+if HAS_GEOIP:
  14
+    from . import GeoIP, GeoIPException
  15
+
  16
+if HAS_GEOS:
  17
+    from ..geos import GEOSGeometry
  18
+
  19
+
12 20
 # Note: Requires use of both the GeoIP country and city datasets.
13 21
 # The GEOIP_DATA path should be the only setting set (the directory
14 22
 # should contain links or the actual database files 'GeoIP.dat' and
15 23
 # 'GeoLiteCity.dat'.
  24
+
  25
+
  26
+@skipUnless(HAS_GEOIP and getattr(settings, "GEOIP_PATH", None),
  27
+    "GeoIP is required along with the GEOIP_DATA setting.")
16 28
 class GeoIPTest(unittest.TestCase):
17 29
 
18 30
     def test01_init(self):
@@ -70,6 +82,7 @@ def test03_country(self):
70 82
             self.assertEqual({'country_code' : 'US', 'country_name' : 'United States'},
71 83
                              g.country(query))
72 84
 
  85
+    @skipUnless(HAS_GEOS, "Geos is required")
73 86
     def test04_city(self):
74 87
         "Testing GeoIP city querying methods."
75 88
         g = GeoIP(country='<foo>')
@@ -105,12 +118,3 @@ def test05_unicode_response(self):
105 118
         g = GeoIP()
106 119
         d = g.city("www.osnabrueck.de")
107 120
         self.assertEqual('Osnabrück', d['city'])
108  
-
109  
-
110  
-def suite():
111  
-    s = unittest.TestSuite()
112  
-    s.addTest(unittest.makeSuite(GeoIPTest))
113  
-    return s
114  
-
115  
-def run(verbosity=1):
116  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
24  django/contrib/gis/geos/__init__.py
@@ -3,12 +3,18 @@
3 3
 for more details: 
4 4
   http://geodjango.org/docs/geos.html
5 5
 """
6  
-from django.contrib.gis.geos.geometry import GEOSGeometry, wkt_regex, hex_regex
7  
-from django.contrib.gis.geos.point import Point
8  
-from django.contrib.gis.geos.linestring import LineString, LinearRing
9  
-from django.contrib.gis.geos.polygon import Polygon
10  
-from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
11  
-from django.contrib.gis.geos.error import GEOSException, GEOSIndexError
12  
-from django.contrib.gis.geos.io import WKTReader, WKTWriter, WKBReader, WKBWriter
13  
-from django.contrib.gis.geos.factory import fromfile, fromstr
14  
-from django.contrib.gis.geos.libgeos import geos_version, geos_version_info, GEOS_PREPARE
  6
+try:
  7
+    from .libgeos import geos_version, geos_version_info, GEOS_PREPARE
  8
+    HAS_GEOS = True
  9
+except ImportError:
  10
+    HAS_GEOS = False
  11
+
  12
+if HAS_GEOS:
  13
+    from .geometry import GEOSGeometry, wkt_regex, hex_regex
  14
+    from .point import Point
  15
+    from .linestring import LineString, LinearRing
  16
+    from .polygon import Polygon
  17
+    from .collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
  18
+    from .error import GEOSException, GEOSIndexError
  19
+    from .io import WKTReader, WKTWriter, WKBReader, WKBWriter
  20
+    from .factory import fromfile, fromstr
28  django/contrib/gis/geos/tests/__init__.py
... ...
@@ -1,28 +0,0 @@
1  
-"""
2  
-GEOS Testing module.
3  
-"""
4  
-from __future__ import absolute_import
5  
-
6  
-from django.utils.unittest import TestSuite, TextTestRunner
7  
-from . import test_geos, test_io, test_geos_mutation, test_mutable_list
8  
-
9  
-test_suites = [
10  
-    test_geos.suite(),
11  
-    test_io.suite(),
12  
-    test_geos_mutation.suite(),
13  
-    test_mutable_list.suite(),
14  
-    ]
15  
-
16  
-def suite():
17  
-    "Builds a test suite for the GEOS tests."
18  
-    s = TestSuite()
19  
-    for suite in test_suites:
20  
-        s.addTest(suite)
21  
-    return s
22  
-
23  
-def run(verbosity=1):
24  
-    "Runs the GEOS tests."
25  
-    TextTestRunner(verbosity=verbosity).run(suite())
26  
-
27  
-if __name__ == '__main__':
28  
-    run(2)
44  django/contrib/gis/geos/tests/test_geos.py
@@ -6,20 +6,28 @@
6 6
 from binascii import a2b_hex, b2a_hex
7 7
 from io import BytesIO
8 8
 
  9
+from django.contrib.gis.gdal import HAS_GDAL
  10
+
9 11
 from django.contrib.gis import memoryview
10  
-from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
11  
-    GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
12  
-    LineString, MultiLineString, fromfile, fromstr, geos_version_info)
13  
-from django.contrib.gis.geos.base import gdal, numpy, GEOSBase
14  
-from django.contrib.gis.geos.libgeos import GEOS_PREPARE
15 12
 from django.contrib.gis.geometry.test_data import TestDataMixin
16 13
 
17 14
 from django.utils.encoding import force_bytes
18 15
 from django.utils import six
19 16
 from django.utils.six.moves import xrange
20 17
 from django.utils import unittest
  18
+from django.utils.unittest import skipUnless
  19
+
  20
+from .. import HAS_GEOS
  21
+
  22
+if HAS_GEOS:
  23
+    from .. import (GEOSException, GEOSIndexError, GEOSGeometry,
  24
+        GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
  25
+        LineString, MultiLineString, fromfile, fromstr, geos_version_info,
  26
+        GEOS_PREPARE)
  27
+    from ..base import gdal, numpy, GEOSBase
21 28
 
22 29
 
  30
+@skipUnless(HAS_GEOS, "Geos is required.")
23 31
 class GEOSTest(unittest.TestCase, TestDataMixin):
24 32
 
25 33
     @property
@@ -198,7 +206,7 @@ def test_ewkt(self):
198 206
                 self.assertEqual(srid, poly.shell.srid)
199 207
                 self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export
200 208
 
201  
-    @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required")
  209
+    @skipUnless(HAS_GDAL, "GDAL is required.")
202 210
     def test_json(self):
203 211
         "Testing GeoJSON input/output (via GDAL)."
204 212
         for g in self.geometries.json_geoms:
@@ -662,6 +670,7 @@ def test_srid(self):
662 670
         p3 = fromstr(p1.hex, srid=-1) # -1 is intended.
663 671
         self.assertEqual(-1, p3.srid)
664 672
 
  673
+    @skipUnless(HAS_GDAL, "GDAL is required.")
665 674
     def test_custom_srid(self):
666 675
         """ Test with a srid unknown from GDAL """
667 676
         pnt = Point(111200, 220900, srid=999999)
@@ -851,7 +860,7 @@ def test_collections_of_collections(self):
851 860
         # And, they should be equal.
852 861
         self.assertEqual(gc1, gc2)
853 862
 
854  
-    @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required")
  863
+    @skipUnless(HAS_GDAL, "GDAL is required.")
855 864
     def test_gdal(self):
856 865
         "Testing `ogr` and `srs` properties."
857 866
         g1 = fromstr('POINT(5 23)')
@@ -878,7 +887,7 @@ def test_copy(self):
878 887
         self.assertNotEqual(poly._ptr, cpy1._ptr)
879 888
         self.assertNotEqual(poly._ptr, cpy2._ptr)
880 889
 
881  
-    @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required to transform geometries")
  890
+    @skipUnless(HAS_GDAL, "GDAL is required to transform geometries")
882 891
     def test_transform(self):
883 892
         "Testing `transform` method."
884 893
         orig = GEOSGeometry('POINT (-104.609 38.255)', 4326)
@@ -903,7 +912,7 @@ def test_transform(self):
903 912
             self.assertAlmostEqual(trans.x, p.x, prec)
904 913
             self.assertAlmostEqual(trans.y, p.y, prec)
905 914
 
906  
-    @unittest.skipUnless(gdal.HAS_GDAL, "gdal is required to transform geometries")
  915
+    @skipUnless(HAS_GDAL, "GDAL is required to transform geometries")
907 916
     def test_transform_3d(self):
908 917
         p3d = GEOSGeometry('POINT (5 23 100)', 4326)
909 918
         p3d.transform(2774)
@@ -912,6 +921,7 @@ def test_transform_3d(self):
912 921
         else:
913 922
             self.assertIsNone(p3d.z)
914 923
 
  924
+    @skipUnless(HAS_GDAL, "GDAL is required.")
915 925
     def test_transform_noop(self):
916 926
         """ Testing `transform` method (SRID match) """
917 927
         # transform() should no-op if source & dest SRIDs match,
@@ -962,6 +972,7 @@ def test_transform_nosrid(self):
962 972
         g = GEOSGeometry('POINT (-104.609 38.255)', srid=-1)
963 973
         self.assertRaises(GEOSException, g.transform, 2774, clone=True)
964 974
 
  975
+    @skipUnless(HAS_GDAL, "GDAL is required.")
965 976
     def test_transform_nogdal(self):
966 977
         """ Testing `transform` method (GDAL not available) """
967 978
         old_has_gdal = gdal.HAS_GDAL
@@ -1016,7 +1027,7 @@ def get_geoms(lst, srid=None):
1016 1027
                 self.assertEqual(geom, tmpg)
1017 1028
                 if not no_srid: self.assertEqual(geom.srid, tmpg.srid)
1018 1029
 
1019  
-    @unittest.skipUnless(GEOS_PREPARE, "geos >= 3.1.0 is required")
  1030
+    @skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
1020 1031
     def test_prepared(self):
1021 1032
         "Testing PreparedGeometry support."
1022 1033
         # Creating a simple multipolygon and getting a prepared version.
@@ -1043,7 +1054,7 @@ def test_line_merge(self):
1043 1054
         for geom, merged in zip(ref_geoms, ref_merged):
1044 1055
             self.assertEqual(merged, geom.merged)
1045 1056
 
1046  
-    @unittest.skipUnless(GEOS_PREPARE, "geos >= 3.1.0 is required")
  1057
+    @skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
1047 1058
     def test_valid_reason(self):
1048 1059
         "Testing IsValidReason support"
1049 1060
 
@@ -1058,7 +1069,7 @@ def test_valid_reason(self):
1058 1069
         self.assertIsInstance(g.valid_reason, six.string_types)
1059 1070
         self.assertTrue(g.valid_reason.startswith("Too few points in geometry component"))
1060 1071
 
1061  
-    @unittest.skipUnless(geos_version_info()['version'] >= '3.2.0', "geos >= 3.2.0 is required")
  1072
+    @skipUnless(HAS_GEOS and geos_version_info()['version'] >= '3.2.0', "geos >= 3.2.0 is required")
1062 1073
     def test_linearref(self):
1063 1074
         "Testing linear referencing"
1064 1075
 
@@ -1091,12 +1102,3 @@ def test_geos_version(self):
1091 1102
             self.assertTrue(m, msg="Unable to parse the version string '%s'" % v_init)
1092 1103
             self.assertEqual(m.group('version'), v_geos)
1093 1104
             self.assertEqual(m.group('capi_version'), v_capi)
1094  
-
1095  
-
1096  
-def suite():
1097  
-    s = unittest.TestSuite()
1098  
-    s.addTest(unittest.makeSuite(GEOSTest))
1099  
-    return s
1100  
-
1101  
-def run(verbosity=2):
1102  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
27  django/contrib/gis/geos/tests/test_geos_mutation.py
@@ -2,15 +2,23 @@
2 2
 # Modified from original contribution by Aryeh Leib Taurog, which was
3 3
 # released under the New BSD license.
4 4
 
5  
-from django.contrib.gis.geos import *
6  
-from django.contrib.gis.geos.error import GEOSIndexError
7 5
 from django.utils import unittest
  6
+from django.utils.unittest import skipUnless
  7
+
  8
+from .. import HAS_GEOS
  9
+
  10
+if HAS_GEOS:
  11
+    from .. import *
  12
+    from ..error import GEOSIndexError
  13
+
8 14
 
9 15
 def getItem(o,i): return o[i]
10 16
 def delItem(o,i): del o[i]
11 17
 def setItem(o,i,v): o[i] = v
12 18
 
13  
-def api_get_distance(x): return x.distance(Point(-200,-200))
  19
+if HAS_GEOS:
  20
+    def api_get_distance(x): return x.distance(Point(-200,-200))
  21
+
14 22
 def api_get_buffer(x): return x.buffer(10)
15 23
 def api_get_geom_typeid(x): return x.geom_typeid
16 24
 def api_get_num_coords(x): return x.num_coords
@@ -29,6 +37,8 @@ def api_get_length(x): return x.length
29 37
                         if hasattr(val, '__call__')
30 38
                         and name.startswith('api_get_') ]
31 39
 
  40
+
  41
+@skipUnless(HAS_GEOS, "Geos is required.")
32 42
 class GEOSMutationTest(unittest.TestCase):
33 43
     """
34 44
     Tests Pythonic Mutability of Python GEOS geometry wrappers
@@ -122,14 +132,3 @@ def test06_Collection(self):
122 132
             lsa = MultiPoint(*map(Point,((5,5),(3,-2),(8,1))))
123 133
             for f in geos_function_tests:
124 134
                 self.assertEqual(f(lsa), f(mp), 'MultiPoint ' + f.__name__)
125  
-
126  
-def suite():
127  
-    s = unittest.TestSuite()
128  
-    s.addTest(unittest.makeSuite(GEOSMutationTest))
129  
-    return s
130  
-
131  
-def run(verbosity=2):
132  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
133  
-
134  
-if __name__ == '__main__':
135  
-    run()
16  django/contrib/gis/geos/tests/test_io.py
@@ -4,10 +4,16 @@
4 4
 import unittest
5 5
 
6 6
 from django.contrib.gis import memoryview
7  
-from django.contrib.gis.geos import GEOSGeometry, WKTReader, WKTWriter, WKBReader, WKBWriter, geos_version_info
8 7
 from django.utils import six
  8
+from django.utils.unittest import skipUnless
9 9
 
  10
+from ..import HAS_GEOS
10 11
 
  12
+if HAS_GEOS:
  13
+    from .. import GEOSGeometry, WKTReader, WKTWriter, WKBReader, WKBWriter, geos_version_info
  14
+
  15
+
  16
+@skipUnless(HAS_GEOS, "Geos is required.")
11 17
 class GEOSIOTest(unittest.TestCase):
12 18
 
13 19
     def test01_wktreader(self):
@@ -109,11 +115,3 @@ def test04_wkbwriter(self):
109 115
             wkb_w.srid = True
110 116
             self.assertEqual(hex3d_srid, wkb_w.write_hex(g))
111 117
             self.assertEqual(wkb3d_srid, wkb_w.write(g))
112  
-
113  
-def suite():
114  
-    s = unittest.TestSuite()
115  
-    s.addTest(unittest.makeSuite(GEOSIOTest))
116  
-    return s
117  
-
118  
-def run(verbosity=2):
119  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
12  django/contrib/gis/geos/tests/test_mutable_list.py
@@ -395,15 +395,3 @@ def test_12_arithmetic(self):
395 395
 
396 396
 class ListMixinTestSingle(ListMixinTest):
397 397
     listType = UserListB
398  
-
399  
-def suite():
400  
-    s = unittest.TestSuite()
401  
-    s.addTest(unittest.makeSuite(ListMixinTest))
402  
-    s.addTest(unittest.makeSuite(ListMixinTestSingle))
403  
-    return s
404  
-
405  
-def run(verbosity=2):
406  
-    unittest.TextTestRunner(verbosity=verbosity).run(suite())
407  
-
408  
-if __name__ == '__main__':
409  
-    run()
97  django/contrib/gis/tests/__init__.py
... ...
@@ -1,13 +1,4 @@
1  
-from django.conf import settings
2  
-from django.test.simple import build_suite, DjangoTestSuiteRunner
3  
-from django.utils import unittest
4  
-
5  
-from .test_geoforms import GeometryFieldTest
6  
-from .test_measure import DistanceTest, AreaTest
7  
-from .test_spatialrefsys import SpatialRefSysTest
8  
-
9  
-
10  
-def geo_apps(namespace=True, runtests=False):
  1
+def geo_apps():
11 2
     """
12 3
     Returns a list of GeoDjango test applications that reside in
13 4
     `django.contrib.gis.tests` that can be used with the current
@@ -36,88 +27,4 @@ def geo_apps(namespace=True, runtests=False):
36 27
         # 3D apps use LayerMapping, which uses GDAL and require GEOS 3.1+.
37 28
         if connection.ops.postgis and GEOS_PREPARE:
38 29
             apps.append('geo3d')
39  
-    if runtests:
40  
-        return [('django.contrib.gis.tests', app) for app in apps]
41  
-    elif namespace:
42  
-        return ['django.contrib.gis.tests.%s' % app
43  
-                for app in apps]
44  
-    else:
45  
-        return apps
46  
-
47  
-
48  
-def geodjango_suite(apps=True):
49  
-    """
50  
-    Returns a TestSuite consisting only of GeoDjango tests that can be run.
51  
-    """
52  
-    import sys
53  
-    from django.db.models import get_app
54  
-
55  
-    suite = unittest.TestSuite()
56  
-
57  
-    # Adding the GEOS tests.
58  
-    from django.contrib.gis.geos import tests as geos_tests
59  
-    suite.addTest(geos_tests.suite())
60  
-
61  
-    # Adding GDAL tests, and any test suite that depends on GDAL, to the
62  
-    # suite if GDAL is available.
63  
-    from django.contrib.gis.gdal import HAS_GDAL
64  
-    if HAS_GDAL:
65  
-        from django.contrib.gis.gdal import tests as gdal_tests
66  
-        suite.addTest(gdal_tests.suite())
67  
-    else:
68  
-        sys.stderr.write('GDAL not available - no tests requiring GDAL will be run.\n')
69  
-
70  
-    # Add GeoIP tests to the suite, if the library and data is available.
71  
-    from django.contrib.gis.geoip import HAS_GEOIP
72  
-    if HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'):
73  
-        from django.contrib.gis.geoip import tests as geoip_tests
74  
-        suite.addTest(geoip_tests.suite())
75  
-
76  
-    # Finally, adding the suites for each of the GeoDjango test apps.
77  
-    if apps:
78  
-        for app_name in geo_apps(namespace=False):
79  
-            suite.addTest(build_suite(get_app(app_name)))
80  
-
81  
-    return suite
82  
-
83  
-
84  
-class GeoDjangoTestSuiteRunner(DjangoTestSuiteRunner):
85  
-
86  
-    def setup_test_environment(self, **kwargs):
87  
-        super(GeoDjangoTestSuiteRunner, self).setup_test_environment(**kwargs)
88  
-
89  
-        # Saving original values of INSTALLED_APPS, ROOT_URLCONF, and SITE_ID.
90  
-        self.old_installed = getattr(settings, 'INSTALLED_APPS', None)
91  
-        self.old_root_urlconf = getattr(settings, 'ROOT_URLCONF', '')
92  
-        self.old_site_id = getattr(settings, 'SITE_ID', None)
93  
-
94  
-        # Constructing the new INSTALLED_APPS, and including applications
95  
-        # within the GeoDjango test namespace.
96  
-        new_installed =  [
97  
-            'django.contrib.sites',
98  
-            'django.contrib.sitemaps',
99  
-            'django.contrib.gis',
100  
-        ]
101  
-
102  
-        # Calling out to `geo_apps` to get GeoDjango applications supported
103  
-        # for testing.
104  
-        new_installed.extend(geo_apps())
105  
-        settings.INSTALLED_APPS = list(self.old_installed) + new_installed
106  
-
107  
-        # SITE_ID needs to be set
108  
-        settings.SITE_ID = 1
109  
-
110  
-        # ROOT_URLCONF needs to be set, else `AttributeErrors` are raised
111  
-        # when TestCases are torn down that have `urls` defined.
112  
-        settings.ROOT_URLCONF = ''
113  
-
114  
-
115  
-    def teardown_test_environment(self, **kwargs):
116  
-        super(GeoDjangoTestSuiteRunner, self).teardown_test_environment(**kwargs)
117  
-        settings.INSTALLED_APPS = self.old_installed
118  
-        settings.ROOT_URLCONF = self.old_root_urlconf
119  
-        settings.SITE_ID = self.old_site_id
120  
-
121  
-
122  
-    def build_suite(self, test_labels, extra_tests=None, **kwargs):
123  
-        return geodjango_suite()
  30
+    return [('django.contrib.gis.tests', app) for app in apps]
31  django/contrib/gis/tests/distapp/tests.py
@@ -2,24 +2,33 @@
2 2
 
3 3
 from django.db import connection
4 4
 from django.db.models import Q
5  
-from django.contrib.gis.geos import GEOSGeometry, LineString
  5
+from django.contrib.gis.geos import HAS_GEOS
6 6
 from django.contrib.gis.measure import D # alias for Distance
7  
-from django.contrib.gis.tests.utils import oracle, postgis, spatialite, no_oracle, no_spatialite
  7
+from django.contrib.gis.tests.utils import (
  8
+    HAS_SPATIAL_DB, oracle, postgis, spatialite, no_oracle, no_spatialite
  9
+)
8 10
 from django.test import TestCase
  11
+from django.utils.unittest import skipUnless
9 12
 
10  
-from .models import (AustraliaCity, Interstate, SouthTexasInterstate,
11  
-    SouthTexasCity, SouthTexasCityFt, CensusZipcode, SouthTexasZipcode)
  13
+if HAS_GEOS and HAS_SPATIAL_DB:
  14
+    from django.contrib.gis.geos import GEOSGeometry, LineString
12 15
 
  16
+    from .models import (AustraliaCity, Interstate, SouthTexasInterstate,
  17
+        SouthTexasCity, SouthTexasCityFt, CensusZipcode, SouthTexasZipcode)
13 18
 
  19
+
  20
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB,
  21
+    "Geos and spatial db are required.")
14 22
 class DistanceTest(TestCase):
15 23
 
16  
-    # A point we are testing distances with -- using a WGS84
17  
-    # coordinate that'll be implicitly transormed to that to
18  
-    # the coordinate system of the field, EPSG:32140 (Texas South Central
19  
-    # w/units in meters)
20  
-    stx_pnt = GEOSGeometry('POINT (-95.370401017314293 29.704867409475465)', 4326)
21  
-    # Another one for Australia
22  
-    au_pnt = GEOSGeometry('POINT (150.791 -34.4919)', 4326)
  24
+    if HAS_GEOS and HAS_SPATIAL_DB:
  25
+        # A point we are testing distances with -- using a WGS84
  26
+        # coordinate that'll be implicitly transormed to that to
  27
+        # the coordinate system of the field, EPSG:32140 (Texas South Central
  28
+        # w/units in meters)
  29
+        stx_pnt = GEOSGeometry('POINT (-95.370401017314293 29.704867409475465)', 4326)
  30
+        # Another one for Australia
  31
+        au_pnt = GEOSGeometry('POINT (150.791 -34.4919)', 4326)
23 32
 
24 33
     def get_names(self, qs):
25 34
         cities = [c.name for c in qs]
19  django/contrib/gis/tests/geo3d/tests.py
@@ -3,14 +3,22 @@
3 3
 import os
4 4
 import re
5 5
 
6  
-from django.contrib.gis.db.models import Union, Extent3D
7  
-from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon
8  
-from django.contrib.gis.utils import LayerMapping, LayerMapError
  6
+from django.contrib.gis.gdal import HAS_GDAL
  7
+from django.contrib.gis.geos import HAS_GEOS
  8
+from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
9 9
 from django.test import TestCase
10 10
 from django.utils._os import upath
  11
+from django.utils.unittest import skipUnless
11 12
 
12  
-from .models import (City3D, Interstate2D, Interstate3D, InterstateProj2D,
13  
-    InterstateProj3D, Point2D, Point3D, MultiPoint3D, Polygon2D, Polygon3D)
  13
+if HAS_GEOS:
  14
+    from django.contrib.gis.db.models import Union, Extent3D
  15
+    from django.contrib.gis.geos import GEOSGeometry, LineString, Point, Polygon
  16
+
  17
+    from .models import (City3D, Interstate2D, Interstate3D, InterstateProj2D,
  18
+        InterstateProj3D, Point2D, Point3D, MultiPoint3D, Polygon2D, Polygon3D)
  19
+
  20
+if HAS_GDAL:
  21
+    from django.contrib.gis.utils import LayerMapping, LayerMapError
14 22
 
15 23
 
16 24
 data_path = os.path.realpath(os.path.join(os.path.dirname(upath(__file__)), '..', 'data'))
@@ -54,6 +62,7 @@
54 62
 )
55 63
 
56 64
 
  65
+@skipUnless(HAS_GEOS and HAS_GDAL and HAS_SPATIAL_DB, "Geos, GDAL and spatial db are required.")
57 66
 class Geo3DTest(TestCase):
58 67
     """
59 68
     Only a subset of the PostGIS routines are 3D-enabled, and this TestCase
12  django/contrib/gis/tests/geoadmin/tests.py
... ...
@@ -1,12 +1,18 @@
1 1
 from __future__ import absolute_import
2 2
 
3 3
 from django.test import TestCase
4  
-from django.contrib.gis import admin
5  
-from django.contrib.gis.geos import GEOSGeometry, Point
  4
+from django.contrib.gis.geos import HAS_GEOS
  5
+from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
  6
+from django.utils.unittest import skipUnless
6 7
 
7  
-from .models import City
  8
+if HAS_GEOS and HAS_SPATIAL_DB:
  9
+    from django.contrib.gis import admin
  10
+    from django.contrib.gis.geos import Point
8 11
 
  12
+    from .models import City
9 13
 
  14
+
  15
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
10 16
 class GeoAdminTest(TestCase):
11 17
     urls = 'django.contrib.gis.tests.geoadmin.urls'
12 18
 
7  django/contrib/gis/tests/geoapp/test_feeds.py
@@ -4,11 +4,16 @@
4 4
 
5 5
 from django.conf import settings
6 6
 from django.contrib.sites.models import Site
  7
+from django.contrib.gis.geos import HAS_GEOS
  8
+from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
7 9
 from django.test import TestCase
  10
+from django.utils.unittest import skipUnless
8 11
 
9  
-from .models import City
  12
+if HAS_GEOS:
  13
+    from .models import City
10 14
 
11 15
 
  16
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
12 17
 class GeoFeedTest(TestCase):
13 18
 
14 19
     urls = 'django.contrib.gis.tests.geoapp.urls'
7  django/contrib/gis/tests/geoapp/test_regress.py
@@ -3,14 +3,19 @@
3 3
 
4 4
 from datetime import datetime
5 5
 
  6
+from django.contrib.gis.geos import HAS_GEOS
6 7
 from django.contrib.gis.tests.utils import no_mysql, no_spatialite
7 8
 from django.contrib.gis.shortcuts import render_to_kmz
  9
+from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
8 10
 from django.db.models import Count, Min
9 11
 from django.test import TestCase
  12
+from django.utils.unittest import skipUnless
10 13
 
11  
-from .models import City, PennsylvaniaCity, State, Truth
  14
+if HAS_GEOS:
  15
+    from .models import City, PennsylvaniaCity, State, Truth
12 16
 
13 17
 
  18
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
14 19
 class GeoRegressionTests(TestCase):
15 20
 
16 21
     def test_update(self):
7  django/contrib/gis/tests/geoapp/test_sitemaps.py
@@ -5,12 +5,17 @@
5 5
 import zipfile
6 6
 
7 7
 from django.conf import settings
  8
+from django.contrib.gis.geos import HAS_GEOS
  9
+from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
8 10
 from django.contrib.sites.models import Site
9 11
 from django.test import TestCase
  12
+from django.utils.unittest import skipUnless
10 13
 
11  
-from .models import City, Country
  14
+if HAS_GEOS:
  15
+    from .models import City, Country
12 16
 
13 17
 
  18
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
14 19
 class GeoSitemapTest(TestCase):
15 20
 
16 21
     urls = 'django.contrib.gis.tests.geoapp.urls'
29  django/contrib/gis/tests/geoapp/tests.py
@@ -3,26 +3,31 @@
3 3
 import re
4 4
 
5 5
 from django.db import connection
6  
-from django.db.utils import DatabaseError
7 6
 from django.contrib.gis import gdal
8  
-from django.contrib.gis.geos import (fromstr, GEOSGeometry,
9  
-    Point, LineString, LinearRing, Polygon, GeometryCollection)
  7
+from django.contrib.gis.geos import HAS_GEOS
10 8
 from django.contrib.gis.tests.utils import (
11  
-    no_mysql, no_oracle, no_spatialite,
  9
+    HAS_SPATIAL_DB, no_mysql, no_oracle, no_spatialite,
12 10
     mysql, oracle, postgis, spatialite)
13 11
 from django.test import TestCase
14 12
 from django.utils import six, unittest
  13
+from django.utils.unittest import skipUnless
15 14
 
16  
-from .models import Country, City, PennsylvaniaCity, State, Track
  15
+if HAS_GEOS:
  16
+    from django.contrib.gis.geos import (fromstr, GEOSGeometry,
  17
+        Point, LineString, LinearRing, Polygon, GeometryCollection)
17 18
 
18  
-from .test_feeds import GeoFeedTest
19  
-from .test_regress import GeoRegressionTests
20  
-from .test_sitemaps import GeoSitemapTest
  19
+    from .models import Country, City, PennsylvaniaCity, State, Track
21 20
 
22  
-
23  
-if not spatialite:
  21
+if HAS_GEOS and not spatialite:
24 22
     from .models import Feature, MinusOneSRID
25 23
 
  24
+
  25
+def postgis_bug_version():
  26
+    spatial_version = getattr(connection.ops, "spatial_version", (0,0,0))
  27
+    return spatial_version and (2, 0, 0) <= spatial_version <= (2, 0, 1)
  28
+
  29
+
  30
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
26 31
 class GeoModelTest(TestCase):
27 32
 
28 33
     def test_fixtures(self):
@@ -197,6 +202,7 @@ def test_raw_sql_query(self):
197 202
         self.assertTrue(isinstance(cities2[0].point, Point))
198 203
 
199 204
 
  205
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
200 206
 class GeoLookupTest(TestCase):
201 207
 
202 208
     @no_mysql
@@ -297,7 +303,7 @@ def test_left_right_lookups(self):
297 303
 
298 304
     # The left/right lookup tests are known failures on PostGIS 2.0/2.0.1
299 305
     # http://trac.osgeo.org/postgis/ticket/2035
300  
-    if connection.ops.postgis and (2, 0, 0) <= connection.ops.spatial_version <= (2, 0, 1):
  306
+    if postgis_bug_version():
301 307
         test_left_right_lookups = unittest.expectedFailure(test_left_right_lookups)
302 308
 
303 309
     def test_equals_lookups(self):
@@ -382,6 +388,7 @@ def test_relate_lookup(self):
382 388
             self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, intersects_mask)).name)
383 389
 
384 390
 
  391
+@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
385 392
 class GeoQuerySetTest(TestCase):
386 393
     # Please keep the tests in GeoQuerySet method's alphabetic order
387 394
 
11  django/contrib/gis/tests/geogapp/tests.py
@@ -5,14 +5,19 @@
5 5
 
6 6
 import os
7 7
 
8  
-from django.contrib.gis import gdal
  8
+from django.contrib.gis.gdal import HAS_GDAL