Ausgabe der neuen DB Einträge
This commit is contained in:
parent
bad48e1627
commit
cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions
|
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Distributed trial test runner tests.
|
||||
"""
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.trial._dist.distreporter}.
|
||||
"""
|
||||
|
||||
from twisted.python.compat import NativeStringIO as StringIO
|
||||
from twisted.trial._dist.distreporter import DistReporter
|
||||
from twisted.trial.unittest import TestCase
|
||||
from twisted.trial.reporter import TreeReporter
|
||||
|
||||
|
||||
|
||||
class DistReporterTests(TestCase):
|
||||
"""
|
||||
Tests for L{DistReporter}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.stream = StringIO()
|
||||
self.distReporter = DistReporter(TreeReporter(self.stream))
|
||||
self.test = TestCase()
|
||||
|
||||
|
||||
def test_startSuccessStop(self):
|
||||
"""
|
||||
Success output only gets sent to the stream after the test has stopped.
|
||||
"""
|
||||
self.distReporter.startTest(self.test)
|
||||
self.assertEqual(self.stream.getvalue(), "")
|
||||
self.distReporter.addSuccess(self.test)
|
||||
self.assertEqual(self.stream.getvalue(), "")
|
||||
self.distReporter.stopTest(self.test)
|
||||
self.assertNotEqual(self.stream.getvalue(), "")
|
||||
|
||||
|
||||
def test_startErrorStop(self):
|
||||
"""
|
||||
Error output only gets sent to the stream after the test has stopped.
|
||||
"""
|
||||
self.distReporter.startTest(self.test)
|
||||
self.assertEqual(self.stream.getvalue(), "")
|
||||
self.distReporter.addError(self.test, "error")
|
||||
self.assertEqual(self.stream.getvalue(), "")
|
||||
self.distReporter.stopTest(self.test)
|
||||
self.assertNotEqual(self.stream.getvalue(), "")
|
||||
|
||||
|
||||
def test_forwardedMethods(self):
|
||||
"""
|
||||
Calling methods of L{DistReporter} add calls to the running queue of
|
||||
the test.
|
||||
"""
|
||||
self.distReporter.startTest(self.test)
|
||||
self.distReporter.addFailure(self.test, "foo")
|
||||
self.distReporter.addError(self.test, "bar")
|
||||
self.distReporter.addSkip(self.test, "egg")
|
||||
self.distReporter.addUnexpectedSuccess(self.test, "spam")
|
||||
self.distReporter.addExpectedFailure(self.test, "err", "foo")
|
||||
self.assertEqual(len(self.distReporter.running[self.test.id()]), 6)
|
||||
|
|
@ -0,0 +1,519 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.trial._dist.disttrial}.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from twisted.internet.protocol import Protocol, ProcessProtocol
|
||||
from twisted.internet.defer import fail, gatherResults, maybeDeferred, succeed
|
||||
from twisted.internet.task import Cooperator, deferLater
|
||||
from twisted.internet.main import CONNECTION_DONE
|
||||
from twisted.internet import reactor, interfaces, error
|
||||
from twisted.python.compat import NativeStringIO as StringIO
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.python.lockfile import FilesystemLock
|
||||
|
||||
from twisted.test.test_cooperator import FakeScheduler
|
||||
from twisted.test.proto_helpers import MemoryReactorClock
|
||||
|
||||
from twisted.trial.unittest import SynchronousTestCase, TestCase
|
||||
from twisted.trial.reporter import Reporter, TreeReporter
|
||||
from twisted.trial.reporter import UncleanWarningsReporterWrapper
|
||||
from twisted.trial.runner import TrialSuite, ErrorHolder
|
||||
|
||||
from twisted.trial._dist.disttrial import DistTrialRunner
|
||||
from twisted.trial._dist.distreporter import DistReporter
|
||||
from twisted.trial._dist.worker import LocalWorker
|
||||
|
||||
from zope.interface import implementer, verify
|
||||
|
||||
|
||||
|
||||
class FakeTransport(object):
|
||||
"""
|
||||
A simple fake process transport.
|
||||
"""
|
||||
|
||||
def writeToChild(self, fd, data):
|
||||
"""
|
||||
Ignore write calls.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@implementer(interfaces.IReactorProcess)
|
||||
class CountingReactor(MemoryReactorClock):
|
||||
"""
|
||||
A fake reactor that counts the calls to L{IReactorCore.run},
|
||||
L{IReactorCore.stop}, and L{IReactorProcess.spawnProcess}.
|
||||
"""
|
||||
spawnCount = 0
|
||||
stopCount = 0
|
||||
runCount = 0
|
||||
|
||||
def __init__(self, workers):
|
||||
MemoryReactorClock.__init__(self)
|
||||
self._workers = workers
|
||||
|
||||
|
||||
def spawnProcess(self, worker, *args, **kwargs):
|
||||
"""
|
||||
See L{IReactorProcess.spawnProcess}.
|
||||
|
||||
@param worker: See L{IReactorProcess.spawnProcess}.
|
||||
@param args: See L{IReactorProcess.spawnProcess}.
|
||||
@param kwargs: See L{IReactorProcess.spawnProcess}.
|
||||
"""
|
||||
self._workers.append(worker)
|
||||
worker.makeConnection(FakeTransport())
|
||||
self.spawnCount += 1
|
||||
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
See L{IReactorCore.stop}.
|
||||
"""
|
||||
MemoryReactorClock.stop(self)
|
||||
self.stopCount += 1
|
||||
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
See L{IReactorCore.run}.
|
||||
"""
|
||||
self.runCount += 1
|
||||
|
||||
# The same as IReactorCore.run, except no stop.
|
||||
self.running = True
|
||||
self.hasRun = True
|
||||
|
||||
for f, args, kwargs in self.whenRunningHooks:
|
||||
f(*args, **kwargs)
|
||||
|
||||
|
||||
|
||||
class CountingReactorTests(SynchronousTestCase):
|
||||
"""
|
||||
Tests for L{CountingReactor}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.workers = []
|
||||
self.reactor = CountingReactor(self.workers)
|
||||
|
||||
|
||||
def test_providesIReactorProcess(self):
|
||||
"""
|
||||
L{CountingReactor} instances provide L{IReactorProcess}.
|
||||
"""
|
||||
verify.verifyObject(interfaces.IReactorProcess, self.reactor)
|
||||
|
||||
|
||||
def test_spawnProcess(self):
|
||||
"""
|
||||
The process protocol for a spawned process is connected to a
|
||||
transport and appended onto the provided C{workers} list, and
|
||||
the reactor's C{spawnCount} increased.
|
||||
"""
|
||||
self.assertFalse(self.reactor.spawnCount)
|
||||
|
||||
proto = Protocol()
|
||||
for count in [1, 2]:
|
||||
self.reactor.spawnProcess(proto, sys.executable,
|
||||
arg=[sys.executable])
|
||||
self.assertTrue(proto.transport)
|
||||
self.assertEqual(self.workers, [proto] * count)
|
||||
self.assertEqual(self.reactor.spawnCount, count)
|
||||
|
||||
|
||||
def test_stop(self):
|
||||
"""
|
||||
Stopping the reactor increments its C{stopCount}
|
||||
"""
|
||||
self.assertFalse(self.reactor.stopCount)
|
||||
for count in [1, 2]:
|
||||
self.reactor.stop()
|
||||
self.assertEqual(self.reactor.stopCount, count)
|
||||
|
||||
|
||||
def test_run(self):
|
||||
"""
|
||||
Running the reactor increments its C{runCount}, does not imply
|
||||
C{stop}, and calls L{IReactorCore.callWhenRunning} hooks.
|
||||
"""
|
||||
self.assertFalse(self.reactor.runCount)
|
||||
|
||||
whenRunningCalls = []
|
||||
self.reactor.callWhenRunning(whenRunningCalls.append, None)
|
||||
|
||||
for count in [1, 2]:
|
||||
self.reactor.run()
|
||||
self.assertEqual(self.reactor.runCount, count)
|
||||
self.assertEqual(self.reactor.stopCount, 0)
|
||||
self.assertEqual(len(whenRunningCalls), count)
|
||||
|
||||
|
||||
|
||||
class EternalTerminationPredicateFactory(object):
|
||||
"""
|
||||
A rigged terminationPredicateFactory for which time never pass.
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
"""
|
||||
See: L{task._Timer}
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class DistTrialRunnerTests(TestCase):
|
||||
"""
|
||||
Tests for L{DistTrialRunner}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create a runner for testing.
|
||||
"""
|
||||
self.runner = DistTrialRunner(TreeReporter, 4, [],
|
||||
workingDirectory=self.mktemp())
|
||||
self.runner._stream = StringIO()
|
||||
|
||||
|
||||
def reap(self, workers):
|
||||
"""
|
||||
Reap the workers and trap L{ConnectionDone} failures on their
|
||||
C{endDeferred}s.
|
||||
|
||||
@param workers: The workers to reap.
|
||||
@type workers: An iterable of L{LocalWorker}
|
||||
"""
|
||||
|
||||
for worker in workers:
|
||||
worker.endDeferred.addErrback(Failure.trap, error.ConnectionDone)
|
||||
worker.processEnded(Failure(CONNECTION_DONE))
|
||||
|
||||
|
||||
def getFakeSchedulerAndEternalCooperator(self):
|
||||
"""
|
||||
Helper to create fake scheduler and cooperator in tests.
|
||||
|
||||
The cooperator has a termination timer which will never inform
|
||||
the scheduler that the task needs to be terminated.
|
||||
|
||||
@return: L{tuple} of (scheduler, cooperator)
|
||||
"""
|
||||
scheduler = FakeScheduler()
|
||||
cooperator = Cooperator(
|
||||
scheduler=scheduler,
|
||||
terminationPredicateFactory=EternalTerminationPredicateFactory,
|
||||
)
|
||||
return scheduler, cooperator
|
||||
|
||||
|
||||
def test_writeResults(self):
|
||||
"""
|
||||
L{DistTrialRunner.writeResults} writes to the stream specified in the
|
||||
init.
|
||||
"""
|
||||
stringIO = StringIO()
|
||||
result = DistReporter(Reporter(stringIO))
|
||||
self.runner.writeResults(result)
|
||||
self.assertTrue(stringIO.tell() > 0)
|
||||
|
||||
|
||||
def test_createLocalWorkers(self):
|
||||
"""
|
||||
C{createLocalWorkers} iterates the list of protocols and create one
|
||||
L{LocalWorker} for each.
|
||||
"""
|
||||
protocols = [object() for x in range(4)]
|
||||
workers = self.runner.createLocalWorkers(protocols, "path")
|
||||
for s in workers:
|
||||
self.assertIsInstance(s, LocalWorker)
|
||||
self.assertEqual(4, len(workers))
|
||||
|
||||
|
||||
def test_launchWorkerProcesses(self):
|
||||
"""
|
||||
Given a C{spawnProcess} function, C{launchWorkerProcess} launches a
|
||||
python process with an existing path as its argument.
|
||||
"""
|
||||
protocols = [ProcessProtocol() for i in range(4)]
|
||||
arguments = []
|
||||
environment = {}
|
||||
|
||||
def fakeSpawnProcess(processProtocol, executable, args=(), env={},
|
||||
path=None, uid=None, gid=None, usePTY=0,
|
||||
childFDs=None):
|
||||
arguments.append(executable)
|
||||
arguments.extend(args)
|
||||
environment.update(env)
|
||||
|
||||
self.runner.launchWorkerProcesses(
|
||||
fakeSpawnProcess, protocols, ["foo"])
|
||||
self.assertEqual(arguments[0], arguments[1])
|
||||
self.assertTrue(os.path.exists(arguments[2]))
|
||||
self.assertEqual("foo", arguments[3])
|
||||
self.assertEqual(os.pathsep.join(sys.path),
|
||||
environment["TRIAL_PYTHONPATH"])
|
||||
|
||||
|
||||
def test_run(self):
|
||||
"""
|
||||
C{run} starts the reactor exactly once and spawns each of the workers
|
||||
exactly once.
|
||||
"""
|
||||
workers = []
|
||||
fakeReactor = CountingReactor(workers)
|
||||
self.addCleanup(self.reap, workers)
|
||||
|
||||
suite = TrialSuite()
|
||||
for i in range(10):
|
||||
suite.addTest(TestCase())
|
||||
self.runner.run(suite, fakeReactor)
|
||||
self.assertEqual(fakeReactor.runCount, 1)
|
||||
self.assertEqual(fakeReactor.spawnCount, self.runner._workerNumber)
|
||||
|
||||
|
||||
def test_runUsedDirectory(self):
|
||||
"""
|
||||
L{DistTrialRunner} checks if the test directory is already locked, and
|
||||
if it is generates a name based on it.
|
||||
"""
|
||||
|
||||
class CountingReactorWithLock(CountingReactor):
|
||||
|
||||
def spawnProcess(oself, worker, *args, **kwargs):
|
||||
oself._workers.append(worker)
|
||||
self.assertEqual(os.path.abspath(worker._logDirectory),
|
||||
os.path.abspath(
|
||||
os.path.join(workingDirectory + "-1",
|
||||
str(oself.spawnCount))))
|
||||
localLock = FilesystemLock(workingDirectory + "-1.lock")
|
||||
self.assertFalse(localLock.lock())
|
||||
oself.spawnCount += 1
|
||||
worker.makeConnection(FakeTransport())
|
||||
worker._ampProtocol.run = lambda *args: succeed(None)
|
||||
|
||||
newDirectory = self.mktemp()
|
||||
os.mkdir(newDirectory)
|
||||
workingDirectory = os.path.join(newDirectory, "_trial_temp")
|
||||
lock = FilesystemLock(workingDirectory + ".lock")
|
||||
lock.lock()
|
||||
self.addCleanup(lock.unlock)
|
||||
self.runner._workingDirectory = workingDirectory
|
||||
|
||||
workers = []
|
||||
|
||||
fakeReactor = CountingReactorWithLock(workers)
|
||||
self.addCleanup(self.reap, workers)
|
||||
|
||||
suite = TrialSuite()
|
||||
for i in range(10):
|
||||
suite.addTest(TestCase())
|
||||
|
||||
self.runner.run(suite, fakeReactor)
|
||||
|
||||
|
||||
def test_minimalWorker(self):
|
||||
"""
|
||||
L{DistTrialRunner} doesn't try to start more workers than the number of
|
||||
tests.
|
||||
"""
|
||||
workers = []
|
||||
fakeReactor = CountingReactor(workers)
|
||||
self.addCleanup(self.reap, workers)
|
||||
|
||||
self.runner.run(TestCase(), fakeReactor)
|
||||
self.assertEqual(fakeReactor.runCount, 1)
|
||||
self.assertEqual(fakeReactor.spawnCount, 1)
|
||||
|
||||
|
||||
def test_runUncleanWarnings(self):
|
||||
"""
|
||||
Running with the C{unclean-warnings} option makes L{DistTrialRunner}
|
||||
uses the L{UncleanWarningsReporterWrapper}.
|
||||
"""
|
||||
workers = []
|
||||
fakeReactor = CountingReactor(workers)
|
||||
self.addCleanup(self.reap, workers)
|
||||
|
||||
self.runner._uncleanWarnings = True
|
||||
result = self.runner.run(TestCase(), fakeReactor)
|
||||
self.assertIsInstance(result, DistReporter)
|
||||
self.assertIsInstance(result.original,
|
||||
UncleanWarningsReporterWrapper)
|
||||
|
||||
|
||||
def test_runWithoutTest(self):
|
||||
"""
|
||||
When the suite contains no test, L{DistTrialRunner} takes a shortcut
|
||||
path without launching any process or starting the reactor.
|
||||
"""
|
||||
fakeReactor = object()
|
||||
suite = TrialSuite()
|
||||
result = self.runner.run(suite, fakeReactor)
|
||||
self.assertIsInstance(result, DistReporter)
|
||||
output = self.runner._stream.getvalue()
|
||||
self.assertIn("Running 0 test", output)
|
||||
self.assertIn("PASSED", output)
|
||||
|
||||
|
||||
def test_runWithoutTestButWithAnError(self):
|
||||
"""
|
||||
Even if there is no test, the suite can contain an error (most likely,
|
||||
an import error): this should make the run fail, and the error should
|
||||
be printed.
|
||||
"""
|
||||
fakeReactor = object()
|
||||
error = ErrorHolder("an error", Failure(RuntimeError("foo bar")))
|
||||
result = self.runner.run(error, fakeReactor)
|
||||
self.assertIsInstance(result, DistReporter)
|
||||
output = self.runner._stream.getvalue()
|
||||
self.assertIn("Running 0 test", output)
|
||||
self.assertIn("foo bar", output)
|
||||
self.assertIn("an error", output)
|
||||
self.assertIn("errors=1", output)
|
||||
self.assertIn("FAILED", output)
|
||||
|
||||
|
||||
def test_runUnexpectedError(self):
|
||||
"""
|
||||
If for some reasons we can't connect to the worker process, the test
|
||||
suite catches and fails.
|
||||
"""
|
||||
|
||||
class CountingReactorWithFail(CountingReactor):
|
||||
|
||||
def spawnProcess(self, worker, *args, **kwargs):
|
||||
self._workers.append(worker)
|
||||
worker.makeConnection(FakeTransport())
|
||||
self.spawnCount += 1
|
||||
worker._ampProtocol.run = self.failingRun
|
||||
|
||||
def failingRun(self, case, result):
|
||||
return fail(RuntimeError("oops"))
|
||||
|
||||
scheduler, cooperator = self.getFakeSchedulerAndEternalCooperator()
|
||||
|
||||
workers = []
|
||||
fakeReactor = CountingReactorWithFail(workers)
|
||||
self.addCleanup(self.reap, workers)
|
||||
|
||||
result = self.runner.run(TestCase(), fakeReactor,
|
||||
cooperator.cooperate)
|
||||
self.assertEqual(fakeReactor.runCount, 1)
|
||||
self.assertEqual(fakeReactor.spawnCount, 1)
|
||||
scheduler.pump()
|
||||
self.assertEqual(1, len(result.original.failures))
|
||||
|
||||
|
||||
def test_runStopAfterTests(self):
|
||||
"""
|
||||
L{DistTrialRunner} calls C{reactor.stop} and unlocks the test directory
|
||||
once the tests have run.
|
||||
"""
|
||||
class CountingReactorWithSuccess(CountingReactor):
|
||||
|
||||
def spawnProcess(self, worker, *args, **kwargs):
|
||||
self._workers.append(worker)
|
||||
worker.makeConnection(FakeTransport())
|
||||
self.spawnCount += 1
|
||||
worker._ampProtocol.run = self.succeedingRun
|
||||
|
||||
def succeedingRun(self, case, result):
|
||||
return succeed(None)
|
||||
|
||||
workingDirectory = self.runner._workingDirectory
|
||||
|
||||
workers = []
|
||||
fakeReactor = CountingReactorWithSuccess(workers)
|
||||
|
||||
self.runner.run(TestCase(), fakeReactor)
|
||||
|
||||
def check():
|
||||
localLock = FilesystemLock(workingDirectory + ".lock")
|
||||
self.assertTrue(localLock.lock())
|
||||
self.assertEqual(1, fakeReactor.stopCount)
|
||||
|
||||
self.assertEqual(list(fakeReactor.triggers.keys()), ["before"])
|
||||
self.assertEqual(list(fakeReactor.triggers["before"]), ["shutdown"])
|
||||
self.reap(workers)
|
||||
|
||||
return deferLater(reactor, 0, check)
|
||||
|
||||
|
||||
def test_runWaitForProcessesDeferreds(self):
|
||||
"""
|
||||
L{DistTrialRunner} waits for the worker processes to stop when the
|
||||
reactor is stopping, and then unlocks the test directory, not trying to
|
||||
stop the reactor again.
|
||||
"""
|
||||
workers = []
|
||||
workingDirectory = self.runner._workingDirectory
|
||||
|
||||
fakeReactor = CountingReactor(workers)
|
||||
self.runner.run(TestCase(), fakeReactor)
|
||||
|
||||
def check(ign):
|
||||
# Let the AMP deferreds fire
|
||||
return deferLater(reactor, 0, realCheck)
|
||||
|
||||
def realCheck():
|
||||
localLock = FilesystemLock(workingDirectory + ".lock")
|
||||
self.assertTrue(localLock.lock())
|
||||
# Stop is not called, as it ought to have been called before
|
||||
self.assertEqual(0, fakeReactor.stopCount)
|
||||
|
||||
self.assertEqual(list(fakeReactor.triggers.keys()), ["before"])
|
||||
self.assertEqual(list(fakeReactor.triggers["before"]), ["shutdown"])
|
||||
self.reap(workers)
|
||||
|
||||
return gatherResults([
|
||||
maybeDeferred(f, *a, **kw)
|
||||
for f, a, kw in fakeReactor.triggers["before"]["shutdown"]
|
||||
]).addCallback(check)
|
||||
|
||||
|
||||
def test_runUntilFailure(self):
|
||||
"""
|
||||
L{DistTrialRunner} can run in C{untilFailure} mode where it will run
|
||||
the given tests until they fail.
|
||||
"""
|
||||
called = []
|
||||
|
||||
class CountingReactorWithSuccess(CountingReactor):
|
||||
|
||||
def spawnProcess(self, worker, *args, **kwargs):
|
||||
self._workers.append(worker)
|
||||
worker.makeConnection(FakeTransport())
|
||||
self.spawnCount += 1
|
||||
worker._ampProtocol.run = self.succeedingRun
|
||||
|
||||
def succeedingRun(self, case, result):
|
||||
called.append(None)
|
||||
if len(called) == 5:
|
||||
return fail(RuntimeError("oops"))
|
||||
return succeed(None)
|
||||
|
||||
workers = []
|
||||
fakeReactor = CountingReactorWithSuccess(workers)
|
||||
self.addCleanup(self.reap, workers)
|
||||
|
||||
scheduler, cooperator = self.getFakeSchedulerAndEternalCooperator()
|
||||
|
||||
result = self.runner.run(
|
||||
TestCase(), fakeReactor, cooperate=cooperator.cooperate,
|
||||
untilFailure=True)
|
||||
scheduler.pump()
|
||||
self.assertEqual(5, len(called))
|
||||
self.assertFalse(result.wasSuccessful())
|
||||
output = self.runner._stream.getvalue()
|
||||
self.assertIn("PASSED", output)
|
||||
self.assertIn("FAIL", output)
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for distributed trial's options management.
|
||||
"""
|
||||
|
||||
import os, sys, gc
|
||||
|
||||
from twisted.trial.unittest import TestCase
|
||||
from twisted.trial._dist.options import WorkerOptions
|
||||
|
||||
|
||||
|
||||
class WorkerOptionsTests(TestCase):
|
||||
"""
|
||||
Tests for L{WorkerOptions}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Build an L{WorkerOptions} object to be used in the tests.
|
||||
"""
|
||||
self.options = WorkerOptions()
|
||||
|
||||
|
||||
def test_standardOptions(self):
|
||||
"""
|
||||
L{WorkerOptions} supports a subset of standard options supported by
|
||||
trial.
|
||||
"""
|
||||
self.addCleanup(sys.setrecursionlimit, sys.getrecursionlimit())
|
||||
if gc.isenabled():
|
||||
self.addCleanup(gc.enable)
|
||||
gc.enable()
|
||||
self.options.parseOptions(["--recursionlimit", "2000", "--disablegc"])
|
||||
self.assertEqual(2000, sys.getrecursionlimit())
|
||||
self.assertFalse(gc.isenabled())
|
||||
|
||||
|
||||
def test_coverage(self):
|
||||
"""
|
||||
L{WorkerOptions.coverdir} returns the C{coverage} child directory of
|
||||
the current directory to be used for storing coverage data.
|
||||
"""
|
||||
self.assertEqual(
|
||||
os.path.realpath(os.path.join(os.getcwd(), "coverage")),
|
||||
self.options.coverdir().path)
|
||||
|
|
@ -0,0 +1,470 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Test for distributed trial worker side.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from zope.interface.verify import verifyObject
|
||||
|
||||
from twisted.trial.reporter import TestResult
|
||||
from twisted.trial.unittest import TestCase
|
||||
from twisted.trial._dist.worker import (
|
||||
LocalWorker, LocalWorkerAMP, LocalWorkerTransport, WorkerProtocol)
|
||||
from twisted.trial._dist import managercommands, workercommands
|
||||
|
||||
from twisted.scripts import trial
|
||||
from twisted.test.proto_helpers import StringTransport
|
||||
|
||||
from twisted.internet.interfaces import ITransport, IAddress
|
||||
from twisted.internet.defer import fail, succeed
|
||||
from twisted.internet.main import CONNECTION_DONE
|
||||
from twisted.internet.error import ConnectionDone
|
||||
from twisted.python.reflect import fullyQualifiedName
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.protocols.amp import AMP
|
||||
from twisted.python.compat import NativeStringIO
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
class FakeAMP(AMP):
|
||||
"""
|
||||
A fake amp protocol.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class WorkerProtocolTests(TestCase):
|
||||
"""
|
||||
Tests for L{WorkerProtocol}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up a transport, a result stream and a protocol instance.
|
||||
"""
|
||||
self.serverTransport = StringTransport()
|
||||
self.clientTransport = StringTransport()
|
||||
self.server = WorkerProtocol()
|
||||
self.server.makeConnection(self.serverTransport)
|
||||
self.client = FakeAMP()
|
||||
self.client.makeConnection(self.clientTransport)
|
||||
|
||||
|
||||
def test_run(self):
|
||||
"""
|
||||
Calling the L{workercommands.Run} command on the client returns a
|
||||
response with C{success} sets to C{True}.
|
||||
"""
|
||||
d = self.client.callRemote(workercommands.Run, testCase="doesntexist")
|
||||
|
||||
def check(result):
|
||||
self.assertTrue(result['success'])
|
||||
|
||||
d.addCallback(check)
|
||||
self.server.dataReceived(self.clientTransport.value())
|
||||
self.clientTransport.clear()
|
||||
self.client.dataReceived(self.serverTransport.value())
|
||||
self.serverTransport.clear()
|
||||
return d
|
||||
|
||||
|
||||
def test_start(self):
|
||||
"""
|
||||
The C{start} command changes the current path.
|
||||
"""
|
||||
curdir = os.path.realpath(os.path.curdir)
|
||||
self.addCleanup(os.chdir, curdir)
|
||||
self.server.start('..')
|
||||
self.assertNotEqual(os.path.realpath(os.path.curdir), curdir)
|
||||
|
||||
|
||||
|
||||
class LocalWorkerAMPTests(TestCase):
|
||||
"""
|
||||
Test case for distributed trial's manager-side local worker AMP protocol
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.managerTransport = StringTransport()
|
||||
self.managerAMP = LocalWorkerAMP()
|
||||
self.managerAMP.makeConnection(self.managerTransport)
|
||||
self.result = TestResult()
|
||||
self.workerTransport = StringTransport()
|
||||
self.worker = AMP()
|
||||
self.worker.makeConnection(self.workerTransport)
|
||||
|
||||
config = trial.Options()
|
||||
self.testName = "twisted.doesnexist"
|
||||
config['tests'].append(self.testName)
|
||||
self.testCase = trial._getSuite(config)._tests.pop()
|
||||
|
||||
self.managerAMP.run(self.testCase, self.result)
|
||||
self.managerTransport.clear()
|
||||
|
||||
|
||||
def pumpTransports(self):
|
||||
"""
|
||||
Sends data from C{self.workerTransport} to C{self.managerAMP}, and then
|
||||
data from C{self.managerTransport} back to C{self.worker}.
|
||||
"""
|
||||
self.managerAMP.dataReceived(self.workerTransport.value())
|
||||
self.workerTransport.clear()
|
||||
self.worker.dataReceived(self.managerTransport.value())
|
||||
|
||||
|
||||
def test_runSuccess(self):
|
||||
"""
|
||||
Run a test, and succeed.
|
||||
"""
|
||||
results = []
|
||||
|
||||
d = self.worker.callRemote(managercommands.AddSuccess,
|
||||
testName=self.testName)
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_runExpectedFailure(self):
|
||||
"""
|
||||
Run a test, and fail expectedly.
|
||||
"""
|
||||
results = []
|
||||
|
||||
d = self.worker.callRemote(managercommands.AddExpectedFailure,
|
||||
testName=self.testName, error='error',
|
||||
todo='todoReason')
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertEqual(self.testCase, self.result.expectedFailures[0][0])
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_runError(self):
|
||||
"""
|
||||
Run a test, and encounter an error.
|
||||
"""
|
||||
results = []
|
||||
errorClass = fullyQualifiedName(ValueError)
|
||||
d = self.worker.callRemote(managercommands.AddError,
|
||||
testName=self.testName, error='error',
|
||||
errorClass=errorClass,
|
||||
frames=[])
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertEqual(self.testCase, self.result.errors[0][0])
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_runErrorWithFrames(self):
|
||||
"""
|
||||
L{LocalWorkerAMP._buildFailure} recreates the C{Failure.frames} from
|
||||
the C{frames} argument passed to C{AddError}.
|
||||
"""
|
||||
results = []
|
||||
errorClass = fullyQualifiedName(ValueError)
|
||||
d = self.worker.callRemote(managercommands.AddError,
|
||||
testName=self.testName, error='error',
|
||||
errorClass=errorClass,
|
||||
frames=["file.py", "invalid code", "3"])
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertEqual(self.testCase, self.result.errors[0][0])
|
||||
self.assertEqual(
|
||||
[('file.py', 'invalid code', 3, [], [])],
|
||||
self.result.errors[0][1].frames)
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_runFailure(self):
|
||||
"""
|
||||
Run a test, and fail.
|
||||
"""
|
||||
results = []
|
||||
failClass = fullyQualifiedName(RuntimeError)
|
||||
d = self.worker.callRemote(managercommands.AddFailure,
|
||||
testName=self.testName, fail='fail',
|
||||
failClass=failClass,
|
||||
frames=[])
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertEqual(self.testCase, self.result.failures[0][0])
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_runSkip(self):
|
||||
"""
|
||||
Run a test, but skip it.
|
||||
"""
|
||||
results = []
|
||||
|
||||
d = self.worker.callRemote(managercommands.AddSkip,
|
||||
testName=self.testName, reason='reason')
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertEqual(self.testCase, self.result.skips[0][0])
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_runUnexpectedSuccesses(self):
|
||||
"""
|
||||
Run a test, and succeed unexpectedly.
|
||||
"""
|
||||
results = []
|
||||
|
||||
d = self.worker.callRemote(managercommands.AddUnexpectedSuccess,
|
||||
testName=self.testName,
|
||||
todo='todo')
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertEqual(self.testCase, self.result.unexpectedSuccesses[0][0])
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_testWrite(self):
|
||||
"""
|
||||
L{LocalWorkerAMP.testWrite} writes the data received to its test
|
||||
stream.
|
||||
"""
|
||||
results = []
|
||||
stream = NativeStringIO()
|
||||
self.managerAMP.setTestStream(stream)
|
||||
|
||||
command = managercommands.TestWrite
|
||||
d = self.worker.callRemote(command,
|
||||
out="Some output")
|
||||
d.addCallback(lambda result: results.append(result['success']))
|
||||
self.pumpTransports()
|
||||
|
||||
self.assertEqual("Some output\n", stream.getvalue())
|
||||
self.assertTrue(results)
|
||||
|
||||
|
||||
def test_stopAfterRun(self):
|
||||
"""
|
||||
L{LocalWorkerAMP.run} calls C{stopTest} on its test result once the
|
||||
C{Run} commands has succeeded.
|
||||
"""
|
||||
result = object()
|
||||
stopped = []
|
||||
|
||||
def fakeCallRemote(command, testCase):
|
||||
return succeed(result)
|
||||
|
||||
self.managerAMP.callRemote = fakeCallRemote
|
||||
|
||||
class StopTestResult(TestResult):
|
||||
|
||||
def stopTest(self, test):
|
||||
stopped.append(test)
|
||||
|
||||
|
||||
d = self.managerAMP.run(self.testCase, StopTestResult())
|
||||
self.assertEqual([self.testCase], stopped)
|
||||
return d.addCallback(self.assertIdentical, result)
|
||||
|
||||
|
||||
|
||||
class FakeAMProtocol(AMP):
|
||||
"""
|
||||
A fake implementation of L{AMP} for testing.
|
||||
"""
|
||||
id = 0
|
||||
dataString = b""
|
||||
|
||||
def dataReceived(self, data):
|
||||
self.dataString += data
|
||||
|
||||
|
||||
def setTestStream(self, stream):
|
||||
self.testStream = stream
|
||||
|
||||
|
||||
|
||||
class FakeTransport(object):
|
||||
"""
|
||||
A fake process transport implementation for testing.
|
||||
"""
|
||||
dataString = b""
|
||||
calls = 0
|
||||
|
||||
def writeToChild(self, fd, data):
|
||||
self.dataString += data
|
||||
|
||||
|
||||
def loseConnection(self):
|
||||
self.calls += 1
|
||||
|
||||
|
||||
|
||||
class LocalWorkerTests(TestCase):
|
||||
"""
|
||||
Tests for L{LocalWorker} and L{LocalWorkerTransport}.
|
||||
"""
|
||||
|
||||
def tidyLocalWorker(self, *args, **kwargs):
|
||||
"""
|
||||
Create a L{LocalWorker}, connect it to a transport, and ensure
|
||||
its log files are closed.
|
||||
|
||||
@param args: See L{LocalWorker}
|
||||
|
||||
@param kwargs: See L{LocalWorker}
|
||||
|
||||
@return: a L{LocalWorker} instance
|
||||
"""
|
||||
worker = LocalWorker(*args, **kwargs)
|
||||
worker.makeConnection(FakeTransport())
|
||||
self.addCleanup(worker._testLog.close)
|
||||
self.addCleanup(worker._outLog.close)
|
||||
self.addCleanup(worker._errLog.close)
|
||||
return worker
|
||||
|
||||
|
||||
def test_childDataReceived(self):
|
||||
"""
|
||||
L{LocalWorker.childDataReceived} forwards the received data to linked
|
||||
L{AMP} protocol if the right file descriptor, otherwise forwards to
|
||||
C{ProcessProtocol.childDataReceived}.
|
||||
"""
|
||||
localWorker = self.tidyLocalWorker(FakeAMProtocol(), '.', 'test.log')
|
||||
localWorker._outLog = BytesIO()
|
||||
localWorker.childDataReceived(4, b"foo")
|
||||
localWorker.childDataReceived(1, b"bar")
|
||||
self.assertEqual(b"foo", localWorker._ampProtocol.dataString)
|
||||
self.assertEqual(b"bar", localWorker._outLog.getvalue())
|
||||
|
||||
|
||||
def test_outReceived(self):
|
||||
"""
|
||||
L{LocalWorker.outReceived} logs the output into its C{_outLog} log
|
||||
file.
|
||||
"""
|
||||
localWorker = self.tidyLocalWorker(FakeAMProtocol(), '.', 'test.log')
|
||||
localWorker._outLog = BytesIO()
|
||||
data = b"The quick brown fox jumps over the lazy dog"
|
||||
localWorker.outReceived(data)
|
||||
self.assertEqual(data, localWorker._outLog.getvalue())
|
||||
|
||||
|
||||
def test_errReceived(self):
|
||||
"""
|
||||
L{LocalWorker.errReceived} logs the errors into its C{_errLog} log
|
||||
file.
|
||||
"""
|
||||
localWorker = self.tidyLocalWorker(FakeAMProtocol(), '.', 'test.log')
|
||||
localWorker._errLog = BytesIO()
|
||||
data = b"The quick brown fox jumps over the lazy dog"
|
||||
localWorker.errReceived(data)
|
||||
self.assertEqual(data, localWorker._errLog.getvalue())
|
||||
|
||||
|
||||
def test_write(self):
|
||||
"""
|
||||
L{LocalWorkerTransport.write} forwards the written data to the given
|
||||
transport.
|
||||
"""
|
||||
transport = FakeTransport()
|
||||
localTransport = LocalWorkerTransport(transport)
|
||||
data = b"The quick brown fox jumps over the lazy dog"
|
||||
localTransport.write(data)
|
||||
self.assertEqual(data, transport.dataString)
|
||||
|
||||
|
||||
def test_writeSequence(self):
|
||||
"""
|
||||
L{LocalWorkerTransport.writeSequence} forwards the written data to the
|
||||
given transport.
|
||||
"""
|
||||
transport = FakeTransport()
|
||||
localTransport = LocalWorkerTransport(transport)
|
||||
data = (b"The quick ", b"brown fox jumps ", b"over the lazy dog")
|
||||
localTransport.writeSequence(data)
|
||||
self.assertEqual(b"".join(data), transport.dataString)
|
||||
|
||||
|
||||
def test_loseConnection(self):
|
||||
"""
|
||||
L{LocalWorkerTransport.loseConnection} forwards the call to the given
|
||||
transport.
|
||||
"""
|
||||
transport = FakeTransport()
|
||||
localTransport = LocalWorkerTransport(transport)
|
||||
localTransport.loseConnection()
|
||||
|
||||
self.assertEqual(transport.calls, 1)
|
||||
|
||||
|
||||
def test_connectionLost(self):
|
||||
"""
|
||||
L{LocalWorker.connectionLost} closes the log streams.
|
||||
"""
|
||||
|
||||
localWorker = self.tidyLocalWorker(FakeAMProtocol(), '.', 'test.log')
|
||||
localWorker.connectionLost(None)
|
||||
self.assertTrue(localWorker._outLog.closed)
|
||||
self.assertTrue(localWorker._errLog.closed)
|
||||
self.assertTrue(localWorker._testLog.closed)
|
||||
|
||||
|
||||
def test_processEnded(self):
|
||||
"""
|
||||
L{LocalWorker.processEnded} calls C{connectionLost} on itself and on
|
||||
the L{AMP} protocol.
|
||||
"""
|
||||
|
||||
transport = FakeTransport()
|
||||
protocol = FakeAMProtocol()
|
||||
localWorker = LocalWorker(protocol, '.', 'test.log')
|
||||
localWorker.makeConnection(transport)
|
||||
localWorker.processEnded(Failure(CONNECTION_DONE))
|
||||
self.assertTrue(localWorker._outLog.closed)
|
||||
self.assertTrue(localWorker._errLog.closed)
|
||||
self.assertTrue(localWorker._testLog.closed)
|
||||
self.assertIdentical(None, protocol.transport)
|
||||
return self.assertFailure(localWorker.endDeferred, ConnectionDone)
|
||||
|
||||
|
||||
def test_addresses(self):
|
||||
"""
|
||||
L{LocalWorkerTransport.getPeer} and L{LocalWorkerTransport.getHost}
|
||||
return L{IAddress} objects.
|
||||
"""
|
||||
localTransport = LocalWorkerTransport(None)
|
||||
self.assertTrue(verifyObject(IAddress, localTransport.getPeer()))
|
||||
self.assertTrue(verifyObject(IAddress, localTransport.getHost()))
|
||||
|
||||
|
||||
def test_transport(self):
|
||||
"""
|
||||
L{LocalWorkerTransport} implements L{ITransport} to be able to be used
|
||||
by L{AMP}.
|
||||
"""
|
||||
localTransport = LocalWorkerTransport(None)
|
||||
self.assertTrue(verifyObject(ITransport, localTransport))
|
||||
|
||||
|
||||
def test_startError(self):
|
||||
"""
|
||||
L{LocalWorker} swallows the exceptions returned by the L{AMP} protocol
|
||||
start method, as it generates unnecessary errors.
|
||||
"""
|
||||
|
||||
def failCallRemote(command, directory):
|
||||
return fail(RuntimeError("oops"))
|
||||
|
||||
protocol = FakeAMProtocol()
|
||||
protocol.callRemote = failCallRemote
|
||||
self.tidyLocalWorker(protocol, '.', 'test.log')
|
||||
|
||||
self.assertEqual([], self.flushLoggedErrors(RuntimeError))
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.trial._dist.workerreporter}.
|
||||
"""
|
||||
|
||||
from twisted.python.compat import _PY3
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.trial.unittest import TestCase, Todo
|
||||
from twisted.trial._dist.workerreporter import WorkerReporter
|
||||
from twisted.trial._dist import managercommands
|
||||
|
||||
|
||||
class FakeAMProtocol(object):
|
||||
"""
|
||||
A fake C{AMP} implementations to track C{callRemote} calls.
|
||||
"""
|
||||
id = 0
|
||||
lastCall = None
|
||||
|
||||
def callRemote(self, command, **kwargs):
|
||||
self.lastCall = command
|
||||
self.lastArgs = kwargs
|
||||
|
||||
|
||||
|
||||
class WorkerReporterTests(TestCase):
|
||||
"""
|
||||
Tests for L{WorkerReporter}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.fakeAMProtocol = FakeAMProtocol()
|
||||
self.workerReporter = WorkerReporter(self.fakeAMProtocol)
|
||||
self.test = TestCase()
|
||||
|
||||
|
||||
def test_addSuccess(self):
|
||||
"""
|
||||
L{WorkerReporter.addSuccess} sends a L{managercommands.AddSuccess}
|
||||
command.
|
||||
"""
|
||||
self.workerReporter.addSuccess(self.test)
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddSuccess)
|
||||
|
||||
|
||||
def test_addError(self):
|
||||
"""
|
||||
L{WorkerReporter.addError} sends a L{managercommands.AddError} command.
|
||||
"""
|
||||
self.workerReporter.addError(self.test, Failure(RuntimeError('error')))
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddError)
|
||||
|
||||
|
||||
def test_addErrorTuple(self):
|
||||
"""
|
||||
Adding an error using L{WorkerReporter.addError} as a
|
||||
C{sys.exc_info}-style tuple sends an L{managercommands.AddError}
|
||||
command.
|
||||
"""
|
||||
self.workerReporter.addError(
|
||||
self.test, (RuntimeError, RuntimeError('error'), None))
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddError)
|
||||
|
||||
|
||||
def test_addFailure(self):
|
||||
"""
|
||||
L{WorkerReporter.addFailure} sends a L{managercommands.AddFailure}
|
||||
command.
|
||||
"""
|
||||
self.workerReporter.addFailure(self.test,
|
||||
Failure(RuntimeError('fail')))
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddFailure)
|
||||
|
||||
|
||||
def test_addFailureTuple(self):
|
||||
"""
|
||||
Adding a failure using L{WorkerReporter.addFailure} as a
|
||||
C{sys.exc_info}-style tuple sends an L{managercommands.AddFailure}
|
||||
message.
|
||||
"""
|
||||
self.workerReporter.addFailure(
|
||||
self.test, (RuntimeError, RuntimeError('fail'), None))
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddFailure)
|
||||
|
||||
|
||||
def test_addFailureNonASCII(self):
|
||||
"""
|
||||
L{WorkerReporter.addFailure} sends a L{managercommands.AddFailure}
|
||||
message when called with a L{Failure}, even if it includes encoded
|
||||
non-ASCII content.
|
||||
"""
|
||||
content = u"\N{SNOWMAN}".encode("utf-8")
|
||||
exception = RuntimeError(content)
|
||||
failure = Failure(exception)
|
||||
self.workerReporter.addFailure(self.test, failure)
|
||||
self.assertEqual(
|
||||
self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddFailure,
|
||||
)
|
||||
self.assertEqual(
|
||||
content,
|
||||
self.fakeAMProtocol.lastArgs["fail"],
|
||||
)
|
||||
if _PY3:
|
||||
test_addFailureNonASCII.skip = (
|
||||
"Exceptions only convert to unicode on Python 3"
|
||||
)
|
||||
|
||||
|
||||
def test_addSkip(self):
|
||||
"""
|
||||
L{WorkerReporter.addSkip} sends a L{managercommands.AddSkip} command.
|
||||
"""
|
||||
self.workerReporter.addSkip(self.test, 'reason')
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddSkip)
|
||||
|
||||
|
||||
def test_addExpectedFailure(self):
|
||||
"""
|
||||
L{WorkerReporter.addExpectedFailure} sends a
|
||||
L{managercommands.AddExpectedFailure} command.
|
||||
protocol.
|
||||
"""
|
||||
self.workerReporter.addExpectedFailure(
|
||||
self.test, Failure(RuntimeError('error')), Todo('todo'))
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddExpectedFailure)
|
||||
|
||||
|
||||
def test_addExpectedFailureNoTodo(self):
|
||||
"""
|
||||
L{WorkerReporter.addExpectedFailure} sends a
|
||||
L{managercommands.AddExpectedFailure} command.
|
||||
protocol.
|
||||
"""
|
||||
self.workerReporter.addExpectedFailure(
|
||||
self.test, Failure(RuntimeError('error')))
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddExpectedFailure)
|
||||
|
||||
|
||||
def test_addUnexpectedSuccess(self):
|
||||
"""
|
||||
L{WorkerReporter.addUnexpectedSuccess} sends a
|
||||
L{managercommands.AddUnexpectedSuccess} command.
|
||||
"""
|
||||
self.workerReporter.addUnexpectedSuccess(self.test, Todo('todo'))
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddUnexpectedSuccess)
|
||||
|
||||
|
||||
def test_addUnexpectedSuccessNoTodo(self):
|
||||
"""
|
||||
L{WorkerReporter.addUnexpectedSuccess} sends a
|
||||
L{managercommands.AddUnexpectedSuccess} command.
|
||||
"""
|
||||
self.workerReporter.addUnexpectedSuccess(self.test)
|
||||
self.assertEqual(self.fakeAMProtocol.lastCall,
|
||||
managercommands.AddUnexpectedSuccess)
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.trial._dist.workertrial}.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import sys
|
||||
import os
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
from twisted.protocols.amp import AMP
|
||||
from twisted.test.proto_helpers import StringTransport
|
||||
from twisted.trial.unittest import TestCase
|
||||
from twisted.trial._dist.workertrial import WorkerLogObserver, main, _setupPath
|
||||
from twisted.trial._dist import (
|
||||
workertrial, _WORKER_AMP_STDIN, _WORKER_AMP_STDOUT, workercommands,
|
||||
managercommands)
|
||||
|
||||
|
||||
|
||||
class FakeAMP(AMP):
|
||||
"""
|
||||
A fake amp protocol.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class WorkerLogObserverTests(TestCase):
|
||||
"""
|
||||
Tests for L{WorkerLogObserver}.
|
||||
"""
|
||||
|
||||
def test_emit(self):
|
||||
"""
|
||||
L{WorkerLogObserver} forwards data to L{managercommands.TestWrite}.
|
||||
"""
|
||||
calls = []
|
||||
|
||||
class FakeClient(object):
|
||||
|
||||
def callRemote(self, method, **kwargs):
|
||||
calls.append((method, kwargs))
|
||||
|
||||
observer = WorkerLogObserver(FakeClient())
|
||||
observer.emit({'message': ['Some log']})
|
||||
self.assertEqual(
|
||||
calls, [(managercommands.TestWrite, {'out': 'Some log'})])
|
||||
|
||||
|
||||
|
||||
class MainTests(TestCase):
|
||||
"""
|
||||
Tests for L{main}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.readStream = BytesIO()
|
||||
self.writeStream = BytesIO()
|
||||
self.patch(workertrial, 'startLoggingWithObserver',
|
||||
self.startLoggingWithObserver)
|
||||
self.addCleanup(setattr, sys, "argv", sys.argv)
|
||||
sys.argv = ["trial"]
|
||||
|
||||
|
||||
def fdopen(self, fd, mode=None):
|
||||
"""
|
||||
Fake C{os.fdopen} implementation which returns C{self.readStream} for
|
||||
the stdin fd and C{self.writeStream} for the stdout fd.
|
||||
"""
|
||||
if fd == _WORKER_AMP_STDIN:
|
||||
self.assertIdentical('rb', mode)
|
||||
return self.readStream
|
||||
elif fd == _WORKER_AMP_STDOUT:
|
||||
self.assertEqual('wb', mode)
|
||||
return self.writeStream
|
||||
else:
|
||||
raise AssertionError("Unexpected fd %r" % (fd,))
|
||||
|
||||
|
||||
def startLoggingWithObserver(self, emit, setStdout):
|
||||
"""
|
||||
Override C{startLoggingWithObserver} for not starting logging.
|
||||
"""
|
||||
self.assertFalse(setStdout)
|
||||
|
||||
|
||||
def test_empty(self):
|
||||
"""
|
||||
If no data is ever written, L{main} exits without writing data out.
|
||||
"""
|
||||
main(self.fdopen)
|
||||
self.assertEqual(b'', self.writeStream.getvalue())
|
||||
|
||||
|
||||
def test_forwardCommand(self):
|
||||
"""
|
||||
L{main} forwards data from its input stream to a L{WorkerProtocol}
|
||||
instance which writes data to the output stream.
|
||||
"""
|
||||
client = FakeAMP()
|
||||
clientTransport = StringTransport()
|
||||
client.makeConnection(clientTransport)
|
||||
client.callRemote(workercommands.Run, testCase="doesntexist")
|
||||
self.readStream = clientTransport.io
|
||||
self.readStream.seek(0, 0)
|
||||
main(self.fdopen)
|
||||
self.assertIn(
|
||||
b"No module named 'doesntexist'", self.writeStream.getvalue())
|
||||
|
||||
|
||||
def test_readInterrupted(self):
|
||||
"""
|
||||
If reading the input stream fails with a C{IOError} with errno
|
||||
C{EINTR}, L{main} ignores it and continues reading.
|
||||
"""
|
||||
excInfos = []
|
||||
|
||||
class FakeStream(object):
|
||||
count = 0
|
||||
|
||||
def read(oself, size):
|
||||
oself.count += 1
|
||||
if oself.count == 1:
|
||||
raise IOError(errno.EINTR)
|
||||
else:
|
||||
excInfos.append(sys.exc_info())
|
||||
return b''
|
||||
|
||||
self.readStream = FakeStream()
|
||||
main(self.fdopen)
|
||||
self.assertEqual(b'', self.writeStream.getvalue())
|
||||
self.assertEqual([(None, None, None)], excInfos)
|
||||
|
||||
|
||||
def test_otherReadError(self):
|
||||
"""
|
||||
L{main} only ignores C{IOError} with C{EINTR} errno: otherwise, the
|
||||
error pops out.
|
||||
"""
|
||||
|
||||
class FakeStream(object):
|
||||
count = 0
|
||||
|
||||
def read(oself, size):
|
||||
oself.count += 1
|
||||
if oself.count == 1:
|
||||
raise IOError("Something else")
|
||||
return ''
|
||||
|
||||
self.readStream = FakeStream()
|
||||
self.assertRaises(IOError, main, self.fdopen)
|
||||
|
||||
|
||||
|
||||
class SetupPathTests(TestCase):
|
||||
"""
|
||||
Tests for L{_setupPath} C{sys.path} manipulation.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.addCleanup(setattr, sys, "path", sys.path[:])
|
||||
|
||||
|
||||
def test_overridePath(self):
|
||||
"""
|
||||
L{_setupPath} overrides C{sys.path} if B{TRIAL_PYTHONPATH} is specified
|
||||
in the environment.
|
||||
"""
|
||||
environ = {"TRIAL_PYTHONPATH": os.pathsep.join(["foo", "bar"])}
|
||||
_setupPath(environ)
|
||||
self.assertEqual(["foo", "bar"], sys.path)
|
||||
|
||||
|
||||
def test_noVariable(self):
|
||||
"""
|
||||
L{_setupPath} doesn't change C{sys.path} if B{TRIAL_PYTHONPATH} is not
|
||||
present in the environment.
|
||||
"""
|
||||
originalPath = sys.path[:]
|
||||
_setupPath({})
|
||||
self.assertEqual(originalPath, sys.path)
|
||||
Loading…
Add table
Add a link
Reference in a new issue