Package qm :: Package test :: Module file_database
[hide private]
[frames] | no frames]

Source Code for Module qm.test.file_database

  1  ######################################################################## 
  2  # 
  3  # File:   file_database.py 
  4  # Author: Mark Mitchell 
  5  # Date:   2001-10-05 
  6  # 
  7  # Contents: 
  8  #   FileDatabase 
  9  #   ExtensionDatabase 
 10  # 
 11  # Copyright (c) 2001, 2002, 2003 by CodeSourcery, LLC.  All rights reserved.  
 12  # 
 13  # For license terms see the file COPYING. 
 14  # 
 15  ######################################################################## 
 16   
 17  ######################################################################## 
 18  # Imports 
 19  ######################################################################## 
 20   
 21  import dircache 
 22  import os 
 23  import os.path 
 24  from   qm.test.database import * 
 25  from   qm.test.directory_suite import * 
 26   
 27  ######################################################################## 
 28  # Classes 
 29  ######################################################################## 
 30   
31 -class FileDatabase(Database):
32 """A 'FileDatabase' stores each test as a single file. 33 34 A 'FileDatabase' is a 'Database' that stores each test, suite, 35 or resource as a single file. In addition, some subdirectories 36 can be considered implicit suites. The contents of the 37 implicit suite are all of the tests and suites contained in the 38 subdirectory. 39 40 'FileDatabase' is an abstract class.""" 41 42 # Methods that deal with tests. 43
44 - def GetTest(self, test_id):
45 """Return the 'TestDescriptor' for the test named 'test_id'. 46 47 'test_id' -- A label naming the test. 48 49 returns -- A 'TestDescriptor' corresponding to 'test_id'. 50 51 raises -- 'NoSuchTestError' if there is no test in the database 52 named 'test_id'.""" 53 54 path = self.GetTestPath(test_id) 55 if not self._IsTestFile(path): 56 raise NoSuchTestError, test_id 57 58 return self._GetTestFromPath(test_id, os.path.normpath(path))
59 60
61 - def GetTestPath(self, test_id):
62 """Return the file containing 'test_id'. 63 64 'test_id' -- The name of a test. 65 66 returns -- The absolute file name of the file that contains, or 67 would contain, 'test_id'. This method works even if no test 68 named 'test_id' exists. 69 70 Derived classes may override this method.""" 71 72 return self._GetPathFromLabel(test_id)
73 74
75 - def _IsTestFile(self, path):
76 """Returns true if 'path' is a test file. 77 78 'path' -- The absolute name of a file. All relevant 79 components in the path name have already been checked to 80 ensure that they are valid labels. 81 82 returns -- True iff the file corresponds to a test. 83 84 Derived classes must override this method.""" 85 86 raise NotImplementedError
87 88 # Methods that deal with suites. 89
90 - def GetSuite(self, suite_id):
91 """Return the 'Suite' for the suite named 'suite_id'. 92 93 'suite_id' -- A label naming the suite. 94 95 returns -- An instance of 'Suite' (or a derived class of 96 'Suite') corresponding to 'suite_id'. 97 98 raises -- 'NoSuchSuiteError' if there is no suite in the database 99 named 'suite_id'.""" 100 101 path = self.GetSuitePath(suite_id) 102 if not self._IsSuiteFile(path): 103 raise NoSuchSuiteError, suite_id 104 105 # There are two kinds of suites: directories (which are 106 # implicit suites), and files (which are explicit suites). 107 if os.path.isdir(path): 108 return DirectorySuite(self, suite_id) 109 else: 110 return self._GetSuiteFromPath(suite_id, os.path.normpath(path))
111 112
113 - def GetSuitePath(self, suite_id):
114 """Return the file containing 'suite_id'. 115 116 'suite_id' -- The name of a suite. 117 118 returns -- The absolute file name of the file (or directory) 119 that contains, or would contain, 'suite_id'. This method works 120 even if no suite named 'suite_id' exists. 121 122 Derived classes may override this method.""" 123 124 return self._GetPathFromLabel(suite_id)
125 126
127 - def _IsSuiteFile(self, path):
128 """Returns true if 'path' is a test suite file or directory. 129 130 'path' -- The absolute name of a file. All relevant 131 components in the path name have already been checked to 132 ensure that they are valid labels. 133 134 returns -- True iff the file corresponds to a test. 135 136 Derived classes may override this method, but only to restrict 137 the set of suites. In particular, a derived class method 138 may return false where this method would return true, but 139 never vice versa. 140 141 Derived classes must override this method.""" 142 143 raise NotImplementedError
144 145 # Methods that deal with resources. 146
147 - def GetResource(self, resource_id):
148 """Return the 'ResourceDescriptor' for the resource named 149 'resource_id'. 150 151 'resource_id' -- A label naming the resource. 152 153 returns -- A 'ResourceDescriptor' corresponding to 'resource_id'. 154 155 raises -- 'NoSuchResourceError' if there is no resource in the 156 database named 'resource_id'.""" 157 158 path = self.GetResourcePath(resource_id) 159 if not self._IsResourceFile(path): 160 raise NoSuchResourceError, resource_id 161 162 return self._GetResourceFromPath(resource_id, os.path.normpath(path))
163 164
165 - def GetResourcePath(self, resource_id):
166 """Return the file containing 'resource_id'. 167 168 'resource_id' -- The name of a resource. 169 170 returns -- The absolute file name of the file that contains, or 171 would contain, 'resource_id'. This method works even if no 172 Resource named 'resource_id' exists. 173 174 Derived classes may override this method.""" 175 176 return self._GetPathFromLabel(resource_id)
177 178
179 - def _IsResourceFile(self, path):
180 """Returns true if 'path' is a resource file. 181 182 'path' -- The absolute name of a file. All relevant 183 components in the path name have already been checked to 184 ensure that they are valid labels. 185 186 returns -- True iff the file corresponds to a resource. 187 188 Derived classes must override this method.""" 189 190 raise NotImplementedError
191 192 # Miscellaneous methods. 193
194 - def GetRoot(self):
195 """Return the root of the test database. 196 197 returns -- The directory that serves as the root of the test 198 database. All paths are relative to this directory. 199 200 Derived classes may override this method.""" 201 202 return self.GetPath()
203 204
205 - def GetSubdirectories(self, directory):
206 """Return the subdirectories of 'directory'. 207 208 'directory' -- A label indicating a directory in the database. 209 210 returns -- A sequence of (relative) labels indictating the 211 subdirectories of 'directory'. For example, if "a.b" and "a.c" 212 are directories in the database, this method will return "b" and 213 "c" given "a" as 'directory'.""" 214 215 subdirs = [] 216 file_dir = self.GetSuitePath(directory) 217 for entry in dircache.listdir(file_dir): 218 if not self._AreLabelsPaths(): 219 root = os.path.splitext(entry)[0] 220 else: 221 root = entry 222 if not self.IsValidLabel(root): 223 continue 224 entry_path = os.path.join(file_dir, entry) 225 if (self._IsSuiteFile(entry_path) 226 and os.path.isdir(entry_path)): 227 subdirs.append(root) 228 return subdirs
229 230
231 - def GetIds(self, kind, directory = "", scan_subdirs = 1):
232 233 # Compute the path name of the directory in which to start. 234 file_dir = self.GetSuitePath(directory) 235 # Get all the files of the appropriate kind. 236 return self._GetLabels(file_dir, scan_subdirs, directory, 237 lambda p: self._IsFile(kind, p))
238 239
240 - def _GetPath(self, kind, id):
241 """Returns the file system path corresponding to 'id'. 242 243 'kind' -- An extension kind. 244 245 'id' -- The name of the entity. 246 247 returns -- The path in which the entity is stored.""" 248 249 return { Database.RESOURCE : self.GetResourcePath, 250 Database.TEST : self.GetTestPath, 251 Database.SUITE : self.GetSuitePath } [kind] (id)
252 253
254 - def _IsFile(self, kind, path):
255 """Returns true if 'path' is a file of the indicated 'kind'. 256 257 'kind' -- One of 'Database.ITEM_KINDS'. 258 259 'path' -- The path to a file. 260 261 returns -- True iff 'path' is a file of the indicated kind. 262 263 Derived classes must override this method.""" 264 265 return { Database.TEST : self._IsTestFile, 266 Database.RESOURCE : self._IsResourceFile, 267 Database.SUITE : self._IsSuiteFile } [kind] (path)
268 269 # Derived classes must override these methods. 270
271 - def _GetTestFromPath(self, test_id, path):
272 """Return a descriptor for the test given by 'path'. 273 274 'test_id' -- The label naming the test. 275 276 'path' -- An absolute path to a test file. The 'path' satisfies 277 '_IsTestFile'. 278 279 returns -- A 'TestDescriptor' corresponding to 'test_id'. 280 281 Derived classes must override this method.""" 282 283 raise NotImplementedError
284 285
286 - def _GetSuiteFromPath(self, suite_id, path):
287 """Return a the 'Suite' given by 'path'. 288 289 'suite_id' -- The label naming the suite. 290 291 'path' -- An absolute path to a suite file. The 'path' 292 satisfies '_IsSuiteFile' and is a file, not a directory. 293 294 returns -- A 'Suite' corresponding to 'suite_id'. 295 296 Derived classes must override this method.""" 297 298 raise NotImplementedError
299 300
301 - def _GetResourceFromPath(self, resource_id, path):
302 """Return a descriptor for the resource given by 'path'. 303 304 'resource_id' -- The label naming the resource. 305 306 'path' -- An absolute path to a resource file. The 'path' 307 satisfies '_IsResourceFile'. 308 309 returns -- A 'ResourceDescriptor' corresponding to 310 'resource_id'. 311 312 Derived classes must override this method.""" 313 314 raise NotImplementedError
315 316
317 - def _GetPathFromLabel(self, label):
318 """Returns the file system path corresponding to 'label'. 319 320 'label' -- The id for a test, test suite, or similar entity. 321 322 returns -- The absolute path for the corresponding entry in 323 the file system, but without any required extension.""" 324 325 return os.path.join(self.GetRoot(), 326 self._GetRelativeLabelPath(label))
327 328
329 - def _GetLabelFromBasename(self, basename):
330 """Returns the label associated with a file named 'basename'. 331 332 'basename' -- The basename of a file, including the extension. 333 334 returns -- The corresponding label. 335 336 Derived classes may override this method.""" 337 338 return basename
339 340
341 - def _GetLabels(self, directory, scan_subdirs, label, predicate):
342 """Returns the labels of entities in 'directory'. 343 344 'directory' -- The absolute path name of the directory in 345 which to begin the search. 346 347 'scan_subdirs' -- True if (and only if) subdirectories of 348 'directory' should be scanned. 349 350 'label' -- The label that corresponds to 'directory'. 351 352 'predicate' -- A function that takes a file name and returns 353 a boolean. 354 355 returns -- Labels for all file names in 'directory'. that 356 satisfy 'predicate' If 'scan_subdirs' is true, subdirectories 357 are scanned as well.""" 358 359 labels = [] 360 361 # Go through all of the files (and subdirectories) in that 362 # directory. 363 for entry in dircache.listdir(directory): 364 entry_label = self._GetLabelFromBasename(entry) 365 # If the label is not valid then pretend it 366 # does not exist. It would not be valid to create an entity 367 # with such an id. 368 if not self.IsValidLabel(entry_label): 369 continue 370 # Compute the full path to 'entry'. 371 entry_path = os.path.join(directory, entry) 372 # If it satisfies the 'predicate', add it to the list. 373 if predicate(entry_path): 374 labels.append(self.JoinLabels(label, entry_label)) 375 # If it is a subdirectory, recurse. 376 if (scan_subdirs and os.path.isdir(entry_path) 377 and self._IsSuiteFile(entry_path)): 378 labels.extend(self._GetLabels(entry_path, 379 scan_subdirs, 380 self.JoinLabels(label, 381 entry_label), 382 predicate)) 383 384 return labels
385 386
387 - def RemoveExtension(self, id, kind):
388 389 path = self._GetPath(kind, id) 390 if not os.path.isfile(path): 391 raise { Database.RESOURCE : NoSuchResourceError, 392 Database.TEST: NoSuchTestError, 393 Database.SUITE: NoSuchSuiteError }[kind], id 394 395 os.remove(path)
396 397
398 - def _AreLabelsPaths(self):
399 """Returns true if labels are to be thought of as file names. 400 401 returns -- True if labels are to be thought of as file names. 402 If this predicate holds, every label is a path, relative to the 403 root of the database. If false, the labels are translated to 404 paths by adding the 'suite_extension' between directories and 405 the 'test_extension' or 'resource_extension' at the end of the 406 name.""" 407 408 return self.label_class == "file_label.FileLabel"
409 410
411 - def _GetRelativeLabelPath(self, label):
412 """Returns a representation of 'label' as a relative filename. 413 414 returns -- A relative filename corresponding to 'label'.""" 415 416 if self._AreLabelsPaths(): 417 return label 418 419 return os.path.join(*self.GetLabelComponents(label))
420 421 422
423 -class ExtensionDatabase(FileDatabase):
424 """An 'ExtensionDatabase' is a 'FileDatabase' where each kind of 425 entity (test, suite, resource) has a particular extension. For 426 example, if tests have the extension '.qmt', then all files ending 427 with '.qmt' are considered tests. If an extension for a particular 428 kind of entity is not specified or is the empty string, then all files 429 will be considered to be that kind of entity. 430 431 'ExtensionDatabase' is an abstract class.""" 432 433 arguments = [ 434 qm.fields.TextField( 435 name="test_extension", 436 title="Test Extension", 437 description="""The extension for test files. 438 439 The extension (including the leading period) used for files 440 containing tests.""", 441 default_value=".qmt"), 442 qm.fields.TextField( 443 name="suite_extension", 444 title="Suite Extension", 445 description="""The extension for suite files. 446 447 The extension (including the leading period) used for files 448 containing suites.""", 449 default_value=".qms"), 450 qm.fields.TextField( 451 name="resource_extension", 452 title="Resource Extension", 453 description="""The extension for resource files. 454 455 The extension (including the leading period) used for files 456 containing resources.""", 457 default_value=".qma"), 458 ] 459
460 - def __init__(self, path, arguments = None, **args):
461 462 super(ExtensionDatabase, self).__init__(path, arguments, **args) 463 self._extensions = { Database.TEST : self.test_extension, 464 Database.RESOURCE : self.resource_extension, 465 Database.SUITE : self.suite_extension }
466
467 - def GetTestExtension(self):
468 """Return the extension that indicates a file is a test. 469 470 returns -- The extension (including the leading period) that 471 indicates that a file is a test.""" 472 473 return self.test_extension
474 475
476 - def GetSuiteExtension(self):
477 """Return the extension that indicates a file is a suite. 478 479 returns -- The extension (including the leading period) that 480 indicates that a file is a suite.""" 481 482 return self.suite_extension
483 484
485 - def GetResourceExtension(self):
486 """Return the extension that indicates a file is a resource. 487 488 returns -- The extension (including the leading period) that 489 indicates that a file is a resource.""" 490 491 return self.resource_extension
492 493
494 - def GetTestPath(self, test_id):
495 496 test_path = self._GetPathFromLabel(test_id) 497 if not self._AreLabelsPaths(): 498 test_path += self.test_extension 499 return test_path
500 501
502 - def _IsTestFile(self, path):
503 504 extension = self.GetTestExtension() 505 if extension: 506 e = os.path.splitext(path)[1] 507 if e != extension: 508 return 0 509 510 return os.path.isfile(path)
511 512
513 - def GetSuitePath(self, suite_id):
514 515 # The top-level suite is just the directory containing the 516 # database; no extension is required. 517 if suite_id == "": 518 return self.GetRoot() 519 else: 520 suite_path = self._GetPathFromLabel(suite_id) 521 if not self._AreLabelsPaths(): 522 suite_path += self.suite_extension 523 return suite_path
524 525
526 - def _IsSuiteFile(self, path):
527 528 if path == self.GetRoot(): 529 return 1 530 531 extension = self.GetSuiteExtension() 532 if extension: 533 e = os.path.splitext(path)[1] 534 if e != extension: 535 return 0 536 537 return os.path.isfile(path) or os.path.isdir(path)
538 539
540 - def GetResourcePath(self, resource_id):
541 542 test_path = self._GetPathFromLabel(resource_id) 543 if not self._AreLabelsPaths(): 544 test_path += self.resource_extension 545 return test_path
546 547
548 - def _IsResourceFile(self, path):
549 550 extension = self.GetResourceExtension() 551 if extension: 552 e = os.path.splitext(path)[1] 553 if e != extension: 554 return 0 555 556 return os.path.isfile(path)
557 558
559 - def _GetPathFromLabel(self, label):
560 561 if self._AreLabelsPaths(): 562 path = label 563 else: 564 path = self._GetRelativeLabelPath(label) 565 566 return os.path.join(self.GetRoot(), path)
567 568
569 - def _GetLabelFromBasename(self, basename):
570 571 if self._AreLabelsPaths(): 572 return basename 573 else: 574 return os.path.splitext(basename)[0]
575 576
577 - def _GetRelativeLabelPath(self, label):
578 """Returns a representation of 'label' as a filename. 579 580 returns -- A filename corresponding to 'label'.""" 581 582 if self._AreLabelsPaths(): 583 return label 584 585 path = "" 586 components = self.GetLabelComponents(label) 587 if not components: 588 return path 589 590 for c in components[:-1]: 591 path = os.path.join(path, c + self.suite_extension) 592 path = os.path.join(path, components[-1]) 593 return path
594 595 596 ######################################################################## 597 # Local Variables: 598 # mode: python 599 # indent-tabs-mode: nil 600 # fill-column: 72 601 # End: 602