def test_nfs_mounted(self, mock_is_nfs_mounted) -> None: 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( typing.cast(EdenInstance, instance), dry_run, instance.mount_table, fs_util=FakeFsUtil(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), 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)
def test_privhelper_check_accessible( self, mock_check_privhelper_connection) -> None: instance = FakeEdenInstance(self.make_temporary_directory()) mount = instance.create_test_mount("path1").path dry_run = False out = TestOutput() exit_code = doctor.cure_what_ails_you( # pyre-fixme[6]: For 1st param expected `EdenInstance` but got # `FakeEdenInstance`. instance, dry_run, instance.mount_table, fs_util=FakeFsUtil(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), out=out, ) self.assertEqual( f"""\ Checking {mount} <green>No issues detected.<reset> """, out.getvalue(), ) self.assertEqual(0, exit_code)
def test_privhelper_check_not_accessible(self, mock_check_privhelper_connection): instance = FakeEdenInstance(self.make_temporary_directory()) mount = instance.create_test_mount("path1").path dry_run = False out = TestOutput() exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=FakeFsUtil(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), out=out, ) self.assertEqual( f"""\ <yellow>- Found problem:<reset> The PrivHelper process is not accessible. To restore the connection to the PrivHelper, run `eden restart` 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/ """, 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=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(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), 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)
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(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), 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_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.", )
def test_edenfs_not_running(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance(self.make_temporary_directory(), status=fb303_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=FakeFsUtil(), proc_utils=self.make_proc_utils(), 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)
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(), proc_utils=self.make_proc_utils(), 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)
def test_twice_failed_bind_unmount( self, mock_buckd_running: MagicMock, mock_expand_path: MagicMock, mock_analyze: MagicMock, mock_bind_unmount: MagicMock, ) -> None: temp_dir = self.make_temporary_directory() repo_path = os.path.join(temp_dir, "test") mock_bind_unmount.return_value = None mock_analyze.return_value = RepoPathDisposition.IS_BIND_MOUNT mock_expand_path.return_value = Path(repo_path) mock_buckd_running.return_value = False instance = FakeEdenInstance(temp_dir) checkout = instance.create_test_mount("mount_dir") redir = Redirection( repo_path=Path(repo_path), redir_type=RedirectionType.BIND, target=None, source="mount", state=RedirectionState.UNKNOWN_MOUNT, ) with self.assertRaises(Exception) as ex: redir.remove_existing(checkout) error_msg = f"Failed to remove {repo_path} since the bind unmount failed" self.assertEqual(str(ex.exception), error_msg)
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_fixup_only_eden_redirection( self, mock_get_effective_redirections: MagicMock, mock_require_checkout: MagicMock, mock_remove_existing: MagicMock, mock_apply: MagicMock, ) -> None: temp_dir = self.make_temporary_directory() repo_path = os.path.join(temp_dir, "test") instance = FakeEdenInstance(temp_dir) checkout = instance.create_test_mount("mount_dir") eden_path = os.path.join(temp_dir, "mount_dir") mock_require_checkout.return_value = (instance, checkout, eden_path) mock_argument_parser = MagicMock(spec=argparse.ArgumentParser) args = argparse.Namespace(mount=eden_path, all_sources=False) redir = Redirection( repo_path=Path(repo_path), redir_type=RedirectionType.BIND, target=None, source="mount", state=RedirectionState.UNKNOWN_MOUNT, ) mock_get_effective_redirections.return_value = {repo_path: redir} test_fixup_cmd = FixupCmd(mock_argument_parser) test_fixup_cmd.run(args) mock_remove_existing.assert_not_called() mock_apply.assert_not_called()
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_materialized_are_accessible(self, mock_debugInodeStatus) -> None: instance = FakeEdenInstance(self.make_temporary_directory()) checkout = instance.create_test_mount("path1") mount = checkout.path # Just create a/b/c folders os.makedirs(mount / "a" / "b") mock_debugInodeStatus.return_value = [ # Pretend that a/b is a file (it's a directory) TreeInodeDebugInfo( 1, b"a", True, b"abcd", [ TreeInodeEntryDebugInfo(b"b", 2, stat.S_IFREG, False, True, b"dcba") ], 1, ), # Pretent that a/b/c is a directory (it doesn't exists) TreeInodeDebugInfo( 2, b"a/b", True, b"dcba", [ TreeInodeEntryDebugInfo(b"c", 3, stat.S_IFREG, False, True, b"1234") ], 1, ), ] tracker = ProblemCollector() check_materialized_are_accessible(tracker, typing.cast(EdenInstance, instance), checkout) self.assertEqual( tracker.problems[0].description(), "a/b/c is inaccessible despite EdenFS believing it should be", ) self.assertEqual( tracker.problems[1].description(), "a/b is known to EdenFS as a file, but is a directory on disk", )
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)
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=FakeFsUtil(), proc_utils=self.make_proc_utils(), out=out, ) v.stdout = out.getvalue() return v
def run_varying_nfs(self, mock_path_read_text) -> NfsDoctorResult: instance = FakeEdenInstance(self.make_temporary_directory()) shared_path = "shared_path" mount_dir = "mount_dir" mock_path_read_text.return_value = shared_path client_path = str(instance.create_test_mount(mount_dir).path) dry_run = True out = TestOutput() exit_code = doctor.cure_what_ails_you( typing.cast(EdenInstance, instance), dry_run, instance.mount_table, fs_util=FakeFsUtil(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), out=out, ) return NfsDoctorResult( mount_dir, shared_path, client_path, instance, exit_code, out.getvalue() )
def test_not_all_mounts_have_watchman_watcher(self, mock_get_roots_for_nuclide, mock_watchman) -> None: 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( # pyre-fixme[6]: For 1st param expected `EdenInstance` but got # `FakeEdenInstance`. instance, dry_run, mount_table=instance.mount_table, fs_util=FakeFsUtil(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), 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_loaded_content(self, mock_debugInodeStatus, mock_getSHA1) -> None: instance = FakeEdenInstance(self.make_temporary_directory()) checkout = instance.create_test_mount("path1") with open(checkout.path / "a", "wb") as f: f.write(b"foobar") mock_getSHA1.return_value = [SHA1Result(b"\x01\x02\x03\x04")] mock_debugInodeStatus.return_value = [ TreeInodeDebugInfo( 1, b"", True, b"abcd", [ TreeInodeEntryDebugInfo(b"a", 2, stat.S_IFREG, True, False, b"1234") ], ) ] def fake_PrjGetOnDiskFileState(path: Path) -> PRJ_FILE_STATE: if path == checkout.path / "a": return PRJ_FILE_STATE.HydratedPlaceholder else: return PRJ_FILE_STATE.Placeholder tracker = ProblemCollector() check_loaded_content( tracker, typing.cast(EdenInstance, instance), checkout, fake_PrjGetOnDiskFileState, ) self.assertEqual( tracker.problems[0].description(), "The on-disk file at a is out of sync from EdenFS. Expected SHA1: 01020304, on-disk SHA1: 8843d7f92416211de9ebb963ff4ce28125932878", )
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_legacy().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(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), out=out, ) return exit_code, out.getvalue(), mounts
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), fs_util=self.fs_util, ) 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)
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(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), 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_eden_not_in_use(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance(self.make_temporary_directory(), status=fb303_status.DEAD) out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, FakeMountTable(), fs_util=FakeFsUtil(), proc_utils=self.make_proc_utils(), out=out, ) self.assertEqual("EdenFS is not in use.\n", out.getvalue()) self.assertEqual(0, exit_code)
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_eden_not_in_use(self, mock_get_roots_for_nuclide, mock_watchman) -> None: instance = FakeEdenInstance(self.make_temporary_directory(), status=fb303_status.DEAD) out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( # pyre-fixme[6]: For 1st param expected `EdenInstance` but got # `FakeEdenInstance`. instance, dry_run, FakeMountTable(), fs_util=FakeFsUtil(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), out=out, ) self.assertEqual("EdenFS is not in use.\n", out.getvalue()) self.assertEqual(0, exit_code)
def setUp(self) -> None: instance = FakeEdenInstance(self.make_temporary_directory()) self.checkout = instance.create_test_mount("path", )
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(), proc_utils=self.make_proc_utils(), kerberos_checker=FakeKerberosChecker(), 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)
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 test_interrupted_transaction(self) -> None: store = self.backing_repo / ".hg" / "store" store.mkdir() journal = store / "journal" journal.write_text("") 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: Found a journal file in backing repo, might have an interrupted transaction Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, out.getvalue(), ) 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=FakeFsUtil(), proc_utils=self.make_proc_utils(), 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