def _test_remount_checkouts(
        self,
        mock_get_roots_for_nuclide,
        mock_watchman,
        dry_run: bool,
        old_edenfs: bool = False,
    ) -> Tuple[int, str, List[Path]]:
        """Test that `eden doctor` remounts configured mount points that are not
        currently mounted.
        """
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)

        mounts = []
        mount1 = instance.create_test_mount("path1")
        mounts.append(mount1.path)
        mounts.append(instance.create_test_mount("path2", active=False).path)
        if old_edenfs:
            # Mimic older versions of edenfs, and do not return mount state data.
            instance.get_thrift_client().change_mount_state(mount1.path, None)

        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            typing.cast(EdenInstance, instance),
            dry_run,
            instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        return exit_code, out.getvalue(), mounts
    def test_nfs_mounted(self, mock_is_nfs_mounted):
        mock_is_nfs_mounted.return_value = True
        instance = FakeEdenInstance(self.make_temporary_directory())
        checkout = instance.create_test_mount("mount_dir")

        dry_run = True
        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        expected = f"""\
<yellow>- Found problem:<reset>
Eden's state directory is on an NFS file system: {instance.state_dir}
  This will likely cause performance problems and/or other errors.
The most common cause for this is if your ~/local symlink does not point to local disk.\
  Make sure that ~/local is a symlink pointing to local disk and then restart Eden.

Checking {checkout.path}
<yellow>- Found problem:<reset>
The Mercurial data directory for {checkout.path}/.hg/sharedpath is at \
{instance.default_backing_repo}/.hg which is on a NFS filesystem. \
Accessing files and directories in this repository will be slow.
<yellow>Discovered 2 problems during --dry-run<reset>
"""
        self.assertEqual(expected, out.getvalue())
        self.assertEqual(1, exit_code)
Exemple #3
0
    def test_edenfs_starting(self, mock_get_roots_for_nuclide, mock_watchman):
        instance = FakeEdenInstance(
            self.make_temporary_directory(), status=fb303_status.STARTING
        )
        instance.create_test_mount("eden-mount")

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            FakeMountTable(),
            fs_util=FakeFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            """\
<yellow>- Found problem:<reset>
Eden is currently still starting.
Please wait for edenfs to finish starting.
If Eden seems to be taking too long to start you can try restarting it
with "eden restart"

<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        self.assertEqual(1, exit_code)
Exemple #4
0
    def test_issue_includes_custom_message_from_config(self) -> None:
        self._mock_disk_usage(blocks=100000000, avail=500000)
        instance = FakeEdenInstance(
            self.make_temporary_directory(),
            config={
                "doctor.low-disk-space-message":
                "Ask your administrator for help."
            },
        )
        problems = self._check_disk_usage(instance=instance)
        self.assertEqual(
            problems[0].description(),
            "/ has only 512000000 bytes available. "
            "Eden lazily loads your files and needs enough disk "
            "space to store these files when loaded. Ask your administrator "
            "for help.",
        )

        self._mock_disk_usage(blocks=100000000, avail=2000000)
        instance = FakeEdenInstance(
            self.make_temporary_directory(),
            config={
                "doctor.low-disk-space-message":
                "Ask your administrator for help."
            },
        )
        problems = self._check_disk_usage(instance=instance)
        self.assertEqual(
            problems[0].description(),
            "/ is 98.00% full. "
            "Eden lazily loads your files and needs enough disk "
            "space to store these files when loaded. Ask your administrator "
            "for help.",
        )
Exemple #5
0
    def setUp(self) -> None:
        tmp_dir = self.make_temporary_directory()
        self.instance = FakeEdenInstance(tmp_dir)

        self.fbsource_client = os.path.join(self.instance.clients_path, "fbsource")
        self.fbsource_bind_mounts = os.path.join(self.fbsource_client, "bind-mounts")
        self.edenfs_path1 = str(
            self.instance.create_test_mount(
                "path1",
                bind_mounts={
                    "fbcode-buck-out": "fbcode/buck-out",
                    "fbandroid-buck-out": "fbandroid/buck-out",
                    "buck-out": "buck-out",
                },
                client_name="fbsource",
                setup_path=False,
            ).path
        )

        # Entries for later inclusion in client bind mount table
        self.client_bm1 = os.path.join(self.fbsource_bind_mounts, "fbcode-buck-out")
        self.client_bm2 = os.path.join(self.fbsource_bind_mounts, "fbandroid-buck-out")
        self.client_bm3 = os.path.join(self.fbsource_bind_mounts, "buck-out")

        # Entries for later inclusion in bind mount table
        self.bm1 = os.path.join(self.edenfs_path1, "fbcode/buck-out")
        self.bm2 = os.path.join(self.edenfs_path1, "fbandroid/buck-out")
        self.bm3 = os.path.join(self.edenfs_path1, "buck-out")
    def test_unconfigured_mounts_dont_crash(self, mock_get_roots_for_nuclide):
        # If Eden advertises that a mount is active, but it is not in the
        # configuration, then at least don't throw an exception.
        instance = FakeEdenInstance(self.make_temporary_directory())
        edenfs_path1 = instance.create_test_mount("path1").path
        edenfs_path2 = instance.create_test_mount("path2").path
        # Remove path2 from the list of mounts in the instance
        instance.remove_checkout_configuration(str(edenfs_path2))

        dry_run = False
        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            f"""\
Checking {edenfs_path1}
Checking {edenfs_path2}
<yellow>- Found problem:<reset>
Checkout {edenfs_path2} is running but not listed in Eden's configuration file.
Running "eden unmount {edenfs_path2}" will unmount this checkout.

