Jim MacArthur pushed to branch jmac/cas_to_cas_oct at BuildStream / buildstream
Commits:
- 
b3ebec7c
by Jim MacArthur at 2018-10-25T14:12:05Z
- 
459f809b
by Jim MacArthur at 2018-10-25T15:48:07Z
- 
b28b6c9e
by Jim MacArthur at 2018-10-25T15:48:23Z
- 
052da4e9
by Jim MacArthur at 2018-10-25T15:48:50Z
2 changed files:
Changes:
| ... | ... | @@ -387,7 +387,7 @@ class CasBasedDirectory(Directory): | 
| 387 | 387 |          return directory
 | 
| 388 | 388 |  | 
| 389 | 389 |      
 | 
| 390 | -    def _resolve(self, name, absolute_symlinks_resolve=True, force_create=False):
 | |
| 390 | +    def _resolve(self, name, absolute_symlinks_resolve=True, force_create=False, first_seen_object = None):
 | |
| 391 | 391 |          """ Resolves any name to an object. If the name points to a symlink in
 | 
| 392 | 392 |          this directory, it returns the thing it points to,
 | 
| 393 | 393 |          recursively. Returns a CasBasedDirectory, FileNode or
 | 
| ... | ... | @@ -406,6 +406,14 @@ class CasBasedDirectory(Directory): | 
| 406 | 406 |              return index_entry.pb_object
 | 
| 407 | 407 |          
 | 
| 408 | 408 |          assert isinstance(index_entry.pb_object, remote_execution_pb2.SymlinkNode)
 | 
| 409 | + | |
| 410 | +        if first_seen_object is None:
 | |
| 411 | +            first_seen_object = index_entry.pb_object
 | |
| 412 | +        else:
 | |
| 413 | +            if index_entry.pb_object == first_seen_object:
 | |
| 414 | +                ### Infinite symlink loop detected ###
 | |
| 415 | +                return None
 | |
| 416 | +        
 | |
| 409 | 417 |          print("Resolving '{}': This is a symlink node in the current directory.".format(name))
 | 
| 410 | 418 |          symlink = index_entry.pb_object
 | 
| 411 | 419 |          components = symlink.target.split(CasBasedDirectory._pb2_path_sep)
 | 
| ... | ... | @@ -439,7 +447,7 @@ class CasBasedDirectory(Directory): | 
| 439 | 447 |                      directory = directory.parent
 | 
| 440 | 448 |              else:
 | 
| 441 | 449 |                  if c in directory.index:
 | 
| 442 | -                    f = directory._resolve(c, absolute_symlinks_resolve)
 | |
| 450 | +                    f = directory._resolve(c, absolute_symlinks_resolve, first_seen_object=first_seen_object)
 | |
| 443 | 451 |                      # Ultimately f must now be a file or directory
 | 
| 444 | 452 |                      if isinstance(f, CasBasedDirectory):
 | 
| 445 | 453 |                          directory = f
 | 
| ... | ... | @@ -453,7 +461,10 @@ class CasBasedDirectory(Directory): | 
| 453 | 461 |                              directory = directory.descend(c, create=True)
 | 
| 454 | 462 |                          elif components:
 | 
| 455 | 463 |                              # Oh dear. We have components left to resolve, but the one we're trying to resolve points to a file.
 | 
| 456 | -                            raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
 | |
| 464 | +                            print("Trying to resolve {}, but found {} was a file.".format(symlink.target, c))
 | |
| 465 | +                            self.delete_entry(c)
 | |
| 466 | +                            directory = directory.descend(c, create=True)
 | |
| 467 | +                            #raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
 | |
| 457 | 468 |                          else:
 | 
| 458 | 469 |                              return f
 | 
| 459 | 470 |                  else:
 | 
| ... | ... | @@ -842,35 +853,34 @@ class CasBasedDirectory(Directory): | 
| 842 | 853 |          if self.parent:
 | 
| 843 | 854 |              self.parent._recalculate_recursing_up(self)
 | 
| 844 | 855 |          
 | 
| 845 | -        # Duplicate the current directory
 | |
| 846 | - | |
| 856 | +        duplicate_test = False
 | |
| 847 | 857 |          
 | 
| 848 | 858 |          print("Original CAS before CAS-based import: {}".format(self.show_files_recursive()))
 | 
| 849 | 859 |          print("Original CAS hash: {}".format(self.ref.hash))
 | 
| 850 | 860 |          duplicate_cas = None
 | 
| 851 | 861 |          self._verify_unique()
 | 
| 852 | 862 |          if isinstance(external_pathspec, CasBasedDirectory):
 | 
| 853 | -            duplicate_cas = CasBasedDirectory(self.context, ref=copy.copy(self.ref))
 | |
| 854 | -            duplicate_cas._verify_unique()
 | |
| 863 | +            if duplicate_test:
 | |
| 864 | +                duplicate_cas = CasBasedDirectory(self.context, ref=copy.copy(self.ref))
 | |
| 865 | +                duplicate_cas._verify_unique()
 | |
| 866 | +                print("Duplicated CAS before file-based import: {}".format(duplicate_cas.show_files_recursive()))
 | |
| 867 | +                print("Duplicate CAS hash: {}".format(duplicate_cas.ref.hash))
 | |
| 855 | 868 |              print("-"*80 + "Performing direct CAS-to-CAS import")
 | 
| 856 | -            print("Duplicated CAS before file-based import: {}".format(duplicate_cas.show_files_recursive()))
 | |
| 857 | -            print("Duplicate CAS hash: {}".format(duplicate_cas.ref.hash))
 | |
| 858 | 869 |              result = self._import_cas_into_cas(external_pathspec, files=files)
 | 
| 859 | 870 |              self._verify_unique()
 | 
| 860 | 871 |              print("Result of cas-to-cas import: {}".format(self.show_files_recursive()))
 | 
| 861 | 872 |              print("-"*80 + "Performing round-trip import via file system")
 | 
| 862 | -            with tempfile.TemporaryDirectory(prefix="roundtrip") as tmpdir:
 | |
| 863 | -                external_pathspec.export_files(tmpdir)
 | |
| 864 | -                if files is None:
 | |
| 865 | -                    files = list(list_relative_paths(tmpdir))
 | |
| 866 | -                print("Importing from filesystem: filelist is: {}".format(files))
 | |
| 867 | -                duplicate_cas._import_files_from_directory(tmpdir, files=files)
 | |
| 868 | -                duplicate_cas._recalculate_recursing_down()
 | |
| 869 | -                if duplicate_cas.parent:
 | |
| 870 | -                    duplicate_cas.parent._recalculate_recursing_up(duplicate_cas)
 | |
| 871 | -                print("Result of direct import: {}".format(duplicate_cas.show_files_recursive()))
 | |
| 872 | -               
 | |
| 873 | - | |
| 873 | +            if duplicate_test:
 | |
| 874 | +                with tempfile.TemporaryDirectory(prefix="roundtrip") as tmpdir:
 | |
| 875 | +                    external_pathspec.export_files(tmpdir)
 | |
| 876 | +                    if files is None:
 | |
| 877 | +                        files = list(list_relative_paths(tmpdir))
 | |
| 878 | +                    print("Importing from filesystem: filelist is: {}".format(files))
 | |
| 879 | +                    duplicate_cas._import_files_from_directory(tmpdir, files=files)
 | |
| 880 | +                    duplicate_cas._recalculate_recursing_down()
 | |
| 881 | +                    if duplicate_cas.parent:
 | |
| 882 | +                        duplicate_cas.parent._recalculate_recursing_up(duplicate_cas)
 | |
| 883 | +                    print("Result of direct import: {}".format(duplicate_cas.show_files_recursive()))
 | |
| 874 | 884 |          else:
 | 
| 875 | 885 |              print("-"*80 + "Performing initial import")
 | 
| 876 | 886 |              if isinstance(external_pathspec, FileBasedDirectory):
 | 
| ... | ... | @@ -20,11 +20,17 @@ class FakeContext(): | 
| 20 | 20 |  # 'F' (file), 'S' (symlink) or 'D' (directory) with content being the contents
 | 
| 21 | 21 |  # for a file or the destination for a symlink.
 | 
| 22 | 22 |  root_filesets = [
 | 
| 23 | +    # Arbitrary test sets
 | |
| 23 | 24 |      [('a/b/c/textfile1', 'F', 'This is textfile 1\n')],
 | 
| 24 | 25 |      [('a/b/c/textfile1', 'F', 'This is the replacement textfile 1\n')],
 | 
| 25 | 26 |      [('a/b/d', 'D', '')],
 | 
| 26 | 27 |      [('a/b/c', 'S', '/a/b/d')],
 | 
| 27 | -    [('a/b/d', 'D', ''), ('a/b/c', 'S', '/a/b/d')]
 | |
| 28 | +    [('a/b/d', 'S', '/a/b/c')],
 | |
| 29 | +    [('a/b/d', 'D', ''), ('a/b/c', 'S', '/a/b/d')], 
 | |
| 30 | +    [('a/b/c', 'D', ''), ('a/b/d', 'S', '/a/b/c')], 
 | |
| 31 | +    [('a/b', 'F', 'This is textfile 1\n')],
 | |
| 32 | +    [('a/b/c', 'F', 'This is textfile 1\n')],
 | |
| 33 | +    [('a/b/c', 'D', '')]
 | |
| 28 | 34 |  ]
 | 
| 29 | 35 |  | 
| 30 | 36 |  empty_hash_ref = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
 | 
| ... | ... | @@ -178,8 +184,9 @@ def _import_test(tmpdir, original, overlay, generator_function, verify_contents= | 
| 178 | 184 |                      assert os.path.islink(realpath)
 | 
| 179 | 185 |                      assert os.readlink(realpath) == content
 | 
| 180 | 186 |              elif typename == 'D':
 | 
| 181 | -                # Note that isdir accepts symlinks to dirs, so a symlink to a dir is acceptable.
 | |
| 182 | -                assert os.path.isdir(realpath)
 | |
| 187 | +                # We can't do any more tests than this because it depends on things present in the original. Blank directories
 | |
| 188 | +                # here will be ignored and the original left in place.
 | |
| 189 | +                assert os.path.lexists(realpath)
 | |
| 183 | 190 |  | 
| 184 | 191 |      # Now do the same thing with filebaseddirectories and check the contents match
 | 
| 185 | 192 |      d3 = create_new_casdir(original, fake_context, tmpdir)
 | 
| ... | ... | @@ -187,14 +194,15 @@ def _import_test(tmpdir, original, overlay, generator_function, verify_contents= | 
| 187 | 194 |      d3.import_files(d2)
 | 
| 188 | 195 |      assert d.ref.hash == d3.ref.hash
 | 
| 189 | 196 |  | 
| 190 | -@pytest.mark.parametrize("original,overlay", combinations(range(1,6)))
 | |
| 197 | +@pytest.mark.parametrize("original,overlay", combinations(range(1,len(root_filesets)+1)))
 | |
| 191 | 198 |  def test_fixed_cas_import(cli, tmpdir, original, overlay):
 | 
| 192 | 199 |      _import_test(tmpdir, original, overlay, generate_import_roots, verify_contents=True)
 | 
| 193 | 200 |  | 
| 194 | 201 |  @pytest.mark.parametrize("original,overlay", combinations(range(1,11)))
 | 
| 195 | -def test_random_cas_import(cli, tmpdir, original, overlay):
 | |
| 202 | +def test_random_cas_import_fast(cli, tmpdir, original, overlay):
 | |
| 196 | 203 |      _import_test(tmpdir, original, overlay, generate_random_root, verify_contents=False)
 | 
| 197 | 204 |  | 
| 205 | +    
 | |
| 198 | 206 |  def _listing_test(tmpdir, root, generator_function):
 | 
| 199 | 207 |      fake_context = FakeContext()
 | 
| 200 | 208 |      fake_context.artifactdir = tmpdir
 | 
