Jim MacArthur pushed to branch jmac/cas_to_cas_oct at BuildStream / buildstream
Commits:
2 changed files:
Changes:
| ... | ... | @@ -298,6 +298,9 @@ class CasBasedDirectory(Directory): |
| 298 | 298 |
else:
|
| 299 | 299 |
if create:
|
| 300 | 300 |
newdir = self._add_directory(subdirectory_spec[0])
|
| 301 |
+ print("Created new directory called {} and descending into it".format(subdirectory_spec[0]))
|
|
| 302 |
+ #if subdirectory_spec[0] == "broken":
|
|
| 303 |
+ # assert False
|
|
| 301 | 304 |
return newdir.descend(subdirectory_spec[1:], create)
|
| 302 | 305 |
else:
|
| 303 | 306 |
error = "No entry called '{}' found in {}. There are directories called {}."
|
| ... | ... | @@ -358,7 +361,7 @@ class CasBasedDirectory(Directory): |
| 358 | 361 |
print("Is {} followable? Resolved to {}".format(name, target))
|
| 359 | 362 |
return isinstance(target, CasBasedDirectory) or target is None
|
| 360 | 363 |
|
| 361 |
- def _resolve_symlink(self, node):
|
|
| 364 |
+ def _resolve_symlink(self, node, force_create=True):
|
|
| 362 | 365 |
"""Same as _resolve_symlink_or_directory but takes a SymlinkNode.
|
| 363 | 366 |
"""
|
| 364 | 367 |
|
| ... | ... | @@ -377,7 +380,10 @@ class CasBasedDirectory(Directory): |
| 377 | 380 |
elif c == "..":
|
| 378 | 381 |
directory = directory.parent
|
| 379 | 382 |
else:
|
| 380 |
- directory = directory.descend(c, create=True)
|
|
| 383 |
+ if c in directory.index or force_create:
|
|
| 384 |
+ directory = directory.descend(c, create=True)
|
|
| 385 |
+ else:
|
|
| 386 |
+ return None
|
|
| 381 | 387 |
return directory
|
| 382 | 388 |
|
| 383 | 389 |
|
| ... | ... | @@ -400,6 +406,7 @@ class CasBasedDirectory(Directory): |
| 400 | 406 |
return index_entry.pb_object
|
| 401 | 407 |
|
| 402 | 408 |
assert isinstance(index_entry.pb_object, remote_execution_pb2.SymlinkNode)
|
| 409 |
+ print("Resolving '{}': This is a symlink node in the current directory.".format(name))
|
|
| 403 | 410 |
symlink = index_entry.pb_object
|
| 404 | 411 |
components = symlink.target.split(CasBasedDirectory._pb2_path_sep)
|
| 405 | 412 |
|
| ... | ... | @@ -443,7 +450,7 @@ class CasBasedDirectory(Directory): |
| 443 | 450 |
print(" resolving {}: file/broken link".format(c))
|
| 444 | 451 |
if f is None and force_create:
|
| 445 | 452 |
print("Creating target of broken link {}".format(c))
|
| 446 |
- return directory.descend(c, create=True)
|
|
| 453 |
+ directory = directory.descend(c, create=True)
|
|
| 447 | 454 |
elif components:
|
| 448 | 455 |
# Oh dear. We have components left to resolve, but the one we're trying to resolve points to a file.
|
| 449 | 456 |
raise VirtualDirectoryError("Reached a file called {} while trying to resolve a symlink; cannot proceed".format(c))
|
| ... | ... | @@ -453,7 +460,7 @@ class CasBasedDirectory(Directory): |
| 453 | 460 |
print(" resolving {}: Non-existent file; must be from a broken symlink.".format(c))
|
| 454 | 461 |
if force_create:
|
| 455 | 462 |
print("Creating target of broken link {} (2)".format(c))
|
| 456 |
- return directory.descend(c, create=True)
|
|
| 463 |
+ directory = directory.descend(c, create=True)
|
|
| 457 | 464 |
else:
|
| 458 | 465 |
return None
|
| 459 | 466 |
|
| ... | ... | @@ -528,6 +535,8 @@ class CasBasedDirectory(Directory): |
| 528 | 535 |
directory_name = split_path[0]
|
| 529 | 536 |
# Hand this off to the importer for that subdir. This will only do one file -
|
| 530 | 537 |
# a better way would be to hand off all the files in this subdir at once.
|
| 538 |
+ # failed here because directory_name didn't point to a directory...
|
|
| 539 |
+ print("Attempting to import into {} from {}".format(directory_name, source_directory))
|
|
| 531 | 540 |
subdir_result = self._import_directory_recursively(directory_name, source_directory,
|
| 532 | 541 |
split_path[1:], path_prefix)
|
| 533 | 542 |
result.combine(subdir_result)
|
| ... | ... | @@ -598,7 +607,7 @@ class CasBasedDirectory(Directory): |
| 598 | 607 |
return [f[len(dirname):] for f in sorted_files if f.startswith(dirname)]
|
| 599 | 608 |
|
| 600 | 609 |
def symlink_target_is_directory(self, symlink_node):
|
| 601 |
- x = self._resolve_symlink(symlink_node)
|
|
| 610 |
+ x = self._resolve_symlink(symlink_node, force_create=False)
|
|
| 602 | 611 |
return isinstance(x, CasBasedDirectory)
|
| 603 | 612 |
|
| 604 | 613 |
def _verify_unique(self):
|
| ... | ... | @@ -636,14 +645,20 @@ class CasBasedDirectory(Directory): |
| 636 | 645 |
subcomponents = CasBasedDirectory.files_in_subdir(files, dirname)
|
| 637 | 646 |
# We will fail at this point if there is a file or symlink to file called 'dirname'.
|
| 638 | 647 |
if dirname in self.index:
|
| 639 |
- x = self._resolve(dirname)
|
|
| 648 |
+ x = self._resolve(dirname, force_create=True)
|
|
| 640 | 649 |
if isinstance(x, remote_execution_pb2.FileNode):
|
| 641 | 650 |
self.delete_entry(dirname)
|
| 642 | 651 |
result.overwritten.append(f)
|
| 643 |
- self.create_directory(dirname)
|
|
| 644 |
- print("Creating destination in {}: {}".format(self, dirname))
|
|
| 645 |
- dest_subdir = self._resolve_symlink_or_directory(dirname)
|
|
| 652 |
+ dest_subdir = self.descend(dirname, create=True)
|
|
| 653 |
+ else:
|
|
| 654 |
+ dest_subdir = x
|
|
| 655 |
+ else:
|
|
| 656 |
+ print("Importing {}: {} does not exist in {}, so it is created as a directory".format(f, dirname, self))
|
|
| 657 |
+
|
|
| 658 |
+ self.create_directory(dirname)
|
|
| 659 |
+ dest_subdir = self._resolve_symlink_or_directory(dirname)
|
|
| 646 | 660 |
src_subdir = source_directory.descend(dirname)
|
| 661 |
+ print("Now recursing into {} to continue adding {}".format(src_subdir, f))
|
|
| 647 | 662 |
import_result = dest_subdir._partial_import_cas_into_cas(src_subdir, subcomponents,
|
| 648 | 663 |
path_prefix=fullname, file_list_required=file_list_required)
|
| 649 | 664 |
result.combine(import_result)
|
| ... | ... | @@ -651,7 +666,19 @@ class CasBasedDirectory(Directory): |
| 651 | 666 |
elif isinstance(source_directory.index[f].buildstream_object, CasBasedDirectory):
|
| 652 | 667 |
# The thing in the input file list is a directory on its own. In which case, replace any existing file, or symlink to file
|
| 653 | 668 |
# with the new, blank directory - if it's neither of those things, or doesn't exist, then just create the dir.
|
| 654 |
- self.create_directory(f)
|
|
| 669 |
+ if f in self.index:
|
|
| 670 |
+ x = self._resolve(f)
|
|
| 671 |
+ if x is None:
|
|
| 672 |
+ # If we're importing a blank directory, and the target has a broken symlink, then do nothing.
|
|
| 673 |
+ pass
|
|
| 674 |
+ elif isinstance(x, remote_execution_pb2.FileNode):
|
|
| 675 |
+ # Files with the same name, or symlinks to files, get removed.
|
|
| 676 |
+ pass
|
|
| 677 |
+ else:
|
|
| 678 |
+ # There's either a symlink (valid or not) or existing directory with this name, so do nothing.
|
|
| 679 |
+ pass
|
|
| 680 |
+ else:
|
|
| 681 |
+ self.create_directory(f)
|
|
| 655 | 682 |
else:
|
| 656 | 683 |
# We're importing a file or symlink - replace anything with the same name.
|
| 657 | 684 |
print("Import of file/symlink {} into this directory. Removing anything existing...".format(f))
|
| ... | ... | @@ -736,18 +763,22 @@ class CasBasedDirectory(Directory): |
| 736 | 763 |
def showdiff(self, other):
|
| 737 | 764 |
print("Diffing {} and {}:".format(self, other))
|
| 738 | 765 |
|
| 739 |
- def compare_list(l1, l2):
|
|
| 766 |
+ def compare_list(l1, l2, name):
|
|
| 740 | 767 |
item2 = None
|
| 741 | 768 |
index = 0
|
| 742 |
- print("Comparing lists: {} vs {}".format([d.name for d in l1], [d.name for d in l2]))
|
|
| 769 |
+ print("Comparing {} lists: {} vs {}".format(name, [d.name for d in l1], [d.name for d in l2]))
|
|
| 743 | 770 |
for item1 in l1:
|
| 744 | 771 |
if index>=len(l2):
|
| 745 | 772 |
print("l2 is short: no item to correspond to '{}' in l1.".format(item1.name))
|
| 746 | 773 |
return False
|
| 747 | 774 |
item2 = l2[index]
|
| 748 | 775 |
if item1.name != item2.name:
|
| 749 |
- print("Items do not match: {}, a {} in l1, vs {}, a {} in l2".format(item1.name, self._describe(item1), item2.name, self._describe(item2)))
|
|
| 776 |
+ print("Items do not match in {} list: {}, a {} in l1, vs {}, a {} in l2".format(name, item1.name, self._describe(item1), item2.name, self._describe(item2)))
|
|
| 750 | 777 |
return False
|
| 778 |
+ if isinstance(item1, remote_execution_pb2.FileNode):
|
|
| 779 |
+ if item1.is_executable != item2.is_executable:
|
|
| 780 |
+ print("Executable flags do not match on file {}.".format(item1.name))
|
|
| 781 |
+ return False
|
|
| 751 | 782 |
index += 1
|
| 752 | 783 |
if index != len(l2):
|
| 753 | 784 |
print("l2 is long: Has extra items {}".format(l2[index:]))
|
| ... | ... | @@ -755,17 +786,19 @@ class CasBasedDirectory(Directory): |
| 755 | 786 |
return True
|
| 756 | 787 |
|
| 757 | 788 |
def compare_pb2_directories(d1, d2):
|
| 758 |
- result = (compare_list(d1.directories, d2.directories)
|
|
| 759 |
- and compare_list(d1.symlinks, d2.symlinks)
|
|
| 760 |
- and compare_list(d1.files, d2.files))
|
|
| 789 |
+ result = (compare_list(d1.directories, d2.directories, "directory")
|
|
| 790 |
+ and compare_list(d1.symlinks, d2.symlinks, "symlink")
|
|
| 791 |
+ and compare_list(d1.files, d2.files, "file"))
|
|
| 761 | 792 |
return result
|
| 762 | 793 |
|
| 763 | 794 |
if not compare_pb2_directories(self.pb2_directory, other.pb2_directory):
|
| 764 | 795 |
return False
|
| 765 | 796 |
|
| 766 | 797 |
for d in self.pb2_directory.directories:
|
| 767 |
- self.index[d.name].buildstream_object.showdiff(other.index[d.name].buildstream_object)
|
|
| 798 |
+ if not self.index[d.name].buildstream_object.showdiff(other.index[d.name].buildstream_object):
|
|
| 799 |
+ return False
|
|
| 768 | 800 |
print("No differences found in {}".format(self))
|
| 801 |
+ return True
|
|
| 769 | 802 |
|
| 770 | 803 |
def show_files_recursive(self):
|
| 771 | 804 |
elems = []
|
| ... | ... | @@ -829,7 +862,8 @@ class CasBasedDirectory(Directory): |
| 829 | 862 |
with tempfile.TemporaryDirectory(prefix="roundtrip") as tmpdir:
|
| 830 | 863 |
external_pathspec.export_files(tmpdir)
|
| 831 | 864 |
if files is None:
|
| 832 |
- files = list_relative_paths(tmpdir)
|
|
| 865 |
+ files = list(list_relative_paths(tmpdir))
|
|
| 866 |
+ print("Importing from filesystem: filelist is: {}".format(files))
|
|
| 833 | 867 |
duplicate_cas._import_files_from_directory(tmpdir, files=files)
|
| 834 | 868 |
duplicate_cas._recalculate_recursing_down()
|
| 835 | 869 |
if duplicate_cas.parent:
|
| ... | ... | @@ -24,59 +24,61 @@ root_filesets = [ |
| 24 | 24 |
[('a/b/c/textfile1', 'F', 'This is the replacement textfile 1\n')],
|
| 25 | 25 |
[('a/b/d', 'D', '')],
|
| 26 | 26 |
[('a/b/c', 'S', '/a/b/d')],
|
| 27 |
- [('a/b/d', 'D', ''), ('a/b/c', 'S', '/a/b/d')],
|
|
| 27 |
+ [('a/b/d', 'D', ''), ('a/b/c', 'S', '/a/b/d')]
|
|
| 28 | 28 |
]
|
| 29 | 29 |
|
| 30 | 30 |
empty_hash_ref = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
| 31 | 31 |
RANDOM_SEED = 69105
|
| 32 | 32 |
|
| 33 | 33 |
|
| 34 |
-def generate_import_roots(directory):
|
|
| 35 |
- for fileset in range(1, len(root_filesets) + 1):
|
|
| 36 |
- rootname = "root{}".format(fileset)
|
|
| 37 |
- rootdir = os.path.join(directory, "content", rootname)
|
|
| 38 |
- |
|
| 39 |
- for (path, typesymbol, content) in root_filesets[fileset - 1]:
|
|
| 40 |
- if typesymbol == 'F':
|
|
| 41 |
- (dirnames, filename) = os.path.split(path)
|
|
| 42 |
- os.makedirs(os.path.join(rootdir, dirnames), exist_ok=True)
|
|
| 43 |
- with open(os.path.join(rootdir, dirnames, filename), "wt") as f:
|
|
| 44 |
- f.write(content)
|
|
| 45 |
- elif typesymbol == 'D':
|
|
| 46 |
- os.makedirs(os.path.join(rootdir, path), exist_ok=True)
|
|
| 47 |
- elif typesymbol == 'S':
|
|
| 48 |
- (dirnames, filename) = os.path.split(path)
|
|
| 49 |
- os.makedirs(os.path.join(rootdir, dirnames), exist_ok=True)
|
|
| 50 |
- os.symlink(content, os.path.join(rootdir, path))
|
|
| 51 |
- |
|
| 52 |
- |
|
| 53 |
-def generate_random_roots(directory):
|
|
| 54 |
- random.seed(RANDOM_SEED)
|
|
| 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]))
|
|
| 34 |
+def generate_import_roots(rootno, directory):
|
|
| 35 |
+ rootname = "root{}".format(rootno)
|
|
| 36 |
+ rootdir = os.path.join(directory, "content", rootname)
|
|
| 37 |
+ |
|
| 38 |
+ for (path, typesymbol, content) in root_filesets[rootno - 1]:
|
|
| 39 |
+ if typesymbol == 'F':
|
|
| 40 |
+ (dirnames, filename) = os.path.split(path)
|
|
| 41 |
+ os.makedirs(os.path.join(rootdir, dirnames), exist_ok=True)
|
|
| 42 |
+ with open(os.path.join(rootdir, dirnames, filename), "wt") as f:
|
|
| 43 |
+ f.write(content)
|
|
| 44 |
+ elif typesymbol == 'D':
|
|
| 45 |
+ os.makedirs(os.path.join(rootdir, path), exist_ok=True)
|
|
| 46 |
+ elif typesymbol == 'S':
|
|
| 47 |
+ (dirnames, filename) = os.path.split(path)
|
|
| 48 |
+ os.makedirs(os.path.join(rootdir, dirnames), exist_ok=True)
|
|
| 49 |
+ os.symlink(content, os.path.join(rootdir, path))
|
|
| 50 |
+ |
|
| 51 |
+ |
|
| 52 |
+def generate_random_root(rootno, directory):
|
|
| 53 |
+ random.seed(RANDOM_SEED+rootno)
|
|
| 54 |
+ rootname = "root{}".format(rootno)
|
|
| 55 |
+ rootdir = os.path.join(directory, "content", rootname)
|
|
| 56 |
+ things = []
|
|
| 57 |
+ locations = ['.']
|
|
| 58 |
+ os.makedirs(rootdir)
|
|
| 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 |
+ description = thing
|
|
| 65 |
+ if thing == 'dir':
|
|
| 66 |
+ os.makedirs(target)
|
|
| 67 |
+ locations.append(os.path.join(location, thingname))
|
|
| 68 |
+ elif thing == 'file':
|
|
| 69 |
+ with open(target, "wt") as f:
|
|
| 70 |
+ f.write("This is node {}\n".format(i))
|
|
| 71 |
+ elif thing == 'link':
|
|
| 72 |
+ # TODO: Make some relative symlinks
|
|
| 73 |
+ if random.randint(1, 3) == 1 or len(things) == 0:
|
|
| 74 |
+ os.symlink("/broken", target)
|
|
| 75 |
+ description = "symlink pointing to /broken"
|
|
| 76 |
+ else:
|
|
| 77 |
+ symlink_destination = random.choice(things)
|
|
| 78 |
+ os.symlink(symlink_destination, target)
|
|
| 79 |
+ description = "symlink pointing to {}".format(symlink_destination)
|
|
| 80 |
+ things.append(os.path.join(location, thingname))
|
|
| 81 |
+ print("Generated {}/{}, a {}".format(rootdir, things[-1], description))
|
|
| 80 | 82 |
|
| 81 | 83 |
|
| 82 | 84 |
def file_contents(path):
|
| ... | ... | @@ -143,20 +145,21 @@ def directory_not_empty(path): |
| 143 | 145 |
return os.listdir(path)
|
| 144 | 146 |
|
| 145 | 147 |
|
| 146 |
-@pytest.mark.parametrize("original,overlay", combinations([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]))
|
|
| 147 |
-def test_cas_import(cli, tmpdir, original, overlay):
|
|
| 148 |
+def _import_test(tmpdir, original, overlay, generator_function, verify_contents=False):
|
|
| 148 | 149 |
fake_context = FakeContext()
|
| 149 | 150 |
fake_context.artifactdir = tmpdir
|
| 150 | 151 |
# Create some fake content
|
| 151 |
- generate_import_roots(tmpdir)
|
|
| 152 |
- generate_random_roots(tmpdir)
|
|
| 152 |
+ generator_function(original, tmpdir)
|
|
| 153 |
+ if original != overlay:
|
|
| 154 |
+ generator_function(overlay, tmpdir)
|
|
| 155 |
+
|
|
| 153 | 156 |
d = create_new_casdir(original, fake_context, tmpdir)
|
| 154 | 157 |
d2 = create_new_casdir(overlay, fake_context, tmpdir)
|
| 155 | 158 |
print("Importing dir {} into {}".format(overlay, original))
|
| 156 | 159 |
d.import_files(d2)
|
| 157 | 160 |
d.export_files(os.path.join(tmpdir, "output"))
|
| 158 | 161 |
|
| 159 |
- if overlay < 6:
|
|
| 162 |
+ if verify_contents:
|
|
| 160 | 163 |
for item in root_filesets[overlay - 1]:
|
| 161 | 164 |
(path, typename, content) = item
|
| 162 | 165 |
realpath = resolve_symlinks(path, os.path.join(tmpdir, "output"))
|
| ... | ... | @@ -184,14 +187,19 @@ def test_cas_import(cli, tmpdir, original, overlay): |
| 184 | 187 |
d3.import_files(d2)
|
| 185 | 188 |
assert d.ref.hash == d3.ref.hash
|
| 186 | 189 |
|
| 190 |
+@pytest.mark.parametrize("original,overlay", combinations(range(1,6)))
|
|
| 191 |
+def test_fixed_cas_import(cli, tmpdir, original, overlay):
|
|
| 192 |
+ _import_test(tmpdir, original, overlay, generate_import_roots, verify_contents=True)
|
|
| 193 |
+ |
|
| 194 |
+@pytest.mark.parametrize("original,overlay", combinations(range(1,11)))
|
|
| 195 |
+def test_random_cas_import(cli, tmpdir, original, overlay):
|
|
| 196 |
+ _import_test(tmpdir, original, overlay, generate_random_root, verify_contents=False)
|
|
| 187 | 197 |
|
| 188 |
-@pytest.mark.parametrize("root", [1, 2, 3, 4, 5, 6])
|
|
| 189 |
-def test_directory_listing(cli, tmpdir, root):
|
|
| 198 |
+def _listing_test(tmpdir, root, generator_function):
|
|
| 190 | 199 |
fake_context = FakeContext()
|
| 191 | 200 |
fake_context.artifactdir = tmpdir
|
| 192 | 201 |
# Create some fake content
|
| 193 |
- generate_import_roots(tmpdir)
|
|
| 194 |
- generate_random_roots(tmpdir)
|
|
| 202 |
+ generator_function(root, tmpdir)
|
|
| 195 | 203 |
|
| 196 | 204 |
d = create_new_filedir(root, tmpdir)
|
| 197 | 205 |
filelist = list(d.list_relative_paths())
|
| ... | ... | @@ -204,3 +212,12 @@ def test_directory_listing(cli, tmpdir, root): |
| 204 | 212 |
print("filelist for root {} via CasBasedDirectory:".format(root))
|
| 205 | 213 |
print("{}".format(filelist2))
|
| 206 | 214 |
assert filelist == filelist2
|
| 215 |
+
|
|
| 216 |
+ |
|
| 217 |
+@pytest.mark.parametrize("root", range(1,11))
|
|
| 218 |
+def test_random_directory_listing(cli, tmpdir, root):
|
|
| 219 |
+ _listing_test(tmpdir, root, generate_random_root)
|
|
| 220 |
+
|
|
| 221 |
+@pytest.mark.parametrize("root", [1, 2, 3, 4, 5])
|
|
| 222 |
+def test_fixed_directory_listing(cli, tmpdir, root):
|
|
| 223 |
+ _listing_test(tmpdir, root, generate_import_roots)
|