<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        self.assertEqual(1, exit_code)
Exemple #7
0
    def test_edenfs_not_running(self, mock_get_roots_for_nuclide,
                                mock_watchman):
        instance = FakeEdenInstance(self.make_temporary_directory(),
                                    status=fb_status.DEAD)
        instance.create_test_mount("eden-mount")

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            FakeMountTable(),
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            """\
<yellow>- Found problem:<reset>
Eden is not running.
To start Eden, run:

    eden start

<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        self.assertEqual(1, exit_code)
Exemple #8
0
    def _test_hash_check(
        self,
        dirstate_hash_hex: str,
        snapshot_hex: str,
        dirstate_parent2_hash_hex=None,
        commit_checker: Optional[Callable[[str], bool]] = None,
    ) -> Tuple[EdenCheckout, doctor.ProblemFixer, str]:
        instance = FakeEdenInstance(self.make_temporary_directory())
        if dirstate_parent2_hash_hex is None:
            checkout = instance.create_test_mount(
                "path1", snapshot=snapshot_hex, dirstate_parent=dirstate_hash_hex
            )
        else:
            checkout = instance.create_test_mount(
                "path1",
                snapshot=snapshot_hex,
                dirstate_parent=(dirstate_hash_hex, dirstate_parent2_hash_hex),
            )

        hg_repo = checkout.instance.get_hg_repo(checkout.path)
        if commit_checker and hg_repo is not None:
            fake_hg_repo = typing.cast(FakeHgRepo, hg_repo)
            fake_hg_repo.commit_checker = commit_checker

        fixer, out = self.create_fixer(dry_run=False)
        check_hg.check_hg(fixer, checkout)
        return checkout, fixer, out.getvalue()
    def _test_hash_check(
        self,
        dirstate_hash_hex: str,
        snapshot_hex: str,
        dirstate_parent2_hash_hex=None,
        commit_checker: Optional[Callable[[bytes, str], bool]] = None,
    ) -> Tuple[EdenCheckout, doctor.ProblemFixer, str]:
        instance = FakeEdenInstance(self.make_temporary_directory())
        if dirstate_parent2_hash_hex is None:
            checkout = instance.create_test_mount(
                "path1", snapshot=snapshot_hex, dirstate_parent=dirstate_hash_hex
            )
        else:
            checkout = instance.create_test_mount(
                "path1",
                snapshot=snapshot_hex,
                dirstate_parent=(dirstate_hash_hex, dirstate_parent2_hash_hex),
            )

        if commit_checker:
            client = typing.cast(FakeClient, checkout.instance.get_thrift_client())
            client.commit_checker = commit_checker

        fixer, out = self.create_fixer(dry_run=False)
        check_hg.check_hg(fixer, checkout)
        return checkout, fixer, out.getvalue()
    def test_edenfs_stopping(self, mock_get_roots_for_nuclide, mock_watchman):
        instance = FakeEdenInstance(
            self.make_temporary_directory(), status=fb_status.STOPPING
        )
        instance.create_test_mount("eden-mount")

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            FakeMountTable(),
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            """\
<yellow>- Found problem:<reset>
Eden is currently shutting down.
Either wait for edenfs to exit, or to forcibly kill Eden, run:

    eden stop --kill

<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        self.assertEqual(1, exit_code)
    def test_edenfs_starting(self, mock_get_roots_for_nuclide, mock_watchman):
        instance = FakeEdenInstance(
            self.make_temporary_directory(), status=fb_status.STARTING
        )
        instance.create_test_mount("eden-mount")

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            FakeMountTable(),
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            """\
<yellow>- Found problem:<reset>
Eden is currently still starting.
Please wait for edenfs to finish starting.
If Eden seems to be taking too long to start you can try restarting it
with "eden restart"

<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        self.assertEqual(1, exit_code)
Exemple #12
0
    def test_unconfigured_mounts_dont_crash(self, mock_get_roots_for_nuclide):
        # If Eden advertises that a mount is active, but it is not in the
        # configuration, then at least don't throw an exception.
        instance = FakeEdenInstance(self.make_temporary_directory())
        edenfs_path1 = instance.create_test_mount("path1").path
        edenfs_path2 = instance.create_test_mount("path2").path
        # Remove path2 from the list of mounts in the instance
        instance.remove_checkout_configuration(str(edenfs_path2))

        dry_run = False
        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=FakeFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            f"""\
Checking {edenfs_path1}
Checking {edenfs_path2}
<yellow>- Found problem:<reset>
Checkout {edenfs_path2} is running but not listed in Eden's configuration file.
Running "eden unmount {edenfs_path2}" will unmount this checkout.

<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        self.assertEqual(1, exit_code)
Exemple #13
0
    def test_nfs_mounted(self, mock_is_nfs_mounted):
        mock_is_nfs_mounted.return_value = True
        instance = FakeEdenInstance(self.make_temporary_directory())
        checkout = instance.create_test_mount("mount_dir")

        dry_run = True
        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        expected = f"""\
<yellow>- Found problem:<reset>
Eden's state directory is on an NFS file system: {instance.state_dir}
  This will likely cause performance problems and/or other errors.
The most common cause for this is if your ~/local symlink does not point to local disk.\
  Make sure that ~/local is a symlink pointing to local disk and then restart Eden.

Checking {checkout.path}
<yellow>- Found problem:<reset>
The Mercurial data directory for {checkout.path}/.hg/sharedpath is at \
{instance.default_backing_repo}/.hg which is on a NFS filesystem. \
Accessing files and directories in this repository will be slow.
<yellow>Discovered 2 problems during --dry-run<reset>
"""
        self.assertEqual(expected, out.getvalue())
        self.assertEqual(1, exit_code)
Exemple #14
0
    def test_edenfs_stopping(self, mock_get_roots_for_nuclide, mock_watchman):
        instance = FakeEdenInstance(
            self.make_temporary_directory(), status=fb303_status.STOPPING
        )
        instance.create_test_mount("eden-mount")

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            FakeMountTable(),
            fs_util=FakeFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            """\
<yellow>- Found problem:<reset>
Eden is currently shutting down.
Either wait for edenfs to exit, or to forcibly kill Eden, run:

    eden stop --kill

<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        self.assertEqual(1, exit_code)
Exemple #15
0
    def test_pwd_not_set(self) -> None:
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)
        mount = instance.create_test_mount("path1").path

        exit_code, out = self._test_with_pwd(instance, pwd=None)
        self.assertEqual(
            out,
            f"""\
