Index: twisted/topfiles/2675.bugfix
===================================================================
--- twisted/topfiles/2675.bugfix	(revision 0)
+++ twisted/topfiles/2675.bugfix	(revision 0)
@@ -0,0 +1 @@
+Allow setting default timeout for trial TestCases via -t/--timeout argument to trial.
Index: twisted/scripts/trial.py
===================================================================
--- twisted/scripts/trial.py	(revision 32505)
+++ twisted/scripts/trial.py	(working copy)
@@ -13,7 +13,7 @@
 from twisted import plugin
 from twisted.python.util import spewer
 from twisted.python.compat import set
-from twisted.trial import runner, itrial, reporter
+from twisted.trial import runner, itrial, reporter, util
 
 
 # Yea, this is stupid.  Leave it for for command-line compatibility for a
@@ -128,7 +128,10 @@
          'Path to use as working directory for tests.'],
         ['reporter', None, 'verbose',
          'The reporter to use for this test run.  See --help-reporters for '
-         'more info.']]
+         'more info.'],
+        ['timeout', 't', util.DEFAULT_TIMEOUT_DURATION,
+         'The default timeout in seconds. You can use the value 0 for no '
+         'timeout but this may cause your tests to hang.', float]]
 
     zsh_actions = {"tbformat":"(plain emacs cgitb)",
                    "reporter":_zshReporterAction}
@@ -338,6 +341,8 @@
         print 'Running tests shuffled with seed %d\n' % config['random']
     if not config['until-failure']:
         loader.suiteFactory = runner.DestructiveTestSuite
+    if config['timeout']:
+        loader.suiteFactory.defaultTimeout = config['timeout']
     return loader
 
 
Index: twisted/trial/test/test_script.py
===================================================================
--- twisted/trial/test/test_script.py	(revision 32505)
+++ twisted/trial/test/test_script.py	(working copy)
@@ -480,3 +480,19 @@
         self.assertDeprecationWarning(self.config.opt_extra,
                                       self.flushWarnings([self.test_xDeprecation]))
 
+
+class DefaultTimeoutTests(unittest.TestCase):
+    """
+    Test for C{--timeout} argument.
+    """
+
+    def test_defaultTimeoutSetOnSuiteFactory(self):
+        """
+        Test C{--timeout} argument results in defaultTimeout being set
+        on the loaded C{suiteFactory}.
+        """
+        options = trial.Options()
+        options.parseOptions(["--timeout", "20"])
+        loader = trial._getLoader(options)
+        self.assertEquals(loader.suiteFactory.defaultTimeout, 20)
+
Index: twisted/trial/test/test_runner.py
===================================================================
--- twisted/trial/test/test_runner.py	(revision 32505)
+++ twisted/trial/test/test_runner.py	(working copy)
@@ -490,6 +490,31 @@
         self.assertEqual(['runcall'], debugger._calls)
 
 
