Package cherrypy :: Package test :: Module logtest
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.test.logtest

  1  """logtest, a unittest.TestCase helper for testing log output.""" 
  2   
  3  import sys 
  4  import time 
  5   
  6  import cherrypy 
  7  from cherrypy._cpcompat import basestring, ntob, unicodestr 
  8   
  9   
 10  try: 
 11      # On Windows, msvcrt.getch reads a single char without output. 
 12      import msvcrt 
13 - def getchar():
14 return msvcrt.getch()
15 except ImportError: 16 # Unix getchr 17 import tty, termios
18 - def getchar():
19 fd = sys.stdin.fileno() 20 old_settings = termios.tcgetattr(fd) 21 try: 22 tty.setraw(sys.stdin.fileno()) 23 ch = sys.stdin.read(1) 24 finally: 25 termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 26 return ch
27 28
29 -class LogCase(object):
30 """unittest.TestCase mixin for testing log messages. 31 32 logfile: a filename for the desired log. Yes, I know modes are evil, 33 but it makes the test functions so much cleaner to set this once. 34 35 lastmarker: the last marker in the log. This can be used to search for 36 messages since the last marker. 37 38 markerPrefix: a string with which to prefix log markers. This should be 39 unique enough from normal log output to use for marker identification. 40 """ 41 42 logfile = None 43 lastmarker = None 44 markerPrefix = ntob("test suite marker: ") 45
46 - def _handleLogError(self, msg, data, marker, pattern):
47 print("") 48 print(" ERROR: %s" % msg) 49 50 if not self.interactive: 51 raise self.failureException(msg) 52 53 p = " Show: [L]og [M]arker [P]attern; [I]gnore, [R]aise, or sys.e[X]it >> " 54 sys.stdout.write(p + ' ') 55 # ARGH 56 sys.stdout.flush() 57 while True: 58 i = getchar().upper() 59 if i not in "MPLIRX": 60 continue 61 print(i.upper()) # Also prints new line 62 if i == "L": 63 for x, line in enumerate(data): 64 if (x + 1) % self.console_height == 0: 65 # The \r and comma should make the next line overwrite 66 sys.stdout.write("<-- More -->\r ") 67 m = getchar().lower() 68 # Erase our "More" prompt 69 sys.stdout.write(" \r ") 70 if m == "q": 71 break 72 print(line.rstrip()) 73 elif i == "M": 74 print(repr(marker or self.lastmarker)) 75 elif i == "P": 76 print(repr(pattern)) 77 elif i == "I": 78 # return without raising the normal exception 79 return 80 elif i == "R": 81 raise self.failureException(msg) 82 elif i == "X": 83 self.exit() 84 sys.stdout.write(p + ' ')
85
86 - def exit(self):
87 sys.exit()
88
89 - def emptyLog(self):
90 """Overwrite self.logfile with 0 bytes.""" 91 open(self.logfile, 'wb').write("")
92
93 - def markLog(self, key=None):
94 """Insert a marker line into the log and set self.lastmarker.""" 95 if key is None: 96 key = str(time.time()) 97 self.lastmarker = key 98 99 open(self.logfile, 'ab+').write(ntob("%s%s\n" % (self.markerPrefix, key),"utf-8"))
100
101 - def _read_marked_region(self, marker=None):
102 """Return lines from self.logfile in the marked region. 103 104 If marker is None, self.lastmarker is used. If the log hasn't 105 been marked (using self.markLog), the entire log will be returned. 106 """ 107 ## # Give the logger time to finish writing? 108 ## time.sleep(0.5) 109 110 logfile = self.logfile 111 marker = marker or self.lastmarker 112 if marker is None: 113 return open(logfile, 'rb').readlines() 114 115 if isinstance(marker, unicodestr): 116 marker = marker.encode('utf-8') 117 data = [] 118 in_region = False 119 for line in open(logfile, 'rb'): 120 if in_region: 121 if (line.startswith(self.markerPrefix) and not marker in line): 122 break 123 else: 124 data.append(line) 125 elif marker in line: 126 in_region = True 127 return data
128
129 - def assertInLog(self, line, marker=None):
130 """Fail if the given (partial) line is not in the log. 131 132 The log will be searched from the given marker to the next marker. 133 If marker is None, self.lastmarker is used. If the log hasn't 134 been marked (using self.markLog), the entire log will be searched. 135 """ 136 data = self._read_marked_region(marker) 137 for logline in data: 138 if line in logline: 139 return 140 msg = "%r not found in log" % line 141 self._handleLogError(msg, data, marker, line)
142
143 - def assertNotInLog(self, line, marker=None):
144 """Fail if the given (partial) line is in the log. 145 146 The log will be searched from the given marker to the next marker. 147 If marker is None, self.lastmarker is used. If the log hasn't 148 been marked (using self.markLog), the entire log will be searched. 149 """ 150 data = self._read_marked_region(marker) 151 for logline in data: 152 if line in logline: 153 msg = "%r found in log" % line 154 self._handleLogError(msg, data, marker, line)
155
156 - def assertLog(self, sliceargs, lines, marker=None):
157 """Fail if log.readlines()[sliceargs] is not contained in 'lines'. 158 159 The log will be searched from the given marker to the next marker. 160 If marker is None, self.lastmarker is used. If the log hasn't 161 been marked (using self.markLog), the entire log will be searched. 162 """ 163 data = self._read_marked_region(marker) 164 if isinstance(sliceargs, int): 165 # Single arg. Use __getitem__ and allow lines to be str or list. 166 if isinstance(lines, (tuple, list)): 167 lines = lines[0] 168 if isinstance(lines, unicodestr): 169 lines = lines.encode('utf-8') 170 if lines not in data[sliceargs]: 171 msg = "%r not found on log line %r" % (lines, sliceargs) 172 self._handleLogError(msg, [data[sliceargs],"--EXTRA CONTEXT--"] + data[sliceargs+1:sliceargs+6], marker, lines) 173 else: 174 # Multiple args. Use __getslice__ and require lines to be list. 175 if isinstance(lines, tuple): 176 lines = list(lines) 177 elif isinstance(lines, basestring): 178 raise TypeError("The 'lines' arg must be a list when " 179 "'sliceargs' is a tuple.") 180 181 start, stop = sliceargs 182 for line, logline in zip(lines, data[start:stop]): 183 if isinstance(line, unicodestr): 184 line = line.encode('utf-8') 185 if line not in logline: 186 msg = "%r not found in log" % line 187 self._handleLogError(msg, data[start:stop], marker, line)
188