| OLD | NEW |
| 1 """ | 1 """ |
| 2 TestCommon.py: a testing framework for commands and scripts | 2 TestCommon.py: a testing framework for commands and scripts |
| 3 with commonly useful error handling | 3 with commonly useful error handling |
| 4 | 4 |
| 5 The TestCommon module provides a simple, high-level interface for writing | 5 The TestCommon module provides a simple, high-level interface for writing |
| 6 tests of executable commands and scripts, especially commands and scripts | 6 tests of executable commands and scripts, especially commands and scripts |
| 7 that interact with the file system. All methods throw exceptions and | 7 that interact with the file system. All methods throw exceptions and |
| 8 exit on failure, with useful error messages. This makes a number of | 8 exit on failure, with useful error messages. This makes a number of |
| 9 explicit checks unnecessary, making the test scripts themselves simpler | 9 explicit checks unnecessary, making the test scripts themselves simpler |
| 10 to write and easier to read. | 10 to write and easier to read. |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 # SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF | 81 # SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF |
| 82 # THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | 82 # THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| 83 # DAMAGE. | 83 # DAMAGE. |
| 84 # | 84 # |
| 85 # THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT | 85 # THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT |
| 86 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | 86 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
| 87 # PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, | 87 # PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, |
| 88 # AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, | 88 # AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, |
| 89 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 89 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| 90 | 90 |
| 91 from __future__ import print_function |
| 92 |
| 91 __author__ = "Steven Knight <knight at baldmt dot com>" | 93 __author__ = "Steven Knight <knight at baldmt dot com>" |
| 92 __revision__ = "TestCommon.py 0.37.D001 2010/01/11 16:55:50 knight" | 94 __revision__ = "TestCommon.py 0.37.D001 2010/01/11 16:55:50 knight" |
| 93 __version__ = "0.37" | 95 __version__ = "0.37" |
| 94 | 96 |
| 95 import copy | 97 import copy |
| 96 import os | 98 import os |
| 97 import os.path | 99 import os.path |
| 98 import stat | 100 import stat |
| 99 import string | |
| 100 import sys | 101 import sys |
| 101 import types | 102 try: |
| 102 import UserList | 103 from UserList import UserList |
| 104 except ImportError: |
| 105 from collections import UserList |
| 103 | 106 |
| 104 from TestCmd import * | 107 from TestCmd import * |
| 105 from TestCmd import __all__ | 108 from TestCmd import __all__ |
| 106 | 109 |
| 107 __all__.extend([ 'TestCommon', | 110 __all__.extend([ 'TestCommon', |
| 108 'exe_suffix', | 111 'exe_suffix', |
| 109 'obj_suffix', | 112 'obj_suffix', |
| 110 'shobj_prefix', | 113 'shobj_prefix', |
| 111 'shobj_suffix', | 114 'shobj_suffix', |
| 112 'lib_prefix', | 115 'lib_prefix', |
| (...skipping 18 matching lines...) Expand all Loading... |
| 131 exe_suffix = '.exe' | 134 exe_suffix = '.exe' |
| 132 obj_suffix = '.o' | 135 obj_suffix = '.o' |
| 133 shobj_suffix = '.os' | 136 shobj_suffix = '.os' |
| 134 shobj_prefix = '' | 137 shobj_prefix = '' |
| 135 lib_prefix = 'lib' | 138 lib_prefix = 'lib' |
| 136 lib_suffix = '.a' | 139 lib_suffix = '.a' |
| 137 dll_prefix = '' | 140 dll_prefix = '' |
| 138 dll_suffix = '.dll' | 141 dll_suffix = '.dll' |
| 139 module_prefix = '' | 142 module_prefix = '' |
| 140 module_suffix = '.dll' | 143 module_suffix = '.dll' |
| 141 elif string.find(sys.platform, 'irix') != -1: | 144 elif sys.platform.find('irix') != -1: |
| 142 exe_suffix = '' | 145 exe_suffix = '' |
| 143 obj_suffix = '.o' | 146 obj_suffix = '.o' |
| 144 shobj_suffix = '.o' | 147 shobj_suffix = '.o' |
| 145 shobj_prefix = '' | 148 shobj_prefix = '' |
| 146 lib_prefix = 'lib' | 149 lib_prefix = 'lib' |
| 147 lib_suffix = '.a' | 150 lib_suffix = '.a' |
| 148 dll_prefix = 'lib' | 151 dll_prefix = 'lib' |
| 149 dll_suffix = '.so' | 152 dll_suffix = '.so' |
| 150 module_prefix = 'lib' | 153 module_prefix = 'lib' |
| 151 module_prefix = '.so' | 154 module_prefix = '.so' |
| 152 elif string.find(sys.platform, 'darwin') != -1: | 155 elif sys.platform.find('darwin') != -1: |
| 153 exe_suffix = '' | 156 exe_suffix = '' |
| 154 obj_suffix = '.o' | 157 obj_suffix = '.o' |
| 155 shobj_suffix = '.os' | 158 shobj_suffix = '.os' |
| 156 shobj_prefix = '' | 159 shobj_prefix = '' |
| 157 lib_prefix = 'lib' | 160 lib_prefix = 'lib' |
| 158 lib_suffix = '.a' | 161 lib_suffix = '.a' |
| 159 dll_prefix = 'lib' | 162 dll_prefix = 'lib' |
| 160 dll_suffix = '.dylib' | 163 dll_suffix = '.dylib' |
| 161 module_prefix = '' | 164 module_prefix = '' |
| 162 module_suffix = '.so' | 165 module_suffix = '.so' |
| 163 elif string.find(sys.platform, 'sunos') != -1: | 166 elif sys.platform.find('sunos') != -1: |
| 164 exe_suffix = '' | 167 exe_suffix = '' |
| 165 obj_suffix = '.o' | 168 obj_suffix = '.o' |
| 166 shobj_suffix = '.os' | 169 shobj_suffix = '.os' |
| 167 shobj_prefix = 'so_' | 170 shobj_prefix = 'so_' |
| 168 lib_prefix = 'lib' | 171 lib_prefix = 'lib' |
| 169 lib_suffix = '.a' | 172 lib_suffix = '.a' |
| 170 dll_prefix = 'lib' | 173 dll_prefix = 'lib' |
| 171 dll_suffix = '.dylib' | 174 dll_suffix = '.dylib' |
| 172 module_prefix = '' | 175 module_prefix = '' |
| 173 module_suffix = '.so' | 176 module_suffix = '.so' |
| 174 else: | 177 else: |
| 175 exe_suffix = '' | 178 exe_suffix = '' |
| 176 obj_suffix = '.o' | 179 obj_suffix = '.o' |
| 177 shobj_suffix = '.os' | 180 shobj_suffix = '.os' |
| 178 shobj_prefix = '' | 181 shobj_prefix = '' |
| 179 lib_prefix = 'lib' | 182 lib_prefix = 'lib' |
| 180 lib_suffix = '.a' | 183 lib_suffix = '.a' |
| 181 dll_prefix = 'lib' | 184 dll_prefix = 'lib' |
| 182 dll_suffix = '.so' | 185 dll_suffix = '.so' |
| 183 module_prefix = 'lib' | 186 module_prefix = 'lib' |
| 184 module_suffix = '.so' | 187 module_suffix = '.so' |
| 185 | 188 |
| 186 def is_List(e): | 189 def is_List(e): |
| 187 return type(e) is types.ListType \ | 190 return type(e) is list \ |
| 188 or isinstance(e, UserList.UserList) | 191 or isinstance(e, UserList) |
| 189 | 192 |
| 190 def is_writable(f): | 193 def is_writable(f): |
| 191 mode = os.stat(f)[stat.ST_MODE] | 194 mode = os.stat(f)[stat.ST_MODE] |
| 192 return mode & stat.S_IWUSR | 195 return mode & stat.S_IWUSR |
| 193 | 196 |
| 194 def separate_files(flist): | 197 def separate_files(flist): |
| 195 existing = [] | 198 existing = [] |
| 196 missing = [] | 199 missing = [] |
| 197 for f in flist: | 200 for f in flist: |
| 198 if os.path.exists(f): | 201 if os.path.exists(f): |
| (...skipping 21 matching lines...) Expand all Loading... |
| 220 # | 223 # |
| 221 # $test->subdir('subdir', ...); | 224 # $test->subdir('subdir', ...); |
| 222 # | 225 # |
| 223 # $test->copy('src_file', 'dst_file'); | 226 # $test->copy('src_file', 'dst_file'); |
| 224 | 227 |
| 225 def __init__(self, **kw): | 228 def __init__(self, **kw): |
| 226 """Initialize a new TestCommon instance. This involves just | 229 """Initialize a new TestCommon instance. This involves just |
| 227 calling the base class initialization, and then changing directory | 230 calling the base class initialization, and then changing directory |
| 228 to the workdir. | 231 to the workdir. |
| 229 """ | 232 """ |
| 230 apply(TestCmd.__init__, [self], kw) | 233 TestCmd.__init__(self, **kw) |
| 231 os.chdir(self.workdir) | 234 os.chdir(self.workdir) |
| 232 | 235 |
| 233 def must_be_writable(self, *files): | 236 def must_be_writable(self, *files): |
| 234 """Ensures that the specified file(s) exist and are writable. | 237 """Ensures that the specified file(s) exist and are writable. |
| 235 An individual file can be specified as a list of directory names, | 238 An individual file can be specified as a list of directory names, |
| 236 in which case the pathname will be constructed by concatenating | 239 in which case the pathname will be constructed by concatenating |
| 237 them. Exits FAILED if any of the files does not exist or is | 240 them. Exits FAILED if any of the files does not exist or is |
| 238 not writable. | 241 not writable. |
| 239 """ | 242 """ |
| 240 files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) | 243 files = map((lambda x: os.path.join(*x) if is_List(x) else x), files) |
| 241 existing, missing = separate_files(files) | 244 existing, missing = separate_files(files) |
| 242 unwritable = filter(lambda x, iw=is_writable: not iw(x), existing) | 245 unwritable = [x for x in existing if not is_writable(x)] |
| 243 if missing: | 246 if missing: |
| 244 print "Missing files: `%s'" % string.join(missing, "', `") | 247 print("Missing files: `%s'" % "', `".join(missing)) |
| 245 if unwritable: | 248 if unwritable: |
| 246 print "Unwritable files: `%s'" % string.join(unwritable, "', `") | 249 print("Unwritable files: `%s'" % "', `".join(unwritable)) |
| 247 self.fail_test(missing + unwritable) | 250 self.fail_test(missing + unwritable) |
| 248 | 251 |
| 249 def must_contain(self, file, required, mode = 'rb'): | 252 def must_contain(self, file, required, mode = 'r'): |
| 250 """Ensures that the specified file contains the required text. | 253 """Ensures that the specified file contains the required text. |
| 251 """ | 254 """ |
| 252 file_contents = self.read(file, mode) | 255 file_contents = self.read(file, mode) |
| 253 contains = (string.find(file_contents, required) != -1) | 256 contains = (file_contents.find(required) != -1) |
| 254 if not contains: | 257 if not contains: |
| 255 print "File `%s' does not contain required string." % file | 258 print("File `%s' does not contain required string." % file) |
| 256 print self.banner('Required string ') | 259 print(self.banner('Required string ')) |
| 257 print required | 260 print(required) |
| 258 print self.banner('%s contents ' % file) | 261 print(self.banner('%s contents ' % file)) |
| 259 print file_contents | 262 print(file_contents) |
| 260 self.fail_test(not contains) | 263 self.fail_test(not contains) |
| 261 | 264 |
| 262 def must_contain_all_lines(self, output, lines, title=None, find=None): | 265 def must_contain_all_lines(self, output, lines, title=None, find=None): |
| 263 """Ensures that the specified output string (first argument) | 266 """Ensures that the specified output string (first argument) |
| 264 contains all of the specified lines (second argument). | 267 contains all of the specified lines (second argument). |
| 265 | 268 |
| 266 An optional third argument can be used to describe the type | 269 An optional third argument can be used to describe the type |
| 267 of output being searched, and only shows up in failure output. | 270 of output being searched, and only shows up in failure output. |
| 268 | 271 |
| 269 An optional fourth argument can be used to supply a different | 272 An optional fourth argument can be used to supply a different |
| 270 function, of the form "find(line, output), to use when searching | 273 function, of the form "find(line, output), to use when searching |
| 271 for lines in the output. | 274 for lines in the output. |
| 272 """ | 275 """ |
| 273 if find is None: | 276 if find is None: |
| 274 find = lambda o, l: string.find(o, l) != -1 | 277 find = lambda o, l: o.find(l) != -1 |
| 275 missing = [] | 278 missing = [] |
| 276 for line in lines: | 279 for line in lines: |
| 277 if not find(output, line): | 280 if not find(output, line): |
| 278 missing.append(line) | 281 missing.append(line) |
| 279 | 282 |
| 280 if missing: | 283 if missing: |
| 281 if title is None: | 284 if title is None: |
| 282 title = 'output' | 285 title = 'output' |
| 283 sys.stdout.write("Missing expected lines from %s:\n" % title) | 286 sys.stdout.write("Missing expected lines from %s:\n" % title) |
| 284 for line in missing: | 287 for line in missing: |
| 285 sys.stdout.write(' ' + repr(line) + '\n') | 288 sys.stdout.write(' ' + repr(line) + '\n') |
| 286 sys.stdout.write(self.banner(title + ' ')) | 289 sys.stdout.write(self.banner(title + ' ')) |
| 287 sys.stdout.write(output) | 290 sys.stdout.write(output) |
| 288 self.fail_test() | 291 self.fail_test() |
| 289 | 292 |
| 290 def must_contain_any_line(self, output, lines, title=None, find=None): | 293 def must_contain_any_line(self, output, lines, title=None, find=None): |
| 291 """Ensures that the specified output string (first argument) | 294 """Ensures that the specified output string (first argument) |
| 292 contains at least one of the specified lines (second argument). | 295 contains at least one of the specified lines (second argument). |
| 293 | 296 |
| 294 An optional third argument can be used to describe the type | 297 An optional third argument can be used to describe the type |
| 295 of output being searched, and only shows up in failure output. | 298 of output being searched, and only shows up in failure output. |
| 296 | 299 |
| 297 An optional fourth argument can be used to supply a different | 300 An optional fourth argument can be used to supply a different |
| 298 function, of the form "find(line, output), to use when searching | 301 function, of the form "find(line, output), to use when searching |
| 299 for lines in the output. | 302 for lines in the output. |
| 300 """ | 303 """ |
| 301 if find is None: | 304 if find is None: |
| 302 find = lambda o, l: string.find(o, l) != -1 | 305 find = lambda o, l: o.find(l) != -1 |
| 303 for line in lines: | 306 for line in lines: |
| 304 if find(output, line): | 307 if find(output, line): |
| 305 return | 308 return |
| 306 | 309 |
| 307 if title is None: | 310 if title is None: |
| 308 title = 'output' | 311 title = 'output' |
| 309 sys.stdout.write("Missing any expected line from %s:\n" % title) | 312 sys.stdout.write("Missing any expected line from %s:\n" % title) |
| 310 for line in lines: | 313 for line in lines: |
| 311 sys.stdout.write(' ' + repr(line) + '\n') | 314 sys.stdout.write(' ' + repr(line) + '\n') |
| 312 sys.stdout.write(self.banner(title + ' ')) | 315 sys.stdout.write(self.banner(title + ' ')) |
| 313 sys.stdout.write(output) | 316 sys.stdout.write(output) |
| 314 self.fail_test() | 317 self.fail_test() |
| 315 | 318 |
| 316 def must_contain_lines(self, lines, output, title=None): | 319 def must_contain_lines(self, lines, output, title=None): |
| 317 # Deprecated; retain for backwards compatibility. | 320 # Deprecated; retain for backwards compatibility. |
| 318 return self.must_contain_all_lines(output, lines, title) | 321 return self.must_contain_all_lines(output, lines, title) |
| 319 | 322 |
| 320 def must_exist(self, *files): | 323 def must_exist(self, *files): |
| 321 """Ensures that the specified file(s) must exist. An individual | 324 """Ensures that the specified file(s) must exist. An individual |
| 322 file be specified as a list of directory names, in which case the | 325 file be specified as a list of directory names, in which case the |
| 323 pathname will be constructed by concatenating them. Exits FAILED | 326 pathname will be constructed by concatenating them. Exits FAILED |
| 324 if any of the files does not exist. | 327 if any of the files does not exist. |
| 325 """ | 328 """ |
| 326 files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) | 329 files = map((lambda x: os.path.join(*x) if is_List(x) else x), files) |
| 327 missing = filter(lambda x: not os.path.exists(x), files) | 330 missing = [f for f in files if not os.path.exists(f)] |
| 328 if missing: | 331 if missing: |
| 329 print "Missing files: `%s'" % string.join(missing, "', `") | 332 print("Missing files: `%s'" % "', `".join(missing)) |
| 330 self.fail_test(missing) | 333 self.fail_test(missing) |
| 331 | 334 |
| 332 def must_match(self, file, expect, mode = 'rb'): | 335 def must_match(self, file, expect, mode = 'r'): |
| 333 """Matches the contents of the specified file (first argument) | 336 """Matches the contents of the specified file (first argument) |
| 334 against the expected contents (second argument). The expected | 337 against the expected contents (second argument). The expected |
| 335 contents are a list of lines or a string which will be split | 338 contents are a list of lines or a string which will be split |
| 336 on newlines. | 339 on newlines. |
| 337 """ | 340 """ |
| 338 file_contents = self.read(file, mode) | 341 file_contents = self.read(file, mode) |
| 339 try: | 342 try: |
| 340 self.fail_test(not self.match(file_contents, expect)) | 343 self.fail_test(not self.match(file_contents, expect)) |
| 341 except KeyboardInterrupt: | 344 except KeyboardInterrupt: |
| 342 raise | 345 raise |
| 343 except: | 346 except: |
| 344 print "Unexpected contents of `%s'" % file | 347 print("Unexpected contents of `%s'" % file) |
| 345 self.diff(expect, file_contents, 'contents ') | 348 self.diff(expect, file_contents, 'contents ') |
| 346 raise | 349 raise |
| 347 | 350 |
| 348 def must_not_contain(self, file, banned, mode = 'rb'): | 351 def must_not_contain(self, file, banned, mode = 'r'): |
| 349 """Ensures that the specified file doesn't contain the banned text. | 352 """Ensures that the specified file doesn't contain the banned text. |
| 350 """ | 353 """ |
| 351 file_contents = self.read(file, mode) | 354 file_contents = self.read(file, mode) |
| 352 contains = (string.find(file_contents, banned) != -1) | 355 contains = (file_contents.find(banned) != -1) |
| 353 if contains: | 356 if contains: |
| 354 print "File `%s' contains banned string." % file | 357 print("File `%s' contains banned string." % file) |
| 355 print self.banner('Banned string ') | 358 print(self.banner('Banned string ')) |
| 356 print banned | 359 print(banned) |
| 357 print self.banner('%s contents ' % file) | 360 print(self.banner('%s contents ' % file)) |
| 358 print file_contents | 361 print(file_contents) |
| 359 self.fail_test(contains) | 362 self.fail_test(contains) |
| 360 | 363 |
| 361 def must_not_contain_any_line(self, output, lines, title=None, find=None): | 364 def must_not_contain_any_line(self, output, lines, title=None, find=None): |
| 362 """Ensures that the specified output string (first argument) | 365 """Ensures that the specified output string (first argument) |
| 363 does not contain any of the specified lines (second argument). | 366 does not contain any of the specified lines (second argument). |
| 364 | 367 |
| 365 An optional third argument can be used to describe the type | 368 An optional third argument can be used to describe the type |
| 366 of output being searched, and only shows up in failure output. | 369 of output being searched, and only shows up in failure output. |
| 367 | 370 |
| 368 An optional fourth argument can be used to supply a different | 371 An optional fourth argument can be used to supply a different |
| 369 function, of the form "find(line, output), to use when searching | 372 function, of the form "find(line, output), to use when searching |
| 370 for lines in the output. | 373 for lines in the output. |
| 371 """ | 374 """ |
| 372 if find is None: | 375 if find is None: |
| 373 find = lambda o, l: string.find(o, l) != -1 | 376 find = lambda o, l: o.find(l) != -1 |
| 374 unexpected = [] | 377 unexpected = [] |
| 375 for line in lines: | 378 for line in lines: |
| 376 if find(output, line): | 379 if find(output, line): |
| 377 unexpected.append(line) | 380 unexpected.append(line) |
| 378 | 381 |
| 379 if unexpected: | 382 if unexpected: |
| 380 if title is None: | 383 if title is None: |
| 381 title = 'output' | 384 title = 'output' |
| 382 sys.stdout.write("Unexpected lines in %s:\n" % title) | 385 sys.stdout.write("Unexpected lines in %s:\n" % title) |
| 383 for line in unexpected: | 386 for line in unexpected: |
| 384 sys.stdout.write(' ' + repr(line) + '\n') | 387 sys.stdout.write(' ' + repr(line) + '\n') |
| 385 sys.stdout.write(self.banner(title + ' ')) | 388 sys.stdout.write(self.banner(title + ' ')) |
| 386 sys.stdout.write(output) | 389 sys.stdout.write(output) |
| 387 self.fail_test() | 390 self.fail_test() |
| 388 | 391 |
| 389 def must_not_contain_lines(self, lines, output, title=None): | 392 def must_not_contain_lines(self, lines, output, title=None): |
| 390 return self.must_not_contain_any_line(output, lines, title) | 393 return self.must_not_contain_any_line(output, lines, title) |
| 391 | 394 |
| 392 def must_not_exist(self, *files): | 395 def must_not_exist(self, *files): |
| 393 """Ensures that the specified file(s) must not exist. | 396 """Ensures that the specified file(s) must not exist. |
| 394 An individual file be specified as a list of directory names, in | 397 An individual file be specified as a list of directory names, in |
| 395 which case the pathname will be constructed by concatenating them. | 398 which case the pathname will be constructed by concatenating them. |
| 396 Exits FAILED if any of the files exists. | 399 Exits FAILED if any of the files exists. |
| 397 """ | 400 """ |
| 398 files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) | 401 files = map((lambda x: os.path.join(*x) if is_List(x) else x), files) |
| 399 existing = filter(os.path.exists, files) | 402 existing = [f for f in files if os.path.exists(f)] |
| 400 if existing: | 403 if existing: |
| 401 print "Unexpected files exist: `%s'" % string.join(existing, "', `") | 404 print("Unexpected files exist: `%s'" % "', `".join(existing)) |
| 402 self.fail_test(existing) | 405 self.fail_test(existing) |
| 403 | 406 |
| 404 def must_not_be_writable(self, *files): | 407 def must_not_be_writable(self, *files): |
| 405 """Ensures that the specified file(s) exist and are not writable. | 408 """Ensures that the specified file(s) exist and are not writable. |
| 406 An individual file can be specified as a list of directory names, | 409 An individual file can be specified as a list of directory names, |
| 407 in which case the pathname will be constructed by concatenating | 410 in which case the pathname will be constructed by concatenating |
| 408 them. Exits FAILED if any of the files does not exist or is | 411 them. Exits FAILED if any of the files does not exist or is |
| 409 writable. | 412 writable. |
| 410 """ | 413 """ |
| 411 files = map(lambda x: is_List(x) and apply(os.path.join, x) or x, files) | 414 files = map((lambda x: os.path.join(*x) if is_List(x) else x), files) |
| 412 existing, missing = separate_files(files) | 415 existing, missing = separate_files(files) |
| 413 writable = filter(is_writable, existing) | 416 writable = [x for x in existing if is_writable(x)] |
| 414 if missing: | 417 if missing: |
| 415 print "Missing files: `%s'" % string.join(missing, "', `") | 418 print("Missing files: `%s'" % "', `".join(missing)) |
| 416 if writable: | 419 if writable: |
| 417 print "Writable files: `%s'" % string.join(writable, "', `") | 420 print("Writable files: `%s'" % "', `".join(writable)) |
| 418 self.fail_test(missing + writable) | 421 self.fail_test(missing + writable) |
| 419 | 422 |
| 420 def _complete(self, actual_stdout, expected_stdout, | 423 def _complete(self, actual_stdout, expected_stdout, |
| 421 actual_stderr, expected_stderr, status, match): | 424 actual_stderr, expected_stderr, status, match): |
| 422 """ | 425 """ |
| 423 Post-processes running a subcommand, checking for failure | 426 Post-processes running a subcommand, checking for failure |
| 424 status and displaying output appropriately. | 427 status and displaying output appropriately. |
| 425 """ | 428 """ |
| 426 if _failed(self, status): | 429 if _failed(self, status): |
| 427 expect = '' | 430 expect = '' |
| 428 if status != 0: | 431 if status != 0: |
| 429 expect = " (expected %s)" % str(status) | 432 expect = " (expected %s)" % str(status) |
| 430 print "%s returned %s%s" % (self.program, str(_status(self)), expect
) | 433 print("%s returned %s%s" % (self.program, str(_status(self)), expect
)) |
| 431 print self.banner('STDOUT ') | 434 print(self.banner('STDOUT ')) |
| 432 print actual_stdout | 435 print(actual_stdout) |
| 433 print self.banner('STDERR ') | 436 print(self.banner('STDERR ')) |
| 434 print actual_stderr | 437 print(actual_stderr) |
| 435 self.fail_test() | 438 self.fail_test() |
| 436 if not expected_stdout is None and not match(actual_stdout, expected_std
out): | 439 if not expected_stdout is None and not match(actual_stdout, expected_std
out): |
| 437 self.diff(expected_stdout, actual_stdout, 'STDOUT ') | 440 self.diff(expected_stdout, actual_stdout, 'STDOUT ') |
| 438 if actual_stderr: | 441 if actual_stderr: |
| 439 print self.banner('STDERR ') | 442 print(self.banner('STDERR ')) |
| 440 print actual_stderr | 443 print(actual_stderr) |
| 441 self.fail_test() | 444 self.fail_test() |
| 442 if not expected_stderr is None and not match(actual_stderr, expected_std
err): | 445 if not expected_stderr is None and not match(actual_stderr, expected_std
err): |
| 443 print self.banner('STDOUT ') | 446 print(self.banner('STDOUT ')) |
| 444 print actual_stdout | 447 print(actual_stdout) |
| 445 self.diff(expected_stderr, actual_stderr, 'STDERR ') | 448 self.diff(expected_stderr, actual_stderr, 'STDERR ') |
| 446 self.fail_test() | 449 self.fail_test() |
| 447 | 450 |
| 448 def start(self, program = None, | 451 def start(self, program = None, |
| 449 interpreter = None, | 452 interpreter = None, |
| 450 arguments = None, | 453 arguments = None, |
| 451 universal_newlines = None, | 454 universal_newlines = None, |
| 452 **kw): | 455 **kw): |
| 453 """ | 456 """ |
| 454 Starts a program or script for the test environment. | 457 Starts a program or script for the test environment. |
| 455 | 458 |
| 456 This handles the "options" keyword argument and exceptions. | 459 This handles the "options" keyword argument and exceptions. |
| 457 """ | 460 """ |
| 458 options = kw.pop('options', None) | 461 options = kw.pop('options', None) |
| 459 if options: | 462 if options: |
| 460 if arguments is None: | 463 if arguments is None: |
| 461 arguments = options | 464 arguments = options |
| 462 else: | 465 else: |
| 463 arguments = options + " " + arguments | 466 arguments = options + " " + arguments |
| 464 | 467 |
| 465 try: | 468 try: |
| 466 return apply(TestCmd.start, | 469 return TestCmd.start(self, program, interpreter, arguments, |
| 467 (self, program, interpreter, arguments, universal_newli
nes), | 470 universal_newlines, **kw) |
| 468 kw) | |
| 469 except KeyboardInterrupt: | 471 except KeyboardInterrupt: |
| 470 raise | 472 raise |
| 471 except Exception, e: | 473 except Exception as e: |
| 472 print self.banner('STDOUT ') | 474 print(self.banner('STDOUT ')) |
| 473 try: | 475 try: |
| 474 print self.stdout() | 476 print(self.stdout()) |
| 475 except IndexError: | 477 except IndexError: |
| 476 pass | 478 pass |
| 477 print self.banner('STDERR ') | 479 print(self.banner('STDERR ')) |
| 478 try: | 480 try: |
| 479 print self.stderr() | 481 print(self.stderr()) |
| 480 except IndexError: | 482 except IndexError: |
| 481 pass | 483 pass |
| 482 cmd_args = self.command_args(program, interpreter, arguments) | 484 cmd_args = self.command_args(program, interpreter, arguments) |
| 483 sys.stderr.write('Exception trying to execute: %s\n' % cmd_args) | 485 sys.stderr.write('Exception trying to execute: %s\n' % cmd_args) |
| 484 raise e | 486 raise e |
| 485 | 487 |
| 486 def finish(self, popen, stdout = None, stderr = '', status = 0, **kw): | 488 def finish(self, popen, stdout = None, stderr = '', status = 0, **kw): |
| 487 """ | 489 """ |
| 488 Finishes and waits for the process being run under control of | 490 Finishes and waits for the process being run under control of |
| 489 the specified popen argument. Additional arguments are similar | 491 the specified popen argument. Additional arguments are similar |
| 490 to those of the run() method: | 492 to those of the run() method: |
| 491 | 493 |
| 492 stdout The expected standard output from | 494 stdout The expected standard output from |
| 493 the command. A value of None means | 495 the command. A value of None means |
| 494 don't test standard output. | 496 don't test standard output. |
| 495 | 497 |
| 496 stderr The expected error output from | 498 stderr The expected error output from |
| 497 the command. A value of None means | 499 the command. A value of None means |
| 498 don't test error output. | 500 don't test error output. |
| 499 | 501 |
| 500 status The expected exit status from the | 502 status The expected exit status from the |
| 501 command. A value of None means don't | 503 command. A value of None means don't |
| 502 test exit status. | 504 test exit status. |
| 503 """ | 505 """ |
| 504 apply(TestCmd.finish, (self, popen,), kw) | 506 TestCmd.finish(self, popen, **kw) |
| 505 match = kw.get('match', self.match) | 507 match = kw.get('match', self.match) |
| 506 self._complete(self.stdout(), stdout, | 508 self._complete(self.stdout(), stdout, |
| 507 self.stderr(), stderr, status, match) | 509 self.stderr(), stderr, status, match) |
| 508 | 510 |
| 509 def run(self, options = None, arguments = None, | 511 def run(self, options = None, arguments = None, |
| 510 stdout = None, stderr = '', status = 0, **kw): | 512 stdout = None, stderr = '', status = 0, **kw): |
| 511 """Runs the program under test, checking that the test succeeded. | 513 """Runs the program under test, checking that the test succeeded. |
| 512 | 514 |
| 513 The arguments are the same as the base TestCmd.run() method, | 515 The arguments are the same as the base TestCmd.run() method, |
| 514 with the addition of: | 516 with the addition of: |
| (...skipping 17 matching lines...) Expand all Loading... |
| 532 not test standard output (stdout = None), and expects that error | 534 not test standard output (stdout = None), and expects that error |
| 533 output is empty (stderr = ""). | 535 output is empty (stderr = ""). |
| 534 """ | 536 """ |
| 535 if options: | 537 if options: |
| 536 if arguments is None: | 538 if arguments is None: |
| 537 arguments = options | 539 arguments = options |
| 538 else: | 540 else: |
| 539 arguments = options + " " + arguments | 541 arguments = options + " " + arguments |
| 540 kw['arguments'] = arguments | 542 kw['arguments'] = arguments |
| 541 match = kw.pop('match', self.match) | 543 match = kw.pop('match', self.match) |
| 542 apply(TestCmd.run, [self], kw) | 544 TestCmd.run(self, **kw) |
| 543 self._complete(self.stdout(), stdout, | 545 self._complete(self.stdout(), stdout, |
| 544 self.stderr(), stderr, status, match) | 546 self.stderr(), stderr, status, match) |
| 545 | 547 |
| 546 def skip_test(self, message="Skipping test.\n"): | 548 def skip_test(self, message="Skipping test.\n"): |
| 547 """Skips a test. | 549 """Skips a test. |
| 548 | 550 |
| 549 Proper test-skipping behavior is dependent on the external | 551 Proper test-skipping behavior is dependent on the external |
| 550 TESTCOMMON_PASS_SKIPS environment variable. If set, we treat | 552 TESTCOMMON_PASS_SKIPS environment variable. If set, we treat |
| 551 the skip as a PASS (exit 0), and otherwise treat it as NO RESULT. | 553 the skip as a PASS (exit 0), and otherwise treat it as NO RESULT. |
| 552 In either case, we print the specified message as an indication | 554 In either case, we print the specified message as an indication |
| (...skipping 20 matching lines...) Expand all Loading... |
| 573 else: | 575 else: |
| 574 # We're under the development directory for this change, | 576 # We're under the development directory for this change, |
| 575 # so this is an Aegis invocation; pass the test (exit 0). | 577 # so this is an Aegis invocation; pass the test (exit 0). |
| 576 self.pass_test() | 578 self.pass_test() |
| 577 | 579 |
| 578 # Local Variables: | 580 # Local Variables: |
| 579 # tab-width:4 | 581 # tab-width:4 |
| 580 # indent-tabs-mode:nil | 582 # indent-tabs-mode:nil |
| 581 # End: | 583 # End: |
| 582 # vim: set expandtab tabstop=4 shiftwidth=4: | 584 # vim: set expandtab tabstop=4 shiftwidth=4: |
| OLD | NEW |