[Notes] [Git][BuildStream/buildstream][jmac/cas_to_cas_oct_v2] 12 commits: Add a main() to virtual_directory_test.py to allow manual testing



Title: GitLab

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

Commits:

2 changed files:

Changes:

  • buildstream/storage/_casbaseddirectory.py
    ... ... @@ -87,9 +87,7 @@ class CasBasedDirectory(Directory):
    87 87
             if ref:
    
    88 88
                 with open(self.cas_cache.objpath(ref), 'rb') as f:
    
    89 89
                     self.pb2_directory.ParseFromString(f.read())
    
    90
    -                print("Opening ref {} and parsed into directory containing: {} {} {}.".format(ref.hash, [d.name for d in self.pb2_directory.directories],
    
    91
    -                                                                                        [d.name for d in self.pb2_directory.symlinks],
    
    92
    -                                                                                        [d.name for d in self.pb2_directory.files]))
    
    90
    +
    
    93 91
             self.ref = ref
    
    94 92
             self.index = OrderedDict()
    
    95 93
             self.parent = parent
    
    ... ... @@ -141,41 +139,6 @@ class CasBasedDirectory(Directory):
    141 139
             # We don't need to do anything more than that; files were already added ealier, and symlinks are
    
    142 140
             # part of the directory structure.
    
    143 141
     
    
    144
    -    def _add_new_blank_directory(self, name) -> Directory:
    
    145
    -        bst_dir = CasBasedDirectory(self.context, parent=self, filename=name)
    
    146
    -        new_pb2_dirnode = self.pb2_directory.directories.add()
    
    147
    -        new_pb2_dirnode.name = name
    
    148
    -        # Calculate the hash for an empty directory
    
    149
    -        if name in self.index:
    
    150
    -            raise VirtualDirectoryError("Creating directory {} would overwrite an existing item in {}"
    
    151
    -                                        .format(name, str(self)))
    
    152
    -        new_pb2_directory = remote_execution_pb2.Directory()
    
    153
    -        self.cas_cache.add_object(digest=new_pb2_dirnode.digest, buffer=new_pb2_directory.SerializeToString())
    
    154
    -        self.index[name] = IndexEntry(new_pb2_dirnode, buildstream_object=bst_dir)
    
    155
    -        return bst_dir
    
    156
    -
    
    157
    -    def create_directory(self, name: str) -> Directory:
    
    158
    -        """Creates a directory if it does not already exist. This does not
    
    159
    -        cause an error if something exists; it will remove files and
    
    160
    -        symlinks to files which have the same name in this
    
    161
    -        directory. Symlinks to directories with the name 'name' are
    
    162
    -        unaltered; it's assumed that the target of that symlink will
    
    163
    -        be used.
    
    164
    -
    
    165
    -        """
    
    166
    -        existing_item = self._find_pb2_entry(name)
    
    167
    -        if isinstance(existing_item, remote_execution_pb2.FileNode):
    
    168
    -            # Directory imported over file with same name
    
    169
    -            self.delete_entry(name)
    
    170
    -        elif isinstance(existing_item, remote_execution_pb2.SymlinkNode):
    
    171
    -            # Directory imported over symlink with same source name
    
    172
    -            if self.symlink_target_is_directory(existing_item):
    
    173
    -                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.
    
    174
    -            else:
    
    175
    -                self.delete_entry(name) # Symlinks to files get replaced
    
    176
    -        return self.descend(name, create=True) # Creates the directory if it doesn't already exist.
    
    177
    -
    
    178
    -
    
    179 142
         def _find_pb2_entry(self, name):
    
    180 143
             if name in self.index:
    
    181 144
                 return self.index[name].pb_object
    
    ... ... @@ -217,16 +180,8 @@ class CasBasedDirectory(Directory):
    217 180
             filenode.is_executable = is_executable
    
    218 181
             self.index[filename] = IndexEntry(filenode, modified=(filename in self.index))
    
    219 182
     
    
    220
    -    def _add_new_link(self, basename, filename):
    
    221
    -        existing_link = self._find_pb2_entry(filename)
    
    222
    -        if existing_link:
    
    223
    -            symlinknode = existing_link
    
    224
    -        else:
    
    225
    -            symlinknode = self.pb2_directory.symlinks.add()
    
    226
    -        symlinknode.name = filename
    
    227
    -        # A symlink node has no digest.
    
    228
    -        symlinknode.target = os.readlink(os.path.join(basename, filename))
    
    229
    -        self.index[filename] = IndexEntry(symlinknode, modified=(existing_link is not None))
    
    183
    +    def _copy_link_from_filesystem(self, basename, filename):
    
    184
    +        self._add_new_link_direct(filename, os.readlink(os.path.join(basename, filename)))
    
    230 185
     
    
    231 186
         def _add_new_link_direct(self, name, target):
    
    232 187
             existing_link = self._find_pb2_entry(name)
    
    ... ... @@ -240,7 +195,6 @@ class CasBasedDirectory(Directory):
    240 195
             symlinknode.target = target
    
    241 196
             self.index[name] = IndexEntry(symlinknode, modified=(existing_link is not None))
    
    242 197
     
    
    243
    -        
    
    244 198
         def delete_entry(self, name):
    
    245 199
             for collection in [self.pb2_directory.files, self.pb2_directory.symlinks, self.pb2_directory.directories]:
    
    246 200
                 for thing in collection:
    
    ... ... @@ -297,9 +251,6 @@ class CasBasedDirectory(Directory):
    297 251
             else:
    
    298 252
                 if create:
    
    299 253
                     newdir = self._add_directory(subdirectory_spec[0])
    
    300
    -                print("Created new directory called {} and descending into it".format(subdirectory_spec[0]))
    
    301
    -                #if subdirectory_spec[0] == "broken":
    
    302
    -                #    assert False
    
    303 254
                     return newdir.descend(subdirectory_spec[1:], create)
    
    304 255
                 else:
    
    305 256
                     error = "No entry called '{}' found in {}. There are directories called {}."
    
    ... ... @@ -316,7 +267,7 @@ class CasBasedDirectory(Directory):
    316 267
             else:
    
    317 268
                 return self
    
    318 269
     
    
    319
    -    def _resolve_symlink_or_directory(self, name):
    
    270
    +    def _force_resolve(self, name):
    
    320 271
             """Used only by _import_files_from_directory. Tries to resolve a
    
    321 272
             directory name or symlink name. 'name' must be an entry in this
    
    322 273
             directory. It must be a single symlink or directory name, not a path
    
    ... ... @@ -338,28 +289,31 @@ class CasBasedDirectory(Directory):
    338 289
             if isinstance(self.index[name].buildstream_object, Directory):
    
    339 290
                 return True
    
    340 291
             target = self._resolve(name)
    
    341
    -        print("Is {} followable? Resolved to {}".format(name, target))
    
    342
    -        return isinstance(target, CasBasedDirectory) or target is None
    
    292
    +        return isinstance(target, CasBasedDirectory) or target is None  #  TODO: But why return True if it's None (broken link/circular loop)? Surely that is against the docstring.
    
    343 293
     
    
    344
    -    def _resolve_symlink(self, node, force_create=True):
    
    345
    -        """Same as _resolve_symlink_or_directory but takes a SymlinkNode.
    
    346
    -        """
    
    347
    -        return self._resolve(node.name, force_create=True)
    
    348
    -    
    
    349 294
         def _resolve(self, name, absolute_symlinks_resolve=True, force_create=False, first_seen_object = None):
    
    350 295
             """ Resolves any name to an object. If the name points to a symlink in
    
    351 296
             this directory, it returns the thing it points to,
    
    352
    -        recursively. Returns a CasBasedDirectory, FileNode or
    
    353
    -        None.
    
    297
    +        recursively.
    
    298
    +
    
    299
    +        Returns a CasBasedDirectory, FileNode or
    
    300
    +        None. None indicates any of these cases:
    
    301
    +        * 'name' does not exist in this directory
    
    302
    +        * 'name' is a broken symlink,
    
    303
    +        * 'name' points to an infinite symlink loop.
    
    304
    +        * 'name' points to an absolute symlink and absolute_symlinks_resolve is False.
    
    354 305
     
    
    355 306
             If force_create is on, will attempt to create directories to make symlinks and directories resolve.
    
    356 307
             If force_create is off, this will never alter this directory.
    
    357 308
     
    
    358 309
             """
    
    359
    -        # First check if it's a normal object and return that
    
    360 310
     
    
    311
    +        # TODO: first_seen_object isn't sufficient. We could get into a loop after following one link and not detect it.
    
    312
    +        # TODO: 'None' is overloaded, maybe we should use exceptions and leave 'None' for actual nonexistent things.
    
    361 313
             if name not in self.index:
    
    362 314
                 return None
    
    315
    +
    
    316
    +        # First check if it's a normal object and return that
    
    363 317
             index_entry = self.index[name]
    
    364 318
             if isinstance(index_entry.buildstream_object, Directory):
    
    365 319
                 return index_entry.buildstream_object
    
    ... ... @@ -375,7 +329,6 @@ class CasBasedDirectory(Directory):
    375 329
                     ### Infinite symlink loop detected ###
    
    376 330
                     return None
    
    377 331
             
    
    378
    -        print("Resolving '{}': This is a symlink node in the current directory.".format(name))
    
    379 332
             symlink = index_entry.pb_object
    
    380 333
             components = symlink.target.split(CasBasedDirectory._pb2_path_sep)
    
    381 334
     
    
    ... ... @@ -386,12 +339,12 @@ class CasBasedDirectory(Directory):
    386 339
                     # Discard the first empty element
    
    387 340
                     components.pop(0)
    
    388 341
                 else:
    
    389
    -                print("  _resolve: Absolute symlink, which we won't resolve.")
    
    342
    +                # Unresolvable absolute symlink
    
    390 343
                     return None
    
    391 344
             else:
    
    392 345
                 start_directory = self
    
    346
    +
    
    393 347
             directory = start_directory
    
    394
    -        print("Resolve {}: starting from {}".format(symlink.target, start_directory))
    
    395 348
             while True:
    
    396 349
                 if not components:
    
    397 350
                     # We ran out of path elements and ended up in a directory
    
    ... ... @@ -407,30 +360,37 @@ class CasBasedDirectory(Directory):
    407 360
                     # returns the root.                
    
    408 361
                 else:
    
    409 362
                     if c in directory.index:
    
    363
    +                    # Recursive resolve and continue
    
    410 364
                         f = directory._resolve(c, absolute_symlinks_resolve, first_seen_object=first_seen_object)
    
    411
    -                    # Ultimately f must now be a file or directory
    
    365
    +
    
    412 366
                         if isinstance(f, CasBasedDirectory):
    
    413 367
                             directory = f
    
    414
    -
    
    368
    +                    elif isinstance(f, remote_execution_pb2.FileNode):
    
    369
    +                        # F is a file
    
    370
    +                        if components:
    
    371
    +                            # We have components still to resolve, but one of the path components
    
    372
    +                            # is a file.
    
    373
    +                            if force_create:
    
    374
    +                                self.delete_entry(c)
    
    375
    +                                directory = directory.descend(c, create=True)
    
    376
    +                            else:
    
    377
    +                                return f # TODO: Why return f? We've got components left and hit a file; this should be an error.
    
    378
    +                                #raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
    
    379
    +                        else:
    
    380
    +                            # It's a file, but there's no components left, so just return that.
    
    381
    +                            return f
    
    415 382
                         else:
    
    416
    -                        # This is a file or None (i.e. broken symlink)
    
    417
    -                        if f is None and force_create:
    
    418
    -                            directory = directory.descend(c, create=True)
    
    419
    -                        elif components and force_create:
    
    420
    -                            # Oh dear. We have components left to resolve, but the one we're trying to resolve points to a file.
    
    421
    -                            print("Trying to resolve {}, but found {} was a file.".format(symlink.target, c))
    
    422
    -                            self.delete_entry(c)
    
    383
    +                        # f is none, which covers many cases
    
    384
    +                        if force_create:
    
    423 385
                                 directory = directory.descend(c, create=True)
    
    424
    -                            #raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
    
    425 386
                             else:
    
    426
    -                            return f
    
    387
    +                            return None
    
    427 388
                     else:
    
    428 389
                         if force_create:
    
    429 390
                             directory = directory.descend(c, create=True)
    
    430 391
                         else:
    
    431 392
                             return None
    
    432
    -
    
    433
    -        # Shouldn't get here.
    
    393
    +        # You can only exit the while loop with a return, so you shouldn't be here.
    
    434 394
             
    
    435 395
     
    
    436 396
         def _check_replacement(self, name, path_prefix, fileListResult):
    
    ... ... @@ -471,7 +431,7 @@ class CasBasedDirectory(Directory):
    471 431
             as a directory tree is descended. """
    
    472 432
             if directory_name in self.index:
    
    473 433
                 if self._is_followable(directory_name): 
    
    474
    -                subdir = self._resolve_symlink_or_directory(directory_name)
    
    434
    +                subdir = self._force_resolve(directory_name)
    
    475 435
                 else:
    
    476 436
                     print("Overwriting unfollowable thing {}".format(directory_name))
    
    477 437
                     self.delete_entry(directory_name)
    
    ... ... @@ -504,7 +464,7 @@ class CasBasedDirectory(Directory):
    504 464
                     result.combine(subdir_result)
    
    505 465
                 elif os.path.islink(import_file):
    
    506 466
                     if self._check_replacement(entry, path_prefix, result):
    
    507
    -                    self._add_new_link(source_directory, entry)
    
    467
    +                    self._copy_link_from_filesystem(source_directory, entry)
    
    508 468
                         result.files_written.append(relative_pathname)
    
    509 469
                 elif os.path.isdir(import_file):
    
    510 470
                     # A plain directory which already exists isn't a problem; just ignore it.
    
    ... ... @@ -517,47 +477,7 @@ class CasBasedDirectory(Directory):
    517 477
             return result
    
    518 478
     
    
    519 479
     
    
    520
    -    def _save(self, name):
    
    521
    -        """ Saves this directory into the content cache as a named ref. This function is not
    
    522
    -        currently in use, but may be useful later. """
    
    523
    -        self._recalculate_recursing_up()
    
    524
    -        self._recalculate_recursing_down()
    
    525
    -        (rel_refpath, refname) = os.path.split(name)
    
    526
    -        refdir = os.path.join(self.cas_directory, 'refs', 'heads', rel_refpath)
    
    527
    -        refname = os.path.join(refdir, refname)
    
    528
    -
    
    529
    -        if not os.path.exists(refdir):
    
    530
    -            os.makedirs(refdir)
    
    531
    -        with open(refname, "wb") as f:
    
    532
    -            f.write(self.ref.SerializeToString())
    
    533
    -
    
    534
    -    def find_updated_files(self, modified_directory, prefix=""):
    
    535
    -        """Find the list of written and overwritten files that would result
    
    536
    -        from importing 'modified_directory' into this one.  This does
    
    537
    -        not change either directory. The reason this exists is for
    
    538
    -        direct imports of cas directories into other ones, which can
    
    539
    -        be done by simply replacing a hash, but we still need the file
    
    540
    -        lists.
    
    541
    -
    
    542
    -        """
    
    543
    -        result = FileListResult()
    
    544
    -        for entry in modified_directory.pb2_directory.directories:
    
    545
    -            existing_dir = self._find_pb2_entry(entry.name)
    
    546
    -            if existing_dir:
    
    547
    -                updates_files = existing_dir.find_updated_files(modified_directory.descend(entry.name),
    
    548
    -                                                                os.path.join(prefix, entry.name))
    
    549
    -                result.combine(updated_files)
    
    550
    -            else:
    
    551
    -                for f in source_directory.descend(entry.name).list_relative_paths():
    
    552
    -                    result.files_written.append(os.path.join(prefix, f))
    
    553
    -                    # None of these can overwrite anything, since the original files don't exist
    
    554
    -        for entry in modified_directory.pb2_directory.files + modified_directory.pb2_directory.symlinks:
    
    555
    -            if self._find_pb2_entry(entry.name):
    
    556
    -                result.files_overwritten.apppend(os.path.join(prefix, entry.name))
    
    557
    -            result.file_written.apppend(os.path.join(prefix, entry.name))
    
    558
    -        return result
    
    559
    -
    
    560
    -    def files_in_subdir(sorted_files, dirname):
    
    480
    +    def _files_in_subdir(sorted_files, dirname):
    
    561 481
             """Filters sorted_files and returns only the ones which have
    
    562 482
                'dirname' as a prefix, with that prefix removed.
    
    563 483
     
    
    ... ... @@ -566,9 +486,6 @@ class CasBasedDirectory(Directory):
    566 486
                 dirname += os.path.sep
    
    567 487
             return [f[len(dirname):] for f in sorted_files if f.startswith(dirname)]
    
    568 488
     
    
    569
    -    def symlink_target_is_directory(self, symlink_node):
    
    570
    -        x = self._resolve_symlink(symlink_node, force_create=False)
    
    571
    -        return isinstance(x, CasBasedDirectory)
    
    572 489
     
    
    573 490
         def _partial_import_cas_into_cas(self, source_directory, files, path_prefix="", file_list_required=True):
    
    574 491
             """ Import only the files and symlinks listed in 'files' from source_directory to this one.
    
    ... ... @@ -589,7 +506,7 @@ class CasBasedDirectory(Directory):
    589 506
                     dirname = components[0]
    
    590 507
                     if dirname not in processed_directories:
    
    591 508
                         # Now strip off the first directory name and import files recursively.
    
    592
    -                    subcomponents = CasBasedDirectory.files_in_subdir(files, dirname)
    
    509
    +                    subcomponents = CasBasedDirectory._files_in_subdir(files, dirname)
    
    593 510
                         # We will fail at this point if there is a file or symlink to file called 'dirname'.
    
    594 511
                         if dirname in self.index:
    
    595 512
                             x = self._resolve(dirname, force_create=True)
    
    ... ... @@ -600,8 +517,7 @@ class CasBasedDirectory(Directory):
    600 517
                             else:
    
    601 518
                                 dest_subdir = x
    
    602 519
                         else:
    
    603
    -                        self.create_directory(dirname)
    
    604
    -                        dest_subdir = self._resolve_symlink_or_directory(dirname)
    
    520
    +                        dest_subdir = self.descend(dirname, create=True)
    
    605 521
                         src_subdir = source_directory.descend(dirname)
    
    606 522
                         import_result = dest_subdir._partial_import_cas_into_cas(src_subdir, subcomponents,
    
    607 523
                                                                                  path_prefix=fullname, file_list_required=file_list_required)
    
    ... ... @@ -622,7 +538,7 @@ class CasBasedDirectory(Directory):
    622 538
                             # There's either a symlink (valid or not) or existing directory with this name, so do nothing.
    
    623 539
                             pass
    
    624 540
                     else:
    
    625
    -                    self.create_directory(f)                    
    
    541
    +                    self.descend(f, create=True)
    
    626 542
                 else:
    
    627 543
                     # We're importing a file or symlink - replace anything with the same name.
    
    628 544
                     print("Import of file/symlink {} into this directory. Removing anything existing...".format(f))
    
    ... ... @@ -639,49 +555,11 @@ class CasBasedDirectory(Directory):
    639 555
                             self._add_new_link_direct(name=f, target=item.target)
    
    640 556
             return result
    
    641 557
     
    
    642
    -    def transfer_node_contents(destination, source):
    
    643
    -        """Transfers all fields from the source PB2 node into the
    
    644
    -        destination. Destination and source must be of the same type and must
    
    645
    -        be a FileNode, SymlinkNode or DirectoryNode.
    
    646
    -        """
    
    647
    -        assert(type(destination) == type(source))
    
    648
    -        destination.name = source.name
    
    649
    -        if isinstance(destination, remote_execution_pb2.FileNode):
    
    650
    -            destination.digest.hash = source.digest.hash
    
    651
    -            destination.digest.size_bytes = source.digest.size_bytes
    
    652
    -            destination.is_executable = source.is_executable
    
    653
    -        elif isinstance(destination, remote_execution_pb2.SymlinkNode):
    
    654
    -            destination.target = source.target
    
    655
    -        elif isinstance(destination, remote_execution_pb2.DirectoryNode):
    
    656
    -            destination.digest.hash = source.digest.hash
    
    657
    -            destination.digest.size_bytes = source.digest.size_bytes
    
    658
    -        else:
    
    659
    -            raise VirtualDirectoryError("Incompatible type '{}' used as destination for transfer_node_contents"
    
    660
    -                                        .format(destination.type))
    
    661
    -
    
    662
    -    def _add_directory_from_node(self, source_node, source_casdir, can_hardlink=False):
    
    663
    -        # Duplicate the given node and add it to our index with a CasBasedDirectory object.
    
    664
    -        # No existing entry with the source node's name can exist.
    
    665
    -        # source_casdir is only needed if can_hardlink is True.
    
    666
    -        assert(self._find_pb2_entry(source_node.name) is None)
    
    667
    -
    
    668
    -        if can_hardlink:
    
    669
    -            new_dir_node = self.pb2_directory.directories.add()
    
    670
    -            CasBasedDirectory.transfer_node_contents(new_dir_node, source_node)
    
    671
    -            self.index[source_node.name] = IndexEntry(source_node, buildstream_object=source_casdir, modified=True)
    
    672
    -        else:
    
    673
    -            new_dir_node = self.pb2_directory.directories.add()
    
    674
    -            CasBasedDirectory.transfer_node_contents(new_dir_node, source_node)
    
    675
    -            buildStreamDirectory = CasBasedDirectory(self.context, ref=source_node.digest,
    
    676
    -                                                     parent=self, filename=source_node.name)
    
    677
    -            self.index[source_node.name] = IndexEntry(source_node, buildstream_object=buildStreamDirectory, modified=True)
    
    678
    -
    
    679 558
         def _import_cas_into_cas(self, source_directory, files=None):
    
    680 559
             """ A full import is significantly quicker than a partial import, because we can just
    
    681 560
             replace one directory with another's hash, without doing any recursion.
    
    682 561
             """
    
    683
    -        if files is None:
    
    684
    -            files = source_directory.list_relative_paths()
    
    562
    +
    
    685 563
             # You must pass a list into _partial_import (not a generator)
    
    686 564
             return self._partial_import_cas_into_cas(source_directory, list(files))
    
    687 565
     
    
    ... ... @@ -771,27 +649,27 @@ class CasBasedDirectory(Directory):
    771 649
             can_link (bool): Ignored, since hard links do not have any meaning within CAS.
    
    772 650
             """
    
    773 651
     
    
    774
    -        print("Directory before import: {}".format(self.show_files_recursive()))
    
    775
    -
    
    776
    -        if isinstance(external_pathspec, CasBasedDirectory):
    
    777
    -            print("-"*80 + "Performing direct CAS-to-CAS import")
    
    778
    -            result = self._import_cas_into_cas(external_pathspec, files=files)
    
    779
    -            print("Result of cas-to-cas import: {}".format(self.show_files_recursive()))
    
    780
    -        else:
    
    781
    -            print("-"*80 + "Performing initial import")
    
    782
    -            if isinstance(external_pathspec, FileBasedDirectory):
    
    783
    -                source_directory = external_pathspec.get_underlying_directory()
    
    784
    -            else:
    
    785
    -                source_directory = external_pathspec
    
    786
    -            if files is None:
    
    652
    +        if files is None:
    
    653
    +            if isinstance(external_pathspec, str):
    
    787 654
                     files = list_relative_paths(external_pathspec)
    
    655
    +            else:
    
    656
    +                assert isinstance(external_pathspec, Directory)
    
    657
    +                files = external_pathspec.list_relative_paths()
    
    658
    +
    
    659
    +        if isinstance(external_pathspec, FileBasedDirectory):
    
    660
    +            source_directory = external_pathspec.get_underlying_directory()
    
    661
    +            result = self._import_files_from_directory(source_directory, files=files)
    
    662
    +        elif isinstance(external_pathspec, str):
    
    663
    +            source_directory = external_pathspec
    
    788 664
                 result = self._import_files_from_directory(source_directory, files=files)
    
    665
    +        else:
    
    666
    +            assert isinstance(external_pathspec, CasBasedDirectory)
    
    667
    +            result = self._import_cas_into_cas(external_pathspec, files=files)
    
    789 668
     
    
    790 669
             # TODO: No notice is taken of report_written, update_utimes or can_link.
    
    791 670
             # Current behaviour is to fully populate the report, which is inefficient,
    
    792 671
             # but still correct.
    
    793 672
     
    
    794
    -
    
    795 673
             # We need to recalculate and store the hashes of all directories both
    
    796 674
             # up and down the tree; we have changed our directory by importing files
    
    797 675
             # which changes our hash and all our parents' hashes of us. The trees
    
    ... ... @@ -919,12 +797,6 @@ class CasBasedDirectory(Directory):
    919 797
                     filelist.append(k)
    
    920 798
             return filelist
    
    921 799
     
    
    922
    -    def _contains_only_directories(self):
    
    923
    -        for (k, v) in self.index.items():
    
    924
    -            if not isinstance(v.buildstream_object, CasBasedDirectory):
    
    925
    -                return False
    
    926
    -        return True
    
    927
    -
    
    928 800
         def list_relative_paths(self, relpath=""):
    
    929 801
             """Provide a list of all relative paths.
    
    930 802
     
    

  • tests/storage/virtual_directory_import.py
    ... ... @@ -8,8 +8,11 @@ from tests.testutils import cli
    8 8
     
    
    9 9
     from buildstream.storage import CasBasedDirectory
    
    10 10
     from buildstream.storage import FileBasedDirectory
    
    11
    +from buildstream._artifactcache import ArtifactCache
    
    12
    +from buildstream._artifactcache.cascache import CASCache
    
    11 13
     from buildstream import utils
    
    12 14
     
    
    15
    +
    
    13 16
     class FakeContext():
    
    14 17
         def __init__(self):
    
    15 18
             self.config_cache_quota = "65536"
    
    ... ... @@ -29,8 +32,8 @@ root_filesets = [
    29 32
         [('a/b/d', 'D', '')],
    
    30 33
         [('a/b/c', 'S', '/a/b/d')],
    
    31 34
         [('a/b/d', 'S', '/a/b/c')],
    
    32
    -    [('a/b/d', 'D', ''), ('a/b/c', 'S', '/a/b/d')], 
    
    33
    -    [('a/b/c', 'D', ''), ('a/b/d', 'S', '/a/b/c')], 
    
    35
    +    [('a/b/d', 'D', ''), ('a/b/c', 'S', '/a/b/d')],
    
    36
    +    [('a/b/c', 'D', ''), ('a/b/d', 'S', '/a/b/c')],
    
    34 37
         [('a/b', 'F', 'This is textfile 1\n')],
    
    35 38
         [('a/b/c', 'F', 'This is textfile 1\n')],
    
    36 39
         [('a/b/c', 'D', '')]
    
    ... ... @@ -59,7 +62,7 @@ def generate_import_roots(rootno, directory):
    59 62
     
    
    60 63
     
    
    61 64
     def generate_random_root(rootno, directory):
    
    62
    -    random.seed(RANDOM_SEED+rootno)
    
    65
    +    random.seed(RANDOM_SEED + rootno)
    
    63 66
         rootname = "root{}".format(rootno)
    
    64 67
         rootdir = os.path.join(directory, "content", rootname)
    
    65 68
         things = []
    
    ... ... @@ -156,14 +159,15 @@ def directory_not_empty(path):
    156 159
     def _import_test(tmpdir, original, overlay, generator_function, verify_contents=False):
    
    157 160
         fake_context = FakeContext()
    
    158 161
         fake_context.artifactdir = tmpdir
    
    162
    +    print("Creating CAS Cache with artifact dir {}".format(tmpdir))
    
    163
    +    fake_context.artifactcache = CASCache(fake_context)
    
    159 164
         # Create some fake content
    
    160 165
         generator_function(original, tmpdir)
    
    161 166
         if original != overlay:
    
    162 167
             generator_function(overlay, tmpdir)
    
    163
    -        
    
    168
    +
    
    164 169
         d = create_new_casdir(original, fake_context, tmpdir)
    
    165 170
     
    
    166
    -    #duplicate_cas = CasBasedDirectory(fake_context, ref=copy.copy(d.ref))
    
    167 171
         duplicate_cas = create_new_casdir(original, fake_context, tmpdir)
    
    168 172
     
    
    169 173
         assert duplicate_cas.ref.hash == d.ref.hash
    
    ... ... @@ -175,7 +179,7 @@ def _import_test(tmpdir, original, overlay, generator_function, verify_contents=
    175 179
         roundtrip_dir = os.path.join(tmpdir, "roundtrip")
    
    176 180
         d2.export_files(roundtrip_dir)
    
    177 181
         d.export_files(export_dir)
    
    178
    -    
    
    182
    +
    
    179 183
         if verify_contents:
    
    180 184
             for item in root_filesets[overlay - 1]:
    
    181 185
                 (path, typename, content) = item
    
    ... ... @@ -195,8 +199,10 @@ def _import_test(tmpdir, original, overlay, generator_function, verify_contents=
    195 199
                         assert os.path.islink(realpath)
    
    196 200
                         assert os.readlink(realpath) == content
    
    197 201
                 elif typename == 'D':
    
    198
    -                # We can't do any more tests than this because it depends on things present in the original. Blank directories
    
    199
    -                # here will be ignored and the original left in place.
    
    202
    +                # We can't do any more tests than this because it
    
    203
    +                # depends on things present in the original. Blank
    
    204
    +                # directories here will be ignored and the original
    
    205
    +                # left in place.
    
    200 206
                     assert os.path.lexists(realpath)
    
    201 207
     
    
    202 208
         # Now do the same thing with filebaseddirectories and check the contents match
    
    ... ... @@ -211,23 +217,22 @@ def _import_test(tmpdir, original, overlay, generator_function, verify_contents=
    211 217
     
    
    212 218
         assert duplicate_cas.ref.hash == d.ref.hash
    
    213 219
     
    
    214
    -    #d3 = create_new_casdir(original, fake_context, tmpdir)
    
    215
    -    #d4 = create_new_filedir(overlay, tmpdir)
    
    216
    -    #d3.import_files(d2)
    
    217
    -    #assert d.ref.hash == d3.ref.hash
    
    218 220
     
    
    219
    -@pytest.mark.parametrize("original,overlay", combinations(range(1,len(root_filesets)+1)))
    
    221
    +@pytest.mark.parametrize("original,overlay", combinations(range(1, len(root_filesets) + 1)))
    
    220 222
     def test_fixed_cas_import(cli, tmpdir, original, overlay):
    
    221 223
         _import_test(tmpdir, original, overlay, generate_import_roots, verify_contents=True)
    
    222 224
     
    
    223
    -@pytest.mark.parametrize("original,overlay", combinations(range(1,11)))
    
    225
    +
    
    226
    +@pytest.mark.parametrize("original,overlay", combinations(range(1, 11)))
    
    224 227
     def test_random_cas_import_fast(cli, tmpdir, original, overlay):
    
    225 228
         _import_test(tmpdir, original, overlay, generate_random_root, verify_contents=False)
    
    226 229
     
    
    227
    -    
    
    230
    +
    
    228 231
     def _listing_test(tmpdir, root, generator_function):
    
    229 232
         fake_context = FakeContext()
    
    230 233
         fake_context.artifactdir = tmpdir
    
    234
    +    print("Creating CAS Cache with artifact dir {}".format(tmpdir))
    
    235
    +    fake_context.artifactcache = CASCache(fake_context)
    
    231 236
         # Create some fake content
    
    232 237
         generator_function(root, tmpdir)
    
    233 238
     
    
    ... ... @@ -242,12 +247,37 @@ def _listing_test(tmpdir, root, generator_function):
    242 247
         print("filelist for root {} via CasBasedDirectory:".format(root))
    
    243 248
         print("{}".format(filelist2))
    
    244 249
         assert filelist == filelist2
    
    245
    -    
    
    246 250
     
    
    247
    -@pytest.mark.parametrize("root", range(1,11))
    
    251
    +
    
    252
    +@pytest.mark.parametrize("root", range(1, 11))
    
    248 253
     def test_random_directory_listing(cli, tmpdir, root):
    
    249 254
         _listing_test(tmpdir, root, generate_random_root)
    
    250
    -    
    
    255
    +
    
    256
    +
    
    251 257
     @pytest.mark.parametrize("root", [1, 2, 3, 4, 5])
    
    252 258
     def test_fixed_directory_listing(cli, tmpdir, root):
    
    253 259
         _listing_test(tmpdir, root, generate_import_roots)
    
    260
    +
    
    261
    +
    
    262
    +def main():
    
    263
    +    for i in range(1, 6):
    
    264
    +        with tempfile.TemporaryDirectory(prefix="/home/jimmacarthur/.cache/buildstream/cas") as tmpdirname:
    
    265
    +            test_fixed_directory_listing(None, tmpdirname, i)
    
    266
    +
    
    267
    +    for i in range(1, 11):
    
    268
    +        with tempfile.TemporaryDirectory(prefix="/home/jimmacarthur/.cache/buildstream/cas") as tmpdirname:
    
    269
    +            test_random_directory_listing(None, tmpdirname, i)
    
    270
    +
    
    271
    +    for i in range(1, 21):
    
    272
    +        for j in range(1, 21):
    
    273
    +            with tempfile.TemporaryDirectory(prefix="/home/jimmacarthur/.cache/buildstream/cas") as tmpdirname:
    
    274
    +                test_random_cas_import_fast(None, tmpdirname, i, j)
    
    275
    +
    
    276
    +    for i in range(1, len(root_filesets) + 1):
    
    277
    +        for j in range(1, len(root_filesets) + 1):
    
    278
    +            with tempfile.TemporaryDirectory(prefix="/home/jimmacarthur/.cache/buildstream/cas") as tmpdirname:
    
    279
    +                test_fixed_cas_import(None, tmpdirname, i, j)
    
    280
    +
    
    281
    +
    
    282
    +if __name__ == "__main__":
    
    283
    +    main()



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