Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #12991 -- Added unittest2 support. Thanks to PaulM for the draf…

…t patch, and to Luke, Karen, Justin, Alex, Łukasz Rekucki, and Chuck Harmston for their help testing and reviewing the final patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14139 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 121d2e36785dc0ce8e7d1c48883fc7b719b21afc 1 parent 1070c57
@freakboy3742 freakboy3742 authored
Showing with 3,875 additions and 1,054 deletions.
  1. +3 −2 django/contrib/admindocs/tests/__init__.py
  2. +3 −3 django/contrib/formtools/tests.py
  3. +2 −2 django/contrib/gis/db/backends/spatialite/creation.py
  4. +1 −1  django/contrib/gis/gdal/tests/__init__.py
  5. +42 −41 django/contrib/gis/gdal/tests/test_envelope.py
  6. +2 −1  django/contrib/gis/gdal/tests/test_geom.py
  7. +4 −3 django/contrib/gis/gdal/tests/test_srs.py
  8. +1 −1  django/contrib/gis/geos/tests/__init__.py
  9. +1 −1  django/contrib/gis/geos/tests/test_geos_mutation.py
  10. +2 −1  django/contrib/gis/geos/tests/test_mutable_list.py
  11. +2 −1  django/contrib/gis/tests/__init__.py
  12. +3 −1 django/contrib/gis/tests/geoapp/test_feeds.py
  13. +9 −5 django/contrib/gis/tests/geoapp/test_sitemaps.py
  14. +17 −17 django/contrib/gis/tests/layermap/tests.py
  15. +2 −2 django/contrib/gis/tests/test_geoforms.py
  16. +27 −26 django/contrib/gis/tests/test_measure.py
  17. +4 −4 django/contrib/gis/tests/test_spatialrefsys.py
  18. +61 −48 django/contrib/markup/tests.py
  19. +1 −1  django/contrib/messages/tests/middleware.py
  20. +4 −3 django/contrib/sessions/tests.py
  21. +90 −0 django/db/backends/__init__.py
  22. +3 −14 django/db/backends/creation.py
  23. +1 −1  django/db/backends/dummy/base.py
  24. +10 −2 django/db/backends/mysql/base.py
  25. +5 −3 django/db/backends/oracle/base.py
  26. +4 −1 django/db/backends/postgresql/base.py
  27. +4 −1 django/db/backends/postgresql_psycopg2/base.py
  28. +23 −2 django/db/backends/sqlite3/base.py
  29. +1 −1  django/test/__init__.py
  30. +11 −48 django/test/simple.py
  31. +28 −4 django/test/testcases.py
  32. +80 −0 django/utils/unittest/__init__.py
  33. +10 −0 django/utils/unittest/__main__.py
  34. +1,083 −0 django/utils/unittest/case.py
  35. +9 −0 django/utils/unittest/collector.py
  36. +64 −0 django/utils/unittest/compatibility.py
  37. +322 −0 django/utils/unittest/loader.py
  38. +241 −0 django/utils/unittest/main.py
  39. +183 −0 django/utils/unittest/result.py
  40. +206 −0 django/utils/unittest/runner.py
  41. +57 −0 django/utils/unittest/signals.py
  42. +287 −0 django/utils/unittest/suite.py
  43. +99 −0 django/utils/unittest/util.py
  44. +28 −0 docs/releases/1.3.txt
  45. +51 −19 docs/topics/testing.txt
  46. +3 −6 tests/modeltests/basic/models.py
  47. +10 −11 tests/modeltests/custom_pk/tests.py
  48. +45 −44 tests/modeltests/fixtures/tests.py
  49. +9 −8 tests/modeltests/lookup/models.py
  50. +1 −1  tests/modeltests/test_client/tests.py
  51. +152 −148 tests/modeltests/transactions/tests.py
  52. +1 −1  tests/modeltests/validation/__init__.py
  53. +3 −1 tests/modeltests/validation/test_unique.py
  54. +2 −1  tests/modeltests/validation/validators.py
  55. +3 −1 tests/modeltests/validators/tests.py
  56. +1 −1  tests/regressiontests/admin_scripts/tests.py
  57. +5 −6 tests/regressiontests/admin_util/tests.py
  58. +34 −32 tests/regressiontests/admin_views/tests.py
  59. +3 −2 tests/regressiontests/admin_widgets/tests.py
  60. +83 −102 tests/regressiontests/aggregation_regress/tests.py
  61. +1 −1  tests/regressiontests/app_loading/tests.py
  62. +1 −1  tests/regressiontests/backends/models.py
  63. +83 −83 tests/regressiontests/backends/tests.py
  64. +1 −1  tests/regressiontests/bash_completion/tests.py
  65. +2 −1  tests/regressiontests/bug639/tests.py
  66. +1 −2  tests/regressiontests/bug8245/tests.py
  67. +2 −1  tests/regressiontests/builtin_server/tests.py
  68. +2 −1  tests/regressiontests/cache/tests.py
  69. +9 −8 tests/regressiontests/datatypes/tests.py
  70. +10 −9 tests/regressiontests/decorators/tests.py
  71. +54 −53 tests/regressiontests/delete_regress/tests.py
  72. +9 −8 tests/regressiontests/dispatch/tests/test_dispatcher.py
  73. +1 −1  tests/regressiontests/dispatch/tests/test_saferef.py
  74. +14 −14 tests/regressiontests/expressions_regress/tests.py
  75. +69 −66 tests/regressiontests/file_storage/tests.py
  76. +4 −4 tests/regressiontests/file_uploads/tests.py
  77. +2 −2 tests/regressiontests/fixtures_regress/models.py
  78. +1 −2  tests/regressiontests/forms/fields.py
  79. +1 −1  tests/regressiontests/forms/input_formats.py
  80. +1 −2  tests/regressiontests/forms/validators.py
  81. +2 −3 tests/regressiontests/forms/widgets.py
  82. +25 −24 tests/regressiontests/httpwrappers/tests.py
  83. +2 −1  tests/regressiontests/humanize/tests.py
  84. +8 −8 tests/regressiontests/introspection/tests.py
  85. +1 −1  tests/regressiontests/localflavor/tests.py
  86. +5 −5 tests/regressiontests/max_lengths/tests.py
  87. +11 −11 tests/regressiontests/model_fields/tests.py
  88. +2 −4 tests/regressiontests/model_regress/models.py
  89. +1 −2  tests/regressiontests/pagination_regress/tests.py
  90. +4 −4 tests/regressiontests/queries/models.py
  91. +1 −2  tests/regressiontests/queries/tests.py
  92. +3 −3 tests/regressiontests/serializers_regress/tests.py
  93. +1 −1  tests/regressiontests/settings_tests/tests.py
  94. +3 −3 tests/regressiontests/templates/loaders.py
  95. +1 −1  tests/regressiontests/templates/nodelist.py
  96. +1 −1  tests/regressiontests/templates/smartif.py
  97. +30 −30 tests/regressiontests/templates/tests.py
  98. +48 −48 tests/regressiontests/test_client_regress/models.py
  99. +2 −2 tests/regressiontests/test_runner/tests.py
  100. +1 −2  tests/regressiontests/urlpatterns_reverse/tests.py
  101. +2 −2 tests/regressiontests/utils/dateformat.py
  102. +1 −2  tests/regressiontests/utils/feedgenerator.py
  103. +1 −2  tests/regressiontests/utils/functional.py
  104. +2 −1  tests/regressiontests/utils/module_loading.py
  105. +1 −2  tests/regressiontests/utils/termcolors.py
  106. +2 −3 tests/runtests.py