Checking {mount}
<green>No issues detected.<reset>
""",
        )
        self.assertEqual(0, exit_code)
    def test_pwd_not_set(self) -> None:
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)
        mount = instance.create_test_mount("path1").path

        exit_code, out = self._test_with_pwd(instance, pwd=None)
        self.assertEqual(
            out,
            f"""\
Checking {mount}
<green>No issues detected.<reset>
""",
        )
        self.assertEqual(0, exit_code)
Exemple #17
0
 def setUp(self) -> None:
     test_config = {
         "doctor.minimum-kernel-version": "4.11.3-67",
         "doctor.known-bad-kernel-versions": "TODO,TEST",
     }
     tmp_dir = self.make_temporary_directory()
     self.instance = FakeEdenInstance(tmp_dir, config=test_config)
Exemple #18
0
    def run_varying_nfs(self, mock_path_read_text):
        instance = FakeEdenInstance(self.make_temporary_directory())
        v = SimpleNamespace(
            mount_dir="mount_dir", shared_path="shared_path", instance=instance
        )
        mock_path_read_text.return_value = v.shared_path
        v.client_path = str(instance.create_test_mount(v.mount_dir).path)

        dry_run = True
        out = TestOutput()
        v.exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        v.stdout = out.getvalue()
        return v
Exemple #19
0
    def run_varying_nfs(self, mock_path_read_text):
        instance = FakeEdenInstance(self.make_temporary_directory())
        v = SimpleNamespace(mount_dir="mount_dir",
                            shared_path="shared_path",
                            instance=instance)
        mock_path_read_text.return_value = v.shared_path
        v.client_path = str(instance.create_test_mount(v.mount_dir).path)

        dry_run = True
        out = TestOutput()
        v.exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        v.stdout = out.getvalue()
        return v
Exemple #20
0
    def _check_disk_usage(self, mock_problem_fixer) -> List[doctor.Problem]:
        instance = FakeEdenInstance(self.make_temporary_directory())

        doctor.check_filesystems.check_disk_usage(
            tracker=mock_problem_fixer,
            mount_paths=["/"],
            instance=typing.cast(EdenInstance, instance),
        )
        if mock_problem_fixer.add_problem.call_args:
            problem = mock_problem_fixer.add_problem.call_args[0][0]
            return [problem]
        return []
Exemple #21
0
    def _test_remount_checkouts(
        self,
        mock_get_roots_for_nuclide,
        mock_watchman,
        dry_run: bool,
        old_edenfs: bool = False,
    ) -> Tuple[int, str, List[Path]]:
        """Test that `eden doctor` remounts configured mount points that are not
        currently mounted.
        """
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)

        mounts = []
        mount1 = instance.create_test_mount("path1")
        mounts.append(mount1.path)
        mounts.append(instance.create_test_mount("path2", active=False).path)
        if old_edenfs:
            # Mimic older versions of edenfs, and do not return mount state data.
            instance.get_thrift_client().change_mount_state(mount1.path, None)

        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            typing.cast(EdenInstance, instance),
            dry_run,
            instance.mount_table,
            fs_util=FakeFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        return exit_code, out.getvalue(), mounts
Exemple #22
0
    def _check_disk_usage(
            self,
            instance: Optional[FakeEdenInstance] = None) -> List[ProblemBase]:
        problem_collector = ProblemCollector()
        if instance is None:
            instance = FakeEdenInstance(self.make_temporary_directory())

        doctor.check_filesystems.check_disk_usage(
            tracker=problem_collector,
            mount_paths=["/"],
            instance=typing.cast(EdenInstance, instance),
        )
        return problem_collector.problems
    def test_pwd_out_of_date(self) -> None:
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)
        mount = instance.create_test_mount("path1").path

        exit_code, out = self._test_with_pwd(instance, pwd=tmp_dir)
        self.assertEqual(
            out,
            f"""\
<yellow>- Found problem:<reset>
Your current working directory is out-of-date.
This can happen if you have (re)started Eden but your shell is still pointing to
the old directory from before the Eden checkouts were mounted.

Run "cd / && cd -" to update your shell's working directory.

Checking {mount}
<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
        )
        self.assertEqual(1, exit_code)
