mirror of
https://github.com/facebook/sapling.git
synced 2024-10-12 01:39:21 +03:00
ef8c3435c4
Summary: We no longer use repository configs, so remove the `repository` subcommand that supported adding and listing these configurations. The main information that used to be included in the repository configuration was the bind mount settings. This has since been replaced with the `.eden-redirections` file that is placed directly in each repository. Reviewed By: wez Differential Revision: D20876462 fbshipit-source-id: cc7d8e6f0a6a2e04fbf3159417af41a44908b3a8
168 lines
6.6 KiB
Python
168 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2.
|
|
|
|
import pathlib
|
|
import subprocess
|
|
import unittest
|
|
from pathlib import Path
|
|
from typing import Tuple
|
|
|
|
from eden.test_support.temporary_directory import TemporaryDirectoryMixin
|
|
|
|
from .lib import edenclient, overlay as overlay_mod, repobase, testcase
|
|
|
|
|
|
FSCK_RETCODE_OK = 0
|
|
FSCK_RETCODE_SKIPPED = 1
|
|
FSCK_RETCODE_WARNINGS = 2
|
|
FSCK_RETCODE_ERRORS = 3
|
|
|
|
|
|
# pyre-ignore[13]: T62487924
|
|
class FsckTest(testcase.EdenRepoTest):
|
|
overlay: overlay_mod.OverlayStore
|
|
|
|
def populate_repo(self) -> None:
|
|
self.repo.write_file("README.md", "tbd\n")
|
|
self.repo.write_file("proj/src/main.c", "int main() { return 0; }\n")
|
|
self.repo.write_file("proj/src/lib.c", "void foo() {}\n")
|
|
self.repo.write_file("proj/src/include/lib.h", "#pragma once\nvoid foo();\n")
|
|
self.repo.write_file(
|
|
"proj/test/test.sh", "#!/bin/bash\necho test\n", mode=0o755
|
|
)
|
|
self.repo.write_file("doc/foo.txt", "foo\n")
|
|
self.repo.write_file("doc/bar.txt", "bar\n")
|
|
self.repo.symlink("proj/doc", "../doc")
|
|
self.repo.commit("Initial commit.")
|
|
|
|
def create_repo(self, name: str) -> repobase.Repository:
|
|
return self.create_hg_repo("main")
|
|
|
|
def setup_eden_test(self) -> None:
|
|
super().setup_eden_test()
|
|
self.overlay = overlay_mod.OverlayStore(self.eden, self.mount_path)
|
|
|
|
def run_fsck(self, *args: str) -> Tuple[int, str]:
|
|
"""Run `eden fsck [args]` and return a tuple of the return code and
|
|
the combined stdout and stderr.
|
|
|
|
The command output will be decoded as UTF-8 and returned as a string.
|
|
"""
|
|
cmd_result = self.eden.run_unchecked(
|
|
"fsck", *args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
|
)
|
|
fsck_out = cmd_result.stdout.decode("utf-8", errors="replace")
|
|
return (cmd_result.returncode, fsck_out)
|
|
|
|
def test_fsck_force_and_check_only(self) -> None:
|
|
"""Test the behavior of the --force and --check-only fsck flags."""
|
|
foo_overlay_path = self.overlay.materialize_file(pathlib.Path("doc/foo.txt"))
|
|
|
|
# Running fsck with the mount still mounted should fail
|
|
returncode, fsck_out = self.run_fsck(self.mount)
|
|
self.assertIn(f"Not checking {self.mount}", fsck_out)
|
|
self.assertEqual(FSCK_RETCODE_SKIPPED, returncode)
|
|
|
|
# Running fsck with --force should override that
|
|
returncode, fsck_out = self.run_fsck(self.mount, "--force")
|
|
self.assertIn(f"warning: could not obtain lock", fsck_out)
|
|
self.assertIn(f"scanning anyway due to --force", fsck_out)
|
|
self.assertIn(f"Checking {self.mount}", fsck_out)
|
|
self.assertEqual(FSCK_RETCODE_OK, returncode)
|
|
|
|
# fsck should perform the check normally without --force
|
|
# if the mount is not mounted
|
|
self.eden.run_cmd("unmount", self.mount)
|
|
returncode, fsck_out = self.run_fsck(self.mount)
|
|
self.assertIn(f"Checking {self.mount}", fsck_out)
|
|
self.assertIn("No issues found", fsck_out)
|
|
self.assertEqual(FSCK_RETCODE_OK, returncode)
|
|
|
|
# Truncate the overlay file for doc/foo.txt to 0 length
|
|
with foo_overlay_path.open("wb"):
|
|
pass
|
|
|
|
# Running fsck with --check-only should report the error but not try to fix it.
|
|
returncode, fsck_out = self.run_fsck("--check-only")
|
|
self.assertIn(f"Checking {self.mount}", fsck_out)
|
|
self.assertRegex(
|
|
fsck_out,
|
|
r"invalid overlay file for materialized file .* \(doc/foo.txt\).*: "
|
|
r"zero-sized overlay file",
|
|
)
|
|
self.assertRegex(fsck_out, r"\b1 errors")
|
|
self.assertRegex(fsck_out, "Not fixing errors: --check-only was specified")
|
|
self.assertEqual(FSCK_RETCODE_ERRORS, returncode)
|
|
|
|
# Running fsck with no arguments should attempt to fix the errors
|
|
returncode, fsck_out = self.run_fsck()
|
|
self.assertRegex(
|
|
fsck_out,
|
|
r"invalid overlay file for materialized file .* \(doc/foo.txt\).*: "
|
|
r"zero-sized overlay file",
|
|
)
|
|
self.assertRegex(fsck_out, r"\b1 errors")
|
|
self.assertRegex(fsck_out, "Beginning repairs")
|
|
self.assertRegex(
|
|
fsck_out, "replacing corrupt file inode 'doc/foo.txt' with an empty file"
|
|
)
|
|
self.assertRegex(fsck_out, "Fixed 1 of 1 issues")
|
|
self.assertEqual(FSCK_RETCODE_ERRORS, returncode)
|
|
|
|
# There should be no more errors if we run fsck again
|
|
returncode, fsck_out = self.run_fsck()
|
|
self.assertIn(f"Checking {self.mount}", fsck_out)
|
|
self.assertIn("No issues found", fsck_out)
|
|
self.assertEqual(FSCK_RETCODE_OK, returncode)
|
|
|
|
def test_fsck_multiple_mounts(self) -> None:
|
|
mount2 = Path(self.mounts_dir) / "second_mount"
|
|
mount3 = Path(self.mounts_dir) / "third_mount"
|
|
mount4 = Path(self.mounts_dir) / "fourth_mount"
|
|
|
|
self.eden.clone(self.repo.path, mount2)
|
|
self.eden.clone(self.repo.path, mount3)
|
|
self.eden.clone(self.repo.path, mount4)
|
|
|
|
# Unmount all but mount3
|
|
self.eden.unmount(Path(self.mount))
|
|
self.eden.unmount(mount2)
|
|
self.eden.unmount(mount4)
|
|
|
|
# Running fsck should check all but mount3
|
|
returncode, fsck_out = self.run_fsck()
|
|
self.assertIn(f"Checking {self.mount}", fsck_out)
|
|
self.assertIn(f"Checking {mount2}", fsck_out)
|
|
self.assertIn(f"Not checking {mount3}", fsck_out)
|
|
self.assertIn(f"Checking {mount4}", fsck_out)
|
|
self.assertEqual(FSCK_RETCODE_SKIPPED, returncode)
|
|
|
|
# Running fsck with --force should check everything
|
|
returncode, fsck_out = self.run_fsck("--force")
|
|
self.assertIn(f"Checking {self.mount}", fsck_out)
|
|
self.assertIn(f"Checking {mount2}", fsck_out)
|
|
self.assertIn(f"Checking {mount3}", fsck_out)
|
|
self.assertIn(f"Checking {mount4}", fsck_out)
|
|
self.assertEqual(FSCK_RETCODE_OK, returncode)
|
|
|
|
|
|
class FsckTestNoEdenfs(unittest.TestCase, TemporaryDirectoryMixin):
|
|
def test_fsck_no_checkouts(self) -> None:
|
|
tmp_dir = self.make_temporary_directory()
|
|
eden = edenclient.EdenFS(Path(tmp_dir))
|
|
cmd_result = eden.run_unchecked(
|
|
"fsck",
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
encoding="utf-8",
|
|
errors="replace",
|
|
)
|
|
self.assertIn(
|
|
"No Eden checkouts are configured. Nothing to check.", cmd_result.stderr
|
|
)
|
|
self.assertEqual("", cmd_result.stdout)
|
|
self.assertEqual(0, cmd_result.returncode)
|