[Notes] [Git][BuildStream/buildstream][jmac/cas_to_cas_oct] 7 commits: Add a tool to show differences in two CAS directories



Title: GitLab

Jim MacArthur pushed to branch jmac/cas_to_cas_oct at BuildStream / buildstream

Commits:

2 changed files:

Changes:

  • buildstream/storage/_casbaseddirectory.py
    ... ... @@ -40,6 +40,8 @@ from ._filebaseddirectory import FileBasedDirectory
    40 40
     from ..utils import FileListResult, safe_copy, list_relative_paths, _relative_symlink_target
    
    41 41
     from .._artifactcache.cascache import CASCache
    
    42 42
     
    
    43
    +import copy # Temporary
    
    44
    +import operator
    
    43 45
     
    
    44 46
     class IndexEntry():
    
    45 47
         """ Used in our index of names to objects to store the 'modified' flag
    
    ... ... @@ -84,7 +86,9 @@ class CasBasedDirectory(Directory):
    84 86
             if ref:
    
    85 87
                 with open(self.cas_cache.objpath(ref), 'rb') as f:
    
    86 88
                     self.pb2_directory.ParseFromString(f.read())
    
    87
    -
    
    89
    +                print("Opening ref {} and parsed into directory containing: {} {} {}.".format(ref.hash, [d.name for d in self.pb2_directory.directories],
    
    90
    +                                                                                        [d.name for d in self.pb2_directory.symlinks],
    
    91
    +                                                                                        [d.name for d in self.pb2_directory.files]))
    
    88 92
             self.ref = ref
    
    89 93
             self.index = OrderedDict()
    
    90 94
             self.parent = parent
    
    ... ... @@ -161,13 +165,13 @@ class CasBasedDirectory(Directory):
    161 165
             existing_item = self._find_pb2_entry(name)
    
    162 166
             if isinstance(existing_item, remote_execution_pb2.FileNode):
    
    163 167
                 # Directory imported over file with same name
    
    164
    -            self.remove_item(name)
    
    168
    +            self.delete_entry(name)
    
    165 169
             elif isinstance(existing_item, remote_execution_pb2.SymlinkNode):
    
    166 170
                 # Directory imported over symlink with same source name
    
    167 171
                 if self.symlink_target_is_directory(existing_item):
    
    168 172
                     return self._resolve_symlink_or_directory(name) # That's fine; any files in the source directory should end up at the target of the symlink.
    
    169 173
                 else:
    
    170
    -                self.remove_item(name) # Symlinks to files get replaced
    
    174
    +                self.delete_entry(name) # Symlinks to files get replaced
    
    171 175
             return self.descend(name, create=True) # Creates the directory if it doesn't already exist.
    
    172 176
     
    
    173 177
     
    
    ... ... @@ -223,11 +227,27 @@ class CasBasedDirectory(Directory):
    223 227
             symlinknode.target = os.readlink(os.path.join(basename, filename))
    
    224 228
             self.index[filename] = IndexEntry(symlinknode, modified=(existing_link is not None))
    
    225 229
     
    
    230
    +    def _add_new_link_direct(self, name, target):
    
    231
    +        existing_link = self._find_pb2_entry(name)
    
    232
    +        if existing_link:
    
    233
    +            symlinknode = existing_link
    
    234
    +        else:
    
    235
    +            symlinknode = self.pb2_directory.symlinks.add()
    
    236
    +        assert(isinstance(symlinknode, remote_execution_pb2.SymlinkNode))
    
    237
    +        symlinknode.name = name
    
    238
    +        # A symlink node has no digest.
    
    239
    +        symlinknode.target = target
    
    240
    +        self.index[name] = IndexEntry(symlinknode, modified=(existing_link is not None))
    
    241
    +
    
    242
    +        
    
    226 243
         def delete_entry(self, name):
    
    227 244
             for collection in [self.pb2_directory.files, self.pb2_directory.symlinks, self.pb2_directory.directories]:
    
    228
    -            if name in collection:
    
    229
    -                collection.remove(name)
    
    245
    +            for thing in collection:
    
    246
    +                if thing.name == name:
    
    247
    +                    print("Removing {} from PB2".format(name))
    
    248
    +                    collection.remove(thing)
    
    230 249
             if name in self.index:
    
    250
    +            print("Removing {} from index".format(name))
    
    231 251
                 del self.index[name]
    
    232 252
     
    
    233 253
         def descend(self, subdirectory_spec, create=False):
    
    ... ... @@ -268,9 +288,12 @@ class CasBasedDirectory(Directory):
    268 288
                     return entry.descend(subdirectory_spec[1:], create)
    
    269 289
                 else:
    
    270 290
                     # May be a symlink
    
    291
    +                target = self._resolve(subdirectory_spec[0])
    
    292
    +                if isinstance(target, CasBasedDirectory):
    
    293
    +                    return target
    
    271 294
                     error = "Cannot descend into {}, which is a '{}' in the directory {}"
    
    272 295
                     raise VirtualDirectoryError(error.format(subdirectory_spec[0],
    
    273
    -                                                         type(entry).__name__,
    
    296
    +                                                         type(self.index[subdirectory_spec[0]].pb_object).__name__,
    
    274 297
                                                              self))
    
    275 298
             else:
    
    276 299
                 if create:
    
    ... ... @@ -308,6 +331,7 @@ class CasBasedDirectory(Directory):
    308 331
                 return self.index[name].buildstream_object
    
    309 332
             # OK then, it's a symlink
    
    310 333
             symlink = self._find_pb2_entry(name)
    
    334
    +        assert isinstance(symlink, remote_execution_pb2.SymlinkNode)
    
    311 335
             absolute = symlink.target.startswith(CasBasedDirectory._pb2_absolute_path_prefix)
    
    312 336
             if absolute:
    
    313 337
                 root = self.find_root()
    
    ... ... @@ -324,6 +348,16 @@ class CasBasedDirectory(Directory):
    324 348
                     directory = directory.descend(c, create=True)
    
    325 349
             return directory
    
    326 350
     
    
    351
    +    def _is_followable(self, name):
    
    352
    +        """ Returns true if this is a directory or symlink to a valid directory. """
    
    353
    +        if name not in self.index:
    
    354
    +            return False
    
    355
    +        if isinstance(self.index[name].buildstream_object, Directory):
    
    356
    +            return True
    
    357
    +        target = self._resolve(name)
    
    358
    +        print("Is {} followable? Resolved to {}".format(name, target))
    
    359
    +        return isinstance(target, CasBasedDirectory) or target is None
    
    360
    +
    
    327 361
         def _resolve_symlink(self, node):
    
    328 362
             """Same as _resolve_symlink_or_directory but takes a SymlinkNode.
    
    329 363
             """
    
    ... ... @@ -348,8 +382,13 @@ class CasBasedDirectory(Directory):
    348 382
     
    
    349 383
         
    
    350 384
         def _resolve(self, name, absolute_symlinks_resolve=True):
    
    351
    -        """ Resolves any name to an object. If the name points to a symlink in this 
    
    352
    -        directory, it returns the thing it points to, recursively. Returns a CasBasedDirectory, FileNode or None. Never creates a directory or otherwise alters the directory. """
    
    385
    +        """ Resolves any name to an object. If the name points to a symlink in
    
    386
    +        this directory, it returns the thing it points to,
    
    387
    +        recursively. Returns a CasBasedDirectory, FileNode or
    
    388
    +        None. Never creates a directory or otherwise alters the
    
    389
    +        directory.
    
    390
    +
    
    391
    +        """
    
    353 392
             # First check if it's a normal object and return that
    
    354 393
     
    
    355 394
             if name not in self.index:
    
    ... ... @@ -408,7 +447,7 @@ class CasBasedDirectory(Directory):
    408 447
                             else:
    
    409 448
                                 return f
    
    410 449
                     else:
    
    411
    -                    print("  resolving {}: nonexistent!".format(c))
    
    450
    +                    print("  resolving {}: Broken symlink".format(c))
    
    412 451
                         return None
    
    413 452
     
    
    414 453
             # Shouldn't get here.
    
    ... ... @@ -426,17 +465,21 @@ class CasBasedDirectory(Directory):
    426 465
                 return True
    
    427 466
             if (isinstance(existing_entry,
    
    428 467
                            (remote_execution_pb2.FileNode, remote_execution_pb2.SymlinkNode))):
    
    468
    +            self.delete_entry(name)
    
    469
    +            print("Processing overwrite of file/symlink {}: Added to overwritten list and deleted".format(name))
    
    429 470
                 fileListResult.overwritten.append(relative_pathname)
    
    430 471
                 return True
    
    431 472
             elif isinstance(existing_entry, remote_execution_pb2.DirectoryNode):
    
    432 473
                 # If 'name' maps to a DirectoryNode, then there must be an entry in index
    
    433 474
                 # pointing to another Directory.
    
    434 475
                 if self.index[name].buildstream_object.is_empty():
    
    476
    +                print("Processing overwrite of directory: Removing original")
    
    435 477
                     self.delete_entry(name)
    
    436 478
                     fileListResult.overwritten.append(relative_pathname)
    
    437 479
                     return True
    
    438 480
                 else:
    
    439 481
                     # We can't overwrite a non-empty directory, so we just ignore it.
    
    482
    +                print("Processing overwrite of non-empty directory: Ignoring overwrite")
    
    440 483
                     fileListResult.ignored.append(relative_pathname)
    
    441 484
                     return False
    
    442 485
             assert False, ("Entry '{}' is not a recognised file/link/directory and not None; it is {}"
    
    ... ... @@ -447,7 +490,13 @@ class CasBasedDirectory(Directory):
    447 490
             """ _import_directory_recursively and _import_files_from_directory will be called alternately
    
    448 491
             as a directory tree is descended. """
    
    449 492
             if directory_name in self.index:
    
    450
    -            subdir = self._resolve_symlink_or_directory(directory_name)
    
    493
    +            if self._is_followable(directory_name): 
    
    494
    +                subdir = self._resolve_symlink_or_directory(directory_name)
    
    495
    +            else:
    
    496
    +                print("Overwriting unfollowable thing {}".format(directory_name))
    
    497
    +                self.delete_entry(directory_name)
    
    498
    +                subdir = self._add_directory(directory_name)
    
    499
    +                # TODO: Add this to the list of overwritten things.
    
    451 500
             else:
    
    452 501
                 subdir = self._add_directory(directory_name)
    
    453 502
             new_path_prefix = os.path.join(path_prefix, directory_name)
    
    ... ... @@ -459,7 +508,10 @@ class CasBasedDirectory(Directory):
    459 508
         def _import_files_from_directory(self, source_directory, files, path_prefix=""):
    
    460 509
             """ Imports files from a traditional directory """
    
    461 510
             result = FileListResult()
    
    462
    -        for entry in sorted(files):
    
    511
    +        for entry in files:
    
    512
    +            print("Importing {} from file system".format(entry))
    
    513
    +            print("...Order of elements was {}".format(", ".join(self.index.keys())))
    
    514
    +
    
    463 515
                 split_path = entry.split(os.path.sep)
    
    464 516
                 # The actual file on the FS we're importing
    
    465 517
                 import_file = os.path.join(source_directory, entry)
    
    ... ... @@ -484,6 +536,8 @@ class CasBasedDirectory(Directory):
    484 536
                     if self._check_replacement(entry, path_prefix, result):
    
    485 537
                         self._add_new_file(source_directory, entry)
    
    486 538
                         result.files_written.append(relative_pathname)
    
    539
    +            print("...Order of elements is now {}".format(", ".join(self.index.keys())))
    
    540
    +
    
    487 541
             return result
    
    488 542
     
    
    489 543
     
    
    ... ... @@ -540,6 +594,17 @@ class CasBasedDirectory(Directory):
    540 594
             x = self._resolve_symlink(symlink_node)
    
    541 595
             return isinstance(x, CasBasedDirectory)
    
    542 596
     
    
    597
    +    def _verify_unique(self):
    
    598
    +        # Verifies that there are no duplicate names in this directory or subdirectories.
    
    599
    +        names = []
    
    600
    +        for entrylist in [self.pb2_directory.files, self.pb2_directory.directories, self.pb2_directory.symlinks]:
    
    601
    +            for e in entrylist:
    
    602
    +                if e.name in names:
    
    603
    +                    raise VirtualDirectoryError("Duplicate entry for name {} found".format(e.name))
    
    604
    +                names.append(e.name)
    
    605
    +        for d in self.pb2_directory.directories:
    
    606
    +            self.index[d.name].buildstream_object._verify_unique()
    
    607
    +    
    
    543 608
         def _partial_import_cas_into_cas(self, source_directory, files, path_prefix="", file_list_required=True):
    
    544 609
             """ Import only the files and symlinks listed in 'files' from source_directory to this one.
    
    545 610
             Args:
    
    ... ... @@ -548,11 +613,11 @@ class CasBasedDirectory(Directory):
    548 613
                path_prefix (str): Prefix used to add entries to the file list result.
    
    549 614
                file_list_required: Whether to update the file list while processing.
    
    550 615
             """
    
    551
    -        print("Beginning partial import of {} into {}".format(source_directory, self))
    
    616
    +        print("Beginning partial import of {} into {}. Files are: >{}<".format(source_directory, self, ", ".join(files)))
    
    552 617
             result = FileListResult()
    
    553 618
             processed_directories = set()
    
    554 619
             for f in files:
    
    555
    -            if f == ".": continue
    
    620
    +            #if f == ".": continue
    
    556 621
                 fullname = os.path.join(path_prefix, f)
    
    557 622
                 components = f.split(os.path.sep)
    
    558 623
                 if len(components)>1:
    
    ... ... @@ -562,6 +627,12 @@ class CasBasedDirectory(Directory):
    562 627
                     if dirname not in processed_directories:
    
    563 628
                         # Now strip off the first directory name and import files recursively.
    
    564 629
                         subcomponents = CasBasedDirectory.files_in_subdir(files, dirname)
    
    630
    +                    # We will fail at this point if there is a file or symlink to file called 'dirname'.
    
    631
    +                    if dirname in self.index:
    
    632
    +                        x = self._resolve(dirname)
    
    633
    +                        if isinstance(x, remote_execution_pb2.FileNode):
    
    634
    +                            self.delete_entry(dirname)
    
    635
    +                            result.overwritten.append(f)
    
    565 636
                         self.create_directory(dirname)
    
    566 637
                         print("Creating destination in {}: {}".format(self, dirname))
    
    567 638
                         dest_subdir = self._resolve_symlink_or_directory(dirname)
    
    ... ... @@ -576,17 +647,24 @@ class CasBasedDirectory(Directory):
    576 647
                     self.create_directory(f)
    
    577 648
                 else:
    
    578 649
                     # We're importing a file or symlink - replace anything with the same name.
    
    579
    -                self._check_replacement(f, path_prefix, result)
    
    580
    -                item = source_directory.index[f].pb_object
    
    581
    -                if isinstance(item, remote_execution_pb2.FileNode):
    
    582
    -                    filenode = self.pb2_directory.files.add(digest=item.digest, name=f,
    
    583
    -                                                            is_executable=item.is_executable)
    
    584
    -                    self.index[f] = IndexEntry(filenode, modified=(fullname in result.overwritten))
    
    585
    -                else:
    
    586
    -                    assert(isinstance(item, remote_execution_pb2.SymlinkNode))
    
    587
    -                    symlinknode = self.pb2_directory.symlinks.add(name=f, target=item.target)
    
    588
    -                    # A symlink node has no digest.
    
    589
    -                    self.index[f] = IndexEntry(symlinknode, modified=(fullname in result.overwritten))
    
    650
    +                print("Import of file/symlink {} into this directory. Removing anything existing...".format(f))
    
    651
    +                print("   ... ordering of nodes in this dir was: {}".format(self.index.keys()))
    
    652
    +                print("   ... symlinks were {}".format([x.name for x in self.pb2_directory.symlinks]))
    
    653
    +                importable = self._check_replacement(f, path_prefix, result)
    
    654
    +                if importable:
    
    655
    +                    print("   ... after replacement of '{}', symlinks are now {}".format(f, [x.name for x in self.pb2_directory.symlinks]))
    
    656
    +                    item = source_directory.index[f].pb_object
    
    657
    +                    if isinstance(item, remote_execution_pb2.FileNode):
    
    658
    +                        print("   ... importing file")
    
    659
    +                        filenode = self.pb2_directory.files.add(digest=item.digest, name=f,
    
    660
    +                                                                is_executable=item.is_executable)
    
    661
    +                        self.index[f] = IndexEntry(filenode, modified=(fullname in result.overwritten))
    
    662
    +                    else:
    
    663
    +                        print("   ... importing symlink")
    
    664
    +                        assert(isinstance(item, remote_execution_pb2.SymlinkNode))
    
    665
    +                        self._add_new_link_direct(name=f, target=item.target)
    
    666
    +                        print("   ... symlinks are now {}".format([x.name for x in self.pb2_directory.symlinks]))
    
    667
    +                    print("   ... ordering of nodes in this dir is now: {}".format(self.index.keys()))
    
    590 668
             return result
    
    591 669
     
    
    592 670
         def transfer_node_contents(destination, source):
    
    ... ... @@ -632,11 +710,69 @@ class CasBasedDirectory(Directory):
    632 710
             """
    
    633 711
             if files is None:
    
    634 712
                 #return self._full_import_cas_into_cas(source_directory, can_hardlink=True)
    
    635
    -            files = source_directory.list_relative_paths()
    
    713
    +            files = list(source_directory.list_relative_paths())
    
    636 714
                 print("Extracted all files from source directory '{}': {}".format(source_directory, files))
    
    637
    -        return self._partial_import_cas_into_cas(source_directory, files)
    
    638
    -
    
    715
    +        return self._partial_import_cas_into_cas(source_directory, list(files))
    
    716
    +
    
    717
    +    def _describe(self, thing):
    
    718
    +        # Describes protocol buffer objects
    
    719
    +        if isinstance(thing, remote_execution_pb2.DirectoryNode):
    
    720
    +            return "directory called {}".format(thing.name)
    
    721
    +        elif isinstance(thing, remote_execution_pb2.SymlinkNode):
    
    722
    +            return "symlink called {} pointing to {}".format(thing.name, thing.target)
    
    723
    +        elif isinstance(thing, remote_execution_pb2.FileNode):
    
    724
    +            return "file called {}".format(thing.name)
    
    725
    +        else:
    
    726
    +            return "strange thing"
    
    727
    +        
    
    728
    +    
    
    729
    +    def showdiff(self, other):
    
    730
    +        print("Diffing {} and {}:".format(self, other))
    
    731
    +
    
    732
    +        def compare_list(l1, l2):
    
    733
    +            item2 = None
    
    734
    +            index = 0
    
    735
    +            print("Comparing lists: {} vs {}".format([d.name for d in l1], [d.name for d in l2]))
    
    736
    +            for item1 in l1:
    
    737
    +                if index>=len(l2):
    
    738
    +                    print("l2 is short: no item to correspond to '{}' in l1.".format(item1.name))
    
    739
    +                    return False
    
    740
    +                item2 = l2[index]
    
    741
    +                if item1.name != item2.name:
    
    742
    +                    print("Items do not match: {}, a {} in l1, vs {}, a {} in l2".format(item1.name, self._describe(item1), item2.name, self._describe(item2)))
    
    743
    +                    return False
    
    744
    +                index += 1
    
    745
    +            if index != len(l2):
    
    746
    +                print("l2 is long: Has extra items {}".format(l2[index:]))
    
    747
    +                return False
    
    748
    +            return True
    
    639 749
     
    
    750
    +        def compare_pb2_directories(d1, d2):
    
    751
    +            result = (compare_list(d1.directories, d2.directories)
    
    752
    +                    and compare_list(d1.symlinks, d2.symlinks)
    
    753
    +                    and compare_list(d1.files, d2.files))
    
    754
    +            return result
    
    755
    +                        
    
    756
    +        if not compare_pb2_directories(self.pb2_directory, other.pb2_directory):
    
    757
    +            return False
    
    758
    +
    
    759
    +        for d in self.pb2_directory.directories:
    
    760
    +            self.index[d.name].buildstream_object.showdiff(other.index[d.name].buildstream_object)
    
    761
    +        print("No differences found in {}".format(self))
    
    762
    +              
    
    763
    +    def show_files_recursive(self):
    
    764
    +        elems = []
    
    765
    +        for (k,v) in self.index.items():
    
    766
    +            if type(v.pb_object) == remote_execution_pb2.DirectoryNode:
    
    767
    +                elems.append("{}=[{}]".format(k, v.buildstream_object.show_files_recursive()))
    
    768
    +            elif type(v.pb_object) == remote_execution_pb2.SymlinkNode:
    
    769
    +                elems.append("{}(s)".format(k))
    
    770
    +            elif type(v.pb_object) == remote_execution_pb2.FileNode:
    
    771
    +                elems.append("{}(f)".format(k))
    
    772
    +            else:
    
    773
    +                elems.append("{}(?)".format(k))
    
    774
    +        return " ".join(elems)
    
    775
    +        
    
    640 776
         def import_files(self, external_pathspec, *, files=None,
    
    641 777
                          report_written=True, update_utimes=False,
    
    642 778
                          can_link=False):
    
    ... ... @@ -659,12 +795,30 @@ class CasBasedDirectory(Directory):
    659 795
             can_link (bool): Ignored, since hard links do not have any meaning within CAS.
    
    660 796
             """
    
    661 797
     
    
    798
    +        print("Directory before import: {}".format(self.show_files_recursive()))
    
    799
    +
    
    800
    +        # Sync self
    
    801
    +        self._recalculate_recursing_down()
    
    802
    +        if self.parent:
    
    803
    +            self.parent._recalculate_recursing_up(self)
    
    804
    +        
    
    805
    +        # Duplicate the current directory
    
    806
    +
    
    807
    +        
    
    808
    +        print("Original CAS before CAS-based import: {}".format(self.show_files_recursive()))
    
    809
    +        print("Original CAS hash: {}".format(self.ref.hash))
    
    662 810
             duplicate_cas = None
    
    811
    +        self._verify_unique()
    
    663 812
             if isinstance(external_pathspec, CasBasedDirectory):
    
    813
    +            duplicate_cas = CasBasedDirectory(self.context, ref=copy.copy(self.ref))
    
    814
    +            duplicate_cas._verify_unique()
    
    815
    +            print("-"*80 + "Performing direct CAS-to-CAS import")
    
    816
    +            print("Duplicated CAS before file-based import: {}".format(duplicate_cas.show_files_recursive()))
    
    817
    +            print("Duplicate CAS hash: {}".format(duplicate_cas.ref.hash))
    
    664 818
                 result = self._import_cas_into_cas(external_pathspec, files=files)
    
    665
    -
    
    666
    -            # Duplicate the current directory and do an import that way.
    
    667
    -            duplicate_cas = CasBasedDirectory(self.context, ref=self.ref)
    
    819
    +            self._verify_unique()
    
    820
    +            print("Result of cas-to-cas import: {}".format(self.show_files_recursive()))
    
    821
    +            print("-"*80 + "Performing round-trip import via file system")
    
    668 822
                 with tempfile.TemporaryDirectory(prefix="roundtrip") as tmpdir:
    
    669 823
                     external_pathspec.export_files(tmpdir)
    
    670 824
                     if files is None:
    
    ... ... @@ -672,8 +826,12 @@ class CasBasedDirectory(Directory):
    672 826
                     duplicate_cas._import_files_from_directory(tmpdir, files=files)
    
    673 827
                     duplicate_cas._recalculate_recursing_down()
    
    674 828
                     if duplicate_cas.parent:
    
    675
    -                    duplicate_cas.parent._recalculate_recursing_up(self)
    
    829
    +                    duplicate_cas.parent._recalculate_recursing_up(duplicate_cas)
    
    830
    +                print("Result of direct import: {}".format(duplicate_cas.show_files_recursive()))
    
    831
    +               
    
    832
    +
    
    676 833
             else:
    
    834
    +            print("-"*80 + "Performing initial import")
    
    677 835
                 if isinstance(external_pathspec, FileBasedDirectory):
    
    678 836
                     source_directory = external_pathspec.get_underlying_directory()
    
    679 837
                 else:
    
    ... ... @@ -697,6 +855,7 @@ class CasBasedDirectory(Directory):
    697 855
                 self.parent._recalculate_recursing_up(self)
    
    698 856
             if duplicate_cas:
    
    699 857
                 if duplicate_cas.ref.hash != self.ref.hash:
    
    858
    +                self.showdiff(duplicate_cas)
    
    700 859
                     raise VirtualDirectoryError("Mismatch between file-imported result {} and cas-to-cas imported result {}.".format(duplicate_cas.ref.hash,self.ref.hash))
    
    701 860
     
    
    702 861
             return result
    
    ... ... @@ -757,6 +916,7 @@ class CasBasedDirectory(Directory):
    757 916
             for entry in self.pb2_directory.symlinks:
    
    758 917
                 src_name = os.path.join(to_directory, entry.name)
    
    759 918
                 target_name = entry.target
    
    919
    +            print("Exporting symlink named {}".format(src_name))
    
    760 920
                 try:
    
    761 921
                     os.symlink(target_name, src_name)
    
    762 922
                 except FileExistsError as e:
    
    ... ... @@ -857,6 +1017,7 @@ class CasBasedDirectory(Directory):
    857 1017
             for (k, v) in sorted(directory_list):
    
    858 1018
                 print("Yielding from subdirectory name {}".format(k))
    
    859 1019
                 yield from v.buildstream_object.list_relative_paths(relpath=os.path.join(relpath, k))
    
    1020
    +        print("List_relative_paths on {} complete".format(relpath))
    
    860 1021
     
    
    861 1022
         def recalculate_hash(self):
    
    862 1023
             """ Recalcuates the hash for this directory and store the results in
    

  • tests/storage/virtual_directory_import.py
    ... ... @@ -50,31 +50,33 @@ def generate_import_roots(directory):
    50 50
                     os.symlink(content, os.path.join(rootdir, path))
    
    51 51
     
    
    52 52
     
    
    53
    -def generate_random_root(directory):
    
    53
    +def generate_random_roots(directory):
    
    54 54
         random.seed(RANDOM_SEED)
    
    55
    -    rootname = "root6"
    
    56
    -    rootdir = os.path.join(directory, "content", rootname)
    
    57
    -    things = []
    
    58
    -    locations = ['.']
    
    59
    -    for i in range(0, 100):
    
    60
    -        location = random.choice(locations)
    
    61
    -        thingname = "node{}".format(i)
    
    62
    -        thing = random.choice(['dir', 'link', 'file'])
    
    63
    -        target = os.path.join(rootdir, location, thingname)
    
    64
    -        if thing == 'dir':
    
    65
    -            os.makedirs(target)
    
    66
    -            locations.append(os.path.join(location, thingname))
    
    67
    -        elif thing == 'file':
    
    68
    -            with open(target, "wt") as f:
    
    69
    -                f.write("This is node {}\n".format(i))
    
    70
    -        elif thing == 'link':
    
    71
    -            # TODO: Make some relative symlinks
    
    72
    -            if random.randint(1, 3) == 1 or len(things) == 0:
    
    73
    -                os.symlink("/broken", target)
    
    74
    -            else:
    
    75
    -                os.symlink(random.choice(things), target)
    
    76
    -        things.append(os.path.join(location, thingname))
    
    77
    -        print("Generated {}/{} ".format(rootdir, things[-1]))
    
    55
    +    for rootno in range(6,13):
    
    56
    +        rootname = "root{}".format(rootno)
    
    57
    +        rootdir = os.path.join(directory, "content", rootname)
    
    58
    +        things = []
    
    59
    +        locations = ['.']
    
    60
    +        os.makedirs(rootdir)
    
    61
    +        for i in range(0, 100):
    
    62
    +            location = random.choice(locations)
    
    63
    +            thingname = "node{}".format(i)
    
    64
    +            thing = random.choice(['dir', 'link', 'file'])
    
    65
    +            target = os.path.join(rootdir, location, thingname)
    
    66
    +            if thing == 'dir':
    
    67
    +                os.makedirs(target)
    
    68
    +                locations.append(os.path.join(location, thingname))
    
    69
    +            elif thing == 'file':
    
    70
    +                with open(target, "wt") as f:
    
    71
    +                    f.write("This is node {}\n".format(i))
    
    72
    +            elif thing == 'link':
    
    73
    +                # TODO: Make some relative symlinks
    
    74
    +                if random.randint(1, 3) == 1 or len(things) == 0:
    
    75
    +                    os.symlink("/broken", target)
    
    76
    +                else:
    
    77
    +                    os.symlink(random.choice(things), target)
    
    78
    +            things.append(os.path.join(location, thingname))
    
    79
    +            print("Generated {}/{} ".format(rootdir, things[-1]))
    
    78 80
     
    
    79 81
     
    
    80 82
     def file_contents(path):
    
    ... ... @@ -141,39 +143,40 @@ def directory_not_empty(path):
    141 143
         return os.listdir(path)
    
    142 144
     
    
    143 145
     
    
    144
    -@pytest.mark.parametrize("original,overlay", combinations([1, 2, 3, 4, 5]))
    
    146
    +@pytest.mark.parametrize("original,overlay", combinations([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]))
    
    145 147
     def test_cas_import(cli, tmpdir, original, overlay):
    
    146 148
         fake_context = FakeContext()
    
    147 149
         fake_context.artifactdir = tmpdir
    
    148 150
         # Create some fake content
    
    149 151
         generate_import_roots(tmpdir)
    
    150
    -    generate_random_root(tmpdir)
    
    152
    +    generate_random_roots(tmpdir)
    
    151 153
         d = create_new_casdir(original, fake_context, tmpdir)
    
    152 154
         d2 = create_new_casdir(overlay, fake_context, tmpdir)
    
    153 155
         print("Importing dir {} into {}".format(overlay, original))
    
    154 156
         d.import_files(d2)
    
    155 157
         d.export_files(os.path.join(tmpdir, "output"))
    
    156 158
         
    
    157
    -    for item in root_filesets[overlay - 1]:
    
    158
    -        (path, typename, content) = item
    
    159
    -        realpath = resolve_symlinks(path, os.path.join(tmpdir, "output"))
    
    160
    -        if typename == 'F':
    
    161
    -            if os.path.isdir(realpath) and directory_not_empty(realpath):
    
    162
    -                # The file should not have overwritten the directory in this case.
    
    163
    -                pass
    
    164
    -            else:
    
    165
    -                assert os.path.isfile(realpath), "{} did not exist in the combined virtual directory".format(path)
    
    166
    -                assert file_contents_are(realpath, content)
    
    167
    -        elif typename == 'S':
    
    168
    -            if os.path.isdir(realpath) and directory_not_empty(realpath):
    
    169
    -                # The symlink should not have overwritten the directory in this case.
    
    170
    -                pass
    
    171
    -            else:
    
    172
    -                assert os.path.islink(realpath)
    
    173
    -                assert os.readlink(realpath) == content
    
    174
    -        elif typename == 'D':
    
    175
    -            # Note that isdir accepts symlinks to dirs, so a symlink to a dir is acceptable.
    
    176
    -            assert os.path.isdir(realpath)
    
    159
    +    if overlay < 6:
    
    160
    +        for item in root_filesets[overlay - 1]:
    
    161
    +            (path, typename, content) = item
    
    162
    +            realpath = resolve_symlinks(path, os.path.join(tmpdir, "output"))
    
    163
    +            if typename == 'F':
    
    164
    +                if os.path.isdir(realpath) and directory_not_empty(realpath):
    
    165
    +                    # The file should not have overwritten the directory in this case.
    
    166
    +                    pass
    
    167
    +                else:
    
    168
    +                    assert os.path.isfile(realpath), "{} did not exist in the combined virtual directory".format(path)
    
    169
    +                    assert file_contents_are(realpath, content)
    
    170
    +            elif typename == 'S':
    
    171
    +                if os.path.isdir(realpath) and directory_not_empty(realpath):
    
    172
    +                    # The symlink should not have overwritten the directory in this case.
    
    173
    +                    pass
    
    174
    +                else:
    
    175
    +                    assert os.path.islink(realpath)
    
    176
    +                    assert os.readlink(realpath) == content
    
    177
    +            elif typename == 'D':
    
    178
    +                # Note that isdir accepts symlinks to dirs, so a symlink to a dir is acceptable.
    
    179
    +                assert os.path.isdir(realpath)
    
    177 180
     
    
    178 181
         # Now do the same thing with filebaseddirectories and check the contents match
    
    179 182
         d3 = create_new_casdir(original, fake_context, tmpdir)
    
    ... ... @@ -188,7 +191,7 @@ def test_directory_listing(cli, tmpdir, root):
    188 191
         fake_context.artifactdir = tmpdir
    
    189 192
         # Create some fake content
    
    190 193
         generate_import_roots(tmpdir)
    
    191
    -    generate_random_root(tmpdir)
    
    194
    +    generate_random_roots(tmpdir)
    
    192 195
     
    
    193 196
         d = create_new_filedir(root, tmpdir)
    
    194 197
         filelist = list(d.list_relative_paths())
    



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]