diff --git a/django/core/management/commands/test.py b/django/core/management/commands/test.py
index 8ebf3da..d7f4caf 100644
--- a/django/core/management/commands/test.py
+++ b/django/core/management/commands/test.py
@@ -1,11 +1,14 @@
 from django.core.management.base import BaseCommand
 from optparse import make_option
 import sys
+from django.test.utils import start_coverage, stop_coverage
 
 class Command(BaseCommand):
     option_list = BaseCommand.option_list + (
         make_option('--noinput', action='store_false', dest='interactive', default=True,
             help='Tells Django to NOT prompt the user for input of any kind.'),
+        make_option('--coverage', action='store_true', dest='coverage', default=False,
+            help='Tells Django to run coverage analysis on your test modules.'),
     )
     help = 'Runs the test suite for the specified applications, or the entire site if no apps are specified.'
     args = '[appname ...]'
@@ -18,8 +21,17 @@ class Command(BaseCommand):
 
         verbosity = int(options.get('verbosity', 1))
         interactive = options.get('interactive', True)
+        use_coverage = options.get('coverage', False)
         test_runner = get_runner(settings)
 
-        failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
+        if use_coverage:
+            use_coverage = start_coverage(verbosity)
+
+        failures = test_runner(test_labels, verbosity=verbosity,
+                               interactive=interactive)
+
         if failures:
             sys.exit(failures)
+
+        if use_coverage:
+            stop_coverage(test_labels)
diff --git a/django/test/utils.py b/django/test/utils.py
index 29babec..3b70467 100644
--- a/django/test/utils.py
+++ b/django/test/utils.py
@@ -65,7 +65,6 @@ def teardown_test_environment():
 
     del mail.outbox
 
-
 def get_runner(settings):
     test_path = settings.TEST_RUNNER.split('.')
     # Allow for Python 2.5 relative paths
@@ -76,3 +75,52 @@ def get_runner(settings):
     test_module = __import__(test_module_name, {}, {}, test_path[-1])
     test_runner = getattr(test_module, test_path[-1])
     return test_runner
+
+def start_coverage(verbosity=1):
+    """
+    Try to start running coverage analysis.
+    This tries to import coverage.py, returning False if it can't import.
+    """
+    try:
+        import coverage
+        if verbosity > 1:
+            print "Running test runner with coverage."
+        coverage.start()
+    except:
+        if verbosity > 0:
+            print "coverage.py module is not available."
+        return False
+
+def stop_coverage(test_labels, exclude_admin=True):
+    #This was undefined if it wasn't imported.
+    #If we get to this point, coverage exists (since it was imported in start)
+    import coverage
+    coverage.stop()
+    coverage_models = []
+    if test_labels:
+        for label in test_labels:
+            if '.' in label:
+                #Test the app, don't know if we really want coverage here.
+                label = label.split('.')[0]
+            try:
+                #This is for the common case of non-runtests
+                coverage_models.append(__import__(label, globals(), locals(), ['']))
+                continue
+            except:
+                #This is for the runtests case, this makes me feel that they
+                #should be seperated totally in code :/
+                for app in ('modeltests','regressiontests'):
+                    try:
+                        coverage_models.append(__import__("%s.%s" % (app,label), globals(), locals(), ['']))
+                    except:
+                        pass
+    else:
+        from django.db.models.loading import get_apps
+        for app in get_apps():
+            if not exclude_admin or app.__name__.find('django.contrib') == -1:
+                coverage_models.append(app)
+    print '---------------------------------------------------------------'
+    print ' Unit Test Code Coverage Results'
+    print '---------------------------------------------------------------'
+    coverage.report(coverage_models, show_missing=1)
+    print '---------------------------------------------------------------'
diff --git a/tests/runtests.py b/tests/runtests.py
index bd7f59b..a7b3ba1 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -85,8 +85,9 @@ class InvalidModelTestCase(unittest.TestCase):
         self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
         self.assert_(not missing, "Missing Errors: " + '\n'.join(missing))
 
-def django_tests(verbosity, interactive, test_labels):
+def django_tests(verbosity, interactive, use_coverage, test_labels):
     from django.conf import settings
+    from django.test.utils import start_coverage, stop_coverage
 
     old_installed_apps = settings.INSTALLED_APPS
     old_test_database_name = settings.TEST_DATABASE_NAME
@@ -154,10 +155,17 @@ def django_tests(verbosity, interactive, test_labels):
         settings.TEST_RUNNER = 'django.test.simple.run_tests'
     test_runner = get_runner(settings)
 
+    if use_coverage:
+        use_coverage = start_coverage(verbosity)
+
     failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
+
     if failures:
         sys.exit(failures)
 
+    if use_coverage:
+        stop_coverage(test_labels, exclude_admin=False)
+
     # Restore the old settings.
     settings.INSTALLED_APPS = old_installed_apps
     settings.ROOT_URLCONF = old_root_urlconf
@@ -176,6 +184,8 @@ if __name__ == "__main__":
         help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
     parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
         help='Tells Django to NOT prompt the user for input of any kind.')
+    parser.add_option('--coverage', action='store_true', dest='coverage', default=False,
+        help='Tells Django to run coverage analysis on your test modules.'),
     parser.add_option('--settings',
         help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
     options, args = parser.parse_args()
@@ -184,4 +194,4 @@ if __name__ == "__main__":
     elif "DJANGO_SETTINGS_MODULE" not in os.environ:
         parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. "
                       "Set it or use --settings.")
-    django_tests(int(options.verbosity), options.interactive, args)
+    django_tests(int(options.verbosity), options.interactive, options.coverage, args)