Exemple #24
0
    def test_pwd_out_of_date(self) -> None:
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)
        mount = instance.create_test_mount("path1").path

        exit_code, out = self._test_with_pwd(instance, pwd=tmp_dir)
        self.assertEqual(
            out,
            f"""\
<yellow>- Found problem:<reset>
Your current working directory is out-of-date.
This can happen if you have (re)started Eden but your shell is still pointing to
the old directory from before the Eden checkouts were mounted.

Run "cd / && cd -" to update your shell's working directory.

Checking {mount}
<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
        )
        self.assertEqual(1, exit_code)
Exemple #25
0
    def test_not_all_mounts_have_watchman_watcher(self,
                                                  mock_get_roots_for_nuclide,
                                                  mock_watchman):
        instance = FakeEdenInstance(self.make_temporary_directory())
        edenfs_path = str(
            instance.create_test_mount("eden-mount", scm_type="git").path)
        edenfs_path_not_watched = str(
            instance.create_test_mount("eden-mount-not-watched",
                                       scm_type="git").path)
        side_effects: List[Dict[str, Any]] = []
        calls = []

        calls.append(call(["watch-list"]))
        side_effects.append({"roots": [edenfs_path]})
        calls.append(call(["watch-project", edenfs_path]))
        side_effects.append({"watcher": "eden"})
        mock_watchman.side_effect = side_effects

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            mount_table=instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            f"Checking {edenfs_path}\n"
            f"Checking {edenfs_path_not_watched}\n"
            "<green>No issues detected.<reset>\n",
            out.getvalue(),
        )
        mock_watchman.assert_has_calls(calls)
        self.assertEqual(0, exit_code)
Exemple #26
0
    def test_watchman_fails(self, mock_watchman):
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)

        mount = instance.create_test_mount("path1", active=False).path

        # Make calls to watchman fail rather than returning expected output
        side_effects = [{"error": "watchman failed"}]
        mock_watchman.side_effect = side_effects

        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            typing.cast(EdenInstance, instance),
            dry_run=False,
            mount_table=instance.mount_table,
            fs_util=FakeFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        # "watchman watch-list" should have been called by the doctor code
        calls = [call(["watch-list"])]
        mock_watchman.assert_has_calls(calls)

        self.assertEqual(
            out.getvalue(),
            f"""\
Checking {mount}
<yellow>- Found problem:<reset>
{mount} is not currently mounted
Remounting {mount}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
        )
        self.assertEqual(exit_code, 0)
    def test_not_all_mounts_have_watchman_watcher(
        self, mock_get_roots_for_nuclide, mock_watchman
    ):
        instance = FakeEdenInstance(self.make_temporary_directory())
        edenfs_path = str(instance.create_test_mount("eden-mount", scm_type="git").path)
        edenfs_path_not_watched = str(
            instance.create_test_mount("eden-mount-not-watched", scm_type="git").path
        )
        side_effects: List[Dict[str, Any]] = []
        calls = []

        calls.append(call(["watch-list"]))
        side_effects.append({"roots": [edenfs_path]})
        calls.append(call(["watch-project", edenfs_path]))
        side_effects.append({"watcher": "eden"})
        mock_watchman.side_effect = side_effects

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            mount_table=instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            f"Checking {edenfs_path}\n"
            f"Checking {edenfs_path_not_watched}\n"
            "<green>No issues detected.<reset>\n",
            out.getvalue(),
        )
        mock_watchman.assert_has_calls(calls)
        self.assertEqual(0, exit_code)
    def test_watchman_fails(self, mock_watchman):
        tmp_dir = self.make_temporary_directory()
        instance = FakeEdenInstance(tmp_dir)

        mount = instance.create_test_mount("path1", active=False).path

        # Make calls to watchman fail rather than returning expected output
        side_effects = [{"error": "watchman failed"}]
        mock_watchman.side_effect = side_effects

        out = TestOutput()
        exit_code = doctor.cure_what_ails_you(
            typing.cast(EdenInstance, instance),
            dry_run=False,
            mount_table=instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        # "watchman watch-list" should have been called by the doctor code
        calls = [call(["watch-list"])]
        mock_watchman.assert_has_calls(calls)

        self.assertEqual(
            out.getvalue(),
            f"""\
Checking {mount}
<yellow>- Found problem:<reset>
{mount} is not currently mounted
Remounting {mount}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
        )
        self.assertEqual(exit_code, 0)
Exemple #29
0
    def test_eden_not_in_use(self, mock_get_roots_for_nuclide, mock_watchman):
        instance = FakeEdenInstance(self.make_temporary_directory(),
                                    status=fb_status.DEAD)

        out = TestOutput()
        dry_run = False
        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            FakeMountTable(),
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual("Eden is not in use.\n", out.getvalue())
        self.assertEqual(0, exit_code)
Exemple #30
0
    def _test_edenfs_version(
        self, mock_rpm_q, rpm_value: str
    ) -> Tuple[doctor.ProblemFixer, str]:
        side_effects: List[str] = []
        calls = []
        calls.append(call())
        side_effects.append(rpm_value)
        mock_rpm_q.side_effect = side_effects

        instance = FakeEdenInstance(
            self.make_temporary_directory(),
            build_info={
                "build_package_version": "20171213",
                "build_package_release": "165642",
            },
        )
        fixer, out = self.create_fixer(dry_run=False)
        doctor.check_edenfs_version(fixer, typing.cast(EdenInstance, instance))
        mock_rpm_q.assert_has_calls(calls)
        return fixer, out.getvalue()
    def test_end_to_end_test_with_various_scenarios(
        self, mock_get_roots_for_nuclide, mock_watchman
    ):
        side_effects: List[Dict[str, Any]] = []
        calls = []
        instance = FakeEdenInstance(self.make_temporary_directory())

        # In edenfs_path1, we will break the snapshot check.
        edenfs_path1_snapshot = "abcd" * 10
        edenfs_path1_dirstate_parent = "12345678" * 5
        edenfs_path1 = str(
            instance.create_test_mount(
                "path1",
                snapshot=edenfs_path1_snapshot,
                dirstate_parent=edenfs_path1_dirstate_parent,
            ).path
        )

        # In edenfs_path2, we will break the inotify check and the Nuclide
        # subscriptions check.
        edenfs_path2 = str(
            instance.create_test_mount("path2", scm_type="git", setup_path=False).path
        )

        # In edenfs_path3, we do not create the .hg directory
        edenfs_path3 = str(instance.create_test_mount("path3", setup_path=False).path)
        os.makedirs(edenfs_path3)

        # Assume all paths are used as root folders in a connected Nuclide.
        mock_get_roots_for_nuclide.return_value = {
            edenfs_path1,
            edenfs_path2,
            edenfs_path3,
        }

        calls.append(call(["watch-list"]))
        side_effects.append({"roots": [edenfs_path1, edenfs_path2, edenfs_path3]})

        calls.append(call(["watch-project", edenfs_path1]))
        side_effects.append({"watcher": "eden"})

        calls.append(call(["debug-get-subscriptions", edenfs_path1]))
        side_effects.append(
            _create_watchman_subscription(
                filewatcher_subscriptions=[f"filewatcher-{edenfs_path1}"]
            )
        )

        calls.append(call(["watch-project", edenfs_path2]))
        side_effects.append({"watcher": "inotify"})
        calls.append(call(["watch-del", edenfs_path2]))
        side_effects.append({"watch-del": True, "root": edenfs_path2})
        calls.append(call(["watch-project", edenfs_path2]))
        side_effects.append({"watcher": "eden"})

        calls.append(call(["debug-get-subscriptions", edenfs_path2]))
        side_effects.append(_create_watchman_subscription(filewatcher_subscriptions=[]))

        calls.append(call(["watch-project", edenfs_path3]))
        side_effects.append({"watcher": "eden"})
        calls.append(call(["debug-get-subscriptions", edenfs_path3]))
        side_effects.append(
            _create_watchman_subscription(
                filewatcher_subscriptions=[f"filewatcher-{edenfs_path3}"]
            )
        )

        mock_watchman.side_effect = side_effects

        out = TestOutput()
        dry_run = False

        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            f"""\
Checking {edenfs_path1}
<yellow>- Found problem:<reset>
Found inconsistent/missing data in {edenfs_path1}/.hg:
  mercurial's parent commit is {edenfs_path1_dirstate_parent}, \
but Eden's internal parent commit is {edenfs_path1_snapshot}
Repairing hg directory contents for {edenfs_path1}...<green>fixed<reset>

Checking {edenfs_path2}
<yellow>- Found problem:<reset>
Watchman is watching {edenfs_path2} with the wrong watcher type: \
"inotify" instead of "eden"
Fixing watchman watch for {edenfs_path2}...<green>fixed<reset>

<yellow>- Found problem:<reset>
Nuclide appears to be used to edit the following directories
under {edenfs_path2}:

  {edenfs_path2}

but the following Watchman subscriptions appear to be missing:

  filewatcher-{edenfs_path2}

This can cause file changes to fail to show up in Nuclide.
Currently, the only workaround for this is to run
"Nuclide Remote Projects: Kill And Restart" from the
command palette in Atom.

Checking {edenfs_path3}
<yellow>- Found problem:<reset>
Missing hg directory: {edenfs_path3}/.hg
Repairing hg directory contents for {edenfs_path3}...<green>fixed<reset>

<yellow>Successfully fixed 3 problems.<reset>
<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        mock_watchman.assert_has_calls(calls)
        self.assertEqual(1, exit_code)
Exemple #32
0
    def test_end_to_end_test_with_various_scenarios(
        self, mock_get_roots_for_nuclide, mock_watchman
    ):
        side_effects: List[Dict[str, Any]] = []
        calls = []
        instance = FakeEdenInstance(self.make_temporary_directory())

        # In edenfs_path1, we will break the snapshot check.
        edenfs_path1_snapshot = "abcd" * 10
        edenfs_path1_dirstate_parent = "12345678" * 5
        edenfs_path1 = str(
            instance.create_test_mount(
                "path1",
                snapshot=edenfs_path1_snapshot,
                dirstate_parent=edenfs_path1_dirstate_parent,
            ).path
        )

        # In edenfs_path2, we will break the inotify check and the Nuclide
        # subscriptions check.
        edenfs_path2 = str(
            instance.create_test_mount("path2", scm_type="git", setup_path=False).path
        )

        # In edenfs_path3, we do not create the .hg directory
        edenfs_path3 = str(instance.create_test_mount("path3", setup_path=False).path)
        os.makedirs(edenfs_path3)

        # Assume all paths are used as root folders in a connected Nuclide.
        mock_get_roots_for_nuclide.return_value = {
            edenfs_path1,
            edenfs_path2,
            edenfs_path3,
        }

        calls.append(call(["watch-list"]))
        side_effects.append({"roots": [edenfs_path1, edenfs_path2, edenfs_path3]})

        calls.append(call(["watch-project", edenfs_path1]))
        side_effects.append({"watcher": "eden"})

        calls.append(call(["debug-get-subscriptions", edenfs_path1]))
        side_effects.append(
            _create_watchman_subscription(
                filewatcher_subscriptions=[f"filewatcher-{edenfs_path1}"]
            )
        )

        calls.append(call(["watch-project", edenfs_path2]))
        side_effects.append({"watcher": "inotify"})
        calls.append(call(["watch-del", edenfs_path2]))
        side_effects.append({"watch-del": True, "root": edenfs_path2})
        calls.append(call(["watch-project", edenfs_path2]))
        side_effects.append({"watcher": "eden"})

        calls.append(call(["debug-get-subscriptions", edenfs_path2]))
        side_effects.append(_create_watchman_subscription(filewatcher_subscriptions=[]))

        calls.append(call(["watch-project", edenfs_path3]))
        side_effects.append({"watcher": "eden"})
        calls.append(call(["debug-get-subscriptions", edenfs_path3]))
        side_effects.append(
            _create_watchman_subscription(
                filewatcher_subscriptions=[f"filewatcher-{edenfs_path3}"]
            )
        )

        mock_watchman.side_effect = side_effects

        out = TestOutput()
        dry_run = False

        exit_code = doctor.cure_what_ails_you(
            instance,
            dry_run,
            instance.mount_table,
            fs_util=FakeFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )

        self.assertEqual(
            f"""\
Checking {edenfs_path1}
<yellow>- Found problem:<reset>
Found inconsistent/missing data in {edenfs_path1}/.hg:
  mercurial's parent commit is {edenfs_path1_dirstate_parent}, \
but Eden's internal parent commit is {edenfs_path1_snapshot}
Repairing hg directory contents for {edenfs_path1}...<green>fixed<reset>

Checking {edenfs_path2}
<yellow>- Found problem:<reset>
Watchman is watching {edenfs_path2} with the wrong watcher type: \
"inotify" instead of "eden"
Fixing watchman watch for {edenfs_path2}...<green>fixed<reset>

<yellow>- Found problem:<reset>
Nuclide appears to be used to edit the following directories
under {edenfs_path2}:

  {edenfs_path2}

but the following Watchman subscriptions appear to be missing:

  filewatcher-{edenfs_path2}

This can cause file changes to fail to show up in Nuclide.
Currently, the only workaround for this is to run
"Nuclide Remote Projects: Kill And Restart" from the
command palette in Atom.

Checking {edenfs_path3}
<yellow>- Found problem:<reset>
Missing hg directory: {edenfs_path3}/.hg
Repairing hg directory contents for {edenfs_path3}...<green>fixed<reset>

<yellow>Successfully fixed 3 problems.<reset>
<yellow>1 issue requires manual attention.<reset>
Ask in the Eden Users group if you need help fixing issues with Eden:
https://fb.facebook.com/groups/eden.users/
""",
            out.getvalue(),
        )
        mock_watchman.assert_has_calls(calls)
        self.assertEqual(1, exit_code)
Exemple #33
0
class BindMountsCheckTest(DoctorTestBase):
    maxDiff = None

    def setUp(self) -> None:
        tmp_dir = self.make_temporary_directory()
        self.instance = FakeEdenInstance(tmp_dir)

        self.fbsource_client = os.path.join(self.instance.clients_path, "fbsource")
        self.fbsource_bind_mounts = os.path.join(self.fbsource_client, "bind-mounts")
        self.edenfs_path1 = str(
            self.instance.create_test_mount(
                "path1",
                bind_mounts={
                    "fbcode-buck-out": "fbcode/buck-out",
                    "fbandroid-buck-out": "fbandroid/buck-out",
                    "buck-out": "buck-out",
                },
                client_name="fbsource",
                setup_path=False,
            ).path
        )

        # Entries for later inclusion in client bind mount table
        self.client_bm1 = os.path.join(self.fbsource_bind_mounts, "fbcode-buck-out")
        self.client_bm2 = os.path.join(self.fbsource_bind_mounts, "fbandroid-buck-out")
        self.client_bm3 = os.path.join(self.fbsource_bind_mounts, "buck-out")

        # Entries for later inclusion in bind mount table
        self.bm1 = os.path.join(self.edenfs_path1, "fbcode/buck-out")
        self.bm2 = os.path.join(self.edenfs_path1, "fbandroid/buck-out")
        self.bm3 = os.path.join(self.edenfs_path1, "buck-out")

    def run_check(
        self,
        mount_table: mtab.MountTable,
        dry_run: bool,
        fs_util: Optional[FakeFsUtil] = None,
    ) -> Tuple[doctor.ProblemFixer, str]:
        fixer, out = self.create_fixer(dry_run)
        if fs_util is None:
            fs_util = FakeFsUtil()
        checkout = EdenCheckout(
            typing.cast(EdenInstance, self.instance),
            Path(self.edenfs_path1),
            Path(self.fbsource_client),
        )
        check_bind_mounts.check_bind_mounts(
            fixer, checkout, mount_table=mount_table, fs_util=fs_util
        )
        return fixer, out.getvalue()

    def test_bind_mounts_okay(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual("", out)
        self.assert_results(fixer, num_problems=0)

    def test_bind_mounts_missing_dry_run(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbcode/buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbandroid/buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=3)

    def test_bind_mounts_missing(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        mount_table.bind_mount_success_paths[self.client_bm1] = self.bm1
        mount_table.bind_mount_success_paths[self.client_bm2] = self.bm2
        mount_table.bind_mount_success_paths[self.client_bm3] = self.bm3

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbcode/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbandroid/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/buck-out...<green>fixed<reset>

""",
            out,
        )
        self.assert_results(fixer, num_problems=3, num_fixed_problems=3)

    def test_bind_mounts_missing_fail(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # These bound mind operations will succeed.
        mount_table.bind_mount_success_paths[self.client_bm1] = self.bm1

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbcode/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbandroid/buck-out...<red>error<reset>
Failed to fix problem: Command 'sudo mount -o bind \
{self.fbsource_bind_mounts}/fbandroid-buck-out \
{self.edenfs_path1}/fbandroid/buck-out' returned non-zero exit status 1.

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/buck-out...<red>error<reset>
Failed to fix problem: Command \
'sudo mount -o bind {self.fbsource_bind_mounts}/buck-out \
{self.edenfs_path1}/buck-out' returned non-zero exit status 1.

""",
            out,
        )
        self.assert_results(
            fixer, num_problems=3, num_fixed_problems=1, num_failed_fixes=2
        )

    def test_bind_mounts_and_dir_missing_dry_run(self):
        mount_table = FakeMountTable()

        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Would create directory {self.fbsource_bind_mounts}/fbcode-buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbcode/buck-out

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbandroid-buck-out
Would create directory {self.fbsource_bind_mounts}/fbandroid-buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbandroid/buck-out

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Would create directory {self.fbsource_bind_mounts}/buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=6)

    def test_bind_mount_wrong_device_dry_run(self):
        # bm1, bm2 should not have same device as edenfs
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbcode/buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbandroid/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=2)

    def test_bind_mount_wrong_device(self):
        # bm1, bm2 should not have same device as edenfs
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # These bound mind operations will succeed.
        mount_table.bind_mount_success_paths[self.client_bm1] = self.bm1
        mount_table.bind_mount_success_paths[self.client_bm2] = self.bm2

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbcode/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbandroid/buck-out...<green>fixed<reset>

""",
            out,
        )
        self.assert_results(fixer, num_problems=2, num_fixed_problems=2)

    def test_client_mount_path_not_dir(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        # Note: client_bm3 is not a directory
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=33188
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Expected {self.fbsource_bind_mounts}/buck-out to be a directory
Please remove the file at {self.fbsource_bind_mounts}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=1, num_manual_fixes=1)

    def test_mount_path_not_dir(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        # Note: bm3 is not a directory
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=33188
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Expected {self.edenfs_path1}/buck-out to be a directory
Please remove the file at {self.edenfs_path1}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=1, num_manual_fixes=1)

    def test_client_bind_mounts_missing_dry_run(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Would create directory {self.fbsource_bind_mounts}/fbcode-buck-out

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Would create directory {self.fbsource_bind_mounts}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=2)

    def test_client_bind_mounts_missing(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Creating directory {self.fbsource_bind_mounts}/fbcode-buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Creating directory {self.fbsource_bind_mounts}/buck-out...<green>fixed<reset>

""",
            out,
        )
        self.assert_results(fixer, num_problems=2, num_fixed_problems=2)

    def test_client_bind_mounts_missing_fail(self):
        mount_table = FakeMountTable()
        fs_util = FakeFsUtil()

        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fs_util.path_error[self.client_bm3] = "Failed to create directory"

        fixer, out = self.run_check(mount_table, dry_run=False, fs_util=fs_util)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Creating directory {self.fbsource_bind_mounts}/fbcode-buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Creating directory {self.fbsource_bind_mounts}/buck-out...<red>error<reset>
