2 # subunit: extensions to python unittest to get test results from subprocesses.
3 # Copyright (C) 2005 Robert Collins <robertc@robertcollins.net>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 return subunit.tests.test_suite()
27 class TestProtocolServer(object):
28 """A class for recieving results from a TestProtocol client."""
36 self.state = TestProtocolServer.OUTSIDE_TEST
38 def _addError(self, offset, line):
39 if (self.state == TestProtocolServer.TEST_STARTED and
40 self.current_test_description == line[offset:-1]):
41 self.state = TestProtocolServer.OUTSIDE_TEST
42 self.current_test_description = None
43 self.addError(self._current_test, RemoteError(""))
44 elif (self.state == TestProtocolServer.TEST_STARTED and
45 self.current_test_description + " [" == line[offset:-1]):
46 self.state = TestProtocolServer.READING_ERROR
49 self.stdOutLineRecieved(line)
51 def _addFailure(self, offset, line):
52 if (self.state == TestProtocolServer.TEST_STARTED and
53 self.current_test_description == line[offset:-1]):
54 self.state = TestProtocolServer.OUTSIDE_TEST
55 self.current_test_description = None
56 self.addFailure(self._current_test, RemoteError())
57 elif (self.state == TestProtocolServer.TEST_STARTED and
58 self.current_test_description + " [" == line[offset:-1]):
59 self.state = TestProtocolServer.READING_FAILURE
62 self.stdOutLineRecieved(line)
64 def _addSuccess(self, offset, line):
65 if (self.state == TestProtocolServer.TEST_STARTED and
66 self.current_test_description == line[offset:-1]):
67 self.addSuccess(self._current_test)
68 self.current_test_description = None
69 self._current_test = None
70 self.state = TestProtocolServer.OUTSIDE_TEST
72 self.stdOutLineRecieved(line)
74 def _appendMessage(self, line):
77 self._message += line[1:]
81 def endQuote(self, line):
82 if self.state == TestProtocolServer.READING_FAILURE:
83 self.state = TestProtocolServer.OUTSIDE_TEST
84 self.current_test_description = None
85 self.addFailure(self._current_test, RemoteError(self._message))
86 elif self.state == TestProtocolServer.READING_ERROR:
87 self.state = TestProtocolServer.OUTSIDE_TEST
88 self.current_test_description = None
89 self.addError(self._current_test, RemoteError(self._message))
91 self.stdOutLineRecieved(line)
93 def lineReceived(self, line):
94 """Call the appropriate local method for the recieved line."""
97 elif (self.state == TestProtocolServer.READING_FAILURE or
98 self.state == TestProtocolServer.READING_ERROR):
99 self._appendMessage(line)
100 elif line.startswith("test:"):
101 self._startTest(6, line)
102 elif line.startswith("testing:"):
103 self._startTest(9, line)
104 elif line.startswith("testing"):
105 self._startTest(8, line)
106 elif line.startswith("test"):
107 self._startTest(5, line)
108 elif line.startswith("error:"):
109 self._addError(7, line)
110 elif line.startswith("error"):
111 self._addError(6, line)
112 elif line.startswith("failure:"):
113 self._addFailure(9, line)
114 elif line.startswith("failure"):
115 self._addFailure(8, line)
116 elif line.startswith("successful:"):
117 self._addSuccess(12, line)
118 elif line.startswith("successful"):
119 self._addSuccess(11, line)
120 elif line.startswith("success:"):
121 self._addSuccess(9, line)
122 elif line.startswith("success"):
123 self._addSuccess(8, line)
125 self.stdOutLineRecieved(line)
127 def lostConnection(self):
128 """The input connection has finished."""
129 if self.state == TestProtocolServer.TEST_STARTED:
130 self.addError(self._current_test,
131 RemoteError("lost connection during test '%s'"
132 % self.current_test_description))
133 elif self.state == TestProtocolServer.READING_ERROR:
134 self.addError(self._current_test,
135 RemoteError("lost connection during "
136 "error report of test "
137 "'%s'" % self.current_test_description))
138 elif self.state == TestProtocolServer.READING_FAILURE:
139 self.addError(self._current_test,
140 RemoteError("lost connection during "
141 "failure report of test "
142 "'%s'" % self.current_test_description))
144 def readFrom(self, pipe):
145 for line in pipe.readlines(pipe):
146 self.lineReceived(line)
147 self.lostConnection()
149 def _startTest(self, offset, line):
150 """Internal call to change state machine. Override startTest()."""
151 if self.state == TestProtocolServer.OUTSIDE_TEST:
152 self.state = TestProtocolServer.TEST_STARTED
153 self._current_test = RemotedTestCase(line[offset:-1])
154 self.current_test_description = line[offset:-1]
155 self.startTest(self._current_test)
157 self.stdOutLineRecieved(line)
159 def stdOutLineRecieved(self, line):
160 sys.stdout.write(line)
163 class RemoteException(Exception):
164 """An exception that occured remotely to python."""
166 def __eq__(self, other):
168 return self.args == other.args
169 except AttributeError:
173 def RemoteError(description=""):
174 if description == "":
176 return (RemoteException("RemoteError:\n%s" % description), None, None)
179 class RemotedTestCase(unittest.TestCase):
180 """A class to represent test cases run in child processes."""
182 def __eq__ (self, other):
184 return self.__description == other.__description
185 except AttributeError:
188 def __init__(self, description):
189 """Create a psuedo test case with description description."""
190 self.__description = description
192 def error(self, label):
193 raise NotImplementedError("%s on RemotedTestCases is not permitted." %
200 self.error("tearDown")
202 def shortDescription(self):
203 return self.__description
206 return "%s.%s" % (self._strclass(), self.__description)
209 return "%s (%s)" % (self.__description, self._strclass())
212 return "<%s description='%s'>" % \
213 (self._strclass(), self.__description)
215 def run(self, result=None):
216 if result is None: result = self.defaultTestResult()
217 result.startTest(self)
218 result.addError(self, RemoteError("Cannot run RemotedTestCases.\n"))
219 result.stopTest(self)
223 return "%s.%s" % (cls.__module__, cls.__name__)