4 # Copyright (c) 2015 Remko Tronçon (https://el-tramo.be)
5 # Copied from https://github.com/remko/pycotap/
7 # Released under the MIT license
9 # Permission is hereby granted, free of charge, to any person obtaining a copy
10 # of this software and associated documentation files (the "Software"), to deal
11 # in the Software without restriction, including without limitation the rights
12 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 # copies of the Software, and to permit persons to whom the Software is
14 # furnished to do so, subject to the following conditions:
16 # The above copyright notice and this permission notice shall be included in
17 # all copies or substantial portions of the Software.
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 if sys
.hexversion
>= 0x03000000:
32 from io
import StringIO
34 from StringIO
import StringIO
37 class LogMode(object) :
38 LogToError
, LogToDiagnostics
, LogToYAML
, LogToAttachment
= range(4)
41 class TAPTestResult(unittest
.TestResult
):
42 def __init__(self
, output_stream
, error_stream
, message_log
, test_output_log
):
43 super(TAPTestResult
, self
).__init
__(self
, output_stream
)
44 self
.output_stream
= output_stream
45 self
.error_stream
= error_stream
46 self
.orig_stdout
= None
47 self
.orig_stderr
= None
49 self
.test_output
= None
50 self
.message_log
= message_log
51 self
.test_output_log
= test_output_log
52 self
.output_stream
.write("TAP version 13\n")
55 def printErrors(self
):
56 self
.print_raw("1..%d\n" % self
.testsRun
)
59 def _set_streams(self
):
60 self
.orig_stdout
= sys
.stdout
61 self
.orig_stderr
= sys
.stderr
62 if self
.message_log
== LogMode
.LogToError
:
63 self
.message
= self
.error_stream
65 self
.message
= StringIO()
66 if self
.test_output_log
== LogMode
.LogToError
:
67 self
.test_output
= self
.error_stream
69 self
.test_output
= StringIO()
71 if self
.message_log
== self
.test_output_log
:
72 self
.test_output
= self
.message
73 sys
.stdout
= sys
.stderr
= self
.test_output
75 def _reset_streams(self
):
76 sys
.stdout
= self
.orig_stdout
77 sys
.stderr
= self
.orig_stderr
80 def print_raw(self
, text
):
81 self
.output_stream
.write(text
)
82 self
.output_stream
.flush()
84 def print_result(self
, result
, test
, directive
= None):
85 self
.output_stream
.write("%s %d %s" % (result
, self
.testsRun
, test
.id()))
87 self
.output_stream
.write(" # " + directive
)
88 self
.output_stream
.write("\n")
89 self
.output_stream
.flush()
91 def ok(self
, test
, directive
= None):
92 self
.print_result("ok", test
, directive
)
94 def not_ok(self
, test
):
95 self
.print_result("not ok", test
)
97 def startTest(self
, test
):
98 super(TAPTestResult
, self
).startTest(test
)
100 def stopTest(self
, test
):
101 super(TAPTestResult
, self
).stopTest(test
)
102 if self
.message_log
== self
.test_output_log
:
103 logs
= [(self
.message_log
, self
.message
, "output")]
106 (self
.test_output_log
, self
.test_output
, "test_output"),
107 (self
.message_log
, self
.message
, "message")
109 for log_mode
, log
, log_name
in logs
:
110 if log_mode
!= LogMode
.LogToError
:
111 output
= log
.getvalue()
113 if log_mode
== LogMode
.LogToYAML
:
114 self
.print_raw(" ---\n")
115 self
.print_raw(" " + log_name
+ ": |\n")
116 self
.print_raw(" " + output
.rstrip().replace("\n", "\n ") + "\n")
117 self
.print_raw(" ...\n")
118 elif log_mode
== LogMode
.LogToAttachment
:
119 self
.print_raw(" ---\n")
120 self
.print_raw(" " + log_name
+ ":\n")
121 self
.print_raw(" File-Name: " + log_name
+ ".txt\n")
122 self
.print_raw(" File-Type: text/plain\n")
123 self
.print_raw(" File-Content: " + base64
.b64encode(output
) + "\n")
124 self
.print_raw(" ...\n")
126 self
.print_raw("# " + output
.rstrip().replace("\n", "\n# ") + "\n")
129 def addSuccess(self
, test
):
130 super(TAPTestResult
, self
).addSuccess(test
)
133 def addError(self
, test
, err
):
134 super(TAPTestResult
, self
).addError(test
, err
)
135 self
.message
.write(self
.errors
[-1][1] + "\n")
138 def addFailure(self
, test
, err
):
139 super(TAPTestResult
, self
).addFailure(test
, err
)
140 self
.message
.write(self
.failures
[-1][1] + "\n")
143 def addSkip(self
, test
, reason
):
144 super(TAPTestResult
, self
).addSkip(test
, reason
)
145 self
.ok(test
, "SKIP " + reason
)
147 def addExpectedFailure(self
, test
, err
):
148 super(TAPTestResult
, self
).addExpectedFailure(test
, err
)
151 def addUnexpectedSuccess(self
, test
):
152 super(TAPTestResult
, self
).addUnexpectedSuccess(test
)
153 self
.message
.write("Unexpected success" + "\n")
157 class TAPTestRunner(object):
159 message_log
= LogMode
.LogToYAML
,
160 test_output_log
= LogMode
.LogToDiagnostics
,
161 output_stream
= sys
.stdout
, error_stream
= sys
.stderr
):
162 self
.output_stream
= output_stream
163 self
.error_stream
= error_stream
164 self
.message_log
= message_log
165 self
.test_output_log
= test_output_log
168 result
= TAPTestResult(
172 self
.test_output_log
)