Failed to fix problem: Failed to create directory

""",
            out,
        )
        self.assert_results(
            fixer, num_problems=2, num_fixed_problems=1, num_failed_fixes=1
        )

    def test_bind_mounts_and_client_dir_missing_dry_run(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbandroid-buck-out
Would create directory {self.fbsource_bind_mounts}/fbandroid-buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbandroid/buck-out

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Would create directory {self.fbsource_bind_mounts}/buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=4)

    def test_bind_mounts_and_client_dir_missing(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # These bound mind operations will succeed.
        mount_table.bind_mount_success_paths[self.client_bm2] = self.bm2
        mount_table.bind_mount_success_paths[self.client_bm3] = self.bm3

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbandroid-buck-out
Creating directory {self.fbsource_bind_mounts}/fbandroid-buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbandroid/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Creating directory {self.fbsource_bind_mounts}/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/buck-out...<green>fixed<reset>

""",
            out,
        )
        self.assert_results(fixer, num_problems=4, num_fixed_problems=4)

    def test_client_bind_mount_multiple_issues_dry_run(self):
        # Bind mount 1 does not exist
        # Bind mount 2 has wrong device type
        # Bind mount 3 is a file instead of a directory
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=33188
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Would create directory {self.fbsource_bind_mounts}/fbcode-buck-out