View
5 django/contrib/admindocs/tests/__init__.py
@@ -1,7 +1,8 @@
-import unittest
-import fields
from django.contrib.admindocs import views
from django.db.models import fields as builtin_fields
+from django.utils import unittest
+
+import fields
class TestFieldType(unittest.TestCase):
View
6 django/contrib/formtools/tests.py
@@ -1,8 +1,8 @@
-import unittest
from django import forms
-from django.contrib.formtools import preview, wizard, utils
from django import http
+from django.contrib.formtools import preview, wizard, utils
from django.test import TestCase
+from django.utils import unittest
success_string = "Done was called!"
@@ -115,7 +115,7 @@ def test_textfield_hash(self):
hash1 = utils.security_hash(None, f1)
hash2 = utils.security_hash(None, f2)
self.assertEqual(hash1, hash2)
-
+
def test_empty_permitted(self):
"""
Regression test for #10643: the security hash should allow forms with
View
4 django/contrib/gis/db/backends/spatialite/creation.py
@@ -22,8 +22,8 @@ def create_test_db(self, verbosity=1, autoclobber=False):
self.connection.close()
self.connection.settings_dict["NAME"] = test_database_name
- can_rollback = self._rollback_works()
- self.connection.settings_dict["SUPPORTS_TRANSACTIONS"] = can_rollback
+ # Confirm the feature set of the test database
+ self.connection.features.confirm()
# Need to load the SpatiaLite initialization SQL before running `syncdb`.
self.load_spatialite_sql()
call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias)
View
2  django/contrib/gis/gdal/tests/__init__.py
@@ -2,7 +2,7 @@
Module for executing all of the GDAL tests. None
of these tests require the use of the database.
"""
-from unittest import TestSuite, TextTestRunner
+from django.utils.unittest import TestSuite, TextTestRunner
# Importing the GDAL test modules.
import test_driver, test_ds, test_envelope, test_geom, test_srs
View
83 django/contrib/gis/gdal/tests/test_envelope.py
@@ -1,5 +1,6 @@
-import unittest
from django.contrib.gis.gdal import Envelope, OGRException
+from django.utils import unittest
+
class TestPoint(object):
def __init__(self, x, y):
@@ -8,8 +9,8 @@ def __init__(self, x, y):
class EnvelopeTest(unittest.TestCase):
- def setUp(self):
- self.e = Envelope(0, 0, 5, 5)
+ def setUp(self):
+ self.e = Envelope(0, 0, 5, 5)
def test01_init(self):
"Testing Envelope initilization."
@@ -23,10 +24,10 @@ def test01_init(self):
self.assertRaises(OGRException, Envelope, ())
self.assertRaises(ValueError, Envelope, 0, 'a', 5, 5)
self.assertRaises(TypeError, Envelope, u'foo')
- self.assertRaises(OGRException, Envelope, (1, 1, 0, 0))
- try:
- Envelope(0, 0, 0, 0)
- except OGRException:
+ self.assertRaises(OGRException, Envelope, (1, 1, 0, 0))
+ try:
+ Envelope(0, 0, 0, 0)
+ except OGRException:
self.fail("shouldn't raise an exception for min_x == max_x or min_y == max_y")
def test02_properties(self):
@@ -49,41 +50,41 @@ def test03_equivalence(self):
self.assertEqual(e1, e2)
self.assertEqual((0.523, 0.217, 253.23, 523.69), e1)
- def test04_expand_to_include_pt_2_params(self):
- "Testing Envelope expand_to_include -- point as two parameters."
- self.e.expand_to_include(2, 6)
- self.assertEqual((0, 0, 5, 6), self.e)
- self.e.expand_to_include(-1, -1)
- self.assertEqual((-1, -1, 5, 6), self.e)
-
- def test05_expand_to_include_pt_2_tuple(self):
- "Testing Envelope expand_to_include -- point as a single 2-tuple parameter."
- self.e.expand_to_include((10, 10))
- self.assertEqual((0, 0, 10, 10), self.e)
- self.e.expand_to_include((-10, -10))
- self.assertEqual((-10, -10, 10, 10), self.e)
+ def test04_expand_to_include_pt_2_params(self):
+ "Testing Envelope expand_to_include -- point as two parameters."
+ self.e.expand_to_include(2, 6)
+ self.assertEqual((0, 0, 5, 6), self.e)
+ self.e.expand_to_include(-1, -1)
+ self.assertEqual((-1, -1, 5, 6), self.e)
+
+ def test05_expand_to_include_pt_2_tuple(self):
+ "Testing Envelope expand_to_include -- point as a single 2-tuple parameter."
+ self.e.expand_to_include((10, 10))
+ self.assertEqual((0, 0, 10, 10), self.e)
+ self.e.expand_to_include((-10, -10))
+ self.assertEqual((-10, -10, 10, 10), self.e)
+
+ def test06_expand_to_include_extent_4_params(self):
+ "Testing Envelope expand_to_include -- extent as 4 parameters."
+ self.e.expand_to_include(-1, 1, 3, 7)
+ self.assertEqual((-1, 0, 5, 7), self.e)
+
+ def test06_expand_to_include_extent_4_tuple(self):
+ "Testing Envelope expand_to_include -- extent as a single 4-tuple parameter."
+ self.e.expand_to_include((-1, 1, 3, 7))
+ self.assertEqual((-1, 0, 5, 7), self.e)
+
+ def test07_expand_to_include_envelope(self):
+ "Testing Envelope expand_to_include with Envelope as parameter."
+ self.e.expand_to_include(Envelope(-1, 1, 3, 7))
+ self.assertEqual((-1, 0, 5, 7), self.e)
- def test06_expand_to_include_extent_4_params(self):
- "Testing Envelope expand_to_include -- extent as 4 parameters."
- self.e.expand_to_include(-1, 1, 3, 7)
- self.assertEqual((-1, 0, 5, 7), self.e)
-
- def test06_expand_to_include_extent_4_tuple(self):
- "Testing Envelope expand_to_include -- extent as a single 4-tuple parameter."
- self.e.expand_to_include((-1, 1, 3, 7))
- self.assertEqual((-1, 0, 5, 7), self.e)
-
- def test07_expand_to_include_envelope(self):
- "Testing Envelope expand_to_include with Envelope as parameter."
- self.e.expand_to_include(Envelope(-1, 1, 3, 7))
- self.assertEqual((-1, 0, 5, 7), self.e)
-
- def test08_expand_to_include_point(self):
- "Testing Envelope expand_to_include with Point as parameter."
- self.e.expand_to_include(TestPoint(-1, 1))
- self.assertEqual((-1, 0, 5, 5), self.e)
- self.e.expand_to_include(TestPoint(10, 10))
- self.assertEqual((-1, 0, 10, 10), self.e)
+ def test08_expand_to_include_point(self):
+ "Testing Envelope expand_to_include with Point as parameter."
+ self.e.expand_to_include(TestPoint(-1, 1))
+ self.assertEqual((-1, 0, 5, 5), self.e)
+ self.e.expand_to_include(TestPoint(10, 10))
+ self.assertEqual((-1, 0, 10, 10), self.e)
def suite():
s = unittest.TestSuite()
View
3  django/contrib/gis/gdal/tests/test_geom.py
@@ -1,8 +1,9 @@
-import unittest
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, \
OGRException, OGRIndexError, SpatialReference, CoordTransform, \
gdal_version
from django.contrib.gis.tests.geometries import *
+from django.utils import unittest
+
class OGRGeomTest(unittest.TestCase):
"This tests the OGR Geometry."
View
7 django/contrib/gis/gdal/tests/test_srs.py
@@ -1,5 +1,6 @@
-import unittest
from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException
+from django.utils import unittest
+
class TestSRS:
def __init__(self, wkt, **kwargs):
@@ -79,7 +80,7 @@ def test04_proj(self):
srs1 = SpatialReference(s.wkt)
srs2 = SpatialReference(s.proj)
self.assertEqual(srs1.proj, srs2.proj)
-
+
def test05_epsg(self):
"Test EPSG import."
for s in srlist:
@@ -159,7 +160,7 @@ def test13_attr_value(self):
self.assertEqual(4326, int(s1['AUTHORITY', 1]))
#for i in range(7): self.assertEqual(0, int(s1['TOWGS84', i]))
self.assertEqual(None, s1['FOOBAR'])
-
+
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(SpatialRefTest))
View
2  django/contrib/gis/geos/tests/__init__.py
@@ -1,7 +1,7 @@
"""
GEOS Testing module.
"""
-from unittest import TestSuite, TextTestRunner
+from django.utils.unittest import TestSuite, TextTestRunner
import test_geos, test_io, test_geos_mutation, test_mutable_list
test_suites = [
View
2  django/contrib/gis/geos/tests/test_geos_mutation.py
@@ -1,12 +1,12 @@
# Copyright (c) 2008-2009 Aryeh Leib Taurog, all rights reserved.
# Modified from original contribution by Aryeh Leib Taurog, which was
# released under the New BSD license.
-import unittest
import django.utils.copycompat as copy
from django.contrib.gis.geos import *
from django.contrib.gis.geos.error import GEOSIndexError
+from django.utils import unittest
def getItem(o,i): return o[i]
def delItem(o,i): del o[i]
View
3  django/contrib/gis/geos/tests/test_mutable_list.py
@@ -3,8 +3,9 @@
#
# Modified from original contribution by Aryeh Leib Taurog, which was
# released under the New BSD license.
-import unittest
from django.contrib.gis.geos.mutable_list import ListMixin
+from django.utils import unittest
+
class UserListA(ListMixin):
_mytype = tuple
View
3  django/contrib/gis/tests/__init__.py
@@ -1,9 +1,10 @@
import sys
-import unittest
from django.conf import settings
from django.db.models import get_app
from django.test.simple import build_suite, DjangoTestSuiteRunner
+from django.utils import unittest
+
def run_tests(*args, **kwargs):
from django.test.simple import run_tests as base_run_tests
View
4 django/contrib/gis/tests/geoapp/test_feeds.py
@@ -1,9 +1,11 @@
-import unittest
from xml.dom import minidom
from django.test import Client
+from django.utils import unittest
+
from models import City
+
class GeoFeedTest(unittest.TestCase):
client = Client()
View
14 django/contrib/gis/tests/geoapp/test_sitemaps.py
@@ -1,9 +1,13 @@
-import unittest, zipfile, cStringIO
+import cStringIO
from xml.dom import minidom
+import zipfile
from django.test import Client
+from django.utils import unittest
+
from models import City, Country
+
class GeoSitemapTest(unittest.TestCase):
client = Client()
@@ -30,7 +34,7 @@ def test_geositemap_kml(self):
urlset = doc.firstChild
self.assertEqual(urlset.getAttribute(u'xmlns'), u'http://www.sitemaps.org/schemas/sitemap/0.9')
self.assertEqual(urlset.getAttribute(u'xmlns:geo'), u'http://www.google.com/geo/schemas/sitemap/1.0')
-
+
urls = urlset.getElementsByTagName('url')
self.assertEqual(2, len(urls)) # Should only be 2 sitemaps.
for url in urls:
@@ -42,7 +46,7 @@ def test_geositemap_kml(self):
# Getting the relative URL since we don't have a real site.
kml_url = url.getElementsByTagName('loc')[0].childNodes[0].data.split('http://example.com')[1]
-
+
if kml_type == 'kml':
kml_doc = minidom.parseString(self.client.get(kml_url).content)
elif kml_type == 'kmz':
@@ -52,7 +56,7 @@ def test_geositemap_kml(self):
self.assertEqual(1, len(zf.filelist))
self.assertEqual('doc.kml', zf.filelist[0].filename)
kml_doc = minidom.parseString(zf.read('doc.kml'))
-
+
# Ensuring the correct number of placemarks are in the KML doc.
if 'city' in kml_url:
model = City
@@ -65,7 +69,7 @@ def test_geositemap_georss(self):
from feeds import feed_dict
doc = minidom.parseString(self.client.get('/geoapp/sitemaps/georss.xml').content)
-
+
# Ensuring the right sitemaps namespaces are present.
urlset = doc.firstChild
self.assertEqual(urlset.getAttribute(u'xmlns'), u'http://www.sitemaps.org/schemas/sitemap/0.9')
View
34 django/contrib/gis/tests/layermap/tests.py
@@ -1,7 +1,7 @@
import os
-import unittest
from decimal import Decimal
+from django.utils import unittest
from django.utils.copycompat import copy
from django.contrib.gis.gdal import DataSource
@@ -15,9 +15,9 @@
co_shp = os.path.join(shp_path, 'counties', 'counties.shp')
inter_shp = os.path.join(shp_path, 'interstates', 'interstates.shp')
-# Dictionaries to hold what's expected in the county shapefile.
+# Dictionaries to hold what's expected in the county shapefile.
NAMES = ['Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo']
-NUMS = [1, 2, 1, 19, 1] # Number of polygons for each.
+NUMS = [1, 2, 1, 19, 1] # Number of polygons for each.
STATES = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado']
class LayerMapTest(unittest.TestCase):
@@ -98,7 +98,7 @@ def test03_layermap_strict(self):
# Two interstate should have imported correctly.
self.assertEqual(2, Interstate.objects.count())
-
+
# Verifying the values in the layer w/the model.
ds = DataSource(inter_shp)
@@ -106,7 +106,7 @@ def test03_layermap_strict(self):
valid_feats = ds[0][:2]
for feat in valid_feats:
istate = Interstate.objects.get(name=feat['Name'].value)
-
+
if feat.fid == 0:
self.assertEqual(Decimal(str(feat['Length'])), istate.length)
elif feat.fid == 1:
@@ -125,7 +125,7 @@ def county_helper(self, county_feat=True):
c = County.objects.get(name=name)
self.assertEqual(n, len(c.mpoly))
self.assertEqual(st, c.state.name) # Checking ForeignKey mapping.
-
+
# Multiple records because `unique` was not set.
if county_feat:
qs = CountyFeat.objects.filter(name=name)
@@ -137,7 +137,7 @@ def test04_layermap_unique_multigeometry_fk(self):
try:
# Telling LayerMapping that we want no transformations performed on the data.
lm = LayerMapping(County, co_shp, co_mapping, transform=False)
-
+
# Specifying the source spatial reference system via the `source_srs` keyword.
lm = LayerMapping(County, co_shp, co_mapping, source_srs=4269)
lm = LayerMapping(County, co_shp, co_mapping, source_srs='NAD83')
@@ -147,7 +147,7 @@ def test04_layermap_unique_multigeometry_fk(self):
lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique=arg)
except:
self.fail('No exception should be raised for proper use of keywords.')
-
+
# Testing invalid params for the `unique` keyword.
for e, arg in ((TypeError, 5.0), (ValueError, 'foobar'), (ValueError, ('name', 'mpolygon'))):
self.assertRaises(e, LayerMapping, County, co_shp, co_mapping, transform=False, unique=arg)
@@ -177,11 +177,11 @@ def test04_layermap_unique_multigeometry_fk(self):
# are not collections will be converted into them. For example,
# a Point column would be converted to MultiPoint. Other things being done
# w/the keyword args:
- # `transform=False`: Specifies that no transform is to be done; this
+ # `transform=False`: Specifies that no transform is to be done; this
# has the effect of ignoring the spatial reference check (because the
# county shapefile does not have implicit spatial reference info).
- #
- # `unique='name'`: Creates models on the condition that they have
+ #
+ # `unique='name'`: Creates models on the condition that they have
# unique county names; geometries from each feature however will be
# appended to the geometry collection of the unique model. Thus,
# all of the various islands in Honolulu county will be in in one
@@ -201,7 +201,7 @@ def test05_test_fid_range_step(self):
"Tests the `fid_range` keyword and the `step` keyword of .save()."
# Function for clearing out all the counties before testing.
def clear_counties(): County.objects.all().delete()
-
+
# Initializing the LayerMapping object to use in these tests.
lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name')
@@ -213,9 +213,9 @@ def clear_counties(): County.objects.all().delete()
# Step keyword should not be allowed w/`fid_range`.
fr = (3, 5) # layer[3:5]
- self.assertRaises(LayerMapError, lm.save, fid_range=fr, step=10)
+ self.assertRaises(LayerMapError, lm.save, fid_range=fr, step=10)
lm.save(fid_range=fr)
-
+
# Features IDs 3 & 4 are for Galveston County, Texas -- only
# one model is returned because the `unique` keyword was set.
qs = County.objects.all()
@@ -229,9 +229,9 @@ def clear_counties(): County.objects.all().delete()
lm.save(fid_range=slice(None, 1), silent=True, strict=True) # layer[:1]
# Only Pueblo & Honolulu counties should be present because of
- # the `unique` keyword. Have to set `order_by` on this QuerySet
+ # the `unique` keyword. Have to set `order_by` on this QuerySet
# or else MySQL will return a different ordering than the other dbs.
- qs = County.objects.order_by('name')
+ qs = County.objects.order_by('name')
self.assertEqual(2, qs.count())
hi, co = tuple(qs)
hi_idx, co_idx = tuple(map(NAMES.index, ('Honolulu', 'Pueblo')))
@@ -265,7 +265,7 @@ def test06_model_inheritance(self):
self.assertEqual(6, ICity1.objects.count())
self.assertEqual(3, ICity2.objects.count())
-
+
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(LayerMapTest))
View
4 django/contrib/gis/tests/test_geoforms.py
@@ -1,8 +1,8 @@
-import unittest
-
from django.forms import ValidationError
from django.contrib.gis import forms
from django.contrib.gis.geos import GEOSGeometry
+from django.utils import unittest
+
class GeometryFieldTest(unittest.TestCase):
View
53 django/contrib/gis/tests/test_measure.py
@@ -1,10 +1,11 @@
"""
-Distance and Area objects to allow for sensible and convienient calculation
+Distance and Area objects to allow for sensible and convienient calculation
and conversions. Here are some tests.
"""
-import unittest
from django.contrib.gis.measure import Distance, Area, D, A
+from django.utils import unittest
+
class DistanceTest(unittest.TestCase):
"Testing the Distance object"
@@ -30,7 +31,7 @@ def testInit(self):
self.assertEqual(d.m, 1.0)
self.assertEqual(d.mm, 1000.0)
-
+
def testInitInvalid(self):
"Testing initialisation from invalid units"
self.assertRaises(AttributeError, D, banana=100)
@@ -40,7 +41,7 @@ def testAccess(self):
d = D(m=100)
self.assertEqual(d.km, 0.1)
self.assertAlmostEqual(d.ft, 328.084, 3)
-
+
def testAccessInvalid(self):
"Testing access in invalid units"
d = D(m=100)
@@ -55,12 +56,12 @@ def testAddition(self):
self.assertEqual(d3.m, 300)
d3 += d1
self.assertEqual(d3.m, 400)
-
+
d4 = d1 - d2
self.assertEqual(d4.m, -100)
d4 -= d1
self.assertEqual(d4.m, -200)
-
+
try:
d5 = d1 + 1
except TypeError, e:
@@ -88,7 +89,7 @@ def testAddition(self):
pass
else:
self.fail('Distance -= number should raise TypeError')
-
+
def testMultiplication(self):
"Test multiplication & division"
d1 = D(m=100)
@@ -99,12 +100,12 @@ def testMultiplication(self):
self.assertEqual(d3.m, 200)
d3 *= 5
self.assertEqual(d3.m, 1000)
-
+
d4 = d1 / 2
self.assertEqual(d4.m, 50)
d4 /= 5
self.assertEqual(d4.m, 10)
-
+
a5 = d1 * D(m=10)
self.assert_(isinstance(a5, Area))
self.assertEqual(a5.sq_m, 100*10)
@@ -115,7 +116,7 @@ def testMultiplication(self):
pass
else:
self.fail('Distance *= Distance should raise TypeError')
-
+
try:
d5 = d1 / D(m=1)
except TypeError, e:
@@ -143,23 +144,23 @@ def testUnitConversions(self):
self.assertEqual(d5._default_unit, 'm')
d6 = d1 / 2
self.assertEqual(d6._default_unit, 'm')
-
+
def testComparisons(self):
"Testing comparisons"
d1 = D(m=100)
d2 = D(km=1)
d3 = D(km=0)
-
+
self.assert_(d2 > d1)
self.assert_(d1 == d1)
self.assert_(d1 < d2)
self.failIf(d3)
-
+
def testUnitsStr(self):
"Testing conversion to strings"
d1 = D(m=100)
d2 = D(km=3.5)
-
+
self.assertEqual(str(d1), '100.0 m')
self.assertEqual(str(d2), '3.5 km')
self.assertEqual(repr(d1), 'Distance(m=100.0)')
@@ -185,7 +186,7 @@ def testInit(self):
a = A(sq_mi=100)
self.assertEqual(a.sq_m, 258998811.0336)
-
+
def testInitInvaliA(self):
"Testing initialisation from invalid units"
self.assertRaises(AttributeError, A, banana=100)
@@ -195,7 +196,7 @@ def testAccess(self):
a = A(sq_m=100)
self.assertEqual(a.sq_km, 0.0001)
self.assertAlmostEqual(a.sq_ft, 1076.391, 3)
-
+
def testAccessInvaliA(self):
"Testing access in invalid units"
a = A(sq_m=100)
@@ -210,12 +211,12 @@ def testAddition(self):
self.assertEqual(a3.sq_m, 300)
a3 += a1
self.assertEqual(a3.sq_m, 400)
-
+
a4 = a1 - a2
self.assertEqual(a4.sq_m, -100)
a4 -= a1
self.assertEqual(a4.sq_m, -200)
-
+
try:
a5 = a1 + 1
except TypeError, e:
@@ -243,7 +244,7 @@ def testAddition(self):
pass
else:
self.fail('Area -= number should raise TypeError')
-
+
def testMultiplication(self):
"Test multiplication & division"
a1 = A(sq_m=100)
@@ -254,12 +255,12 @@ def testMultiplication(self):
self.assertEqual(a3.sq_m, 200)
a3 *= 5
self.assertEqual(a3.sq_m, 1000)
-
+
a4 = a1 / 2
self.assertEqual(a4.sq_m, 50)
a4 /= 5
self.assertEqual(a4.sq_m, 10)
-
+
try:
a5 = a1 * A(sq_m=1)
except TypeError, e:
@@ -273,7 +274,7 @@ def testMultiplication(self):
pass
else:
self.fail('Area *= Area should raise TypeError')
-
+
try:
a5 = a1 / A(sq_m=1)
except TypeError, e:
@@ -301,23 +302,23 @@ def testUnitConversions(self):
self.assertEqual(a5._default_unit, 'sq_m')
a6 = a1 / 2
self.assertEqual(a6._default_unit, 'sq_m')
-
+
def testComparisons(self):
"Testing comparisons"
a1 = A(sq_m=100)
a2 = A(sq_km=1)
a3 = A(sq_km=0)
-
+
self.assert_(a2 > a1)
self.assert_(a1 == a1)
self.assert_(a1 < a2)
self.failIf(a3)
-
+
def testUnitsStr(self):
"Testing conversion to strings"
a1 = A(sq_m=100)
a2 = A(sq_km=3.5)
-
+
self.assertEqual(str(a1), '100.0 sq_m')
self.assertEqual(str(a2), '3.5 sq_km')
self.assertEqual(repr(a1), 'Area(sq_m=100.0)')
View
8 django/contrib/gis/tests/test_spatialrefsys.py
@@ -1,7 +1,7 @@
-import unittest
-
from django.db import connection
from django.contrib.gis.tests.utils import mysql, no_mysql, oracle, postgis, spatialite
+from django.utils import unittest
+
test_srs = ({'srid' : 4326,
'auth_name' : ('EPSG', True),
@@ -9,7 +9,7 @@
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
'srtext14' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
- 'spheroid' : 'WGS 84', 'name' : 'WGS 84',
+ 'spheroid' : 'WGS 84', 'name' : 'WGS 84',
'geographic' : True, 'projected' : False, 'spatialite' : True,
'ellipsoid' : (6378137.0, 6356752.3, 298.257223563), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
'eprec' : (1, 1, 9),
@@ -49,7 +49,7 @@ def test01_retrieve(self):
auth_name, oracle_flag = sd['auth_name']
if postgis or (oracle and oracle_flag):
self.assertEqual(True, srs.auth_name.startswith(auth_name))
-
+
self.assertEqual(sd['auth_srid'], srs.auth_srid)
# No proj.4 and different srtext on oracle backends :(
View
109 django/contrib/markup/tests.py
@@ -1,77 +1,90 @@
# Quick tests for the markup templatetags (django.contrib.markup)
-
import re
-import unittest
from django.template import Template, Context, add_to_builtins
+from django.utils import unittest
from django.utils.html import escape
add_to_builtins('django.contrib.markup.templatetags.markup')
+try:
+ import textile
+except ImportError:
+ textile = None
+
+try:
+ import markdown
+except ImportError:
+ markdown = None
+
+try:
+ import docutils
+except ImportError:
+ docutils = None
+
class Templates(unittest.TestCase):
- def test_textile(self):
- try:
- import textile
- except ImportError:
- textile = None
- textile_content = """Paragraph 1
+ textile_content = """Paragraph 1
Paragraph 2 with "quotes" and @code@"""
- t = Template("{{ textile_content|textile }}")
- rendered = t.render(Context(locals())).strip()
- if textile:
- self.assertEqual(rendered.replace('\t', ''), """<p>Paragraph 1</p>
+ markdown_content = """Paragraph 1
-<p>Paragraph 2 with &#8220;quotes&#8221; and <code>code</code></p>""")
- else:
- self.assertEqual(rendered, escape(textile_content))
+## An h2"""
- def test_markdown(self):
- try:
- import markdown
- except ImportError:
- markdown = None
+ rest_content = """Paragraph 1
- markdown_content = """Paragraph 1
+Paragraph 2 with a link_
-## An h2"""
+.. _link: http://www.example.com/"""
- t = Template("{{ markdown_content|markdown }}")
- rendered = t.render(Context(locals())).strip()
- if markdown:
- pattern = re.compile("""<p>Paragraph 1\s*</p>\s*<h2>\s*An h2</h2>""")
- self.assert_(pattern.match(rendered))
- else:
- self.assertEqual(rendered, markdown_content)
- def test_docutils(self):
- try:
- import docutils
- except ImportError:
- docutils = None
+ @unittest.skipUnless(textile, 'texttile not installed')
+ def test_textile(self):
+ t = Template("{{ textile_content|textile }}")
+ rendered = t.render(Context({'textile_content':self.textile_content})).strip()
+ self.assertEqual(rendered.replace('\t', ''), """<p>Paragraph 1</p>
- rest_content = """Paragraph 1
+<p>Paragraph 2 with &#8220;quotes&#8221; and <code>code</code></p>""")
-Paragraph 2 with a link_
+ @unittest.skipIf(textile, 'texttile is installed')
+ def test_no_textile(self):
+ t = Template("{{ textile_content|textile }}")
+ rendered = t.render(Context({'textile_content':self.textile_content})).strip()
+ self.assertEqual(rendered, escape(self.textile_content))
-.. _link: http://www.example.com/"""
+ @unittest.skipUnless(markdown, 'markdown not installed')
+ def test_markdown(self):
+ t = Template("{{ markdown_content|markdown }}")
+ rendered = t.render(Context({'markdown_content':self.markdown_content})).strip()
+ pattern = re.compile("""<p>Paragraph 1\s*</p>\s*<h2>\s*An h2</h2>""")
+ self.assertTrue(pattern.match(rendered))
+
+ @unittest.skipIf(markdown, 'markdown is installed')
+ def test_no_markdown(self):
+ t = Template("{{ markdown_content|markdown }}")
+ rendered = t.render(Context({'markdown_content':self.markdown_content})).strip()
+ self.assertEqual(rendered, self.markdown_content)
+ @unittest.skipUnless(docutils, 'docutils not installed')
+ def test_docutils(self):
t = Template("{{ rest_content|restructuredtext }}")
- rendered = t.render(Context(locals())).strip()
- if docutils:
- # Different versions of docutils return slightly different HTML
- try:
- # Docutils v0.4 and earlier
- self.assertEqual(rendered, """<p>Paragraph 1</p>
+ rendered = t.render(Context({'rest_content':self.rest_content})).strip()
+ # Different versions of docutils return slightly different HTML
+ try:
+ # Docutils v0.4 and earlier
+ self.assertEqual(rendered, """<p>Paragraph 1</p>
<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>""")
- except AssertionError, e:
- # Docutils from SVN (which will become 0.5)
- self.assertEqual(rendered, """<p>Paragraph 1</p>
+ except AssertionError, e:
+ # Docutils from SVN (which will become 0.5)
+ self.assertEqual(rendered, """<p>Paragraph 1</p>
<p>Paragraph 2 with a <a class="reference external" href="http://www.example.com/">link</a></p>""")
- else:
- self.assertEqual(rendered, rest_content)
+
+ @unittest.skipIf(docutils, 'docutils is installed')
+ def test_no_docutils(self):
+ t = Template("{{ rest_content|restructuredtext }}")
+ rendered = t.render(Context({'rest_content':self.rest_content})).strip()
+ self.assertEqual(rendered, self.rest_content)
if __name__ == '__main__':
View
2  django/contrib/messages/tests/middleware.py
@@ -1,6 +1,6 @@
-import unittest
from django import http
from django.contrib.messages.middleware import MessageMiddleware
+from django.utils import unittest
class MiddlewareTest(unittest.TestCase):
View
7 django/contrib/sessions/tests.py
@@ -1,4 +1,7 @@
from datetime import datetime, timedelta
+import shutil
+import tempfile
+
from django.conf import settings
from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
from django.contrib.sessions.backends.cache import SessionStore as CacheSession
@@ -8,9 +11,7 @@
from django.contrib.sessions.models import Session
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
-import shutil
-import tempfile
-import unittest
+from django.utils import unittest
class SessionTestsMixin(object):
View
90 django/db/backends/__init__.py
@@ -20,6 +20,7 @@ def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
self.queries = []
self.settings_dict = settings_dict
self.alias = alias
+ self.vendor = 'unknown'
def __eq__(self, other):
return self.settings_dict == other.settings_dict
@@ -87,16 +88,105 @@ class BaseDatabaseFeatures(object):
needs_datetime_string_cast = True
empty_fetchmany_value = []
update_can_self_select = True
+
+ # Does the backend distinguish between '' and None?
interprets_empty_strings_as_nulls = False
+
can_use_chunked_reads = True
can_return_id_from_insert = False
uses_autocommit = False
uses_savepoints = False
+
# If True, don't use integer foreign keys referring to, e.g., positive
# integer primary keys.
related_fields_match_type = False
allow_sliced_subqueries = True
+ # Does the default test database allow multiple connections?
+ # Usually an indication that the test database is in-memory
+ test_db_allows_multiple_connections = True
+
+ # Can an object be saved without an explicit primary key?
+ supports_unspecified_pk = False
+
+ # Can a fixture contain forward references? i.e., are
+ # FK constraints checked at the end of transaction, or
+ # at the end of each save operation?
+ supports_forward_references = True
+
+ # Does a dirty transaction need to be rolled back
+ # before the cursor can be used again?
+ requires_rollback_on_dirty_transaction = False
+
+ # Does the backend allow very long model names without error?
+ supports_long_model_names = True
+
+ # Is there a REAL datatype in addition to floats/doubles?
+ has_real_datatype = False
+ supports_subqueries_in_group_by = True
+ supports_bitwise_or = True
+
+ # Do time/datetime fields have microsecond precision?
+ supports_microsecond_precision = True
+
+ # Does the __regex lookup support backreferencing and grouping?
+ supports_regex_backreferencing = True
+
+ # Can date/datetime lookups be performed using a string?
+ supports_date_lookup_using_string = True
+
+ # Can datetimes with timezones be used?
+ supports_timezones = True
+
+ # When performing a GROUP BY, is an ORDER BY NULL required
+ # to remove any ordering?
+ requires_explicit_null_ordering_when_grouping = False
+
+ # Is there a 1000 item limit on query parameters?
+ supports_1000_query_paramters = True
+
+ # Can an object have a primary key of 0? MySQL says No.
+ allows_primary_key_0 = True
+
+ # Features that need to be confirmed at runtime
+ # Cache whether the confirmation has been performed.
+ _confirmed = False
+ supports_transactions = None
+ supports_stddev = None
+
+ def __init__(self, connection):
+ self.connection = connection
+
+ def confirm(self):
+ "Perform manual checks of any database features that might vary between installs"
+ self._confirmed = True
+ self.supports_transactions = self._supports_transactions()
+ self.supports_stddev = self._supports_stddev()
+
+ def _supports_transactions(self):
+ "Confirm support for transactions"
+ cursor = self.connection.cursor()
+ cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
+ self.connection._commit()
+ cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)')
+ self.connection._rollback()
+ cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST')
+ count, = cursor.fetchone()
+ cursor.execute('DROP TABLE ROLLBACK_TEST')
+ self.connection._commit()
+ return count == 0
+
+ def _supports_stddev(self):
+ "Confirm support for STDDEV and related stats functions"
+ class StdDevPop(object):
+ sql_function = 'STDDEV_POP'
+
+ try:
+ self.connection.ops.check_aggregate_support(StdDevPop())
+ except DatabaseError:
+ self.supports_stddev = False
+
+
class BaseDatabaseOperations(object):
"""
This class encapsulates all backend-specific differences, such as the way
View
17 django/db/backends/creation.py
@@ -347,8 +347,9 @@ def create_test_db(self, verbosity=1, autoclobber=False):
self.connection.close()
self.connection.settings_dict["NAME"] = test_database_name
- can_rollback = self._rollback_works()
- self.connection.settings_dict["SUPPORTS_TRANSACTIONS"] = can_rollback
+
+ # Confirm the feature set of the test database
+ self.connection.features.confirm()
# Report syncdb messages at one level lower than that requested.
# This ensures we don't get flooded with messages during testing
@@ -405,18 +406,6 @@ def _create_test_db(self, verbosity, autoclobber):
return test_database_name
- def _rollback_works(self):
- cursor = self.connection.cursor()
- cursor.execute('CREATE TABLE ROLLBACK_TEST (X INT)')
- self.connection._commit()
- cursor.execute('INSERT INTO ROLLBACK_TEST (X) VALUES (8)')
- self.connection._rollback()
- cursor.execute('SELECT COUNT(X) FROM ROLLBACK_TEST')
- count, = cursor.fetchone()
- cursor.execute('DROP TABLE ROLLBACK_TEST')
- self.connection._commit()
- return count == 0
-
def destroy_test_db(self, old_database_name, verbosity=1):
"""
Destroy a test database, prompting the user for confirmation if the
View
2  django/db/backends/dummy/base.py
@@ -42,7 +42,7 @@ class DatabaseWrapper(object):
_rollback = ignore
def __init__(self, settings_dict, alias, *args, **kwargs):
- self.features = BaseDatabaseFeatures()
+ self.features = BaseDatabaseFeatures(self)
self.ops = DatabaseOperations()
self.client = DatabaseClient(self)
self.creation = BaseDatabaseCreation(self)
View
12 django/db/backends/mysql/base.py
@@ -124,6 +124,14 @@ class DatabaseFeatures(BaseDatabaseFeatures):
allows_group_by_pk = True
related_fields_match_type = True
allow_sliced_subqueries = False
+ supports_forward_references = False
+ supports_long_model_names = False
+ supports_microsecond_precision = False
+ supports_regex_backreferencing = False
+ supports_date_lookup_using_string = False
+ supports_timezones = False
+ requires_explicit_null_ordering_when_grouping = True
+ allows_primary_key_0 = False
class DatabaseOperations(BaseDatabaseOperations):
compiler_module = "django.db.backends.mysql.compiler"
@@ -231,7 +239,7 @@ def max_name_length(self):
return 64
class DatabaseWrapper(BaseDatabaseWrapper):
-
+ vendor = 'mysql'
operators = {
'exact': '= %s',
'iexact': 'LIKE %s',
@@ -253,7 +261,7 @@ def __init__(self, *args, **kwargs):
super(DatabaseWrapper, self).__init__(*args, **kwargs)
self.server_version = None
- self.features = DatabaseFeatures()
+ self.features = DatabaseFeatures(self)
self.ops = DatabaseOperations()
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
View
8 django/db/backends/oracle/base.py
@@ -50,7 +50,9 @@ class DatabaseFeatures(BaseDatabaseFeatures):
uses_savepoints = True
can_return_id_from_insert = True
allow_sliced_subqueries = False
-
+ supports_subqueries_in_group_by = True
+ supports_timezones = False
+ supports_bitwise_or = False
class DatabaseOperations(BaseDatabaseOperations):
compiler_module = "django.db.backends.oracle.compiler"
@@ -314,7 +316,7 @@ def combine_expression(self, connector, sub_expressions):
class DatabaseWrapper(BaseDatabaseWrapper):
-
+ vendor = 'oracle'
operators = {
'exact': '= %s',
'iexact': '= UPPER(%s)',
@@ -334,7 +336,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def __init__(self, *args, **kwargs):
super(DatabaseWrapper, self).__init__(*args, **kwargs)
- self.features = DatabaseFeatures()
+ self.features = DatabaseFeatures(self)
self.ops = DatabaseOperations()
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
View
5 django/db/backends/postgresql/base.py
@@ -80,8 +80,11 @@ def __iter__(self):
class DatabaseFeatures(BaseDatabaseFeatures):
uses_savepoints = True
+ requires_rollback_on_dirty_transaction = True
+ has_real_datatype = True
class DatabaseWrapper(BaseDatabaseWrapper):
+ vendor = 'postgresql'
operators = {
'exact': '= %s',
'iexact': '= UPPER(%s)',
@@ -108,7 +111,7 @@ def __init__(self, *args, **kwargs):
DeprecationWarning
)
- self.features = DatabaseFeatures()
+ self.features = DatabaseFeatures(self)
self.ops = DatabaseOperations(self)
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
View
5 django/db/backends/postgresql_psycopg2/base.py
@@ -67,6 +67,8 @@ def __iter__(self):
class DatabaseFeatures(BaseDatabaseFeatures):
needs_datetime_string_cast = False
can_return_id_from_insert = False
+ requires_rollback_on_dirty_transaction = True
+ has_real_datatype = True
class DatabaseOperations(PostgresqlDatabaseOperations):
def last_executed_query(self, cursor, sql, params):
@@ -79,6 +81,7 @@ def return_insert_id(self):
return "RETURNING %s", ()
class DatabaseWrapper(BaseDatabaseWrapper):
+ vendor = 'postgresql'
operators = {
'exact': '= %s',
'iexact': '= UPPER(%s)',
@@ -99,7 +102,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def __init__(self, *args, **kwargs):
super(DatabaseWrapper, self).__init__(*args, **kwargs)
- self.features = DatabaseFeatures()
+ self.features = DatabaseFeatures(self)
autocommit = self.settings_dict["OPTIONS"].get('autocommit', False)
self.features.uses_autocommit = autocommit
self._set_isolation_level(int(not autocommit))
View
25 django/db/backends/sqlite3/base.py
@@ -60,6 +60,27 @@ class DatabaseFeatures(BaseDatabaseFeatures):
# setting ensures we always read result sets fully into memory all in one
# go.
can_use_chunked_reads = False
+ test_db_allows_multiple_connections = False
+ supports_unspecified_pk = True
+ supports_1000_query_paramters = False
+
+ def _supports_stddev(self):
+ """Confirm support for STDDEV and related stats functions
+
+ SQLite supports STDDEV as an extension package; so
+ connection.ops.check_aggregate_support() can't unilaterally
+ rule out support for STDDEV. We need to manually check
+ whether the call works.
+ """
+ cursor = self.connection.cursor()
+ cursor.execute('CREATE TABLE STDDEV_TEST (X INT)')
+ try:
+ cursor.execute('SELECT STDDEV(*) FROM STDDEV_TEST')
+ has_support = True
+ except utils.DatabaseError:
+ has_support = False
+ cursor.execute('DROP TABLE STDDEV_TEST')
+ return has_support
class DatabaseOperations(BaseDatabaseOperations):
def date_extract_sql(self, lookup_type, field_name):
@@ -129,7 +150,7 @@ def convert_values(self, value, field):
return value
class DatabaseWrapper(BaseDatabaseWrapper):
-
+ vendor = 'sqlite'
# SQLite requires LIKE statements to include an ESCAPE clause if the value
# being escaped has a percent or underscore in it.
# See http://www.sqlite.org/lang_expr.html for an explanation.
@@ -153,7 +174,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def __init__(self, *args, **kwargs):
super(DatabaseWrapper, self).__init__(*args, **kwargs)
- self.features = DatabaseFeatures()
+ self.features = DatabaseFeatures(self)
self.ops = DatabaseOperations()
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
View
2  django/test/__init__.py
@@ -3,5 +3,5 @@
"""
from django.test.client import Client
-from django.test.testcases import TestCase, TransactionTestCase
+from django.test.testcases import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
from django.test.utils import Approximate
View
59 django/test/simple.py
@@ -1,12 +1,12 @@
import sys
import signal
-import unittest
from django.conf import settings
from django.db.models import get_app, get_apps
from django.test import _doctest as doctest
from django.test.utils import setup_test_environment, teardown_test_environment
from django.test.testcases import OutputChecker, DocTestRunner, TestCase
+from django.utils import unittest
# The module name for tests outside models.py
TEST_MODULE = 'tests'
@@ -14,52 +14,13 @@
doctestOutputChecker = OutputChecker()
class DjangoTestRunner(unittest.TextTestRunner):
-
- def __init__(self, verbosity=0, failfast=False, **kwargs):
- super(DjangoTestRunner, self).__init__(verbosity=verbosity, **kwargs)
- self.failfast = failfast
- self._keyboard_interrupt_intercepted = False
-
- def run(self, *args, **kwargs):
- """
- Runs the test suite after registering a custom signal handler
- that triggers a graceful exit when Ctrl-C is pressed.
- """
- self._default_keyboard_interrupt_handler = signal.signal(signal.SIGINT,
- self._keyboard_interrupt_handler)
- try:
- result = super(DjangoTestRunner, self).run(*args, **kwargs)
- finally:
- signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
- return result
-
- def _keyboard_interrupt_handler(self, signal_number, stack_frame):
- """
- Handles Ctrl-C by setting a flag that will stop the test run when
- the currently running test completes.
- """
- self._keyboard_interrupt_intercepted = True
- sys.stderr.write(" <Test run halted by Ctrl-C> ")
- # Set the interrupt handler back to the default handler, so that
- # another Ctrl-C press will trigger immediate exit.
- signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
-
- def _makeResult(self):
- result = super(DjangoTestRunner, self)._makeResult()
- failfast = self.failfast
-
- def stoptest_override(func):
- def stoptest(test):
- # If we were set to failfast and the unit test failed,
- # or if the user has typed Ctrl-C, report and quit
- if (failfast and not result.wasSuccessful()) or \
- self._keyboard_interrupt_intercepted:
- result.stop()
- func(test)
- return stoptest
-
- result.stopTest = stoptest_override(result.stopTest)
- return result
+ def __init__(self, *args, **kwargs):
+ import warnings
+ warnings.warn(
+ "DjangoTestRunner is deprecated; it's functionality is indistinguishable from TextTestRunner",
+ PendingDeprecationWarning
+ )
+ super(DjangoTestRunner, self).__init__(*args, **kwargs)
def get_tests(app_module):
try:
@@ -232,6 +193,7 @@ def __init__(self, verbosity=1, interactive=True, failfast=True, **kwargs):
def setup_test_environment(self, **kwargs):
setup_test_environment()
settings.DEBUG = False
+ unittest.installHandler()
def build_suite(self, test_labels, extra_tests=None, **kwargs):
suite = unittest.TestSuite()
@@ -271,7 +233,7 @@ def setup_databases(self, **kwargs):
return old_names, mirrors
def run_suite(self, suite, **kwargs):
- return DjangoTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
+ return unittest.TextTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
def teardown_databases(self, old_config, **kwargs):
from django.db import connections
@@ -284,6 +246,7 @@ def teardown_databases(self, old_config, **kwargs):
connection.creation.destroy_test_db(old_name, self.verbosity)
def teardown_test_environment(self, **kwargs):
+ unittest.removeHandler()
teardown_test_environment()
def suite_result(self, suite, result, **kwargs):
View
32 django/test/testcases.py
@@ -1,5 +1,4 @@
import re
-import unittest
from urlparse import urlsplit, urlunsplit
from xml.dom.minidom import parseString, Node
@@ -7,12 +6,14 @@
from django.core import mail
from django.core.management import call_command
from django.core.urlresolvers import clear_url_caches
-from django.db import transaction, connections, DEFAULT_DB_ALIAS
+from django.db import transaction, connection, connections, DEFAULT_DB_ALIAS
from django.http import QueryDict
from django.test import _doctest as doctest
from django.test.client import Client
-from django.utils import simplejson
+from django.utils import simplejson, unittest
from django.utils.encoding import smart_str
+from django.utils.functional import wraps
+
try:
all
@@ -22,6 +23,7 @@
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)", lambda m: "Decimal(\"%s\")" % m.groups()[0], s)
+
def to_list(value):
"""
Puts value into a list if it's not already one.
@@ -472,7 +474,7 @@ def connections_support_transactions():
Returns True if all connections support transactions. This is messy
because 2.4 doesn't support any or all.
"""
- return all(conn.settings_dict['SUPPORTS_TRANSACTIONS']
+ return all(conn.features.supports_transactions
for conn in connections.all())
class TestCase(TransactionTestCase):
@@ -528,3 +530,25 @@ def _fixture_teardown(self):
for connection in connections.all():
connection.close()
+
+def _deferredSkip(condition, reason):
+ def decorator(test_item):
+ if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
+ @wraps(test_item)
+ def skip_wrapper(*args, **kwargs):
+ if condition():
+ raise unittest.SkipTest(reason)
+ test_item = skip_wrapper
+ test_item.__unittest_skip_why__ = reason
+ return test_item
+ return decorator
+
+def skipIfDBFeature(feature):
+ "Skip a test if a database has the named feature"
+ return _deferredSkip(lambda: getattr(connection.features, feature),
+ "Database has feature %s" % feature)
+
+def skipUnlessDBFeature(feature):
+ "Skip a test unless a database has the named feature"
+ return _deferredSkip(lambda: not getattr(connection.features, feature),
+ "Database doesn't support feature %s" % feature)
View
80 django/utils/unittest/__init__.py
@@ -0,0 +1,80 @@
+"""
+unittest2
+
+unittest2 is a backport of the new features added to the unittest testing
+framework in Python 2.7. It is tested to run on Python 2.4 - 2.6.
+
+To use unittest2 instead of unittest simply replace ``import unittest`` with
+``import unittest2``.
+
+
+Copyright (c) 1999-2003 Steve Purcell
+Copyright (c) 2003-2010 Python Software Foundation
+This module is free software, and you may redistribute it and/or modify
+it under the same terms as Python itself, so long as this copyright message
+and disclaimer are retained in their original form.
+
+IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
+THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
+AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+"""
+
+import sys
+
+# Django hackery to load the appropriate version of unittest
+
+if sys.version_info >= (2,7):
+ # unittest2 features are native in Python 2.7
+ from unittest import *
+else:
+ try:
+ # check the system path first
+ from unittest2 import *
+ except ImportError:
+ # otherwise use our bundled version
+ __all__ = ['TestResult', 'TestCase', 'TestSuite',
+ 'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main',
+ 'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless',
+ 'expectedFailure', 'TextTestResult', '__version__', 'collector']
+
+ __version__ = '0.5.1'
+
+ # Expose obsolete functions for backwards compatibility
+ __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
+
+
+ from django.utils.unittest.collector import collector
+ from django.utils.unittest.result import TestResult
+ from django.utils.unittest.case import \
+ TestCase, FunctionTestCase, SkipTest, skip, skipIf,\
+ skipUnless, expectedFailure
+
+ from django.utils.unittest.suite import BaseTestSuite, TestSuite
+ from django.utils.unittest.loader import \
+ TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,\
+ findTestCases
+
+ from django.utils.unittest.main import TestProgram, main, main_
+ from django.utils.unittest.runner import TextTestRunner, TextTestResult
+
+ try:
+ from django.utils.unittest.signals import\
+ installHandler, registerResult, removeResult, removeHandler
+ except ImportError:
+ # Compatibility with platforms that don't have the signal module
+ pass
+ else:
+ __all__.extend(['installHandler', 'registerResult', 'removeResult',
+ 'removeHandler'])
+
+ # deprecated
+ _TextTestResult = TextTestResult
+
+ __unittest = True
View
10 django/utils/unittest/__main__.py
@@ -0,0 +1,10 @@
+"""Main entry point"""
+
+import sys
+if sys.argv[0].endswith("__main__.py"):
+ sys.argv[0] = "unittest2"
+
+__unittest = True
+
+from django.utils.unittest.main import main_
+main_()
View
1,083 django/utils/unittest/case.py
@@ -0,0 +1,1083 @@
+"""Test case implementation"""
+
+import sys
+import difflib
+import pprint
+import re
+import unittest
+import warnings
+
+from django.utils.unittest import result
+from django.utils.unittest.util import\
+ safe_repr, safe_str, strclass,\
+ unorderable_list_difference
+
+from django.utils.unittest.compatibility import wraps
+
+__unittest = True
+
+
+DIFF_OMITTED = ('\nDiff is %s characters long. '
+ 'Set self.maxDiff to None to see it.')
+
+class SkipTest(Exception):
+ """
+ Raise this exception in a test to skip it.
+
+ Usually you can use TestResult.skip() or one of the skipping decorators
+ instead of raising this directly.
+ """
+
+class _ExpectedFailure(Exception):
+ """
+ Raise this when a test is expected to fail.
+
+ This is an implementation detail.
+ """
+
+ def __init__(self, exc_info):
+ # can't use super because Python 2.4 exceptions are old style
+ Exception.__init__(self)
+ self.exc_info = exc_info
+
+class _UnexpectedSuccess(Exception):
+ """
+ The test was supposed to fail, but it didn't!
+ """
+
+def _id(obj):
+ return obj
+
+def skip(reason):
+ """
+ Unconditionally skip a test.
+ """
+ def decorator(test_item):
+ if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
+ @wraps(test_item)
+ def skip_wrapper(*args, **kwargs):
+ raise SkipTest(reason)
+ test_item = skip_wrapper
+
+ test_item.__unittest_skip__ = True
+ test_item.__unittest_skip_why__ = reason
+ return test_item
+ return decorator
+
+def skipIf(condition, reason):
+ """
+ Skip a test if the condition is true.
+ """
+ if condition:
+ return skip(reason)
+ return _id
+
+def skipUnless(condition, reason):
+ """
+ Skip a test unless the condition is true.
+ """
+ if not condition:
+ return skip(reason)
+ return _id
+
+
+def expectedFailure(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except Exception:
+ raise _ExpectedFailure(sys.exc_info())
+ raise _UnexpectedSuccess
+ return wrapper
+
+
+class _AssertRaisesContext(object):
+ """A context manager used to implement TestCase.assertRaises* methods."""
+
+ def __init__(self, expected, test_case, expected_regexp=None):
+ self.expected = expected
+ self.failureException = test_case.failureException
+ self.expected_regexp = expected_regexp
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ if exc_type is None:
+ try:
+ exc_name = self.expected.__name__
+ except AttributeError:
+ exc_name = str(self.expected)
+ raise self.failureException(
+ "%s not raised" % (exc_name,))
+ if not issubclass(exc_type, self.expected):
+ # let unexpected exceptions pass through
+ return False
+ self.exception = exc_value # store for later retrieval
+ if self.expected_regexp is None:
+ return True
+
+ expected_regexp = self.expected_regexp
+ if isinstance(expected_regexp, basestring):
+ expected_regexp = re.compile(expected_regexp)
+ if not expected_regexp.search(str(exc_value)):
+ raise self.failureException('"%s" does not match "%s"' %
+ (expected_regexp.pattern, str(exc_value)))
+ return True
+
+
+class _TypeEqualityDict(object):
+
+ def __init__(self, testcase):
+ self.testcase = testcase
+ self._store = {}
+
+ def __setitem__(self, key, value):
+ self._store[key] = value
+
+ def __getitem__(self, key):
+ value = self._store[key]
+ if isinstance(value, basestring):
+ return getattr(self.testcase, value)
+ return value
+
+ def get(self, key, default=None):
+ if key in self._store:
+ return self[key]
+ return default
+
+
+class TestCase(unittest.TestCase):
+ """A class whose instances are single test cases.
+
+ By default, the test code itself should be placed in a method named
+ 'runTest'.
+
+ If the fixture may be used for many test cases, create as
+ many test methods as are needed. When instantiating such a TestCase
+ subclass, specify in the constructor arguments the name of the test method
+ that the instance is to execute.
+
+ Test authors should subclass TestCase for their own tests. Construction
+ and deconstruction of the test's environment ('fixture') can be
+ implemented by overriding the 'setUp' and 'tearDown' methods respectively.
+
+ If it is necessary to override the __init__ method, the base class
+ __init__ method must always be called. It is important that subclasses
+ should not change the signature of their __init__ method, since instances
+ of the classes are instantiated automatically by parts of the framework
+ in order to be run.
+ """
+
+ # This attribute determines which exception will be raised when
+ # the instance's assertion methods fail; test methods raising this
+ # exception will be deemed to have 'failed' rather than 'errored'
+
+ failureException = AssertionError
+
+ # This attribute sets the maximum length of a diff in failure messages
+ # by assert methods using difflib. It is looked up as an instance attribute
+ # so can be configured by individual tests if required.
+
+ maxDiff = 80*8
+
+ # This attribute determines whether long messages (including repr of
+ # objects used in assert methods) will be printed on failure in *addition*
+ # to any explicit message passed.
+
+ longMessage = True
+
+ # Attribute used by TestSuite for classSetUp
+
+ _classSetupFailed = False
+
+ def __init__(self, methodName='runTest'):
+ """Create an instance of the class that will use the named test
+ method when executed. Raises a ValueError if the instance does
+ not have a method with the specified name.
+ """
+ self._testMethodName = methodName
+ self._resultForDoCleanups = None
+ try:
+ testMethod = getattr(self, methodName)
+ except AttributeError:
+ raise ValueError("no such test method in %s: %s" % \
+ (self.__class__, methodName))
+ self._testMethodDoc = testMethod.__doc__
+ self._cleanups = []
+
+ # Map types to custom assertEqual functions that will compare
+ # instances of said type in more detail to generate a more useful
+ # error message.
+ self._type_equality_funcs = _TypeEqualityDict(self)
+ self.addTypeEqualityFunc(dict, 'assertDictEqual')
+ self.addTypeEqualityFunc(list, 'assertListEqual')
+ self.addTypeEqualityFunc(tuple, 'assertTupleEqual')
+ self.addTypeEqualityFunc(set, 'assertSetEqual')
+ self.addTypeEqualityFunc(frozenset, 'assertSetEqual')
+ self.addTypeEqualityFunc(unicode, 'assertMultiLineEqual')
+
+ def addTypeEqualityFunc(self, typeobj, function):
+ """Add a type specific assertEqual style function to compare a type.
+
+ This method is for use by TestCase subclasses that need to register
+ their own type equality functions to provide nicer error messages.
+
+ Args:
+ typeobj: The data type to call this function on when both values
+ are of the same type in assertEqual().
+ function: The callable taking two arguments and an optional
+ msg= argument that raises self.failureException with a
+ useful error message when the two arguments are not equal.
+ """
+ self._type_equality_funcs[typeobj] = function
+
+ def addCleanup(self, function, *args, **kwargs):
+ """Add a function, with arguments, to be called when the test is
+ completed. Functions added are called on a LIFO basis and are
+ called after tearDown on test failure or success.
+
+ Cleanup items are called even if setUp fails (unlike tearDown)."""
+ self._cleanups.append((function, args, kwargs))
+
+ def setUp(self):
+ "Hook method for setting up the test fixture before exercising it."
+
+ @classmethod
+ def setUpClass(cls):
+ "Hook method for setting up class fixture before running tests in the class."
+
+ @classmethod
+ def tearDownClass(cls):
+ "Hook method for deconstructing the class fixture after running all tests in the class."
+
+ def tearDown(self):
+ "Hook method for deconstructing the test fixture after testing it."
+
+ def countTestCases(self):
+ return 1
+
+ def defaultTestResult(self):
+ return result.TestResult()
+
+ def shortDescription(self):
+ """Returns a one-line description of the test, or None if no
+ description has been provided.
+
+ The default implementation of this method returns the first line of
+ the specified test method's docstring.
+ """
+ doc = self._testMethodDoc
+ return doc and doc.split("\n")[0].strip() or None
+
+
+ def id(self):
+ return "%s.%s" % (strclass(self.__class__), self._testMethodName)
+
+ def __eq__(self, other):
+ if type(self) is not type(other):
+ return NotImplemented
+
+ return self._testMethodName == other._testMethodName
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ return hash((type(self), self._testMethodName))
+
+ def __str__(self):
+ return "%s (%s)" % (self._testMethodName, strclass(self.__class__))
+
+ def __repr__(self):
+ return "<%s testMethod=%s>" % \
+ (strclass(self.__class__), self._testMethodName)
+
+ def _addSkip(self, result, reason):
+ addSkip = getattr(result, 'addSkip', None)
+ if addSkip is not None:
+ addSkip(self, reason)
+ else:
+ warnings.warn("Use of a TestResult without an addSkip method is deprecated",
+ DeprecationWarning, 2)
+ result.addSuccess(self)
+
+ def run(self, result=None):
+ orig_result = result
+ if result is None:
+ result = self.defaultTestResult()
+ startTestRun = getattr(result, 'startTestRun', None)
+ if startTestRun is not None:
+ startTestRun()
+
+ self._resultForDoCleanups = result
+ result.startTest(self)
+
+ testMethod = getattr(self, self._testMethodName)
+
+ if (getattr(self.__class__, "__unittest_skip__", False) or
+ getattr(testMethod, "__unittest_skip__", False)):
+ # If the class or method was skipped.
+ try:
+ skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
+ or getattr(testMethod, '__unittest_skip_why__', ''))
+ self._addSkip(result, skip_why)
+ finally:
+ result.stopTest(self)
+ return
+ try:
+ success = False
+ try:
+ self.setUp()
+ except SkipTest, e:
+ self._addSkip(result, str(e))
+ except Exception:
+ result.addError(self, sys.exc_info())
+ else:
+ try:
+ testMethod()
+ except self.failureException:
+ result.addFailure(self, sys.exc_info())
+ except _ExpectedFailure, e:
+ addExpectedFailure = getattr(result, 'addExpectedFailure', None)
+ if addExpectedFailure is not None:
+ addExpectedFailure(self, e.exc_info)
+ else:
+ warnings.warn("Use of a TestResult without an addExpectedFailure method is deprecated",
+ DeprecationWarning)
+ result.addSuccess(self)
+ except _UnexpectedSuccess:
+ addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None)
+ if addUnexpectedSuccess is not None:
+ addUnexpectedSuccess(self)
+ else:
+ warnings.warn("Use of a TestResult without an addUnexpectedSuccess method is deprecated",
+ DeprecationWarning)
+ result.addFailure(self, sys.exc_info())
+ except SkipTest, e:
+ self._addSkip(result, str(e))
+ except Exception:
+ result.addError(self, sys.exc_info())
+ else:
+ success = True
+
+ try:
+ self.tearDown()
+ except Exception:
+ result.addError(self, sys.exc_info())
+ success = False
+
+ cleanUpSuccess = self.doCleanups()
+ success = success and cleanUpSuccess
+ if success:
+ result.addSuccess(self)
+ finally:
+ result.stopTest(self)
+ if orig_result is None:
+ stopTestRun = getattr(result, 'stopTestRun', None)
+ if stopTestRun is not None:
+ stopTestRun()
+
+ def doCleanups(self):
+ """Execute all cleanup functions. Normally called for you after
+ tearDown."""
+ result = self._resultForDoCleanups
+ ok = True
+ while self._cleanups:
+ function, args, kwargs = self._cleanups.pop(-1)
+ try:
+ function(*args, **kwargs)
+ except Exception:
+ ok = False
+ result.addError(self, sys.exc_info())
+ return ok
+
+ def __call__(self, *args, **kwds):
+ return self.run(*args, **kwds)
+
+ def debug(self):
+ """Run the test without collecting errors in a TestResult"""
+ self.setUp()
+ getattr(self, self._testMethodName)()
+ self.tearDown()
+ while self._cleanups:
+ function, args, kwargs = self._cleanups.pop(-1)
+ function(*args, **kwargs)
+
+ def skipTest(self, reason):
+ """Skip this test."""
+ raise SkipTest(reason)
+
+ def fail(self, msg=None):
+ """Fail immediately, with the given message."""
+ raise self.failureException(msg)
+
+ def assertFalse(self, expr, msg=None):
+ "Fail the test if the expression is true."
+ if expr:
+ msg = self._formatMessage(msg, "%s is not False" % safe_repr(expr))
+ raise self.failureException(msg)
+
+ def assertTrue(self, expr, msg=None):
+ """Fail the test unless the expression is true."""
+ if not expr:
+ msg = self._formatMessage(msg, "%s is not True" % safe_repr(expr))
+ raise self.failureException(msg)
+
+ def _formatMessage(self, msg, standardMsg):
+ """Honour the longMessage attribute when generating failure messages.
+ If longMessage is False this means:
+ * Use only an explicit message if it is provided
+ * Otherwise use the standard message for the assert
+
+ If longMessage is True:
+ * Use the standard message
+ * If an explicit message is provided, plus ' : ' and the explicit message
+ """
+ if not self.longMessage:
+ return msg or standardMsg
+ if msg is None:
+ return standardMsg
+ try:
+ return '%s : %s' % (standardMsg, msg)
+ except UnicodeDecodeError:
+ return '%s : %s' % (safe_str(standardMsg), safe_str(msg))
+
+
+ def assertRaises(self, excClass, callableObj=None, *args, **kwargs):
+ """Fail unless an exception of class excClass is thrown
+ by callableObj when invoked with arguments args and keyword
+ arguments kwargs. If a different type of exception is
+ thrown, it will not be caught, and the test case will be
+ deemed to have suffered an error, exactly as for an
+ unexpected exception.
+
+ If called with callableObj omitted or None, will return a
+ context object used like this::
+
+ with self.assertRaises(SomeException):
+ do_something()
+
+ The context manager keeps a reference to the exception as