1 """logtest, a unittest.TestCase helper for testing log output."""
10 # On Windows, msvcrt.getch reads a single char without output.
18 fd
= sys
.stdin
.fileno()
19 old_settings
= termios
.tcgetattr(fd
)
21 tty
.setraw(sys
.stdin
.fileno())
22 ch
= sys
.stdin
.read(1)
24 termios
.tcsetattr(fd
, termios
.TCSADRAIN
, old_settings
)
28 class LogCase(object):
29 """unittest.TestCase mixin for testing log messages.
31 logfile: a filename for the desired log. Yes, I know modes are evil,
32 but it makes the test functions so much cleaner to set this once.
34 lastmarker: the last marker in the log. This can be used to search for
35 messages since the last marker.
37 markerPrefix: a string with which to prefix log markers. This should be
38 unique enough from normal log output to use for marker identification.
43 markerPrefix
= "test suite marker: "
45 def _handleLogError(self
, msg
, data
, marker
, pattern
):
47 print(" ERROR: %s" % msg
)
49 if not self
.interactive
:
50 raise self
.failureException(msg
)
52 p
= " Show: [L]og [M]arker [P]attern; [I]gnore, [R]aise, or sys.e[X]it >> "
60 print(i
.upper()) # Also prints new line
62 for x
, line
in enumerate(data
):
63 if (x
+ 1) % self
.console_height
== 0:
64 # The \r and comma should make the next line overwrite
65 print "<-- More -->\r",
67 # Erase our "More" prompt
73 print(repr(marker
or self
.lastmarker
))
77 # return without raising the normal exception
80 raise self
.failureException(msg
)
89 """Overwrite self.logfile with 0 bytes."""
90 open(self
.logfile
, 'wb').write("")
92 def markLog(self
, key
=None):
93 """Insert a marker line into the log and set self.lastmarker."""
95 key
= str(time
.time())
98 open(self
.logfile
, 'ab+').write("%s%s\n" % (self
.markerPrefix
, key
))
100 def _read_marked_region(self
, marker
=None):
101 """Return lines from self.logfile in the marked region.
103 If marker is None, self.lastmarker is used. If the log hasn't
104 been marked (using self.markLog), the entire log will be returned.
106 ## # Give the logger time to finish writing?
109 logfile
= self
.logfile
110 marker
= marker
or self
.lastmarker
112 return open(logfile
, 'rb').readlines()
116 for line
in open(logfile
, 'rb'):
118 if (line
.startswith(self
.markerPrefix
) and not marker
in line
):
126 def assertInLog(self
, line
, marker
=None):
127 """Fail if the given (partial) line is not in the log.
129 The log will be searched from the given marker to the next marker.
130 If marker is None, self.lastmarker is used. If the log hasn't
131 been marked (using self.markLog), the entire log will be searched.
133 data
= self
._read
_marked
_region
(marker
)
137 msg
= "%r not found in log" % line
138 self
._handleLogError
(msg
, data
, marker
, line
)
140 def assertNotInLog(self
, line
, marker
=None):
141 """Fail if the given (partial) line is in the log.
143 The log will be searched from the given marker to the next marker.
144 If marker is None, self.lastmarker is used. If the log hasn't
145 been marked (using self.markLog), the entire log will be searched.
147 data
= self
._read
_marked
_region
(marker
)
150 msg
= "%r found in log" % line
151 self
._handleLogError
(msg
, data
, marker
, line
)
153 def assertLog(self
, sliceargs
, lines
, marker
=None):
154 """Fail if log.readlines()[sliceargs] is not contained in 'lines'.
156 The log will be searched from the given marker to the next marker.
157 If marker is None, self.lastmarker is used. If the log hasn't
158 been marked (using self.markLog), the entire log will be searched.
160 data
= self
._read
_marked
_region
(marker
)
161 if isinstance(sliceargs
, int):
162 # Single arg. Use __getitem__ and allow lines to be str or list.
163 if isinstance(lines
, (tuple, list)):
165 if lines
not in data
[sliceargs
]:
166 msg
= "%r not found on log line %r" % (lines
, sliceargs
)
167 self
._handleLogError
(msg
, [data
[sliceargs
]], marker
, lines
)
169 # Multiple args. Use __getslice__ and require lines to be list.
170 if isinstance(lines
, tuple):
172 elif isinstance(lines
, basestring
):
173 raise TypeError("The 'lines' arg must be a list when "
174 "'sliceargs' is a tuple.")
176 start
, stop
= sliceargs
177 for line
, logline
in zip(lines
, data
[start
:stop
]):
178 if line
not in logline
:
179 msg
= "%r not found in log" % line
180 self
._handleLogError
(msg
, data
[start
:stop
], marker
, line
)