<yellow>- Found problem:<reset>
Expected {self.fbsource_bind_mounts}/buck-out to be a directory
Please remove the file at {self.fbsource_bind_mounts}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=2, num_manual_fixes=1)

    def test_client_bind_mount_multiple_issues(self):
        # Bind mount 1 does not exist
        # Bind mount 2 has wrong device type
        # Bind mount 3 is a file instead of a directory
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=33188
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Creating directory {self.fbsource_bind_mounts}/fbcode-buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Expected {self.fbsource_bind_mounts}/buck-out to be a directory
Please remove the file at {self.fbsource_bind_mounts}/buck-out

""",
            out,
        )
        self.assert_results(
            fixer, num_problems=2, num_fixed_problems=1, num_manual_fixes=1
        )
class CorruptHgTest(DoctorTestBase):
    maxDiff = None

    def setUp(self) -> None:
        self.instance = FakeEdenInstance(self.make_temporary_directory())
        self.checkout = self.instance.create_test_mount("test_mount", scm_type="hg")
        self.backing_repo = typing.cast(
            FakeEdenInstance, self.checkout.instance
        ).default_backing_repo

    def test_unreadable_hg_shared_path_is_a_problem(self) -> None:
        sharedpath_path = self.checkout.path / ".hg" / "sharedpath"
        sharedpath_path.unlink()
        sharedpath_path.symlink_to(sharedpath_path.name)

        out = self.cure_what_ails_you(dry_run=True)
        self.assertIn(
            "Failed to read .hg/sharedpath: "
            "[Errno 40] Too many levels of symbolic links",
            out.getvalue(),
        )

    def test_truncated_hg_dirstate_is_a_problem(self) -> None:
        dirstate_path = self.checkout.path / ".hg" / "dirstate"
        os.truncate(dirstate_path, dirstate_path.stat().st_size - 1)

        out = self.cure_what_ails_you(dry_run=True)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
Found inconsistent/missing data in {self.checkout.path}/.hg:
  error parsing .hg/dirstate: Reached EOF while reading checksum \
hash in {self.checkout.path}/.hg/dirstate.

Would repair hg directory contents for {self.checkout.path}

<yellow>Discovered 1 problem during --dry-run<reset>
""",
            out.getvalue(),
        )

    def test_missing_sharedpath_and_requires(self) -> None:
        sharedpath_path = self.checkout.path / ".hg" / "sharedpath"
        sharedpath_path.unlink()
        requires_path = self.checkout.path / ".hg" / "requires"
        requires_path.unlink()

        out = self.cure_what_ails_you(dry_run=False)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
