summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/south/tests
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/south/tests')
-rw-r--r--lib/python2.7/site-packages/south/tests/__init__.py109
-rw-r--r--lib/python2.7/site-packages/south/tests/autodetection.py360
-rw-r--r--lib/python2.7/site-packages/south/tests/brokenapp/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/brokenapp/migrations/0001_depends_on_unmigrated.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/brokenapp/migrations/0002_depends_on_unknown.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/brokenapp/migrations/0003_depends_on_higher.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/brokenapp/migrations/0004_higher.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/brokenapp/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/brokenapp/models.py55
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_a/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_a/migrations/0001_first.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_a/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_a/models.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_b/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_b/migrations/0001_first.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_b/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/circular_b/models.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/db.py1060
-rw-r--r--lib/python2.7/site-packages/south/tests/db_firebird.py39
-rw-r--r--lib/python2.7/site-packages/south/tests/db_mysql.py164
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/migrations/0001_a.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/migrations/0002_a.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/migrations/0003_a.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/migrations/0004_a.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/migrations/0005_a.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_a/models.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/migrations/0001_b.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/migrations/0002_b.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/migrations/0003_b.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/migrations/0004_b.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/migrations/0005_b.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_b/models.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/migrations/0001_c.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/migrations/0002_c.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/migrations/0003_c.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/migrations/0004_c.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/migrations/0005_c.py13
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/deps_c/models.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/emptyapp/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/emptyapp/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/emptyapp/models.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/fakeapp/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/fakeapp/migrations/0001_spam.py17
-rw-r--r--lib/python2.7/site-packages/south/tests/fakeapp/migrations/0002_eggs.py20
-rw-r--r--lib/python2.7/site-packages/south/tests/fakeapp/migrations/0003_alter_spam.py18
-rw-r--r--lib/python2.7/site-packages/south/tests/fakeapp/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/fakeapp/models.py111
-rw-r--r--lib/python2.7/site-packages/south/tests/freezer.py15
-rw-r--r--lib/python2.7/site-packages/south/tests/inspector.py109
-rw-r--r--lib/python2.7/site-packages/south/tests/logger.py82
-rw-r--r--lib/python2.7/site-packages/south/tests/logic.py902
-rw-r--r--lib/python2.7/site-packages/south/tests/non_managed/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/non_managed/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/non_managed/models.py16
-rw-r--r--lib/python2.7/site-packages/south/tests/otherfakeapp/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0001_first.py15
-rw-r--r--lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0002_second.py11
-rw-r--r--lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0003_third.py14
-rw-r--r--lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/__init__.py0
-rw-r--r--lib/python2.7/site-packages/south/tests/otherfakeapp/models.py1
66 files changed, 3367 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/south/tests/__init__.py b/lib/python2.7/site-packages/south/tests/__init__.py
new file mode 100644
index 0000000..26779e3
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/__init__.py
@@ -0,0 +1,109 @@
+from __future__ import print_function
+
+#import unittest
+import os
+import sys
+from functools import wraps
+from django.conf import settings
+from south.hacks import hacks
+
+# Make sure skipping tests is available.
+try:
+ # easiest and best is unittest included in Django>=1.3
+ from django.utils import unittest
+except ImportError:
+ # earlier django... use unittest from stdlib
+ import unittest
+# however, skipUnless was only added in Python 2.7;
+# if not available, we need to do something else
+try:
+ skipUnless = unittest.skipUnless #@UnusedVariable
+except AttributeError:
+ def skipUnless(condition, message):
+ def decorator(testfunc):
+ @wraps(testfunc)
+ def wrapper(self):
+ if condition:
+ # Apply method
+ testfunc(self)
+ else:
+ # The skip exceptions are not available either...
+ print("Skipping", testfunc.__name__,"--", message)
+ return wrapper
+ return decorator
+
+# ditto for skipIf
+try:
+ skipIf = unittest.skipIf #@UnusedVariable
+except AttributeError:
+ def skipIf(condition, message):
+ def decorator(testfunc):
+ @wraps(testfunc)
+ def wrapper(self):
+ if condition:
+ print("Skipping", testfunc.__name__,"--", message)
+ else:
+ # Apply method
+ testfunc(self)
+ return wrapper
+ return decorator
+
+# Add the tests directory so fakeapp is on sys.path
+test_root = os.path.dirname(__file__)
+sys.path.append(test_root)
+
+# Note: the individual test files are imported below this.
+
+class Monkeypatcher(unittest.TestCase):
+
+ """
+ Base test class for tests that play with the INSTALLED_APPS setting at runtime.
+ """
+
+ def create_fake_app(self, name):
+
+ class Fake:
+ pass
+
+ fake = Fake()
+ fake.__name__ = name
+ try:
+ fake.migrations = __import__(name + ".migrations", {}, {}, ['migrations'])
+ except ImportError:
+ pass
+ return fake
+
+ def setUp(self):
+ """
+ Changes the Django environment so we can run tests against our test apps.
+ """
+ if hasattr(self, 'installed_apps'):
+ hacks.store_app_cache_state()
+ hacks.set_installed_apps(self.installed_apps)
+ # Make sure dependencies are calculated for new apps
+ Migrations._dependencies_done = False
+
+ def tearDown(self):
+ """
+ Undoes what setUp did.
+ """
+ if hasattr(self, 'installed_apps'):
+ hacks.reset_installed_apps()
+ hacks.restore_app_cache_state()
+
+
+# Try importing all tests if asked for (then we can run 'em)
+try:
+ skiptest = settings.SKIP_SOUTH_TESTS
+except:
+ skiptest = True
+
+if not skiptest:
+ from south.tests.db import *
+ from south.tests.db_mysql import *
+ from south.tests.db_firebird import *
+ from south.tests.logic import *
+ from south.tests.autodetection import *
+ from south.tests.logger import *
+ from south.tests.inspector import *
+ from south.tests.freezer import *
diff --git a/lib/python2.7/site-packages/south/tests/autodetection.py b/lib/python2.7/site-packages/south/tests/autodetection.py
new file mode 100644
index 0000000..c320d3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/autodetection.py
@@ -0,0 +1,360 @@
+from south.tests import unittest
+
+from south.creator.changes import AutoChanges, InitialChanges
+from south.migration.base import Migrations
+from south.tests import Monkeypatcher
+from south.creator import freezer
+from south.orm import FakeORM
+from south.v2 import SchemaMigration
+
+try:
+ from django.utils.six.moves import reload_module
+except ImportError:
+ # Older django, no python3 support
+ reload_module = reload
+
+class TestComparison(unittest.TestCase):
+
+ """
+ Tests the comparison methods of startmigration.
+ """
+
+ def test_no_change(self):
+ "Test with a completely unchanged definition."
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['southdemo.Lizard']"}),
+ ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['southdemo.Lizard']"}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.related.ForeignKey', ['ohhai', 'there'], {'to': "somewhere", "from": "there"}),
+ ('django.db.models.fields.related.ForeignKey', ['ohhai', 'there'], {"from": "there", 'to': "somewhere"}),
+ ),
+ False,
+ )
+
+
+ def test_pos_change(self):
+ "Test with a changed positional argument."
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['hi'], {'to': "foo"}),
+ ('django.db.models.fields.CharField', [], {'to': "foo"}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', [], {'to': "foo"}),
+ ('django.db.models.fields.CharField', ['bye'], {'to': "foo"}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['pi'], {'to': "foo"}),
+ ('django.db.models.fields.CharField', ['pi'], {'to': "foo"}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['pisdadad'], {'to': "foo"}),
+ ('django.db.models.fields.CharField', ['pi'], {'to': "foo"}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['hi'], {}),
+ ('django.db.models.fields.CharField', [], {}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', [], {}),
+ ('django.db.models.fields.CharField', ['bye'], {}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['pi'], {}),
+ ('django.db.models.fields.CharField', ['pi'], {}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['pi'], {}),
+ ('django.db.models.fields.CharField', ['45fdfdf'], {}),
+ ),
+ True,
+ )
+
+
+ def test_kwd_change(self):
+ "Test a changed keyword argument"
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['pi'], {'to': "foo"}),
+ ('django.db.models.fields.CharField', ['pi'], {'to': "blue"}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', [], {'to': "foo"}),
+ ('django.db.models.fields.CharField', [], {'to': "blue"}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['b'], {'to': "foo"}),
+ ('django.db.models.fields.CharField', ['b'], {'to': "blue"}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', [], {'to': "foo"}),
+ ('django.db.models.fields.CharField', [], {}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['a'], {'to': "foo"}),
+ ('django.db.models.fields.CharField', ['a'], {}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', [], {}),
+ ('django.db.models.fields.CharField', [], {'to': "foo"}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('django.db.models.fields.CharField', ['a'], {}),
+ ('django.db.models.fields.CharField', ['a'], {'to': "foo"}),
+ ),
+ True,
+ )
+
+
+
+ def test_backcompat_nochange(self):
+ "Test that the backwards-compatable comparison is working"
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', [], {}),
+ ('django.db.models.fields.CharField', [], {}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', ['ack'], {}),
+ ('django.db.models.fields.CharField', ['ack'], {}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', [], {'to':'b'}),
+ ('django.db.models.fields.CharField', [], {'to':'b'}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', ['hah'], {'to':'you'}),
+ ('django.db.models.fields.CharField', ['hah'], {'to':'you'}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', ['hah'], {'to':'you'}),
+ ('django.db.models.fields.CharField', ['hah'], {'to':'heh'}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', ['hah'], {}),
+ ('django.db.models.fields.CharField', [], {'to':"orm['appname.hah']"}),
+ ),
+ False,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', ['hah'], {}),
+ ('django.db.models.fields.CharField', [], {'to':'hah'}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', ['hah'], {}),
+ ('django.db.models.fields.CharField', [], {'to':'rrr'}),
+ ),
+ True,
+ )
+
+ self.assertEqual(
+ AutoChanges.different_attributes(
+ ('models.CharField', ['hah'], {}),
+ ('django.db.models.fields.IntField', [], {'to':'hah'}),
+ ),
+ True,
+ )
+
+class TestNonManagedIgnored(Monkeypatcher):
+
+ installed_apps = ["non_managed"]
+
+ full_defs = {
+ 'non_managed.legacy': {
+ 'Meta': {'object_name': 'Legacy', 'db_table': "'legacy_table'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True'}),
+ 'size': ('django.db.models.fields.IntegerField', [], {})
+ }
+ }
+
+ def test_not_added_init(self):
+
+ migrations = Migrations("non_managed")
+ changes = InitialChanges(migrations)
+ change_list = changes.get_changes()
+ if list(change_list):
+ self.fail("Initial migration creates table for non-managed model")
+
+ def test_not_added_auto(self):
+
+ empty_defs = { }
+ class EmptyMigration(SchemaMigration):
+ "Serves as fake previous migration"
+
+ def forwards(self, orm):
+ pass
+
+ def backwards(self, orm):
+ pass
+
+ models = empty_defs
+
+ complete_apps = ['non_managed']
+
+ migrations = Migrations("non_managed")
+ empty_orm = FakeORM(EmptyMigration, "non_managed")
+ changes = AutoChanges(
+ migrations = migrations,
+ old_defs = empty_defs,
+ old_orm = empty_orm,
+ new_defs = self.full_defs,
+ )
+ change_list = changes.get_changes()
+ if list(change_list):
+ self.fail("Auto migration creates table for non-managed model")
+
+ def test_not_deleted_auto(self):
+
+ empty_defs = { }
+ old_defs = freezer.freeze_apps(["non_managed"])
+ class InitialMigration(SchemaMigration):
+ "Serves as fake previous migration"
+
+ def forwards(self, orm):
+ pass
+
+ def backwards(self, orm):
+ pass
+
+ models = self.full_defs
+
+ complete_apps = ['non_managed']
+
+ migrations = Migrations("non_managed")
+ initial_orm = FakeORM(InitialMigration, "non_managed")
+ changes = AutoChanges(
+ migrations = migrations,
+ old_defs = self.full_defs,
+ old_orm = initial_orm,
+ new_defs = empty_defs,
+ )
+ change_list = changes.get_changes()
+ if list(change_list):
+ self.fail("Auto migration deletes table for non-managed model")
+
+ def test_not_modified_auto(self):
+
+ fake_defs = {
+ 'non_managed.legacy': {
+ 'Meta': {'object_name': 'Legacy', 'db_table': "'legacy_table'", 'managed': 'False'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True'}),
+ #'size': ('django.db.models.fields.IntegerField', [], {}) # The "change" is the addition of this field
+ }
+ }
+ class InitialMigration(SchemaMigration):
+ "Serves as fake previous migration"
+
+ def forwards(self, orm):
+ pass
+
+ def backwards(self, orm):
+ pass
+
+ models = fake_defs
+
+ complete_apps = ['non_managed']
+
+ from non_managed import models as dummy_import_to_force_loading_models # TODO: Does needing this indicate a bug in MokeyPatcher?
+ reload_module(dummy_import_to_force_loading_models) # really force...
+
+ migrations = Migrations("non_managed")
+ initial_orm = FakeORM(InitialMigration, "non_managed")
+ changes = AutoChanges(
+ migrations = migrations,
+ old_defs = fake_defs,
+ old_orm = initial_orm,
+ new_defs = self.full_defs
+ )
+ change_list = changes.get_changes()
+ if list(change_list):
+ self.fail("Auto migration changes table for non-managed model")
diff --git a/lib/python2.7/site-packages/south/tests/brokenapp/__init__.py b/lib/python2.7/site-packages/south/tests/brokenapp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/brokenapp/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0001_depends_on_unmigrated.py b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0001_depends_on_unmigrated.py
new file mode 100644
index 0000000..d53f836
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0001_depends_on_unmigrated.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('unknown', '0001_initial')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0002_depends_on_unknown.py b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0002_depends_on_unknown.py
new file mode 100644
index 0000000..389af80
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0002_depends_on_unknown.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('fakeapp', '9999_unknown')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0003_depends_on_higher.py b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0003_depends_on_higher.py
new file mode 100644
index 0000000..319069b
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0003_depends_on_higher.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('brokenapp', '0004_higher')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0004_higher.py b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0004_higher.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/0004_higher.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/brokenapp/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/brokenapp/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/brokenapp/models.py b/lib/python2.7/site-packages/south/tests/brokenapp/models.py
new file mode 100644
index 0000000..a7d84dc
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/brokenapp/models.py
@@ -0,0 +1,55 @@
+# -*- coding: UTF-8 -*-
+
+from django.db import models
+from django.contrib.auth.models import User as UserAlias
+
+def default_func():
+ return "yays"
+
+# An empty case.
+class Other1(models.Model): pass
+
+# Nastiness.
+class HorribleModel(models.Model):
+ "A model to test the edge cases of model parsing"
+
+ ZERO, ONE = range(2)
+
+ # First, some nice fields
+ name = models.CharField(max_length=255)
+ short_name = models.CharField(max_length=50)
+ slug = models.SlugField(unique=True)
+
+ # A ForeignKey, to a model above, and then below
+ o1 = models.ForeignKey(Other1)
+ o2 = models.ForeignKey('Other2')
+
+ # Now to something outside
+ user = models.ForeignKey(UserAlias, related_name="horribles")
+
+ # Unicode!
+ code = models.CharField(max_length=25, default="↑↑↓↓←→←→BA")
+
+ # Odd defaults!
+ class_attr = models.IntegerField(default=ZERO)
+ func = models.CharField(max_length=25, default=default_func)
+
+ # Time to get nasty. Define a non-field choices, and use it
+ choices = [('hello', '1'), ('world', '2')]
+ choiced = models.CharField(max_length=20, choices=choices)
+
+ class Meta:
+ db_table = "my_fave"
+ verbose_name = "Dr. Strangelove," + \
+ """or how I learned to stop worrying
+and love the bomb"""
+
+ # Now spread over multiple lines
+ multiline = \
+ models.TextField(
+ )
+
+# Special case.
+class Other2(models.Model):
+ # Try loading a field without a newline after it (inspect hates this)
+ close_but_no_cigar = models.PositiveIntegerField(primary_key=True) \ No newline at end of file
diff --git a/lib/python2.7/site-packages/south/tests/circular_a/__init__.py b/lib/python2.7/site-packages/south/tests/circular_a/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_a/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/circular_a/migrations/0001_first.py b/lib/python2.7/site-packages/south/tests/circular_a/migrations/0001_first.py
new file mode 100644
index 0000000..b0d90eb
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_a/migrations/0001_first.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('circular_b', '0001_first')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/circular_a/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/circular_a/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_a/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/circular_a/models.py b/lib/python2.7/site-packages/south/tests/circular_a/models.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_a/models.py
diff --git a/lib/python2.7/site-packages/south/tests/circular_b/__init__.py b/lib/python2.7/site-packages/south/tests/circular_b/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_b/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/circular_b/migrations/0001_first.py b/lib/python2.7/site-packages/south/tests/circular_b/migrations/0001_first.py
new file mode 100644
index 0000000..b11b120
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_b/migrations/0001_first.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('circular_a', '0001_first')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/circular_b/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/circular_b/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_b/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/circular_b/models.py b/lib/python2.7/site-packages/south/tests/circular_b/models.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/circular_b/models.py
diff --git a/lib/python2.7/site-packages/south/tests/db.py b/lib/python2.7/site-packages/south/tests/db.py
new file mode 100644
index 0000000..e63c563
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/db.py
@@ -0,0 +1,1060 @@
+import datetime
+from warnings import filterwarnings
+
+from south.db import db, generic
+from django.db import connection, models, IntegrityError as DjangoIntegrityError
+
+from south.tests import unittest, skipIf, skipUnless
+from south.utils.py3 import text_type, with_metaclass
+
+# Create a list of error classes from the various database libraries
+errors = []
+try:
+ from psycopg2 import ProgrammingError
+ errors.append(ProgrammingError)
+except ImportError:
+ pass
+errors = tuple(errors)
+
+# On SQL Server, the backend's IntegrityError is not (a subclass of) Django's.
+try:
+ from sql_server.pyodbc.base import IntegrityError as SQLServerIntegrityError
+ IntegrityError = (DjangoIntegrityError, SQLServerIntegrityError)
+except ImportError:
+ IntegrityError = DjangoIntegrityError
+
+try:
+ from south.db import mysql
+except ImportError:
+ mysql = None
+
+
+class TestOperations(unittest.TestCase):
+
+ """
+ Tests if the various DB abstraction calls work.
+ Can only test a limited amount due to DB differences.
+ """
+
+ def setUp(self):
+ db.debug = False
+ try:
+ import MySQLdb
+ except ImportError:
+ pass
+ else:
+ filterwarnings('ignore', category=MySQLdb.Warning)
+ db.clear_deferred_sql()
+ db.start_transaction()
+
+ def tearDown(self):
+ db.rollback_transaction()
+
+ def test_create(self):
+ """
+ Test creation of tables.
+ """
+ cursor = connection.cursor()
+ # It needs to take at least 2 args
+ self.assertRaises(TypeError, db.create_table)
+ self.assertRaises(TypeError, db.create_table, "test1")
+ # Empty tables (i.e. no columns) are not fine, so make at least 1
+ db.create_table("test1", [('email_confirmed', models.BooleanField(default=False))])
+ # And should exist
+ cursor.execute("SELECT * FROM test1")
+ # Make sure we can't do the same query on an empty table
+ try:
+ cursor.execute("SELECT * FROM nottheretest1")
+ except:
+ pass
+ else:
+ self.fail("Non-existent table could be selected!")
+
+ @skipUnless(db.raises_default_errors, 'This database does not raise errors on missing defaults.')
+ def test_create_default(self):
+ """
+ Test creation of tables, make sure defaults are not left in the database
+ """
+ db.create_table("test_create_default", [('a', models.IntegerField()),
+ ('b', models.IntegerField(default=17))])
+ cursor = connection.cursor()
+ self.assertRaises(IntegrityError, cursor.execute, "INSERT INTO test_create_default(a) VALUES (17)")
+
+ def test_delete(self):
+ """
+ Test deletion of tables.
+ """
+ cursor = connection.cursor()
+ db.create_table("test_deltable", [('email_confirmed', models.BooleanField(default=False))])
+ db.delete_table("test_deltable")
+ # Make sure it went
+ try:
+ cursor.execute("SELECT * FROM test_deltable")
+ except:
+ pass
+ else:
+ self.fail("Just-deleted table could be selected!")
+
+ def test_nonexistent_delete(self):
+ """
+ Test deletion of nonexistent tables.
+ """
+ try:
+ db.delete_table("test_nonexistdeltable")
+ except:
+ pass
+ else:
+ self.fail("Non-existent table could be deleted!")
+
+ def test_foreign_keys(self):
+ """
+ Tests foreign key creation, especially uppercase (see #61)
+ """
+ Test = db.mock_model(model_name='Test', db_table='test5a',
+ db_tablespace='', pk_field_name='ID',
+ pk_field_type=models.AutoField, pk_field_args=[])
+ db.create_table("test5a", [('ID', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True))])
+ db.create_table("test5b", [
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('UNIQUE', models.ForeignKey(Test)),
+ ])
+ db.execute_deferred_sql()
+
+ @skipUnless(db.supports_foreign_keys, 'Foreign keys can only be deleted on '
+ 'engines that support them.')
+ def test_recursive_foreign_key_delete(self):
+ """
+ Test that recursive foreign keys are deleted correctly (see #1065)
+ """
+ Test = db.mock_model(model_name='Test', db_table='test_rec_fk_del',
+ db_tablespace='', pk_field_name='id',
+ pk_field_type=models.AutoField, pk_field_args=[])
+ db.create_table('test_rec_fk_del', [
+ ('id', models.AutoField(primary_key=True, auto_created=True)),
+ ('fk', models.ForeignKey(Test)),
+ ])
+ db.execute_deferred_sql()
+ db.delete_foreign_key('test_rec_fk_del', 'fk_id')
+
+ def test_rename(self):
+ """
+ Test column renaming
+ """
+ cursor = connection.cursor()
+ db.create_table("test_rn", [('spam', models.BooleanField(default=False))])
+ # Make sure we can select the column
+ cursor.execute("SELECT spam FROM test_rn")
+ # Rename it
+ db.rename_column("test_rn", "spam", "eggs")
+ cursor.execute("SELECT eggs FROM test_rn")
+ db.commit_transaction()
+ db.start_transaction()
+ try:
+ cursor.execute("SELECT spam FROM test_rn")
+ except:
+ pass
+ else:
+ self.fail("Just-renamed column could be selected!")
+ db.rollback_transaction()
+ db.delete_table("test_rn")
+ db.start_transaction()
+
+ def test_dry_rename(self):
+ """
+ Test column renaming while --dry-run is turned on (should do nothing)
+ See ticket #65
+ """
+ cursor = connection.cursor()
+ db.create_table("test_drn", [('spam', models.BooleanField(default=False))])
+ # Make sure we can select the column
+ cursor.execute("SELECT spam FROM test_drn")
+ # Rename it
+ db.dry_run = True
+ db.rename_column("test_drn", "spam", "eggs")
+ db.dry_run = False
+ cursor.execute("SELECT spam FROM test_drn")
+ db.commit_transaction()
+ db.start_transaction()
+ try:
+ cursor.execute("SELECT eggs FROM test_drn")
+ except:
+ pass
+ else:
+ self.fail("Dry-renamed new column could be selected!")
+ db.rollback_transaction()
+ db.delete_table("test_drn")
+ db.start_transaction()
+
+ def test_table_rename(self):
+ """
+ Test column renaming
+ """
+ cursor = connection.cursor()
+ db.create_table("testtr", [('spam', models.BooleanField(default=False))])
+ # Make sure we can select the column
+ cursor.execute("SELECT spam FROM testtr")
+ # Rename it
+ db.rename_table("testtr", "testtr2")
+ cursor.execute("SELECT spam FROM testtr2")
+ db.commit_transaction()
+ db.start_transaction()
+ try:
+ cursor.execute("SELECT spam FROM testtr")
+ except:
+ pass
+ else:
+ self.fail("Just-renamed column could be selected!")
+ db.rollback_transaction()
+ db.delete_table("testtr2")
+ db.start_transaction()
+
+ def test_percents_in_defaults(self):
+ """
+ Test that % in a default gets escaped to %%.
+ """
+ try:
+ db.create_table("testpind", [('cf', models.CharField(max_length=255, default="It should be 2%!"))])
+ except IndexError:
+ self.fail("% was not properly escaped in column SQL.")
+ db.delete_table("testpind")
+
+ def test_index(self):
+ """
+ Test the index operations
+ """
+ db.create_table("test3", [
+ ('SELECT', models.BooleanField(default=False)),
+ ('eggs', models.IntegerField(unique=True)),
+ ])
+ db.execute_deferred_sql()
+ # Add an index on that column
+ db.create_index("test3", ["SELECT"])
+ # Add another index on two columns
+ db.create_index("test3", ["SELECT", "eggs"])
+ # Delete them both
+ db.delete_index("test3", ["SELECT"])
+ db.delete_index("test3", ["SELECT", "eggs"])
+ # Delete the unique index/constraint
+ if db.backend_name != "sqlite3":
+ db.delete_unique("test3", ["eggs"])
+ db.delete_table("test3")
+
+ def test_primary_key(self):
+ """
+ Test the primary key operations
+ """
+
+ db.create_table("test_pk", [
+ ('id', models.IntegerField(primary_key=True)),
+ ('new_pkey', models.IntegerField()),
+ ('eggs', models.IntegerField(unique=True)),
+ ])
+ db.execute_deferred_sql()
+ # Remove the default primary key, and make eggs it
+ db.delete_primary_key("test_pk")
+ db.create_primary_key("test_pk", "new_pkey")
+ # Try inserting a now-valid row pair
+ db.execute("INSERT INTO test_pk (id, new_pkey, eggs) VALUES (1, 2, 3)")
+ db.execute("INSERT INTO test_pk (id, new_pkey, eggs) VALUES (1, 3, 4)")
+ db.delete_table("test_pk")
+
+ def test_primary_key_implicit(self):
+ """
+ Tests that changing primary key implicitly fails.
+ """
+ db.create_table("test_pki", [
+ ('id', models.IntegerField(primary_key=True)),
+ ('new_pkey', models.IntegerField()),
+ ('eggs', models.IntegerField(unique=True)),
+ ])
+ db.execute_deferred_sql()
+ # Fiddle with alter_column to attempt to make it remove the primary key
+ db.alter_column("test_pki", "id", models.IntegerField())
+ db.alter_column("test_pki", "new_pkey", models.IntegerField(primary_key=True))
+ # Try inserting a should-be-valid row pair
+ db.execute("INSERT INTO test_pki (id, new_pkey, eggs) VALUES (1, 2, 3)")
+ db.execute("INSERT INTO test_pki (id, new_pkey, eggs) VALUES (2, 2, 4)")
+ db.delete_table("test_pki")
+
+ def test_add_columns(self):
+ """
+ Test adding columns
+ """
+ db.create_table("test_addc", [
+ ('spam', models.BooleanField(default=False)),
+ ('eggs', models.IntegerField()),
+ ])
+ # Add a column
+ db.add_column("test_addc", "add1", models.IntegerField(default=3))
+ User = db.mock_model(model_name='User', db_table='auth_user', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField, pk_field_args=[], pk_field_kwargs={})
+ # insert some data so we can test the default value of the added fkey
+ db.execute("INSERT INTO test_addc (spam, eggs, add1) VALUES (%s, 1, 2)", [False])
+ db.add_column("test_addc", "user", models.ForeignKey(User, null=True))
+ db.execute_deferred_sql()
+ # try selecting from the user_id column to make sure it was actually created
+ val = db.execute("SELECT user_id FROM test_addc")[0][0]
+ self.assertEquals(val, None)
+ db.delete_column("test_addc", "add1")
+ # make sure adding an indexed field works
+ db.add_column("test_addc", "add2", models.CharField(max_length=15, db_index=True, default='pi'))
+ db.execute_deferred_sql()
+ db.delete_table("test_addc")
+
+ def test_delete_columns(self):
+ """
+ Test deleting columns
+ """
+ db.create_table("test_delc", [
+ ('spam', models.BooleanField(default=False)),
+ ('eggs', models.IntegerField(db_index=True, unique=True)),
+ ])
+ db.delete_column("test_delc", "eggs")
+
+ def test_add_nullbool_column(self):
+ """
+ Test adding NullBoolean columns
+ """
+ db.create_table("test_addnbc", [
+ ('spam', models.BooleanField(default=False)),
+ ('eggs', models.IntegerField()),
+ ])
+ # Add a column
+ db.add_column("test_addnbc", "add1", models.NullBooleanField())
+ # Add a column with a default
+ db.add_column("test_addnbc", "add2", models.NullBooleanField(default=True))
+ # insert some data so we can test the default values of the added column
+ db.execute("INSERT INTO test_addnbc (spam, eggs) VALUES (%s, 1)", [False])
+ # try selecting from the new columns to make sure they were properly created
+ false, null1, null2 = db.execute("SELECT spam,add1,add2 FROM test_addnbc")[0][0:3]
+ self.assertIsNone(null1, "Null boolean field with no value inserted returns non-null")
+ self.assertIsNone(null2, "Null boolean field (added with default) with no value inserted returns non-null")
+ self.assertEquals(false, False)
+ db.delete_table("test_addnbc")
+
+ def test_alter_columns(self):
+ """
+ Test altering columns
+ """
+ db.create_table("test_alterc", [
+ ('spam', models.BooleanField(default=False)),
+ ('eggs', models.IntegerField()),
+ ])
+ db.execute_deferred_sql()
+ # Change eggs to be a FloatField
+ db.alter_column("test_alterc", "eggs", models.FloatField())
+ db.execute_deferred_sql()
+ db.delete_table("test_alterc")
+ db.execute_deferred_sql()
+
+ def test_alter_char_default(self):
+ """
+ Test altering column defaults with char fields
+ """
+ db.create_table("test_altercd", [
+ ('spam', models.CharField(max_length=30)),
+ ('eggs', models.IntegerField()),
+ ])
+ # Change spam default
+ db.alter_column("test_altercd", "spam", models.CharField(max_length=30, default="loof", null=True))
+ # Assert the default is not in the database
+ db.execute("INSERT INTO test_altercd (eggs) values (12)")
+ null = db.execute("SELECT spam FROM test_altercd")[0][0]
+ self.assertFalse(null, "Default for char field was installed into database")
+
+ # Change again to a column with default and not null
+ db.alter_column("test_altercd", "spam", models.CharField(max_length=30, default="loof", null=False))
+ # Assert the default is not in the database
+ if 'oracle' in db.backend_name:
+ # Oracle special treatment -- nulls are always allowed in char columns, so
+ # inserting doesn't raise an integrity error; so we check again as above
+ db.execute("DELETE FROM test_altercd")
+ db.execute("INSERT INTO test_altercd (eggs) values (12)")
+ null = db.execute("SELECT spam FROM test_altercd")[0][0]
+ self.assertFalse(null, "Default for char field was installed into database")
+ else:
+ # For other backends, insert should now just fail
+ self.assertRaises(IntegrityError,
+ db.execute, "INSERT INTO test_altercd (eggs) values (12)")
+
+ @skipIf('oracle' in db.backend_name, "Oracle does not differentiate empty trings from null")
+ def test_default_empty_string(self):
+ """
+ Test altering column defaults with char fields
+ """
+ db.create_table("test_cd_empty", [
+ ('spam', models.CharField(max_length=30, default='')),
+ ('eggs', models.CharField(max_length=30)),
+ ])
+ # Create a record
+ db.execute("INSERT INTO test_cd_empty (spam, eggs) values ('1','2')")
+ # Add a column
+ db.add_column("test_cd_empty", "ham", models.CharField(max_length=30, default=''))
+
+ empty = db.execute("SELECT ham FROM test_cd_empty")[0][0]
+ self.assertEquals(empty, "", "Empty Default for char field isn't empty string")
+
+ @skipUnless('oracle' in db.backend_name, "Oracle does not differentiate empty trings from null")
+ def test_oracle_strings_null(self):
+ """
+ Test that under Oracle, CherFields are created as null even when specified not-null,
+ because otherwise they would not be able to hold empty strings (which Oracle equates
+ with nulls).
+ Verify fix of #1269.
+ """
+ db.create_table("test_ora_char_nulls", [
+ ('spam', models.CharField(max_length=30, null=True)),
+ ('eggs', models.CharField(max_length=30)),
+ ])
+ db.add_column("test_ora_char_nulls", "ham", models.CharField(max_length=30))
+ db.alter_column("test_ora_char_nulls", "spam", models.CharField(max_length=30, null=False))
+ # So, by the look of it, we should now have three not-null columns
+ db.execute("INSERT INTO test_ora_char_nulls VALUES (NULL, NULL, NULL)")
+
+
+ def test_mysql_defaults(self):
+ """
+ Test MySQL default handling for BLOB and TEXT.
+ """
+ db.create_table("test_altermyd", [
+ ('spam', models.BooleanField(default=False)),
+ ('eggs', models.TextField()),
+ ])
+ # Change eggs to be a FloatField
+ db.alter_column("test_altermyd", "eggs", models.TextField(null=True))
+ db.delete_table("test_altermyd")
+
+ def test_alter_column_postgres_multiword(self):
+ """
+ Tests altering columns with multiple words in Postgres types (issue #125)
+ e.g. 'datetime with time zone', look at django/db/backends/postgresql/creation.py
+ """
+ db.create_table("test_multiword", [
+ ('col_datetime', models.DateTimeField(null=True)),
+ ('col_integer', models.PositiveIntegerField(null=True)),
+ ('col_smallint', models.PositiveSmallIntegerField(null=True)),
+ ('col_float', models.FloatField(null=True)),
+ ])
+
+ # test if 'double precision' is preserved
+ db.alter_column('test_multiword', 'col_float', models.FloatField('float', null=True))
+
+ # test if 'CHECK ("%(column)s" >= 0)' is stripped
+ db.alter_column('test_multiword', 'col_integer', models.PositiveIntegerField(null=True))
+ db.alter_column('test_multiword', 'col_smallint', models.PositiveSmallIntegerField(null=True))
+
+ # test if 'with timezone' is preserved
+ if db.backend_name == "postgres":
+ db.execute("INSERT INTO test_multiword (col_datetime) VALUES ('2009-04-24 14:20:55+02')")
+ db.alter_column('test_multiword', 'col_datetime', models.DateTimeField(auto_now=True))
+ assert db.execute("SELECT col_datetime = '2009-04-24 14:20:55+02' FROM test_multiword")[0][0]
+
+ db.delete_table("test_multiword")
+
+ @skipUnless(db.has_check_constraints, 'Only applies to databases that '
+ 'support CHECK constraints.')
+ def test_alter_constraints(self):
+ """
+ Tests that going from a PostiveIntegerField to an IntegerField drops
+ the constraint on the database.
+ """
+ # Make the test table
+ db.create_table("test_alterc", [
+ ('num', models.PositiveIntegerField()),
+ ])
+ db.execute_deferred_sql()
+ # Add in some test values
+ db.execute("INSERT INTO test_alterc (num) VALUES (1)")
+ db.execute("INSERT INTO test_alterc (num) VALUES (2)")
+ # Ensure that adding a negative number is bad
+ db.commit_transaction()
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alterc (num) VALUES (-3)")
+ except:
+ db.rollback_transaction()
+ else:
+ self.fail("Could insert a negative integer into a PositiveIntegerField.")
+ # Alter it to a normal IntegerField
+ db.alter_column("test_alterc", "num", models.IntegerField())
+ db.execute_deferred_sql()
+ # It should now work
+ db.execute("INSERT INTO test_alterc (num) VALUES (-3)")
+ db.delete_table("test_alterc")
+ # We need to match up for tearDown
+ db.start_transaction()
+
+ @skipIf(db.backend_name == "sqlite3", "SQLite backend doesn't support this "
+ "yet.")
+ def test_unique(self):
+ """
+ Tests creating/deleting unique constraints.
+ """
+ db.create_table("test_unique2", [
+ ('id', models.AutoField(primary_key=True)),
+ ])
+ db.create_table("test_unique", [
+ ('spam', models.BooleanField(default=False)),
+ ('eggs', models.IntegerField()),
+ ('ham', models.ForeignKey(db.mock_model('Unique2', 'test_unique2'))),
+ ])
+ db.execute_deferred_sql()
+ # Add a constraint
+ db.create_unique("test_unique", ["spam"])
+ db.execute_deferred_sql()
+ # Shouldn't do anything during dry-run
+ db.dry_run = True
+ db.delete_unique("test_unique", ["spam"])
+ db.dry_run = False
+ db.delete_unique("test_unique", ["spam"])
+ db.create_unique("test_unique", ["spam"])
+ # Special preparations for Sql Server
+ if db.backend_name == "pyodbc":
+ db.execute("SET IDENTITY_INSERT test_unique2 ON;")
+ db.execute("INSERT INTO test_unique2 (id) VALUES (1)")
+ db.execute("INSERT INTO test_unique2 (id) VALUES (2)")
+ db.commit_transaction()
+ db.start_transaction()
+
+
+ # Test it works
+ TRUE = (True,)
+ FALSE = (False,)
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 0, 1)", TRUE)
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 1, 2)", FALSE)
+ try:
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 2, 1)", FALSE)
+ except:
+ db.rollback_transaction()
+ else:
+ self.fail("Could insert non-unique item.")
+
+ # Drop that, add one only on eggs
+ db.delete_unique("test_unique", ["spam"])
+ db.execute("DELETE FROM test_unique")
+ db.create_unique("test_unique", ["eggs"])
+ db.start_transaction()
+
+ # Test similarly
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 0, 1)", TRUE)
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 1, 2)", FALSE)
+ try:
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 1, 1)", TRUE)
+ except:
+ db.rollback_transaction()
+ else:
+ self.fail("Could insert non-unique item.")
+
+ # Drop those, test combined constraints
+ db.delete_unique("test_unique", ["eggs"])
+ db.execute("DELETE FROM test_unique")
+ db.create_unique("test_unique", ["spam", "eggs", "ham_id"])
+ db.start_transaction()
+ # Test similarly
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 0, 1)", TRUE)
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 1, 1)", FALSE)
+ try:
+ db.execute("INSERT INTO test_unique (spam, eggs, ham_id) VALUES (%s, 0, 1)", TRUE)
+ except:
+ db.rollback_transaction()
+ else:
+ self.fail("Could insert non-unique pair.")
+ db.delete_unique("test_unique", ["spam", "eggs", "ham_id"])
+ db.start_transaction()
+
+ def test_alter_unique(self):
+ """
+ Tests that unique constraints are not affected when
+ altering columns (that's handled by create_/delete_unique)
+ """
+ db.create_table("test_alter_unique", [
+ ('spam', models.IntegerField()),
+ ('eggs', models.IntegerField(unique=True)),
+ ])
+ db.execute_deferred_sql()
+
+ # Make sure the unique constraint is created
+ db.execute('INSERT INTO test_alter_unique (spam, eggs) VALUES (0, 42)')
+ db.commit_transaction()
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique (spam, eggs) VALUES (1, 42)")
+ except:
+ pass
+ else:
+ self.fail("Could insert the same integer twice into a unique field.")
+ db.rollback_transaction()
+
+ # Alter without unique=True (should not affect anything)
+ db.alter_column("test_alter_unique", "eggs", models.IntegerField())
+
+ # Insertion should still fail
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique (spam, eggs) VALUES (1, 42)")
+ except:
+ pass
+ else:
+ self.fail("Could insert the same integer twice into a unique field after alter_column with unique=False.")
+ db.rollback_transaction()
+
+ # Delete the unique index/constraint
+ if db.backend_name != "sqlite3":
+ db.delete_unique("test_alter_unique", ["eggs"])
+ db.delete_table("test_alter_unique")
+ db.start_transaction()
+
+ # Test multi-field constraint
+ db.create_table("test_alter_unique2", [
+ ('spam', models.IntegerField()),
+ ('eggs', models.IntegerField()),
+ ])
+ db.create_unique('test_alter_unique2', ('spam', 'eggs'))
+ db.execute_deferred_sql()
+ db.execute('INSERT INTO test_alter_unique2 (spam, eggs) VALUES (0, 42)')
+ db.commit_transaction()
+ # Verify that constraint works
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique2 (spam, eggs) VALUES (1, 42)")
+ except:
+ self.fail("Looks like multi-field unique constraint applied to only one field.")
+ db.rollback_transaction()
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique2 (spam, eggs) VALUES (0, 43)")
+ except:
+ self.fail("Looks like multi-field unique constraint applied to only one field.")
+ db.rollback_transaction()
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique2 (spam, eggs) VALUES (0, 42)")
+ except:
+ pass
+ else:
+ self.fail("Could insert the same pair twice into unique-together fields.")
+ db.rollback_transaction()
+ # Altering one column should not drop or modify multi-column constraint
+ db.alter_column("test_alter_unique2", "eggs", models.PositiveIntegerField())
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique2 (spam, eggs) VALUES (1, 42)")
+ except:
+ self.fail("Altering one column broken multi-column unique constraint.")
+ db.rollback_transaction()
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique2 (spam, eggs) VALUES (0, 43)")
+ except:
+ self.fail("Altering one column broken multi-column unique constraint.")
+ db.rollback_transaction()
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_alter_unique2 (spam, eggs) VALUES (0, 42)")
+ except:
+ pass
+ else:
+ self.fail("Could insert the same pair twice into unique-together fields after alter_column with unique=False.")
+ db.rollback_transaction()
+ db.delete_table("test_alter_unique2")
+ db.start_transaction()
+
+ def test_capitalised_constraints(self):
+ """
+ Under PostgreSQL at least, capitalised constraints must be quoted.
+ """
+ db.create_table("test_capconst", [
+ ('SOMECOL', models.PositiveIntegerField(primary_key=True)),
+ ])
+ # Alter it so it's not got the check constraint
+ db.alter_column("test_capconst", "SOMECOL", models.IntegerField())
+
+ def test_text_default(self):
+ """
+ MySQL cannot have blank defaults on TEXT columns.
+ """
+ db.create_table("test_textdef", [
+ ('textcol', models.TextField(blank=True)),
+ ])
+
+ def test_text_to_char(self):
+ """
+ On Oracle, you can't simply ALTER TABLE MODIFY a textfield to a charfield
+ """
+ value = "kawabanga"
+ db.create_table("test_text_to_char", [
+ ('textcol', models.TextField()),
+ ])
+ db.execute_deferred_sql()
+ db.execute("INSERT INTO test_text_to_char VALUES (%s)", [value])
+ db.alter_column("test_text_to_char", "textcol", models.CharField(max_length=100))
+ db.execute_deferred_sql()
+ after = db.execute("select * from test_text_to_char")[0][0]
+ self.assertEqual(value, after, "Change from text to char altered value [ %r != %r ]" % (value, after))
+
+ def test_char_to_text(self):
+ """
+ On Oracle, you can't simply ALTER TABLE MODIFY a charfield to a textfield either
+ """
+ value = "agnabawak"
+ db.create_table("test_char_to_text", [
+ ('textcol', models.CharField(max_length=100)),
+ ])
+ db.execute_deferred_sql()
+ db.execute("INSERT INTO test_char_to_text VALUES (%s)", [value])
+ db.alter_column("test_char_to_text", "textcol", models.TextField())
+ db.execute_deferred_sql()
+ after = db.execute("select * from test_char_to_text")[0][0]
+ after = text_type(after) # Oracle text fields return a sort of lazy string -- force evaluation
+ self.assertEqual(value, after, "Change from char to text altered value [ %r != %r ]" % (value, after))
+
+ @skipUnless(db.raises_default_errors, 'This database does not raise errors on missing defaults.')
+ def test_datetime_default(self):
+ """
+ Test that defaults are correctly not created for datetime columns
+ """
+ end_of_world = datetime.datetime(2012, 12, 21, 0, 0, 1)
+
+ try:
+ from django.utils import timezone
+ except ImportError:
+ pass
+ else:
+ from django.conf import settings
+ if getattr(settings, 'USE_TZ', False):
+ end_of_world = end_of_world.replace(tzinfo=timezone.utc)
+
+ db.create_table("test_datetime_def", [
+ ('col0', models.IntegerField(null=True)),
+ ('col1', models.DateTimeField(default=end_of_world)),
+ ('col2', models.DateTimeField(null=True)),
+ ])
+ db.execute_deferred_sql()
+ # insert a row
+ db.execute("INSERT INTO test_datetime_def (col0, col1, col2) values (null,%s,null)", [end_of_world])
+ db.alter_column("test_datetime_def", "col2", models.DateTimeField(default=end_of_world))
+ db.add_column("test_datetime_def", "col3", models.DateTimeField(default=end_of_world))
+ db.execute_deferred_sql()
+ db.commit_transaction()
+ # In the single existing row, we now expect col1=col2=col3=end_of_world...
+ db.start_transaction()
+ ends = db.execute("select col1,col2,col3 from test_datetime_def")[0]
+ self.failUnlessEqual(len(ends), 3)
+ for e in ends:
+ self.failUnlessEqual(e, end_of_world)
+ db.commit_transaction()
+ # ...but there should not be a default in the database for col1 or col3
+ for cols in ["col1,col2", "col2,col3"]:
+ db.start_transaction()
+ statement = "insert into test_datetime_def (col0,%s) values (null,%%s,%%s)" % cols
+ self.assertRaises(
+ IntegrityError,
+ db.execute, statement, [end_of_world, end_of_world]
+ )
+ db.rollback_transaction()
+
+ db.start_transaction() # To preserve the sanity and semantics of this test class
+
+ def test_add_unique_fk(self):
+ """
+ Test adding a ForeignKey with unique=True or a OneToOneField
+ """
+ db.create_table("test_add_unique_fk", [
+ ('spam', models.BooleanField(default=False))
+ ])
+
+ db.add_column("test_add_unique_fk", "mock1", models.ForeignKey(db.mock_model('User', 'auth_user'), null=True, unique=True))
+ db.add_column("test_add_unique_fk", "mock2", models.OneToOneField(db.mock_model('User', 'auth_user'), null=True))
+ db.execute_deferred_sql()
+
+ db.delete_table("test_add_unique_fk")
+
+ @skipUnless(db.has_check_constraints, 'Only applies to databases that '
+ 'support CHECK constraints.')
+ def test_column_constraint(self):
+ """
+ Tests that the value constraint of PositiveIntegerField is enforced on
+ the database level.
+ """
+ db.create_table("test_column_constraint", [
+ ('spam', models.PositiveIntegerField()),
+ ])
+ db.execute_deferred_sql()
+
+ # Make sure we can't insert negative values
+ db.commit_transaction()
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_column_constraint VALUES (-42)")
+ except:
+ pass
+ else:
+ self.fail("Could insert a negative value into a PositiveIntegerField.")
+ db.rollback_transaction()
+
+ # remove constraint
+ db.alter_column("test_column_constraint", "spam", models.IntegerField())
+ db.execute_deferred_sql()
+ # make sure the insertion works now
+ db.execute('INSERT INTO test_column_constraint VALUES (-42)')
+ db.execute('DELETE FROM test_column_constraint')
+
+ # add it back again
+ db.alter_column("test_column_constraint", "spam", models.PositiveIntegerField())
+ db.execute_deferred_sql()
+ # it should fail again
+ db.start_transaction()
+ try:
+ db.execute("INSERT INTO test_column_constraint VALUES (-42)")
+ except:
+ pass
+ else:
+ self.fail("Could insert a negative value after changing an IntegerField to a PositiveIntegerField.")
+ db.rollback_transaction()
+
+ db.delete_table("test_column_constraint")
+ db.start_transaction()
+
+ def test_sql_defaults(self):
+ """
+ Test that sql default value is correct for non-string field types.
+ Datetimes are handled in test_datetime_default.
+ """
+
+ class CustomField(with_metaclass(models.SubfieldBase, models.CharField)):
+ description = 'CustomField'
+ def get_default(self):
+ if self.has_default():
+ if callable(self.default):
+ return self.default()
+ return self.default
+ return super(CustomField, self).get_default()
+ def get_prep_value(self, value):
+ if not value:
+ return value
+ return ','.join(map(str, value))
+ def to_python(self, value):
+ if not value or isinstance(value, list):
+ return value
+ return list(map(int, value.split(',')))
+
+ false_value = db.has_booleans and 'False' or '0'
+ defaults = (
+ (models.CharField(default='sukasuka'), 'DEFAULT \'sukasuka'),
+ (models.BooleanField(default=False), 'DEFAULT %s' % false_value),
+ (models.IntegerField(default=42), 'DEFAULT 42'),
+ (CustomField(default=[2012, 2018, 2021, 2036]), 'DEFAULT \'2012,2018,2021,2036')
+ )
+ for field, sql_test_str in defaults:
+ sql = db.column_sql('fish', 'YAAAAAAZ', field)
+ if sql_test_str not in sql:
+ self.fail("default sql value was not properly generated for field %r.\nSql was %s" % (field, sql))
+
+ def test_make_added_foreign_key_not_null(self):
+ # Table for FK to target
+ User = db.mock_model(model_name='User', db_table='auth_user', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField, pk_field_args=[], pk_field_kwargs={})
+ # Table with no foreign key
+ db.create_table("test_fk", [
+ ('eggs', models.IntegerField()),
+ ])
+ db.execute_deferred_sql()
+
+ # Add foreign key
+ db.add_column("test_fk", 'foreik', models.ForeignKey(User, null=True))
+ db.execute_deferred_sql()
+
+ # Make the FK not null
+ db.alter_column("test_fk", "foreik_id", models.ForeignKey(User))
+ db.execute_deferred_sql()
+
+ def test_make_foreign_key_null(self):
+ # Table for FK to target
+ User = db.mock_model(model_name='User', db_table='auth_user', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField, pk_field_args=[], pk_field_kwargs={})
+ # Table with no foreign key
+ db.create_table("test_make_fk_null", [
+ ('eggs', models.IntegerField()),
+ ('foreik', models.ForeignKey(User))
+ ])
+ db.execute_deferred_sql()
+
+ # Make the FK null
+ db.alter_column("test_make_fk_null", "foreik_id", models.ForeignKey(User, null=True))
+ db.execute_deferred_sql()
+
+ def test_change_foreign_key_target(self):
+ # Tables for FK to target
+ User = db.mock_model(model_name='User', db_table='auth_user', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField, pk_field_args=[], pk_field_kwargs={})
+ db.create_table("test_fk_changed_target", [
+ ('eggs', models.IntegerField(primary_key=True)),
+ ])
+ Egg = db.mock_model(model_name='Egg', db_table='test_fk_changed_target', db_tablespace='', pk_field_name='eggs', pk_field_type=models.AutoField, pk_field_args=[], pk_field_kwargs={})
+ # Table with a foreign key to the wrong table
+ db.create_table("test_fk_changing", [
+ ('egg', models.ForeignKey(User, null=True)),
+ ])
+ db.execute_deferred_sql()
+
+ # Change foreign key pointing
+ db.alter_column("test_fk_changing", "egg_id", models.ForeignKey(Egg, null=True))
+ db.execute_deferred_sql()
+
+ # Test that it is pointing at the right table now
+ try:
+ non_user_id = db.execute("SELECT MAX(id) FROM auth_user")[0][0] + 1
+ except (TypeError, IndexError):
+ # Got a "None" or no records, treat as 0
+ non_user_id = 17
+ db.execute("INSERT INTO test_fk_changed_target (eggs) VALUES (%s)", [non_user_id])
+ db.execute("INSERT INTO test_fk_changing (egg_id) VALUES (%s)", [non_user_id])
+ db.commit_transaction()
+ db.start_transaction() # The test framework expects tests to end in transaction
+
+ def test_alter_double_indexed_column(self):
+ # Table for FK to target
+ User = db.mock_model(model_name='User', db_table='auth_user', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField, pk_field_args=[], pk_field_kwargs={})
+ # Table with no foreign key
+ db.create_table("test_2indexed", [
+ ('eggs', models.IntegerField()),
+ ('foreik', models.ForeignKey(User))
+ ])
+ db.create_unique("test_2indexed", ["eggs", "foreik_id"])
+ db.execute_deferred_sql()
+
+ # Make the FK null
+ db.alter_column("test_2indexed", "foreik_id", models.ForeignKey(User, null=True))
+ db.execute_deferred_sql()
+
+class TestCacheGeneric(unittest.TestCase):
+ base_ops_cls = generic.DatabaseOperations
+ def setUp(self):
+ class CacheOps(self.base_ops_cls):
+ def __init__(self):
+ self._constraint_cache = {}
+ self.cache_filled = 0
+ self.settings = {'NAME': 'db'}
+
+ def _fill_constraint_cache(self, db, table):
+ self.cache_filled += 1
+ self._constraint_cache.setdefault(db, {})
+ self._constraint_cache[db].setdefault(table, {})
+
+ @generic.invalidate_table_constraints
+ def clear_con(self, table):
+ pass
+
+ @generic.copy_column_constraints
+ def cp_column(self, table, column_old, column_new):
+ pass
+
+ @generic.delete_column_constraints
+ def rm_column(self, table, column):
+ pass
+
+ @generic.copy_column_constraints
+ @generic.delete_column_constraints
+ def mv_column(self, table, column_old, column_new):
+ pass
+
+ def _get_setting(self, attr):
+ return self.settings[attr]
+ self.CacheOps = CacheOps
+
+ def test_cache(self):
+ ops = self.CacheOps()
+ self.assertEqual(0, ops.cache_filled)
+ self.assertFalse(ops.lookup_constraint('db', 'table'))
+ self.assertEqual(1, ops.cache_filled)
+ self.assertFalse(ops.lookup_constraint('db', 'table'))
+ self.assertEqual(1, ops.cache_filled)
+ ops.clear_con('table')
+ self.assertEqual(1, ops.cache_filled)
+ self.assertFalse(ops.lookup_constraint('db', 'table'))
+ self.assertEqual(2, ops.cache_filled)
+ self.assertFalse(ops.lookup_constraint('db', 'table', 'column'))
+ self.assertEqual(2, ops.cache_filled)
+
+ cache = ops._constraint_cache
+ cache['db']['table']['column'] = 'constraint'
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column'))
+ self.assertEqual([('column', 'constraint')], ops.lookup_constraint('db', 'table'))
+ self.assertEqual(2, ops.cache_filled)
+
+ # invalidate_table_constraints
+ ops.clear_con('new_table')
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column'))
+ self.assertEqual(2, ops.cache_filled)
+
+ self.assertFalse(ops.lookup_constraint('db', 'new_table'))
+ self.assertEqual(3, ops.cache_filled)
+
+ # delete_column_constraints
+ cache['db']['table']['column'] = 'constraint'
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column'))
+ ops.rm_column('table', 'column')
+ self.assertEqual([], ops.lookup_constraint('db', 'table', 'column'))
+ self.assertEqual([], ops.lookup_constraint('db', 'table', 'noexist_column'))
+
+ # copy_column_constraints
+ cache['db']['table']['column'] = 'constraint'
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column'))
+ ops.cp_column('table', 'column', 'column_new')
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column_new'))
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column'))
+
+ # copy + delete
+ cache['db']['table']['column'] = 'constraint'
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column'))
+ ops.mv_column('table', 'column', 'column_new')
+ self.assertEqual('constraint', ops.lookup_constraint('db', 'table', 'column_new'))
+ self.assertEqual([], ops.lookup_constraint('db', 'table', 'column'))
+
+ def test_valid(self):
+ ops = self.CacheOps()
+ # none of these should vivify a table into a valid state
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+ ops.clear_con('table')
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+ ops.rm_column('table', 'column')
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+
+ # these should change the cache state
+ ops.lookup_constraint('db', 'table')
+ self.assertTrue(ops._is_valid_cache('db', 'table'))
+ ops.lookup_constraint('db', 'table', 'column')
+ self.assertTrue(ops._is_valid_cache('db', 'table'))
+ ops.clear_con('table')
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+
+ def test_valid_implementation(self):
+ # generic fills the cache on a per-table basis
+ ops = self.CacheOps()
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+ self.assertFalse(ops._is_valid_cache('db', 'other_table'))
+ ops.lookup_constraint('db', 'table')
+ self.assertTrue(ops._is_valid_cache('db', 'table'))
+ self.assertFalse(ops._is_valid_cache('db', 'other_table'))
+ ops.lookup_constraint('db', 'other_table')
+ self.assertTrue(ops._is_valid_cache('db', 'table'))
+ self.assertTrue(ops._is_valid_cache('db', 'other_table'))
+ ops.clear_con('table')
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+ self.assertTrue(ops._is_valid_cache('db', 'other_table'))
+
+if mysql:
+ class TestCacheMysql(TestCacheGeneric):
+ base_ops_cls = mysql.DatabaseOperations
+
+ def test_valid_implementation(self):
+ # mysql fills the cache on a per-db basis
+ ops = self.CacheOps()
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+ self.assertFalse(ops._is_valid_cache('db', 'other_table'))
+ ops.lookup_constraint('db', 'table')
+ self.assertTrue(ops._is_valid_cache('db', 'table'))
+ self.assertTrue(ops._is_valid_cache('db', 'other_table'))
+ ops.lookup_constraint('db', 'other_table')
+ self.assertTrue(ops._is_valid_cache('db', 'table'))
+ self.assertTrue(ops._is_valid_cache('db', 'other_table'))
+ ops.clear_con('table')
+ self.assertFalse(ops._is_valid_cache('db', 'table'))
+ self.assertTrue(ops._is_valid_cache('db', 'other_table'))
diff --git a/lib/python2.7/site-packages/south/tests/db_firebird.py b/lib/python2.7/site-packages/south/tests/db_firebird.py
new file mode 100644
index 0000000..2b6bd53
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/db_firebird.py
@@ -0,0 +1,39 @@
+from django.db import models
+
+from south.db import db
+from south.tests import unittest, skipUnless
+
+
+class FirebirdTests(unittest.TestCase):
+
+ """
+ Tests firebird related issues
+ """
+
+ def setUp(self):
+ print('=' * 80)
+ print('Begin Firebird test')
+
+ def tearDown(self):
+ print('End Firebird test')
+ print('=' * 80)
+
+ @skipUnless(db.backend_name == "firebird", "Firebird-only test")
+ def test_firebird_double_index_creation_1317(self):
+ """
+ Tests foreign key creation, especially uppercase (see #61)
+ """
+ Test = db.mock_model(model_name='Test',
+ db_table='test5a',
+ db_tablespace='',
+ pk_field_name='ID',
+ pk_field_type=models.AutoField,
+ pk_field_args=[]
+ )
+ db.create_table("test5a", [('ID', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True))])
+ db.create_table("test5b", [
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('UNIQUE', models.ForeignKey(Test)),
+ ])
+ db.execute_deferred_sql()
+
diff --git a/lib/python2.7/site-packages/south/tests/db_mysql.py b/lib/python2.7/site-packages/south/tests/db_mysql.py
new file mode 100644
index 0000000..e83596c
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/db_mysql.py
@@ -0,0 +1,164 @@
+# Additional MySQL-specific tests
+# Written by: F. Gabriel Gosselin <gabrielNOSPAM@evidens.ca>
+# Based on tests by: aarranz
+from south.tests import unittest, skipUnless
+
+
+from south.db import db, generic, mysql
+from django.db import connection, models
+
+from south.utils.py3 import with_metaclass
+
+
+# A class decoration may be used in lieu of this when Python 2.5 is the
+# minimum.
+class TestMySQLOperationsMeta(type):
+
+ def __new__(mcs, name, bases, dict_):
+ decorator = skipUnless(db.backend_name == "mysql", 'MySQL-specific tests')
+
+ for key, method in dict_.items():
+ if key.startswith('test'):
+ dict_[key] = decorator(method)
+
+ return type.__new__(mcs, name, bases, dict_)
+
+class TestMySQLOperations(with_metaclass(TestMySQLOperationsMeta, unittest.TestCase)):
+ """MySQL-specific tests"""
+
+ def setUp(self):
+ db.debug = False
+ db.clear_deferred_sql()
+
+ def tearDown(self):
+ pass
+
+ def _create_foreign_tables(self, main_name, reference_name):
+ # Create foreign table and model
+ Foreign = db.mock_model(model_name='Foreign', db_table=reference_name,
+ db_tablespace='', pk_field_name='id',
+ pk_field_type=models.AutoField,
+ pk_field_args=[])
+ db.create_table(reference_name, [
+ ('id', models.AutoField(primary_key=True)),
+ ])
+ # Create table with foreign key
+ db.create_table(main_name, [
+ ('id', models.AutoField(primary_key=True)),
+ ('foreign', models.ForeignKey(Foreign)),
+ ])
+ return Foreign
+
+ def test_constraint_references(self):
+ """Tests that referred table is reported accurately"""
+ main_table = 'test_cns_ref'
+ reference_table = 'test_cr_foreign'
+ db.start_transaction()
+ self._create_foreign_tables(main_table, reference_table)
+ db.execute_deferred_sql()
+ constraint = db._find_foreign_constraints(main_table, 'foreign_id')[0]
+ references = db._lookup_constraint_references(main_table, constraint)
+ self.assertEquals((reference_table, 'id'), references)
+ db.delete_table(main_table)
+ db.delete_table(reference_table)
+
+ def test_reverse_column_constraint(self):
+ """Tests that referred column in a foreign key (ex. id) is found"""
+ main_table = 'test_reverse_ref'
+ reference_table = 'test_rr_foreign'
+ db.start_transaction()
+ self._create_foreign_tables(main_table, reference_table)
+ db.execute_deferred_sql()
+ inverse = db._lookup_reverse_constraint(reference_table, 'id')
+ (cname, rev_table, rev_column) = inverse[0]
+ self.assertEquals(main_table, rev_table)
+ self.assertEquals('foreign_id', rev_column)
+ db.delete_table(main_table)
+ db.delete_table(reference_table)
+
+ def test_delete_fk_column(self):
+ main_table = 'test_drop_foreign'
+ ref_table = 'test_df_ref'
+ self._create_foreign_tables(main_table, ref_table)
+ db.execute_deferred_sql()
+ constraints = db._find_foreign_constraints(main_table, 'foreign_id')
+ self.assertEquals(len(constraints), 1)
+ db.delete_column(main_table, 'foreign_id')
+ constraints = db._find_foreign_constraints(main_table, 'foreign_id')
+ self.assertEquals(len(constraints), 0)
+ db.delete_table(main_table)
+ db.delete_table(ref_table)
+
+ def test_rename_fk_column(self):
+ main_table = 'test_rename_foreign'
+ ref_table = 'test_rf_ref'
+ self._create_foreign_tables(main_table, ref_table)
+ db.execute_deferred_sql()
+ constraints = db._find_foreign_constraints(main_table, 'foreign_id')
+ self.assertEquals(len(constraints), 1)
+ db.rename_column(main_table, 'foreign_id', 'reference_id')
+ db.execute_deferred_sql() #Create constraints
+ constraints = db._find_foreign_constraints(main_table, 'reference_id')
+ self.assertEquals(len(constraints), 1)
+ db.delete_table(main_table)
+ db.delete_table(ref_table)
+
+ def test_rename_fk_inbound(self):
+ """
+ Tests that the column referred to by an external column can be renamed.
+ Edge case, but also useful as stepping stone to renaming tables.
+ """
+ main_table = 'test_rename_fk_inbound'
+ ref_table = 'test_rfi_ref'
+ self._create_foreign_tables(main_table, ref_table)
+ db.execute_deferred_sql()
+ constraints = db._lookup_reverse_constraint(ref_table, 'id')
+ self.assertEquals(len(constraints), 1)
+ db.rename_column(ref_table, 'id', 'rfi_id')
+ db.execute_deferred_sql() #Create constraints
+ constraints = db._lookup_reverse_constraint(ref_table, 'rfi_id')
+ self.assertEquals(len(constraints), 1)
+ cname = db._find_foreign_constraints(main_table, 'foreign_id')[0]
+ (rtable, rcolumn) = db._lookup_constraint_references(main_table, cname)
+ self.assertEquals(rcolumn, 'rfi_id')
+ db.delete_table(main_table)
+ db.delete_table(ref_table)
+
+ def test_rename_constrained_table(self):
+ """Renames a table with a foreign key column (towards another table)"""
+ main_table = 'test_rn_table'
+ ref_table = 'test_rt_ref'
+ renamed_table = 'test_renamed_table'
+ self._create_foreign_tables(main_table, ref_table)
+ db.execute_deferred_sql()
+ constraints = db._find_foreign_constraints(main_table, 'foreign_id')
+ self.assertEquals(len(constraints), 1)
+ db.rename_table(main_table, renamed_table)
+ db.execute_deferred_sql() #Create constraints
+ constraints = db._find_foreign_constraints(renamed_table, 'foreign_id')
+ self.assertEquals(len(constraints), 1)
+ (rtable, rcolumn) = db._lookup_constraint_references(
+ renamed_table, constraints[0])
+ self.assertEquals(rcolumn, 'id')
+ db.delete_table(renamed_table)
+ db.delete_table(ref_table)
+
+ def test_renamed_referenced_table(self):
+ """Rename a table referred to in a foreign key"""
+ main_table = 'test_rn_refd_table'
+ ref_table = 'test_rrt_ref'
+ renamed_table = 'test_renamed_ref'
+ self._create_foreign_tables(main_table, ref_table)
+ db.execute_deferred_sql()
+ constraints = db._lookup_reverse_constraint(ref_table)
+ self.assertEquals(len(constraints), 1)
+ db.rename_table(ref_table, renamed_table)
+ db.execute_deferred_sql() #Create constraints
+ constraints = db._find_foreign_constraints(main_table, 'foreign_id')
+ self.assertEquals(len(constraints), 1)
+ (rtable, rcolumn) = db._lookup_constraint_references(
+ main_table, constraints[0])
+ self.assertEquals(renamed_table, rtable)
+ db.delete_table(main_table)
+ db.delete_table(renamed_table)
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/__init__.py b/lib/python2.7/site-packages/south/tests/deps_a/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/migrations/0001_a.py b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0001_a.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0001_a.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/migrations/0002_a.py b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0002_a.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0002_a.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/migrations/0003_a.py b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0003_a.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0003_a.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/migrations/0004_a.py b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0004_a.py
new file mode 100644
index 0000000..e5c2977
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0004_a.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('deps_b', '0003_b')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/migrations/0005_a.py b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0005_a.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/migrations/0005_a.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/deps_a/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_a/models.py b/lib/python2.7/site-packages/south/tests/deps_a/models.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_a/models.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/__init__.py b/lib/python2.7/site-packages/south/tests/deps_b/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/migrations/0001_b.py b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0001_b.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0001_b.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/migrations/0002_b.py b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0002_b.py
new file mode 100644
index 0000000..459ea5d
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0002_b.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('deps_a', '0002_a')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/migrations/0003_b.py b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0003_b.py
new file mode 100644
index 0000000..1692888
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0003_b.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('deps_a', '0003_a')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/migrations/0004_b.py b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0004_b.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0004_b.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/migrations/0005_b.py b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0005_b.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/migrations/0005_b.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/deps_b/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_b/models.py b/lib/python2.7/site-packages/south/tests/deps_b/models.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_b/models.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/__init__.py b/lib/python2.7/site-packages/south/tests/deps_c/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/migrations/0001_c.py b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0001_c.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0001_c.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/migrations/0002_c.py b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0002_c.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0002_c.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/migrations/0003_c.py b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0003_c.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0003_c.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/migrations/0004_c.py b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0004_c.py
new file mode 100644
index 0000000..d27ed3a
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0004_c.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/migrations/0005_c.py b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0005_c.py
new file mode 100644
index 0000000..459ea5d
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/migrations/0005_c.py
@@ -0,0 +1,13 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = [('deps_a', '0002_a')]
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/deps_c/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/deps_c/models.py b/lib/python2.7/site-packages/south/tests/deps_c/models.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/deps_c/models.py
diff --git a/lib/python2.7/site-packages/south/tests/emptyapp/__init__.py b/lib/python2.7/site-packages/south/tests/emptyapp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/emptyapp/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/emptyapp/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/emptyapp/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/emptyapp/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/emptyapp/models.py b/lib/python2.7/site-packages/south/tests/emptyapp/models.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/emptyapp/models.py
diff --git a/lib/python2.7/site-packages/south/tests/fakeapp/__init__.py b/lib/python2.7/site-packages/south/tests/fakeapp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/fakeapp/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0001_spam.py b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0001_spam.py
new file mode 100644
index 0000000..9739648
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0001_spam.py
@@ -0,0 +1,17 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ # Model 'Spam'
+ db.create_table("southtest_spam", (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('weight', models.FloatField()),
+ ('expires', models.DateTimeField()),
+ ('name', models.CharField(max_length=255))
+ ))
+
+ def backwards(self):
+ db.delete_table("southtest_spam")
+
diff --git a/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0002_eggs.py b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0002_eggs.py
new file mode 100644
index 0000000..3ec8399
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0002_eggs.py
@@ -0,0 +1,20 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+
+ Spam = db.mock_model(model_name='Spam', db_table='southtest_spam', db_tablespace='', pk_field_name='id', pk_field_type=models.AutoField)
+
+ db.create_table("southtest_eggs", (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('size', models.FloatField()),
+ ('quantity', models.IntegerField()),
+ ('spam', models.ForeignKey(Spam)),
+ ))
+
+ def backwards(self):
+
+ db.delete_table("southtest_eggs")
+
diff --git a/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0003_alter_spam.py b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0003_alter_spam.py
new file mode 100644
index 0000000..39126c2
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/0003_alter_spam.py
@@ -0,0 +1,18 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+
+ db.alter_column("southtest_spam", 'weight', models.FloatField(null=True))
+
+ def backwards(self):
+
+ db.alter_column("southtest_spam", 'weight', models.FloatField())
+
+ models = {
+ "fakeapp.bug135": {
+ 'date': ('models.DateTimeField', [], {'default': 'datetime.datetime(2009, 5, 6, 15, 33, 15, 780013)'}),
+ }
+ }
diff --git a/lib/python2.7/site-packages/south/tests/fakeapp/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/fakeapp/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/fakeapp/models.py b/lib/python2.7/site-packages/south/tests/fakeapp/models.py
new file mode 100644
index 0000000..cc39eb7
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/fakeapp/models.py
@@ -0,0 +1,111 @@
+# -*- coding: UTF-8 -*-
+
+from django.db import models
+from django.contrib.auth.models import User as UserAlias
+
+from south.modelsinspector import add_introspection_rules
+
+on_delete_is_available = hasattr(models, "PROTECT") # models here is django.db.models
+
+def default_func():
+ return "yays"
+
+# An empty case.
+class Other1(models.Model): pass
+
+# Another one
+class Other3(models.Model): pass
+def get_sentinel_object():
+ """
+ A function to return the object to be used in place of any deleted object,
+ when using the SET option for on_delete.
+ """
+ # Create a new one, so we always have an instance to test with. Can't work!
+ return Other3()
+
+# Nastiness.
+class HorribleModel(models.Model):
+ "A model to test the edge cases of model parsing"
+
+ ZERO, ONE = 0, 1
+
+ # First, some nice fields
+ name = models.CharField(max_length=255)
+ short_name = models.CharField(max_length=50)
+ slug = models.SlugField(unique=True)
+
+ # A ForeignKey, to a model above, and then below
+ o1 = models.ForeignKey(Other1)
+ o2 = models.ForeignKey('Other2')
+
+ if on_delete_is_available:
+ o_set_null_on_delete = models.ForeignKey('Other3', null=True, on_delete=models.SET_NULL)
+ o_cascade_delete = models.ForeignKey('Other3', null=True, on_delete=models.CASCADE, related_name="cascademe")
+ o_protect = models.ForeignKey('Other3', null=True, on_delete=models.PROTECT, related_name="dontcascademe")
+ o_default_on_delete = models.ForeignKey('Other3', null=True, default=1, on_delete=models.SET_DEFAULT, related_name="setmedefault")
+ o_set_on_delete_function = models.ForeignKey('Other3', null=True, default=1, on_delete=models.SET(get_sentinel_object), related_name="setsentinel")
+ o_set_on_delete_value = models.ForeignKey('Other3', null=True, default=1, on_delete=models.SET(get_sentinel_object()), related_name="setsentinelwithactualvalue") # dubious case
+ o_no_action_on_delete = models.ForeignKey('Other3', null=True, default=1, on_delete=models.DO_NOTHING, related_name="deletemeatyourperil")
+
+
+ # Now to something outside
+ user = models.ForeignKey(UserAlias, related_name="horribles")
+
+ # Unicode!
+ code = models.CharField(max_length=25, default="↑↑↓↓←→←→BA")
+
+ # Odd defaults!
+ class_attr = models.IntegerField(default=ZERO)
+ func = models.CharField(max_length=25, default=default_func)
+
+ # Time to get nasty. Define a non-field choices, and use it
+ choices = [('hello', '1'), ('world', '2')]
+ choiced = models.CharField(max_length=20, choices=choices)
+
+ class Meta:
+ db_table = "my_fave"
+ verbose_name = "Dr. Strangelove," + \
+ """or how I learned to stop worrying
+and love the bomb"""
+
+ # Now spread over multiple lines
+ multiline = \
+ models.TextField(
+ )
+
+# Special case.
+class Other2(models.Model):
+ # Try loading a field without a newline after it (inspect hates this)
+ close_but_no_cigar = models.PositiveIntegerField(primary_key=True)
+
+class CustomField(models.IntegerField):
+ def __init__(self, an_other_model, **kwargs):
+ super(CustomField, self).__init__(**kwargs)
+ self.an_other_model = an_other_model
+
+add_introspection_rules([
+ (
+ [CustomField],
+ [],
+ {'an_other_model': ('an_other_model', {})},
+ ),
+], ['^south\.tests\.fakeapp\.models\.CustomField'])
+
+class BaseModel(models.Model):
+ pass
+
+class SubModel(BaseModel):
+ others = models.ManyToManyField(Other1)
+ custom = CustomField(Other2)
+
+class CircularA(models.Model):
+ c = models.ForeignKey('CircularC')
+
+class CircularB(models.Model):
+ a = models.ForeignKey(CircularA)
+
+class CircularC(models.Model):
+ b = models.ForeignKey(CircularB)
+
+class Recursive(models.Model):
+ self = models.ForeignKey('self')
diff --git a/lib/python2.7/site-packages/south/tests/freezer.py b/lib/python2.7/site-packages/south/tests/freezer.py
new file mode 100644
index 0000000..82c4402
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/freezer.py
@@ -0,0 +1,15 @@
+from south.tests import unittest
+
+from south.creator.freezer import model_dependencies
+from south.tests.fakeapp import models
+
+class TestFreezer(unittest.TestCase):
+ def test_dependencies(self):
+ self.assertEqual(set(model_dependencies(models.SubModel)),
+ set([models.BaseModel, models.Other1, models.Other2]))
+
+ self.assertEqual(set(model_dependencies(models.CircularA)),
+ set([models.CircularA, models.CircularB, models.CircularC]))
+
+ self.assertEqual(set(model_dependencies(models.Recursive)),
+ set([models.Recursive]))
diff --git a/lib/python2.7/site-packages/south/tests/inspector.py b/lib/python2.7/site-packages/south/tests/inspector.py
new file mode 100644
index 0000000..dcd6d57
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/inspector.py
@@ -0,0 +1,109 @@
+
+from south.tests import Monkeypatcher, skipUnless
+from south.modelsinspector import (convert_on_delete_handler, get_value,
+ IsDefault, models, value_clean)
+
+from fakeapp.models import HorribleModel, get_sentinel_object
+
+
+on_delete_is_available = hasattr(models, "PROTECT") # models here is django.db.models
+skipUnlessOnDeleteAvailable = skipUnless(on_delete_is_available, "not testing on_delete -- not available on Django<1.3")
+
+class TestModelInspector(Monkeypatcher):
+
+ """
+ Tests if the various parts of the modelinspector work.
+ """
+
+ def test_get_value(self):
+
+ # Let's start nicely.
+ name = HorribleModel._meta.get_field_by_name("name")[0]
+ slug = HorribleModel._meta.get_field_by_name("slug")[0]
+ user = HorribleModel._meta.get_field_by_name("user")[0]
+
+ # Simple int retrieval
+ self.assertEqual(
+ get_value(name, ["max_length", {}]),
+ "255",
+ )
+
+ # Bool retrieval
+ self.assertEqual(
+ get_value(slug, ["unique", {}]),
+ "True",
+ )
+
+ # String retrieval
+ self.assertEqual(
+ get_value(user, ["rel.related_name", {}]),
+ "'horribles'",
+ )
+
+ # Default triggering
+ self.assertEqual(
+ get_value(slug, ["unique", {"default": False}]),
+ "True",
+ )
+ self.assertRaises(
+ IsDefault,
+ get_value,
+ slug,
+ ["unique", {"default": True}],
+ )
+
+ @skipUnlessOnDeleteAvailable
+ def test_get_value_on_delete(self):
+
+ # First validate the FK fields with on_delete options
+ o_set_null_on_delete = HorribleModel._meta.get_field_by_name("o_set_null_on_delete")[0]
+ o_cascade_delete = HorribleModel._meta.get_field_by_name("o_cascade_delete")[0]
+ o_protect = HorribleModel._meta.get_field_by_name("o_protect")[0]
+ o_default_on_delete = HorribleModel._meta.get_field_by_name("o_default_on_delete")[0]
+ o_set_on_delete_function = HorribleModel._meta.get_field_by_name("o_set_on_delete_function")[0]
+ o_set_on_delete_value = HorribleModel._meta.get_field_by_name("o_set_on_delete_value")[0]
+ o_no_action_on_delete = HorribleModel._meta.get_field_by_name("o_no_action_on_delete")[0]
+ # TODO this is repeated from the introspection_details in modelsinspector:
+ # better to refactor that so we can reference these settings, in case they
+ # must change at some point.
+ on_delete = ["rel.on_delete", {"default": models.CASCADE, "is_django_function": True, "converter": convert_on_delete_handler, }]
+
+ # Foreign Key cascade update/delete
+ self.assertRaises(
+ IsDefault,
+ get_value,
+ o_cascade_delete,
+ on_delete,
+ )
+ self.assertEqual(
+ get_value(o_protect, on_delete),
+ "models.PROTECT",
+ )
+ self.assertEqual(
+ get_value(o_no_action_on_delete, on_delete),
+ "models.DO_NOTHING",
+ )
+ self.assertEqual(
+ get_value(o_set_null_on_delete, on_delete),
+ "models.SET_NULL",
+ )
+ self.assertEqual(
+ get_value(o_default_on_delete, on_delete),
+ "models.SET_DEFAULT",
+ )
+ # For now o_set_on_delete raises, see modelsinspector.py
+ #self.assertEqual(
+ # get_value(o_set_on_delete_function, on_delete),
+ # "models.SET(get_sentinel_object)",
+ #)
+ self.assertRaises(
+ ValueError,
+ get_value,
+ o_set_on_delete_function,
+ on_delete,
+ )
+ self.assertEqual(
+ get_value(o_set_on_delete_value, on_delete),
+ "models.SET(%s)" % value_clean(get_sentinel_object()),
+ )
+ \ No newline at end of file
diff --git a/lib/python2.7/site-packages/south/tests/logger.py b/lib/python2.7/site-packages/south/tests/logger.py
new file mode 100644
index 0000000..78d159d
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/logger.py
@@ -0,0 +1,82 @@
+import io
+import logging
+import os
+import tempfile
+from south.tests import unittest
+import sys
+
+from django.conf import settings
+from django.db import connection, models
+
+from south.db import db
+from south.logger import close_logger
+
+class TestLogger(unittest.TestCase):
+
+ """
+ Tests if the logging is working reasonably. Some tests ignored if you don't
+ have write permission to the disk.
+ """
+
+ def setUp(self):
+ db.debug = False
+ self.test_path = tempfile.mkstemp(suffix=".south.log")[1]
+
+ def test_db_execute_logging_nofile(self):
+ "Does logging degrade nicely if SOUTH_LOGGING_ON not set?"
+ settings.SOUTH_LOGGING_ON = False # this needs to be set to False
+ # to avoid issues where other tests
+ # set this to True. settings is shared
+ # between these tests.
+ db.create_table("test9", [('email_confirmed', models.BooleanField(default=False))])
+
+ def test_db_execute_logging_off_with_basic_config(self):
+ """
+ Does the south logger avoid outputing debug information with
+ south logging turned off and python logging configured with
+ a basic config?"
+ """
+ settings.SOUTH_LOGGING_ON = False
+
+ # Set root logger to capture WARNING and worse
+ logging_stream = io.StringIO()
+ logging.basicConfig(stream=logging_stream, level=logging.WARNING)
+
+ db.create_table("test12", [('email_confirmed', models.BooleanField(default=False))])
+
+ # since south logging is off, and our root logger is at WARNING
+ # we should not find DEBUG info in the log
+ self.assertEqual(logging_stream.getvalue(), '')
+
+ def test_db_execute_logging_validfile(self):
+ "Does logging work when passing in a valid file?"
+ settings.SOUTH_LOGGING_ON = True
+ settings.SOUTH_LOGGING_FILE = self.test_path
+ # Check to see if we can make the logfile
+ try:
+ fh = open(self.test_path, "w")
+ except IOError:
+ # Permission was denied, ignore the test.
+ return
+ else:
+ fh.close()
+ # Do an action which logs
+ db.create_table("test10", [('email_confirmed', models.BooleanField(default=False))])
+ # Close the logged file
+ close_logger()
+ try:
+ os.remove(self.test_path)
+ except:
+ # It's a tempfile, it's not vital we remove it.
+ pass
+
+ def test_db_execute_logging_missingfilename(self):
+ "Does logging raise an error if there is a missing filename?"
+ settings.SOUTH_LOGGING_ON = True
+ settings.SOUTH_LOGGING_FILE = None
+ self.assertRaises(
+ IOError,
+ db.create_table,
+ "test11",
+ [('email_confirmed', models.BooleanField(default=False))],
+ )
diff --git a/lib/python2.7/site-packages/south/tests/logic.py b/lib/python2.7/site-packages/south/tests/logic.py
new file mode 100644
index 0000000..2b21cef
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/logic.py
@@ -0,0 +1,902 @@
+from south.tests import unittest
+
+import datetime
+import sys
+try:
+ set # builtin, python >=2.6
+except NameError:
+ from sets import Set as set # in stdlib, python >=2.3
+
+from south import exceptions
+from south.migration import migrate_app
+from south.migration.base import all_migrations, Migrations
+from south.creator.changes import ManualChanges
+from south.migration.utils import depends, flatten, get_app_label
+from south.models import MigrationHistory
+from south.tests import Monkeypatcher
+from south.db import db
+
+
+
+class TestBrokenMigration(Monkeypatcher):
+ installed_apps = ["fakeapp", "otherfakeapp", "brokenapp"]
+
+ def test_broken_dependencies(self):
+ self.assertRaises(
+ exceptions.DependsOnUnmigratedApplication,
+ Migrations.calculate_dependencies,
+ force=True,
+ )
+ #depends_on_unknown = self.brokenapp['0002_depends_on_unknown']
+ #self.assertRaises(exceptions.DependsOnUnknownMigration,
+ # depends_on_unknown.dependencies)
+ #depends_on_higher = self.brokenapp['0003_depends_on_higher']
+ #self.assertRaises(exceptions.DependsOnHigherMigration,
+ # depends_on_higher.dependencies)
+
+
+class TestMigration(Monkeypatcher):
+ installed_apps = ["fakeapp", "otherfakeapp"]
+
+ def setUp(self):
+ super(TestMigration, self).setUp()
+ self.fakeapp = Migrations('fakeapp')
+ self.otherfakeapp = Migrations('otherfakeapp')
+ Migrations.calculate_dependencies(force=True)
+
+ def test_str(self):
+ migrations = [str(m) for m in self.fakeapp]
+ self.assertEqual(['fakeapp:0001_spam',
+ 'fakeapp:0002_eggs',
+ 'fakeapp:0003_alter_spam'],
+ migrations)
+
+ def test_repr(self):
+ migrations = [repr(m) for m in self.fakeapp]
+ self.assertEqual(['<Migration: fakeapp:0001_spam>',
+ '<Migration: fakeapp:0002_eggs>',
+ '<Migration: fakeapp:0003_alter_spam>'],
+ migrations)
+
+ def test_app_label(self):
+ self.assertEqual(['fakeapp', 'fakeapp', 'fakeapp'],
+ [m.app_label() for m in self.fakeapp])
+
+ def test_name(self):
+ self.assertEqual(['0001_spam', '0002_eggs', '0003_alter_spam'],
+ [m.name() for m in self.fakeapp])
+
+ def test_full_name(self):
+ self.assertEqual(['fakeapp.migrations.0001_spam',
+ 'fakeapp.migrations.0002_eggs',
+ 'fakeapp.migrations.0003_alter_spam'],
+ [m.full_name() for m in self.fakeapp])
+
+ def test_migration(self):
+ # Can't use vanilla import, modules beginning with numbers aren't in grammar
+ M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
+ M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
+ M3 = __import__("fakeapp.migrations.0003_alter_spam", {}, {}, ['Migration']).Migration
+ self.assertEqual([M1, M2, M3],
+ [m.migration().Migration for m in self.fakeapp])
+ self.assertRaises(exceptions.UnknownMigration,
+ self.fakeapp['9999_unknown'].migration)
+
+ def test_previous(self):
+ self.assertEqual([None,
+ self.fakeapp['0001_spam'],
+ self.fakeapp['0002_eggs']],
+ [m.previous() for m in self.fakeapp])
+
+ def test_dependencies(self):
+ "Test that the dependency detection works."
+ self.assertEqual([
+ set([]),
+ set([self.fakeapp['0001_spam']]),
+ set([self.fakeapp['0002_eggs']])
+ ],
+ [m.dependencies for m in self.fakeapp],
+ )
+ self.assertEqual([
+ set([self.fakeapp['0001_spam']]),
+ set([self.otherfakeapp['0001_first']]),
+ set([
+ self.otherfakeapp['0002_second'],
+ self.fakeapp['0003_alter_spam'],
+ ])
+ ],
+ [m.dependencies for m in self.otherfakeapp],
+ )
+
+ def test_forwards_plan(self):
+ self.assertEqual([
+ [self.fakeapp['0001_spam']],
+ [
+ self.fakeapp['0001_spam'],
+ self.fakeapp['0002_eggs']
+ ],
+ [
+ self.fakeapp['0001_spam'],
+ self.fakeapp['0002_eggs'],
+ self.fakeapp['0003_alter_spam'],
+ ]
+ ],
+ [m.forwards_plan() for m in self.fakeapp],
+ )
+ self.assertEqual([
+ [
+ self.fakeapp['0001_spam'],
+ self.otherfakeapp['0001_first']
+ ],
+ [
+ self.fakeapp['0001_spam'],
+ self.otherfakeapp['0001_first'],
+ self.otherfakeapp['0002_second']
+ ],
+ [
+ self.fakeapp['0001_spam'],
+ self.otherfakeapp['0001_first'],
+ self.otherfakeapp['0002_second'],
+ self.fakeapp['0002_eggs'],
+ self.fakeapp['0003_alter_spam'],
+ self.otherfakeapp['0003_third'],
+ ]
+ ],
+ [m.forwards_plan() for m in self.otherfakeapp],
+ )
+
+ def test_is_before(self):
+ F1 = self.fakeapp['0001_spam']
+ F2 = self.fakeapp['0002_eggs']
+ F3 = self.fakeapp['0003_alter_spam']
+ O1 = self.otherfakeapp['0001_first']
+ O2 = self.otherfakeapp['0002_second']
+ O3 = self.otherfakeapp['0003_third']
+ self.assertTrue(F1.is_before(F2))
+ self.assertTrue(F1.is_before(F3))
+ self.assertTrue(F2.is_before(F3))
+ self.assertEqual(O3.is_before(O1), False)
+ self.assertEqual(O3.is_before(O2), False)
+ self.assertEqual(O2.is_before(O2), False)
+ self.assertEqual(O2.is_before(O1), False)
+ self.assertEqual(F2.is_before(O1), None)
+ self.assertEqual(F2.is_before(O2), None)
+ self.assertEqual(F2.is_before(O3), None)
+
+
+class TestMigrationDependencies(Monkeypatcher):
+ installed_apps = ['deps_a', 'deps_b', 'deps_c']
+
+ def setUp(self):
+ super(TestMigrationDependencies, self).setUp()
+ self.deps_a = Migrations('deps_a')
+ self.deps_b = Migrations('deps_b')
+ self.deps_c = Migrations('deps_c')
+ Migrations.calculate_dependencies(force=True)
+
+ def test_dependencies(self):
+ self.assertEqual(
+ [
+ set([]),
+ set([self.deps_a['0001_a']]),
+ set([self.deps_a['0002_a']]),
+ set([
+ self.deps_a['0003_a'],
+ self.deps_b['0003_b'],
+ ]),
+ set([self.deps_a['0004_a']]),
+ ],
+ [m.dependencies for m in self.deps_a],
+ )
+ self.assertEqual(
+ [
+ set([]),
+ set([
+ self.deps_b['0001_b'],
+ self.deps_a['0002_a']
+ ]),
+ set([
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a']
+ ]),
+ set([self.deps_b['0003_b']]),
+ set([self.deps_b['0004_b']]),
+ ],
+ [m.dependencies for m in self.deps_b],
+ )
+ self.assertEqual(
+ [
+ set([]),
+ set([self.deps_c['0001_c']]),
+ set([self.deps_c['0002_c']]),
+ set([self.deps_c['0003_c']]),
+ set([
+ self.deps_c['0004_c'],
+ self.deps_a['0002_a']
+ ]),
+ ],
+ [m.dependencies for m in self.deps_c],
+ )
+
+ def test_dependents(self):
+ self.assertEqual([set([self.deps_a['0002_a']]),
+ set([self.deps_c['0005_c'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a']]),
+ set([self.deps_b['0003_b'],
+ self.deps_a['0004_a']]),
+ set([self.deps_a['0005_a']]),
+ set([])],
+ [m.dependents for m in self.deps_a])
+ self.assertEqual([set([self.deps_b['0002_b']]),
+ set([self.deps_b['0003_b']]),
+ set([self.deps_b['0004_b'],
+ self.deps_a['0004_a']]),
+ set([self.deps_b['0005_b']]),
+ set([])],
+ [m.dependents for m in self.deps_b])
+ self.assertEqual([set([self.deps_c['0002_c']]),
+ set([self.deps_c['0003_c']]),
+ set([self.deps_c['0004_c']]),
+ set([self.deps_c['0005_c']]),
+ set([])],
+ [m.dependents for m in self.deps_c])
+
+ def test_forwards_plan(self):
+ self.assertEqual([[self.deps_a['0001_a']],
+ [self.deps_a['0001_a'],
+ self.deps_a['0002_a']],
+ [self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_a['0003_a']],
+ [self.deps_b['0001_b'],
+ self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a'],
+ self.deps_b['0003_b'],
+ self.deps_a['0004_a']],
+ [self.deps_b['0001_b'],
+ self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a'],
+ self.deps_b['0003_b'],
+ self.deps_a['0004_a'],
+ self.deps_a['0005_a']]],
+ [m.forwards_plan() for m in self.deps_a])
+ self.assertEqual([[self.deps_b['0001_b']],
+ [self.deps_b['0001_b'],
+ self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_b['0002_b']],
+ [self.deps_b['0001_b'],
+ self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a'],
+ self.deps_b['0003_b']],
+ [self.deps_b['0001_b'],
+ self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a'],
+ self.deps_b['0003_b'],
+ self.deps_b['0004_b']],
+ [self.deps_b['0001_b'],
+ self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a'],
+ self.deps_b['0003_b'],
+ self.deps_b['0004_b'],
+ self.deps_b['0005_b']]],
+ [m.forwards_plan() for m in self.deps_b])
+ self.assertEqual([[self.deps_c['0001_c']],
+ [self.deps_c['0001_c'],
+ self.deps_c['0002_c']],
+ [self.deps_c['0001_c'],
+ self.deps_c['0002_c'],
+ self.deps_c['0003_c']],
+ [self.deps_c['0001_c'],
+ self.deps_c['0002_c'],
+ self.deps_c['0003_c'],
+ self.deps_c['0004_c']],
+ [self.deps_c['0001_c'],
+ self.deps_c['0002_c'],
+ self.deps_c['0003_c'],
+ self.deps_c['0004_c'],
+ self.deps_a['0001_a'],
+ self.deps_a['0002_a'],
+ self.deps_c['0005_c']]],
+ [m.forwards_plan() for m in self.deps_c])
+
+ def test_backwards_plan(self):
+ self.assertEqual([
+ [
+ self.deps_c['0005_c'],
+ self.deps_b['0005_b'],
+ self.deps_b['0004_b'],
+ self.deps_a['0005_a'],
+ self.deps_a['0004_a'],
+ self.deps_b['0003_b'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a'],
+ self.deps_a['0002_a'],
+ self.deps_a['0001_a'],
+ ],
+ [
+ self.deps_c['0005_c'],
+ self.deps_b['0005_b'],
+ self.deps_b['0004_b'],
+ self.deps_a['0005_a'],
+ self.deps_a['0004_a'],
+ self.deps_b['0003_b'],
+ self.deps_b['0002_b'],
+ self.deps_a['0003_a'],
+ self.deps_a['0002_a'],
+ ],
+ [
+ self.deps_b['0005_b'],
+ self.deps_b['0004_b'],
+ self.deps_a['0005_a'],
+ self.deps_a['0004_a'],
+ self.deps_b['0003_b'],
+ self.deps_a['0003_a'],
+ ],
+ [
+ self.deps_a['0005_a'],
+ self.deps_a['0004_a'],
+ ],
+ [
+ self.deps_a['0005_a'],
+ ]
+ ], [m.backwards_plan() for m in self.deps_a])
+ self.assertEqual([
+ [
+ self.deps_b['0005_b'],
+ self.deps_b['0004_b'],
+ self.deps_a['0005_a'],
+ self.deps_a['0004_a'],
+ self.deps_b['0003_b'],
+ self.deps_b['0002_b'],
+ self.deps_b['0001_b'],
+ ],
+ [
+ self.deps_b['0005_b'],
+ self.deps_b['0004_b'],
+ self.deps_a['0005_a'],
+ self.deps_a['0004_a'],
+ self.deps_b['0003_b'],
+ self.deps_b['0002_b'],
+ ],
+ [
+ self.deps_b['0005_b'],
+ self.deps_b['0004_b'],
+ self.deps_a['0005_a'],
+ self.deps_a['0004_a'],
+ self.deps_b['0003_b'],
+ ],
+ [
+ self.deps_b['0005_b'],
+ self.deps_b['0004_b'],
+ ],
+ [
+ self.deps_b['0005_b'],
+ ],
+ ], [m.backwards_plan() for m in self.deps_b])
+ self.assertEqual([
+ [
+ self.deps_c['0005_c'],
+ self.deps_c['0004_c'],
+ self.deps_c['0003_c'],
+ self.deps_c['0002_c'],
+ self.deps_c['0001_c'],
+ ],
+ [
+ self.deps_c['0005_c'],
+ self.deps_c['0004_c'],
+ self.deps_c['0003_c'],
+ self.deps_c['0002_c'],
+ ],
+ [
+ self.deps_c['0005_c'],
+ self.deps_c['0004_c'],
+ self.deps_c['0003_c'],
+ ],
+ [
+ self.deps_c['0005_c'],
+ self.deps_c['0004_c'],
+ ],
+ [self.deps_c['0005_c']]
+ ], [m.backwards_plan() for m in self.deps_c])
+
+
+class TestCircularDependencies(Monkeypatcher):
+ installed_apps = ["circular_a", "circular_b"]
+
+ def test_plans(self):
+ Migrations.calculate_dependencies(force=True)
+ circular_a = Migrations('circular_a')
+ circular_b = Migrations('circular_b')
+ self.assertRaises(
+ exceptions.CircularDependency,
+ circular_a[-1].forwards_plan,
+ )
+ self.assertRaises(
+ exceptions.CircularDependency,
+ circular_b[-1].forwards_plan,
+ )
+ self.assertRaises(
+ exceptions.CircularDependency,
+ circular_a[-1].backwards_plan,
+ )
+ self.assertRaises(
+ exceptions.CircularDependency,
+ circular_b[-1].backwards_plan,
+ )
+
+
+class TestMigrations(Monkeypatcher):
+ installed_apps = ["fakeapp", "otherfakeapp"]
+
+ def test_all(self):
+
+ M1 = Migrations(__import__("fakeapp", {}, {}, ['']))
+ M2 = Migrations(__import__("otherfakeapp", {}, {}, ['']))
+
+ self.assertEqual(
+ [M1, M2],
+ list(all_migrations()),
+ )
+
+ def test(self):
+
+ M1 = Migrations(__import__("fakeapp", {}, {}, ['']))
+
+ self.assertEqual(M1, Migrations("fakeapp"))
+ self.assertEqual(M1, Migrations(self.create_fake_app("fakeapp")))
+
+ def test_application(self):
+ fakeapp = Migrations("fakeapp")
+ application = __import__("fakeapp", {}, {}, [''])
+ self.assertEqual(application, fakeapp.application)
+
+ def test_migration(self):
+ # Can't use vanilla import, modules beginning with numbers aren't in grammar
+ M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
+ M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
+ migration = Migrations('fakeapp')
+ self.assertEqual(M1, migration['0001_spam'].migration().Migration)
+ self.assertEqual(M2, migration['0002_eggs'].migration().Migration)
+ self.assertRaises(exceptions.UnknownMigration,
+ migration['0001_jam'].migration)
+
+ def test_guess_migration(self):
+ # Can't use vanilla import, modules beginning with numbers aren't in grammar
+ M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
+ migration = Migrations('fakeapp')
+ self.assertEqual(M1, migration.guess_migration("0001_spam").migration().Migration)
+ self.assertEqual(M1, migration.guess_migration("0001_spa").migration().Migration)
+ self.assertEqual(M1, migration.guess_migration("0001_sp").migration().Migration)
+ self.assertEqual(M1, migration.guess_migration("0001_s").migration().Migration)
+ self.assertEqual(M1, migration.guess_migration("0001_").migration().Migration)
+ self.assertEqual(M1, migration.guess_migration("0001").migration().Migration)
+ self.assertRaises(exceptions.UnknownMigration,
+ migration.guess_migration, "0001-spam")
+ self.assertRaises(exceptions.MultiplePrefixMatches,
+ migration.guess_migration, "000")
+ self.assertRaises(exceptions.MultiplePrefixMatches,
+ migration.guess_migration, "")
+ self.assertRaises(exceptions.UnknownMigration,
+ migration.guess_migration, "0001_spams")
+ self.assertRaises(exceptions.UnknownMigration,
+ migration.guess_migration, "0001_jam")
+
+ def test_app_label(self):
+ names = ['fakeapp', 'otherfakeapp']
+ self.assertEqual(names,
+ [Migrations(n).app_label() for n in names])
+
+ def test_full_name(self):
+ names = ['fakeapp', 'otherfakeapp']
+ self.assertEqual([n + '.migrations' for n in names],
+ [Migrations(n).full_name() for n in names])
+
+
+class TestMigrationLogic(Monkeypatcher):
+
+ """
+ Tests if the various logic functions in migration actually work.
+ """
+
+ installed_apps = ["fakeapp", "otherfakeapp"]
+
+ def setUp(self):
+ super(TestMigrationLogic, self).setUp()
+ MigrationHistory.objects.all().delete()
+
+ def assertListEqual(self, list1, list2, msg=None):
+ list1 = set(list1)
+ list2 = set(list2)
+ return self.assert_(list1 == list2, "%s is not equal to %s" % (list1, list2))
+
+ def test_find_ghost_migrations(self):
+ pass
+
+ def test_apply_migrations(self):
+ migrations = Migrations("fakeapp")
+
+ # We should start with no migrations
+ self.assertEqual(list(MigrationHistory.objects.all()), [])
+
+ # Apply them normally
+ migrate_app(migrations, target_name=None, fake=False,
+ load_initial_data=True)
+
+ # We should finish with all migrations
+ self.assertListEqual(
+ (("fakeapp", "0001_spam"),
+ ("fakeapp", "0002_eggs"),
+ ("fakeapp", "0003_alter_spam"),),
+ MigrationHistory.objects.values_list("app_name", "migration"),
+ )
+
+ # Now roll them backwards
+ migrate_app(migrations, target_name="zero", fake=False)
+
+ # Finish with none
+ self.assertEqual(list(MigrationHistory.objects.all()), [])
+
+
+ def test_migration_merge_forwards(self):
+ migrations = Migrations("fakeapp")
+
+ # We should start with no migrations
+ self.assertEqual(list(MigrationHistory.objects.all()), [])
+
+ # Insert one in the wrong order
+ MigrationHistory.objects.create(app_name = "fakeapp",
+ migration = "0002_eggs",
+ applied = datetime.datetime.now())
+
+ # Did it go in?
+ self.assertListEqual(
+ (("fakeapp", "0002_eggs"),),
+ MigrationHistory.objects.values_list("app_name", "migration"),
+ )
+
+ # Apply them normally
+ self.assertRaises(exceptions.InconsistentMigrationHistory,
+ migrate_app,
+ migrations, target_name=None, fake=False)
+ self.assertRaises(exceptions.InconsistentMigrationHistory,
+ migrate_app,
+ migrations, target_name='zero', fake=False)
+ try:
+ migrate_app(migrations, target_name=None, fake=False)
+ except exceptions.InconsistentMigrationHistory as e:
+ self.assertEqual(
+ [
+ (
+ migrations['0002_eggs'],
+ migrations['0001_spam'],
+ )
+ ],
+ e.problems,
+ )
+ try:
+ migrate_app(migrations, target_name="zero", fake=False)
+ except exceptions.InconsistentMigrationHistory as e:
+ self.assertEqual(
+ [
+ (
+ migrations['0002_eggs'],
+ migrations['0001_spam'],
+ )
+ ],
+ e.problems,
+ )
+
+ # Nothing should have changed (no merge mode!)
+ self.assertListEqual(
+ (("fakeapp", "0002_eggs"),),
+ MigrationHistory.objects.values_list("app_name", "migration"),
+ )
+
+ # Apply with merge
+ migrate_app(migrations, target_name=None, merge=True, fake=False)
+
+ # We should finish with all migrations
+ self.assertListEqual(
+ (("fakeapp", "0001_spam"),
+ ("fakeapp", "0002_eggs"),
+ ("fakeapp", "0003_alter_spam"),),
+ MigrationHistory.objects.values_list("app_name", "migration"),
+ )
+
+ # Now roll them backwards
+ migrate_app(migrations, target_name="0002", fake=False)
+ migrate_app(migrations, target_name="0001", fake=True)
+ migrate_app(migrations, target_name="zero", fake=False)
+
+ # Finish with none
+ self.assertEqual(list(MigrationHistory.objects.all()), [])
+
+ def test_alter_column_null(self):
+
+ def null_ok(eat_exception=True):
+ from django.db import connection, transaction
+ # the DBAPI introspection module fails on postgres NULLs.
+ cursor = connection.cursor()
+
+ # SQLite has weird now()
+ if db.backend_name == "sqlite3":
+ now_func = "DATETIME('NOW')"
+ # So does SQLServer... should we be using a backend attribute?
+ elif db.backend_name == "pyodbc":
+ now_func = "GETDATE()"
+ elif db.backend_name == "oracle":
+ now_func = "SYSDATE"
+ else:
+ now_func = "NOW()"
+
+ try:
+ if db.backend_name == "pyodbc":
+ cursor.execute("SET IDENTITY_INSERT southtest_spam ON;")
+ cursor.execute("INSERT INTO southtest_spam (id, weight, expires, name) VALUES (100, NULL, %s, 'whatever');" % now_func)
+ except:
+ if eat_exception:
+ transaction.rollback()
+ return False
+ else:
+ raise
+ else:
+ cursor.execute("DELETE FROM southtest_spam")
+ transaction.commit()
+ return True
+
+ MigrationHistory.objects.all().delete()
+ migrations = Migrations("fakeapp")
+
+ # by default name is NOT NULL
+ migrate_app(migrations, target_name="0002", fake=False)
+ self.failIf(null_ok())
+ self.assertListEqual(
+ (("fakeapp", "0001_spam"),
+ ("fakeapp", "0002_eggs"),),
+ MigrationHistory.objects.values_list("app_name", "migration"),
+ )
+
+ # after 0003, it should be NULL
+ migrate_app(migrations, target_name="0003", fake=False)
+ self.assert_(null_ok(False))
+ self.assertListEqual(
+ (("fakeapp", "0001_spam"),
+ ("fakeapp", "0002_eggs"),
+ ("fakeapp", "0003_alter_spam"),),
+ MigrationHistory.objects.values_list("app_name", "migration"),
+ )
+
+ # make sure it is NOT NULL again
+ migrate_app(migrations, target_name="0002", fake=False)
+ self.failIf(null_ok(), 'weight not null after migration')
+ self.assertListEqual(
+ (("fakeapp", "0001_spam"),
+ ("fakeapp", "0002_eggs"),),
+ MigrationHistory.objects.values_list("app_name", "migration"),
+ )
+
+ # finish with no migrations, otherwise other tests fail...
+ migrate_app(migrations, target_name="zero", fake=False)
+ self.assertEqual(list(MigrationHistory.objects.all()), [])
+
+ def test_dependencies(self):
+
+ fakeapp = Migrations("fakeapp")
+ otherfakeapp = Migrations("otherfakeapp")
+
+ # Test a simple path
+ self.assertEqual([fakeapp['0001_spam'],
+ fakeapp['0002_eggs'],
+ fakeapp['0003_alter_spam']],
+ fakeapp['0003_alter_spam'].forwards_plan())
+
+ # And a complex one.
+ self.assertEqual(
+ [
+ fakeapp['0001_spam'],
+ otherfakeapp['0001_first'],
+ otherfakeapp['0002_second'],
+ fakeapp['0002_eggs'],
+ fakeapp['0003_alter_spam'],
+ otherfakeapp['0003_third']
+ ],
+ otherfakeapp['0003_third'].forwards_plan(),
+ )
+
+
+class TestMigrationUtils(Monkeypatcher):
+ installed_apps = ["fakeapp", "otherfakeapp"]
+
+ def test_get_app_label(self):
+ self.assertEqual(
+ "southtest",
+ get_app_label(self.create_fake_app("southtest.models")),
+ )
+ self.assertEqual(
+ "baz",
+ get_app_label(self.create_fake_app("foo.bar.baz.models")),
+ )
+
+class TestUtils(unittest.TestCase):
+
+ def test_flatten(self):
+ self.assertEqual([], list(flatten(iter([]))))
+ self.assertEqual([], list(flatten(iter([iter([]), ]))))
+ self.assertEqual([1], list(flatten(iter([1]))))
+ self.assertEqual([1, 2], list(flatten(iter([1, 2]))))
+ self.assertEqual([1, 2], list(flatten(iter([iter([1]), 2]))))
+ self.assertEqual([1, 2], list(flatten(iter([iter([1, 2])]))))
+ self.assertEqual([1, 2, 3], list(flatten(iter([iter([1, 2]), 3]))))
+ self.assertEqual([1, 2, 3],
+ list(flatten(iter([iter([1]), iter([2]), 3]))))
+ self.assertEqual([1, 2, 3],
+ list(flatten([[1], [2], 3])))
+
+ def test_depends(self):
+ graph = {'A1': []}
+ self.assertEqual(['A1'],
+ depends('A1', lambda n: graph[n]))
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2']}
+ self.assertEqual(['A1', 'A2', 'A3'],
+ depends('A3', lambda n: graph[n]))
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2', 'A1']}
+ self.assertEqual(['A1', 'A2', 'A3'],
+ depends('A3', lambda n: graph[n]))
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2', 'A1', 'B1'],
+ 'B1': []}
+ self.assertEqual(
+ ['B1', 'A1', 'A2', 'A3'],
+ depends('A3', lambda n: graph[n]),
+ )
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2', 'A1', 'B2'],
+ 'B1': [],
+ 'B2': ['B1']}
+ self.assertEqual(
+ ['B1', 'B2', 'A1', 'A2', 'A3'],
+ depends('A3', lambda n: graph[n]),
+ )
+ graph = {'A1': [],
+ 'A2': ['A1', 'B1'],
+ 'A3': ['A2'],
+ 'B1': ['A1']}
+ self.assertEqual(['A1', 'B1', 'A2', 'A3'],
+ depends('A3', lambda n: graph[n]))
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2', 'A1', 'B2'],
+ 'B1': [],
+ 'B2': ['B1', 'C1'],
+ 'C1': ['B1']}
+ self.assertEqual(
+ ['B1', 'C1', 'B2', 'A1', 'A2', 'A3'],
+ depends('A3', lambda n: graph[n]),
+ )
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2', 'B2', 'A1', 'C1'],
+ 'B1': ['A1'],
+ 'B2': ['B1', 'C2', 'A1'],
+ 'C1': ['B1'],
+ 'C2': ['C1', 'A1'],
+ 'C3': ['C2']}
+ self.assertEqual(
+ ['A1', 'B1', 'C1', 'C2', 'B2', 'A2', 'A3'],
+ depends('A3', lambda n: graph[n]),
+ )
+
+ def assertCircularDependency(self, trace, target, graph):
+ "Custom assertion that checks a circular dependency is detected correctly."
+ self.assertRaises(
+ exceptions.CircularDependency,
+ depends,
+ target,
+ lambda n: graph[n],
+ )
+ try:
+ depends(target, lambda n: graph[n])
+ except exceptions.CircularDependency as e:
+ self.assertEqual(trace, e.trace)
+
+ def test_depends_cycle(self):
+ graph = {'A1': ['A1']}
+ self.assertCircularDependency(
+ ['A1', 'A1'],
+ 'A1',
+ graph,
+ )
+ graph = {'A1': [],
+ 'A2': ['A1', 'A2'],
+ 'A3': ['A2']}
+ self.assertCircularDependency(
+ ['A2', 'A2'],
+ 'A3',
+ graph,
+ )
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2', 'A3'],
+ 'A4': ['A3']}
+ self.assertCircularDependency(
+ ['A3', 'A3'],
+ 'A4',
+ graph,
+ )
+ graph = {'A1': ['B1'],
+ 'B1': ['A1']}
+ self.assertCircularDependency(
+ ['A1', 'B1', 'A1'],
+ 'A1',
+ graph,
+ )
+ graph = {'A1': [],
+ 'A2': ['A1', 'B2'],
+ 'A3': ['A2'],
+ 'B1': [],
+ 'B2': ['B1', 'A2'],
+ 'B3': ['B2']}
+ self.assertCircularDependency(
+ ['A2', 'B2', 'A2'],
+ 'A3',
+ graph,
+ )
+ graph = {'A1': [],
+ 'A2': ['A1', 'B3'],
+ 'A3': ['A2'],
+ 'B1': [],
+ 'B2': ['B1', 'A2'],
+ 'B3': ['B2']}
+ self.assertCircularDependency(
+ ['A2', 'B3', 'B2', 'A2'],
+ 'A3',
+ graph,
+ )
+ graph = {'A1': [],
+ 'A2': ['A1'],
+ 'A3': ['A2', 'B2'],
+ 'A4': ['A3'],
+ 'B1': ['A3'],
+ 'B2': ['B1']}
+ self.assertCircularDependency(
+ ['A3', 'B2', 'B1', 'A3'],
+ 'A4',
+ graph,
+ )
+
+class TestManualChanges(Monkeypatcher):
+ installed_apps = ["fakeapp", "otherfakeapp"]
+
+ def test_suggest_name(self):
+ migrations = Migrations('fakeapp')
+ change = ManualChanges(migrations,
+ [],
+ ['fakeapp.slug'],
+ [])
+ self.assertEquals(change.suggest_name(),
+ 'add_field_fakeapp_slug')
+
+ change = ManualChanges(migrations,
+ [],
+ [],
+ ['fakeapp.slug'])
+ self.assertEquals(change.suggest_name(),
+ 'add_index_fakeapp_slug')
diff --git a/lib/python2.7/site-packages/south/tests/non_managed/__init__.py b/lib/python2.7/site-packages/south/tests/non_managed/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/non_managed/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/non_managed/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/non_managed/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/non_managed/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/non_managed/models.py b/lib/python2.7/site-packages/south/tests/non_managed/models.py
new file mode 100644
index 0000000..e520d94
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/non_managed/models.py
@@ -0,0 +1,16 @@
+# -*- coding: UTF-8 -*-
+
+"""
+An app with a model that is not managed for testing that South does
+not try to manage it in any way
+"""
+from django.db import models
+
+class Legacy(models.Model):
+
+ name = models.CharField(max_length=10)
+ size = models.IntegerField()
+
+ class Meta:
+ db_table = "legacy_table"
+ managed = False
diff --git a/lib/python2.7/site-packages/south/tests/otherfakeapp/__init__.py b/lib/python2.7/site-packages/south/tests/otherfakeapp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/otherfakeapp/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0001_first.py b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0001_first.py
new file mode 100644
index 0000000..ad9c095
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0001_first.py
@@ -0,0 +1,15 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = (
+ ("fakeapp", "0001_spam"),
+ )
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0002_second.py b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0002_second.py
new file mode 100644
index 0000000..7c0fb0c
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0002_second.py
@@ -0,0 +1,11 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
+
diff --git a/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0003_third.py b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0003_third.py
new file mode 100644
index 0000000..fa8ed97
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/0003_third.py
@@ -0,0 +1,14 @@
+from south.db import db
+from django.db import models
+
+class Migration:
+
+ depends_on = (
+ ("fakeapp", "0003_alter_spam"),
+ )
+
+ def forwards(self):
+ pass
+
+ def backwards(self):
+ pass
diff --git a/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/__init__.py b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/otherfakeapp/migrations/__init__.py
diff --git a/lib/python2.7/site-packages/south/tests/otherfakeapp/models.py b/lib/python2.7/site-packages/south/tests/otherfakeapp/models.py
new file mode 100644
index 0000000..93a4b8e
--- /dev/null
+++ b/lib/python2.7/site-packages/south/tests/otherfakeapp/models.py
@@ -0,0 +1 @@
+# This file left intentionally blank. \ No newline at end of file