1#!/usr/bin/env python 2# 3# Public Domain 2014-2018 MongoDB, Inc. 4# Public Domain 2008-2014 WiredTiger, Inc. 5# 6# This is free and unencumbered software released into the public domain. 7# 8# Anyone is free to copy, modify, publish, use, compile, sell, or 9# distribute this software, either in source code form or as a compiled 10# binary, for any purpose, commercial or non-commercial, and by any 11# means. 12# 13# In jurisdictions that recognize copyright laws, the author or authors 14# of this software dedicate any and all copyright interest in the 15# software to the public domain. We make this dedication for the benefit 16# of the public at large and to the detriment of our heirs and 17# successors. We intend this dedication to be an overt act of 18# relinquishment in perpetuity of all present and future rights to this 19# software under copyright law. 20# 21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27# OTHER DEALINGS IN THE SOFTWARE. 28 29import os, re, subprocess, sys 30from run import wt_builddir 31from wttest import WiredTigerTestCase 32 33# suite_subprocess.py 34# Run a subprocess within the test suite 35# Used as a 'mixin' class along with a WiredTigerTestCase class 36class suite_subprocess: 37 subproc = None 38 39 def has_error_in_file(self, filename): 40 """ 41 Return whether the file contains 'ERROR'. 42 WT utilities issue a 'WT_ERROR' output string upon error. 43 """ 44 with open(filename, 'r') as f: 45 for line in f: 46 if 'ERROR' in line: 47 return True 48 return False 49 50 def check_no_error_in_file(self, filename, match='ERROR'): 51 """ 52 Raise an error and show output context if the file contains 'ERROR'. 53 WT utilities issue a 'WT_ERROR' output string upon error. 54 """ 55 lines = [] 56 hasError = False 57 hasPrevious = False # do we need to prefix an ellipsis? 58 hasNext = False # do we need to suffix an ellipsis? 59 with open(filename, 'r') as f: 60 for line in f: 61 lines.append(line) 62 hasError = hasError or match in line 63 if hasError: 64 if len(lines) > 10: 65 hasNext = True 66 break 67 else: 68 if len(lines) > 5: 69 lines.pop(0) 70 hasPrevious = True 71 if hasError: 72 print '**************** ' + match + ' in output file: ' + filename + ' ****************' 73 if hasPrevious: 74 print '...' 75 for line in lines: 76 print line, 77 if hasNext: 78 print '...' 79 print '********************************' 80 self.fail('ERROR found in output file: ' + filename) 81 82 # If the string is of the form '/.../', then return just the embedded 83 # pattern, otherwise, return None 84 def convert_to_pattern(self, s): 85 if len(s) >= 2 and s[0] == '/' and s[-1] == '/': 86 return s[1:-1] 87 else: 88 return None 89 90 def check_file_content(self, filename, expect): 91 with open(filename, 'r') as f: 92 got = f.read(len(expect) + 100) 93 self.assertEqual(got, expect, filename + ': does not contain expected:\n\'' + expect + '\', but contains:\n\'' + got + '\'.') 94 95 def check_file_contains_one_of(self, filename, expectlist): 96 """ 97 Check that the file contains the expected string in the first 100K bytes 98 """ 99 maxbytes = 1024*100 100 with open(filename, 'r') as f: 101 got = f.read(maxbytes) 102 found = False 103 for expect in expectlist: 104 pat = self.convert_to_pattern(expect) 105 if pat == None: 106 if expect in got: 107 found = True 108 break 109 else: 110 if re.search(pat, got): 111 found = True 112 break 113 if not found: 114 if len(expectlist) == 1: 115 expect = '\'' + expectlist[0] + '\'' 116 else: 117 expect = str(expectlist) 118 gotstr = '\'' + \ 119 (got if len(got) < 1000 else (got[0:1000] + '...')) + '\'' 120 if len(got) >= maxbytes: 121 self.fail(filename + ': does not contain expected ' + expect + ', or output is too large, got ' + gotstr) 122 else: 123 self.fail(filename + ': does not contain expected ' + expect + ', got ' + gotstr) 124 125 def check_file_contains(self, filename, expect): 126 self.check_file_contains_one_of(filename, [expect]) 127 128 def check_empty_file(self, filename): 129 """ 130 Raise an error if the file is not empty 131 """ 132 filesize = os.path.getsize(filename) 133 if filesize > 0: 134 with open(filename, 'r') as f: 135 contents = f.read(1000) 136 print 'ERROR: ' + filename + ' expected to be empty, but contains:\n' 137 print contents + '...\n' 138 self.assertEqual(filesize, 0, filename + ': expected to be empty') 139 140 def check_non_empty_file(self, filename): 141 """ 142 Raise an error if the file is empty 143 """ 144 filesize = os.path.getsize(filename) 145 if filesize == 0: 146 print 'ERROR: ' + filename + ' should not be empty (this command expected error output)' 147 self.assertNotEqual(filesize, 0, filename + ': expected to not be empty') 148 149 def verbose_env(self, envvar): 150 return envvar + '=' + str(os.environ.get(envvar)) + '\n' 151 152 def show_outputs(self, procargs, message, filenames): 153 out = 'ERROR: wt command ' + message + ': ' + str(procargs) + '\n' + \ 154 self.verbose_env('PATH') + \ 155 self.verbose_env('LD_LIBRARY_PATH') + \ 156 self.verbose_env('DYLD_LIBRARY_PATH') + \ 157 self.verbose_env('PYTHONPATH') + \ 158 'output files follow:' 159 WiredTigerTestCase.prout(out) 160 for filename in filenames: 161 maxbytes = 1024*100 162 with open(filename, 'r') as f: 163 contents = f.read(maxbytes) 164 if len(contents) > 0: 165 if len(contents) >= maxbytes: 166 contents += '...\n' 167 sepline = '*' * 50 + '\n' 168 out = sepline + filename + '\n' + sepline + contents 169 WiredTigerTestCase.prout(out) 170 171 # Run the wt utility. 172 def runWt(self, args, infilename=None, 173 outfilename=None, errfilename=None, closeconn=True, 174 reopensession=True, failure=False): 175 176 # Close the connection to guarantee everything is flushed, and that 177 # we can open it from another process. 178 if closeconn: 179 self.close_conn() 180 181 wtoutname = outfilename or "wt.out" 182 wterrname = errfilename or "wt.err" 183 with open(wterrname, "w") as wterr: 184 with open(wtoutname, "w") as wtout: 185 # Prefer running the actual 'wt' executable rather than the 186 # 'wt' script created by libtool. On OS/X with System Integrity 187 # Protection enabled, running a shell script strips 188 # environment variables needed to run 'wt'. 189 if sys.platform == "darwin": 190 wtexe = os.path.join(wt_builddir, ".libs", "wt") 191 else: 192 wtexe = os.path.join(wt_builddir, "wt") 193 procargs = [ wtexe ] 194 if self._gdbSubprocess: 195 procargs = [ "gdb", "--args" ] + procargs 196 elif self._lldbSubprocess: 197 procargs = [ "lldb", "--" ] + procargs 198 procargs.extend(args) 199 if self._gdbSubprocess: 200 infilepart = "" 201 if infilename != None: 202 infilepart = "<" + infilename + " " 203 print str(procargs) 204 print "*********************************************" 205 print "**** Run 'wt' via: run " + \ 206 " ".join(procargs[3:]) + infilepart + \ 207 ">" + wtoutname + " 2>" + wterrname 208 print "*********************************************" 209 returncode = subprocess.call(procargs) 210 elif self._lldbSubprocess: 211 infilepart = "" 212 if infilename != None: 213 infilepart = "<" + infilename + " " 214 print str(procargs) 215 print "*********************************************" 216 print "**** Run 'wt' via: run " + \ 217 " ".join(procargs[3:]) + infilepart + \ 218 ">" + wtoutname + " 2>" + wterrname 219 print "*********************************************" 220 returncode = subprocess.call(procargs) 221 elif infilename: 222 with open(infilename, "r") as wtin: 223 returncode = subprocess.call( 224 procargs, stdin=wtin, stdout=wtout, stderr=wterr) 225 else: 226 returncode = subprocess.call( 227 procargs, stdout=wtout, stderr=wterr) 228 if failure: 229 if returncode == 0: 230 self.show_outputs(procargs, "expected failure, got success", 231 [wtoutname, wterrname]) 232 self.assertNotEqual(returncode, 0, 233 'expected failure: "' + \ 234 str(procargs) + '": exited ' + str(returncode)) 235 else: 236 if returncode != 0: 237 self.show_outputs(procargs, "expected success, got failure", 238 [wtoutname, wterrname]) 239 self.assertEqual(returncode, 0, 240 'expected success: "' + \ 241 str(procargs) + '": exited ' + str(returncode)) 242 if errfilename == None: 243 self.check_empty_file(wterrname) 244 if outfilename == None: 245 self.check_empty_file(wtoutname) 246 247 # Reestablish the connection if needed 248 if reopensession and closeconn: 249 self.open_conn() 250