Found inconsistent/missing data in {self.checkout.path}/.hg:
  error reading .hg/requires: [Errno 2] No such file or directory: \
{str(requires_path)!r}
  error reading .hg/sharedpath: [Errno 2] No such file or directory: \
{str(sharedpath_path)!r}
Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
            out.getvalue(),
        )
        self.assertIn("eden\n", requires_path.read_text())
        self.assertEqual(sharedpath_path.read_text(), str(self.backing_repo / ".hg"))

    def test_missing_hg_dir(self) -> None:
        hg_dir = self.checkout.path / ".hg"
        shutil.rmtree(hg_dir)

        out = self.cure_what_ails_you(dry_run=False)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
Missing hg directory: {self.checkout.path}/.hg
Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
            out.getvalue(),
        )
        self._verify_hg_dir()

    def test_empty_hg_dir(self) -> None:
        hg_dir = self.checkout.path / ".hg"
        shutil.rmtree(hg_dir)
        hg_dir.mkdir()

        out = self.cure_what_ails_you(dry_run=False)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
No contents present in hg directory: {self.checkout.path}/.hg
Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
            out.getvalue(),
        )
        self._verify_hg_dir()

    def _verify_hg_dir(self) -> None:
        hg_dir = self.checkout.path / ".hg"
        self.assertTrue((hg_dir / "dirstate").is_file())
        self.assertTrue((hg_dir / "hgrc").is_file())
        self.assertTrue((hg_dir / "requires").is_file())
        self.assertTrue((hg_dir / "sharedpath").is_file())
        self.assertTrue((hg_dir / "shared").is_file())
        self.assertTrue((hg_dir / "bookmarks").is_file())
        self.assertTrue((hg_dir / "branch").is_file())

        self.assert_dirstate_p0(self.checkout, FakeEdenInstance.default_commit_hash)
        self.assertIn("[extensions]\neden =\n", (hg_dir / "hgrc").read_text())
        self.assertIn("eden\n", (hg_dir / "requires").read_text())
        self.assertEqual(
            (hg_dir / "sharedpath").read_text(), str(self.backing_repo / ".hg")
        )
        self.assertEqual((hg_dir / "shared").read_text(), "bookmarks\n")
        self.assertEqual((hg_dir / "bookmarks").read_text(), "")
        self.assertEqual((hg_dir / "branch").read_text(), "default\n")

    def cure_what_ails_you(self, dry_run: bool) -> TestOutput:
        out = TestOutput()
        doctor.cure_what_ails_you(
            typing.cast(EdenInstance, self.instance),
            dry_run,
            self.instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        return out
 def setUp(self) -> None:
     self.instance = FakeEdenInstance(self.make_temporary_directory())
     self.checkout = self.instance.create_test_mount("test_mount", scm_type="hg")
     self.backing_repo = typing.cast(
         FakeEdenInstance, self.checkout.instance
     ).default_backing_repo
Exemple #36
0
 def setUp(self) -> None:
     self.instance = FakeEdenInstance(self.make_temporary_directory())
     self.checkout = self.instance.create_test_mount("test_mount",
                                                     scm_type="hg")
     self.backing_repo = typing.cast(
         FakeEdenInstance, self.checkout.instance).default_backing_repo
Exemple #37
0
class CorruptHgTest(DoctorTestBase):
    maxDiff = None

    def setUp(self) -> None:
        self.instance = FakeEdenInstance(self.make_temporary_directory())
        self.checkout = self.instance.create_test_mount("test_mount",
                                                        scm_type="hg")
        self.backing_repo = typing.cast(
            FakeEdenInstance, self.checkout.instance).default_backing_repo

    def test_unreadable_hg_shared_path_is_a_problem(self) -> None:
        sharedpath_path = self.checkout.path / ".hg" / "sharedpath"
        sharedpath_path.unlink()
        sharedpath_path.symlink_to(sharedpath_path.name)

        out = self.cure_what_ails_you(dry_run=True)
        self.assertIn(
            "Failed to read .hg/sharedpath: "
            "[Errno 40] Too many levels of symbolic links",
            out.getvalue(),
        )

    def test_truncated_hg_dirstate_is_a_problem(self) -> None:
        dirstate_path = self.checkout.path / ".hg" / "dirstate"
        os.truncate(dirstate_path, dirstate_path.stat().st_size - 1)

        out = self.cure_what_ails_you(dry_run=True)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
Found inconsistent/missing data in {self.checkout.path}/.hg:
  error parsing .hg/dirstate: Reached EOF while reading checksum \
hash in {self.checkout.path}/.hg/dirstate.

Would repair hg directory contents for {self.checkout.path}

<yellow>Discovered 1 problem during --dry-run<reset>
""",
            out.getvalue(),
        )

    def test_missing_sharedpath_and_requires(self) -> None:
        sharedpath_path = self.checkout.path / ".hg" / "sharedpath"
        sharedpath_path.unlink()
        requires_path = self.checkout.path / ".hg" / "requires"
        requires_path.unlink()

        out = self.cure_what_ails_you(dry_run=False)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
Found inconsistent/missing data in {self.checkout.path}/.hg:
  error reading .hg/requires: [Errno 2] No such file or directory: \
{str(requires_path)!r}
  error reading .hg/sharedpath: [Errno 2] No such file or directory: \
{str(sharedpath_path)!r}
Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
            out.getvalue(),
        )
        self.assertIn("eden\n", requires_path.read_text())
        self.assertEqual(sharedpath_path.read_text(),
                         str(self.backing_repo / ".hg"))

    def test_missing_hg_dir(self) -> None:
        hg_dir = self.checkout.path / ".hg"
        shutil.rmtree(hg_dir)

        out = self.cure_what_ails_you(dry_run=False)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
Missing hg directory: {self.checkout.path}/.hg
Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
            out.getvalue(),
        )
        self._verify_hg_dir()

    def test_empty_hg_dir(self) -> None:
        hg_dir = self.checkout.path / ".hg"
        shutil.rmtree(hg_dir)
        hg_dir.mkdir()

        out = self.cure_what_ails_you(dry_run=False)
        self.assertEqual(
            f"""\
Checking {self.checkout.path}
<yellow>- Found problem:<reset>
No contents present in hg directory: {self.checkout.path}/.hg
Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset>

<yellow>Successfully fixed 1 problem.<reset>
""",
            out.getvalue(),
        )
        self._verify_hg_dir()

    def _verify_hg_dir(self) -> None:
        hg_dir = self.checkout.path / ".hg"
        self.assertTrue((hg_dir / "dirstate").is_file())
        self.assertTrue((hg_dir / "hgrc").is_file())
        self.assertTrue((hg_dir / "requires").is_file())
        self.assertTrue((hg_dir / "sharedpath").is_file())
        self.assertTrue((hg_dir / "shared").is_file())
        self.assertTrue((hg_dir / "bookmarks").is_file())
        self.assertTrue((hg_dir / "branch").is_file())

        self.assert_dirstate_p0(self.checkout,
                                FakeEdenInstance.default_commit_hash)
        self.assertIn("[extensions]\neden =\n", (hg_dir / "hgrc").read_text())
        self.assertIn("eden\n", (hg_dir / "requires").read_text())
        self.assertEqual((hg_dir / "sharedpath").read_text(),
                         str(self.backing_repo / ".hg"))
        self.assertEqual((hg_dir / "shared").read_text(), "bookmarks\n")
        self.assertEqual((hg_dir / "bookmarks").read_text(), "")
        self.assertEqual((hg_dir / "branch").read_text(), "default\n")

    def cure_what_ails_you(self, dry_run: bool) -> TestOutput:
        out = TestOutput()
        doctor.cure_what_ails_you(
            typing.cast(EdenInstance, self.instance),
            dry_run,
            self.instance.mount_table,
            fs_util=filesystem.LinuxFsUtil(),
            process_finder=self.make_process_finder(),
            out=out,
        )
        return out