+    def test_defaultTimeout(self):
+        """
+        The integral value passed by --timeout argument to trial is used
+        to set the default timeout value on loaded tests that do not
+        explicitly set a timeout otherwise.
+        """
+        self.parseOptions(['--timeout', '25', 'twisted.trial.test.sample'])
+        myRunner = self.getRunner()
+        suite = trial._getSuite(self.config)
+        # Gather up non-stdlib unittests (i.e. trial.unittest.TestCases 
+        # instances only)
+        tests = [test for test in unittest._iterateTests(suite)
+                 if isinstance(test, unittest.TestCase)]
+        firstTest = tests[0]
+        firstTest.timeout = 1
+        secondTest = tests[1]
+        secondTest.timeout = 2
+        # Remaining tests in suit that do not explicity set a timeout
+        tests = tests[2:]
+        result = myRunner.run(suite)
+        self.assertEquals(firstTest.timeout, 1)
+        self.assertEquals(secondTest.timeout, 2)
+        for test in tests:
+            self.assertEquals(test.timeout, 25)
+        self.flushWarnings()
 
 class RemoveSafelyTests(unittest.TestCase):
     """
Index: twisted/trial/test/test_deferred.py
===================================================================
--- twisted/trial/test/test_deferred.py	(revision 32505)
+++ twisted/trial/test/test_deferred.py	(working copy)
@@ -60,17 +60,12 @@
 
 
 class TestNeverFire(unittest.TestCase):
-    def setUp(self):
-        self._oldTimeout = util.DEFAULT_TIMEOUT_DURATION
-        util.DEFAULT_TIMEOUT_DURATION = 0.1
 
-    def tearDown(self):
-        util.DEFAULT_TIMEOUT_DURATION = self._oldTimeout
-
     def _loadSuite(self, klass):
         loader = runner.TestLoader()
         r = reporter.TestResult()
         s = loader.loadClass(klass)
+        s.defaultTimeout = 0.1
         return r, s
 
     def test_setUp(self):
@@ -85,6 +80,7 @@
         self.failUnless(result.errors[0][1].check(defer.TimeoutError))
 
 
+
 class TestTester(unittest.TestCase):
     def getTest(self, name):
         raise NotImplementedError("must override me")
Index: twisted/trial/runner.py
===================================================================
--- twisted/trial/runner.py	(revision 32505)
+++ twisted/trial/runner.py	(working copy)
@@ -149,6 +149,9 @@
             if result.shouldStop:
                 break
             test = self._tests.pop(0)
+            if self.defaultTimeout:
+                if isinstance(test, unittest.TestCase):
+                    self._suggestTimeout(test, self.defaultTimeout)
             test(result)
         return result
 
Index: twisted/trial/unittest.py
===================================================================
--- twisted/trial/unittest.py	(revision 32505)
+++ twisted/trial/unittest.py	(working copy)
@@ -710,13 +710,15 @@
                     result.addExpectedFailure(self, f, todo)
                 else:
                     result.addError(self, f)
-        onTimeout = utils.suppressWarnings(
-            onTimeout, util.suppress(category=DeprecationWarning))
         method = getattr(self, methodName)
         d = defer.maybeDeferred(utils.runWithWarningsSuppressed,
                                 self.getSuppress(), method)
-        call = reactor.callLater(timeout, onTimeout, d)
-        d.addBoth(lambda x : call.active() and call.cancel() or x)
+        # If a timeout is not explicitly given we let the test hang
+        if timeout is not None:
+            onTimeout = utils.suppressWarnings(
+                onTimeout, util.suppress(category=DeprecationWarning))
+            call = reactor.callLater(timeout, onTimeout, d)
+            d.addBoth(lambda x : call.active() and call.cancel() or x)
         return d
 
     def shortDescription(self):
@@ -1165,12 +1167,13 @@
         """
         Returns the timeout value set on this test. Checks on the instance
         first, then the class, then the module, then packages. As soon as it
-        finds something with a C{timeout} attribute, returns that. Returns
-        L{util.DEFAULT_TIMEOUT_DURATION} if it cannot find anything. See
-        L{TestCase} docstring for more details.
+        finds something with a C{timeout} attribute, returns that. If it
+        cannot find any value for timeout, then C{None} is returned which may
+        result in a test hang.
         """
-        timeout =  util.acquireAttribute(self._parents, 'timeout',
-                                         util.DEFAULT_TIMEOUT_DURATION)
+        timeout =  util.acquireAttribute(self._parents, 'timeout', None)
+        if timeout is None:
+            return None
         try:
             return float(timeout)
         except (ValueError, TypeError):
@@ -1180,7 +1183,6 @@
             # both do this.
             warnings.warn("'timeout' attribute needs to be a number.",
                           category=DeprecationWarning)
-            return util.DEFAULT_TIMEOUT_DURATION
 
     def getSuppress(self):
         """
@@ -1384,10 +1386,13 @@
 class TestSuite(pyunit.TestSuite):
     """
     Extend the standard library's C{TestSuite} with support for the visitor
-    pattern and a consistently overrideable C{run} method.
+    pattern and a consistently overrideable C{run} method. This also supplies
+    C{defaultTimeout} which can be applied to instances of
+    L{twisted.trial.unittest.TestCase}.
     """
 
     visit = suiteVisit
+    defaultTimeout = None
 
     def __call__(self, result):
         return self.run(result)
@@ -1402,11 +1407,27 @@
         for test in self._tests:
             if result.shouldStop:
                 break
+            if self.defaultTimeout:
+                if isinstance(test, TestCase):
+                    self._suggestTimeout(test, self.defaultTimeout)
             test(result)
         return result
 
 
+    def _suggestTimeout(self, test, timeout):
+        """
+        Suggest the timeout for the L{TestCase}. If C{timeout} is C{None}
+        this method is a noop. Also, if the current C{TestCase} already
+        supplies a non-C{None} timeout value, this method is a noop. Otherwise
+        the given C{timeout} will be set on this test instance.
+        """
+        if timeout is None:
+            return
+        testTimeout = util.acquireAttribute(test._parents, 'timeout', None)
+        if testTimeout is None:
+            test.timeout = timeout
 
+
 class TestDecorator(components.proxyForInterface(itrial.ITestCase,
                                                  "_originalTest")):
     """
