示例#1
0
class ProjectRecoveryModelTest(unittest.TestCase):
    def setUp(self):
        self.pr = ProjectRecovery(multifileinterpreter=None)

        # Make absolutely sure that the workbench-recovery directory is cleared.
        if os.path.exists(self.pr.recovery_directory):
            shutil.rmtree(self.pr.recovery_directory)

        # Set up some checkpoints
        self.setup_some_checkpoints()

        self.pr._make_process_from_pid = mock.MagicMock()
        self.pr._is_mantid_workbench_process = mock.MagicMock(
            return_value=True)
        self.prm = ProjectRecoveryModel(self.pr, mock.MagicMock())

    def tearDown(self):
        # Make sure to clear the hostname layer between tests
        ADS.clear()

        if os.path.exists(self.pid):
            shutil.rmtree(self.pid)

    def setup_some_checkpoints(self):
        self.pr._spin_off_another_time_thread = mock.MagicMock()
        directory = self.pr.recovery_directory_hostname

        # Add a numbered folder for the pid
        self.pid = os.path.join(directory, "3000000")
        if not os.path.exists(self.pid):
            os.makedirs(self.pid)
        self.pr._recovery_directory_pid = self.pid

        # Add 5 workspaces
        for ii in range(0, 5):
            CreateSampleWorkspace(OutputWorkspace=str(ii))

        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.pr.recovery_save()

    def test_find_number_of_workspaces_in_directory(self):
        # Expect 0 as just checkpoints
        self.assertEqual(
            self.prm.find_number_of_workspaces_in_directory(self.pid), 0)

        self.assertTrue(os.path.exists(self.pid))
        list_dir = os.listdir(self.pid)
        list_dir.sort()
        self.assertEqual(
            self.prm.find_number_of_workspaces_in_directory(
                os.path.join(self.pid, list_dir[0])), 5)

    def test_get_row_as_string(self):
        row = self.prm.rows[0]
        self.assertEqual(self.prm.get_row(row[0]), row)

    def test_get_row_as_int(self):
        row = self.prm.rows[0]
        self.assertEqual(self.prm.get_row(0), row)

    def test_get_row_as_string_not_found(self):
        row = ["", "", ""]
        self.assertEqual(self.prm.get_row("asdadasdasd"), row)

    def test_start_mantid_normally(self):
        self.prm.start_mantid_normally()
        self.assertEqual(self.prm.presenter.close_view.call_count, 1)

    def test_recover_selected_checkpoint(self):
        checkpoint = os.listdir(self.pid)[0]
        self.prm._start_recovery_of_checkpoint = mock.MagicMock()
        self.prm.recover_selected_checkpoint(checkpoint)

        self.assertEqual(
            1,
            self.prm.presenter.change_start_mantid_to_cancel_label.call_count)
        self.assertEqual(1, self.prm._start_recovery_of_checkpoint.call_count)

    def test_open_selected_in_editor(self):
        checkpoint = os.listdir(self.pid)[0]
        self.prm.project_recovery.open_checkpoint_in_script_editor = mock.MagicMock(
        )
        self.prm.open_selected_in_editor(checkpoint)

        self.assertEqual(
            1, self.prm.project_recovery.open_checkpoint_in_script_editor.
            call_count)
        self.assertEqual(
            self.prm.project_recovery.open_checkpoint_in_script_editor.
            call_args, mock.call(os.path.join(self.pid, checkpoint)))

    def test_decide_last_checkpoint(self):
        CreateSampleWorkspace(OutputWorkspace="6")
        self.pr.recovery_save()

        checkpoints = os.listdir(self.pid)
        checkpoints.sort()

        last_checkpoint = self.prm.decide_last_checkpoint()
        self.assertEqual(checkpoints[-1], os.path.basename(last_checkpoint))

    def test_fill_rows(self):
        # wait a second so that we can add a second checkpoint with a different name, because the checkpoints differ at
        # most by a second.
        time.sleep(1)

        CreateSampleWorkspace(OutputWorkspace="6")
        self.pr.recovery_save()

        self.prm.fill_rows()

        checkpoints = os.listdir(self.pid)
        checkpoints.sort()

        self.assertEqual(["", "", ""], self.prm.rows[2])
        self.assertEqual([checkpoints[0].replace("T", " "), "5", "No"],
                         self.prm.rows[1])
        self.assertEqual([checkpoints[1].replace("T", " "), "6", "No"],
                         self.prm.rows[0])

    def test_get_number_of_checkpoints(self):
        self.assertEqual(int(ConfigService.getString(NO_OF_CHECKPOINTS_KEY)),
                         self.prm.get_number_of_checkpoints())

    def test_update_checkpoint_tried(self):
        checkpoints = os.listdir(self.pid)

        self.assertEqual(self.prm.rows[0][2], "No")
        self.prm._update_checkpoint_tried(
            os.path.join(self.pid, checkpoints[0]))
        self.assertEqual(self.prm.rows[0][2], "Yes")
class ProjectRecoverySaverTest(unittest.TestCase):
    def setUp(self):
        self.working_directory = tempfile.mkdtemp()

        self.pr = ProjectRecovery(None)
        self.pr_saver = self.pr.saver

    def tearDown(self):
        ADS.clear()
        if os.path.exists(self.pr.recovery_directory_hostname):
            shutil.rmtree(self.pr.recovery_directory_hostname)

        if os.path.exists(self.working_directory):
            shutil.rmtree(self.working_directory)

    def test_add_lock_file(self):
        self.pr_saver._add_lock_file(self.working_directory)
        self.assertTrue(
            os.path.exists(
                os.path.join(self.working_directory, self.pr.lock_file_name)))

    def test_remove_lock_file(self):
        self.pr_saver._add_lock_file(self.working_directory)
        self.assertTrue(
            os.path.exists(
                os.path.join(self.working_directory, self.pr.lock_file_name)))

        self.pr_saver._remove_lock_file(self.working_directory)
        self.assertTrue(not os.path.exists(
            os.path.join(self.working_directory, self.pr.lock_file_name)))

    def test_recovery_save_when_nothing_is_present(self):
        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.assertIsNone(self.pr.recovery_save())

    def test_recovery_save_with_just_workspaces(self):
        CreateSampleWorkspace(OutputWorkspace="ws")
        self.pr_saver._spin_off_another_time_thread = mock.MagicMock()

        # Do save
        self.pr_saver.recovery_save()

        # Check 0.py was made
        checkpoint = self.pr.listdir_fullpath(
            self.pr.recovery_directory_pid)[0]
        self.assertTrue(os.path.exists(os.path.join(checkpoint, "0.py")))
        self.assertEqual(
            self.pr_saver._spin_off_another_time_thread.call_count, 1)

    @mock.patch(
        'workbench.projectrecovery.projectrecoverysaver.find_all_windows_that_are_savable'
    )
    def test_recovery_save_with_just_interfaces(self,
                                                windows_that_are_savable):
        CreateSampleWorkspace(OutputWorkspace="ws")
        windows_that_are_savable.return_value = [[
            FakeEncoder(), FakeEncoder()
        ]]
        self.pr_saver._spin_off_another_time_thread = mock.MagicMock()
        ADS.clear()

        self.pr_saver.gfm = mock.MagicMock()
        self.pr_saver.gfm.figs = {}

        self.pr_saver.recovery_save()

        # Check no 0.py was made
        checkpoint = self.pr.listdir_fullpath(
            self.pr.recovery_directory_pid)[0]
        self.assertTrue(not os.path.exists(os.path.join(checkpoint, "0.py")))
        self.assertEqual(
            self.pr_saver._spin_off_another_time_thread.call_count, 1)

        # Read the .json file and check nothing is in workspace and something is in the interfaces dictionary
        with open(
                os.path.join(checkpoint, (os.path.basename(checkpoint) +
                                          self.pr.recovery_file_ext))) as f:
            dictionary = json.load(f)
            self.assertEqual(len(dictionary["interfaces"]), 1)
            self.assertEqual(len(dictionary["workspaces"]), 0)

    def test_empty_group_workspaces(self):
        CreateSampleWorkspace(OutputWorkspace="ws")
        CreateSampleWorkspace(OutputWorkspace="ws1")
        GroupWorkspaces(OutputWorkspace="Group", InputWorkspaces="ws,ws1")
        group_workspace = ADS.retrieve("Group")
        group_workspace.remove("ws")
        group_workspace.remove("ws1")

        self.assertTrue(self.pr_saver._empty_group_workspace(group_workspace))

    @mock.patch('workbench.projectrecovery.projectrecoverysaver.Timer')
    def test_spin_of_another_thread(self, timer):
        self.pr_saver._spin_off_another_time_thread()
        timer.assert_has_calls([mock.call().start()])

    def test_save_workspaces(self):
        CreateSampleWorkspace(OutputWorkspace="ws1")
        CreateSampleWorkspace(OutputWorkspace="ws2")

        self.pr_saver._save_workspaces(self.working_directory)

        # Assert that the 0.py and 1.py that are expected are made
        self.assertTrue(
            os.path.exists(os.path.join(self.working_directory, "0.py")))
        self.assertTrue(
            os.path.exists(os.path.join(self.working_directory, "1.py")))

    def test_save_project(self):
        self.pr_saver.gfm = mock.MagicMock()
        self.pr_saver.gfm.figs = {}

        self.pr_saver._save_project(self.working_directory)

        project_file = os.path.join(self.working_directory,
                                    (os.path.basename(self.working_directory) +
                                     self.pr.recovery_file_ext))
        self.assertTrue(os.path.exists(project_file))
        with open(project_file) as f:
            dictionary = json.load(f)
            self.assertEqual(len(dictionary["interfaces"]), 0)
            self.assertEqual(len(dictionary["workspaces"]), 0)

    @mock.patch(
        'workbench.projectrecovery.projectrecoverysaver.find_all_windows_that_are_savable'
    )
    def test_recovery_save_with_both_workspace_and_interfaces(
            self, windows_that_are_savable):
        CreateSampleWorkspace(OutputWorkspace="ws")
        windows_that_are_savable.return_value = [[
            FakeEncoder(), FakeEncoder()
        ]]
        self.pr_saver._spin_off_another_time_thread = mock.MagicMock()

        self.pr_saver.gfm = mock.MagicMock()
        self.pr_saver.gfm.figs = {}

        self.pr_saver.recovery_save()

        # Check 0.py was made
        checkpoint = self.pr.listdir_fullpath(
            self.pr.recovery_directory_pid)[0]
        self.assertTrue(os.path.exists(os.path.join(checkpoint, "0.py")))
        self.assertEqual(
            self.pr_saver._spin_off_another_time_thread.call_count, 1)

        # Read the .json file and check nothing is in workspace and something is in the interfaces dictionary
        with open(
                os.path.join(checkpoint, (os.path.basename(checkpoint) +
                                          self.pr.recovery_file_ext))) as f:
            dictionary = json.load(f)
            self.assertEqual(len(dictionary["interfaces"]), 1)
            self.assertEqual(len(dictionary["workspaces"]), 1)

    def test_start_recovery_thread_if_thread_on_is_false(self):
        self.pr_saver._timer_thread = mock.MagicMock()
        self.pr_saver.thread_on = False
        self.pr_saver.pr.recovery_enabled = True

        self.pr_saver.start_recovery_thread()

        self.assertEqual(self.pr_saver._timer_thread.start.call_count, 1)

    def test_stop_recovery_thread(self):
        self.pr_saver._timer_thread = mock.MagicMock()

        self.pr_saver.stop_recovery_thread()

        self.assertEqual(self.pr_saver._timer_thread.cancel.call_count, 1)
示例#3
0
class ProjectRecoveryTest(unittest.TestCase):
    def setUp(self):
        self.multifileinterpreter = mock.MagicMock()
        self.pr = ProjectRecovery(self.multifileinterpreter)
        self.working_directory = tempfile.mkdtemp()
        # Make sure there is actually a different modified time on the files
        self.firstPath = tempfile.mkdtemp()
        self.secondPath = tempfile.mkdtemp()
        self.thirdPath = tempfile.mkdtemp()

        # offset the date modified stamps in the past in case future modified dates
        # cause any problems
        finalFileDateTime = datetime.datetime.fromtimestamp(os.path.getmtime(self.thirdPath))

        dateoffset = finalFileDateTime - datetime.timedelta(hours=2)
        modTime = time.mktime(dateoffset.timetuple())
        os.utime(self.firstPath, (modTime, modTime))
        dateoffset = finalFileDateTime - datetime.timedelta(hours=1)
        modTime = time.mktime(dateoffset.timetuple())
        os.utime(self.secondPath, (modTime, modTime))

    def tearDown(self):
        ADS.clear()
        if os.path.exists(self.pr.recovery_directory_hostname):
            shutil.rmtree(self.pr.recovery_directory_hostname)

        if os.path.exists(self.working_directory):
            shutil.rmtree(self.working_directory)

        if os.path.exists(self.firstPath):
            shutil.rmtree(self.firstPath)

        if os.path.exists(self.secondPath):
            shutil.rmtree(self.secondPath)

        if os.path.exists(self.thirdPath):
            shutil.rmtree(self.thirdPath)

    def test_constructor_settings_are_set(self):
        # Test the paths set in the constructor that are generated.
        self.assertEqual(self.pr.recovery_directory,
                         os.path.join(ConfigService.getAppDataDirectory(), "workbench-recovery"))
        self.assertEqual(self.pr.recovery_directory_hostname,
                         os.path.join(ConfigService.getAppDataDirectory(), "workbench-recovery", socket.gethostname()))
        self.assertEqual(self.pr.recovery_directory_pid,
                         os.path.join(ConfigService.getAppDataDirectory(), "workbench-recovery", socket.gethostname(),
                                      str(os.getpid())))
        self.assertEqual(self.pr.recovery_order_workspace_history_file,
                         os.path.join(ConfigService.getAppDataDirectory(), "ordered_recovery.py"))

        # Test config service values
        self.assertEqual(self.pr.time_between_saves, int(ConfigService[SAVING_TIME_KEY]))
        self.assertEqual(self.pr.maximum_num_checkpoints, int(ConfigService[NO_OF_CHECKPOINTS_KEY]))
        self.assertEqual(self.pr.recovery_enabled, ("true" == ConfigService[RECOVERY_ENABLED_KEY].lower()))

    def test_start_recovery_thread_if_thread_on_is_true(self):
        self.pr._timer_thread = mock.MagicMock()
        self.pr.thread_on = True
        self.pr.recovery_enabled = True

        self.pr.start_recovery_thread()

        self.assertEqual(self.pr._timer_thread.start.call_count, 0)

    def test_remove_empty_dir(self):
        if not os.path.exists(self.pr.recovery_directory):
            os.makedirs(self.pr.recovery_directory)

        empty = os.path.join(self.pr.recovery_directory, "empty")
        os.mkdir(os.path.join(self.pr.recovery_directory, "empty"))

        # Feed parent to temp directory here, on Linux = '/tmp'
        parent_path, _ = os.path.split(empty)
        self.pr._remove_empty_folders_from_dir(parent_path)

        self.assertTrue(not os.path.exists(empty))

    @mock.patch('workbench.projectrecovery.projectrecovery.logger')
    def test_remove_empty_dir_logs_outside_of_workbench_directory(self, logger_mock):
        os.mkdir(os.path.join(self.working_directory, "temp"))
        self.pr._remove_empty_folders_from_dir(self.working_directory)

        logger_mock.warning.assert_called_once()

    @mock.patch('workbench.projectrecovery.projectrecovery.logger')
    def test_remove_all_folders_from_dir_outside_of_mantid_dir_logs_warning(self, logger_mock):
        self.pr._remove_directory_and_directory_trees(self.working_directory)

        logger_mock.warning.assert_called_once()

    def test_remove_all_folders_from_dir_doesnt_raise_inside_of_mantid_dir(self):
        temp_dir = os.path.join(self.pr.recovery_directory, "tempDir")
        os.mkdir(temp_dir)

        self.pr._remove_directory_and_directory_trees(temp_dir)

        self.assertTrue(not os.path.exists(temp_dir))

    @unittest.skipIf(is_macOS(), "Can be unreliable on macOS and is a test of logic not OS capability")
    def test_sort_paths_by_last_modified(self):
        paths = [self.secondPath, self.thirdPath, self.firstPath]
        paths = self.pr.sort_by_last_modified(paths)

        self.assertListEqual(paths, [self.firstPath, self.secondPath, self.thirdPath])

    def test_get_pid_folder_to_be_used_to_load_a_checkpoint_from(self):
        self.pr._make_process_from_pid = mock.MagicMock()
        self.pr._is_mantid_workbench_process = mock.MagicMock(return_value=True)
        # Needs to be a high number outside of possible pids
        one = os.path.join(self.pr.recovery_directory_hostname, "10000000")
        two = os.path.join(self.pr.recovery_directory_hostname, "20000000")

        # Make the first pid folder, neither pid will be in use but it should select the oldest first
        os.makedirs(one)
        time.sleep(0.01)
        os.makedirs(two)

        result = self.pr.get_pid_folder_to_load_a_checkpoint_from()
        self.assertEqual(one, result)

    def test_get_pid_folder_returns_pid_if_access_denied(self):
        pid = os.path.join(self.pr.recovery_directory_hostname, "10000000")
        os.makedirs(pid)
        with mock.patch('psutil.Process.cmdline', side_effect=psutil.AccessDenied()):
            result = self.pr.get_pid_folder_to_load_a_checkpoint_from()
        self.assertEqual(pid, result)

    def test_list_dir_full_path(self):
        one = os.path.join(self.working_directory, "10000000")
        two = os.path.join(self.working_directory, "20000000")
        os.makedirs(one)
        os.makedirs(two)

        # There is no concern for list order in this equality assertion
        self.assertCountEqual([one, two], self.pr.listdir_fullpath(self.working_directory))

    def test_recovery_save_when_nothing_is_present(self):
        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.assertIsNone(self.pr.recovery_save())

    def test_check_for_recovery_where_no_checkpoints_exist(self):
        self.assertTrue(not self.pr.check_for_recover_checkpoint())

    @staticmethod
    def create_checkpoints_at_directory(directory, number):
        for ii in range(0, number):
            os.makedirs(os.path.join(directory, (str(ii) + "00000000"), "spare_dir"))

    def test_check_for_recovery_when_2_instances_exist_with_2_checkpoints(self):
        # Doesn't do recovery so returns false

        # Create the 2 checkpoints
        self.create_checkpoints_at_directory(self.pr.recovery_directory_hostname, 2)
        self.pr._number_of_other_workbench_processes = mock.MagicMock(return_value=2)

        self.assertTrue(not self.pr.check_for_recover_checkpoint())

    def test_check_for_recovery_when_2_instances_exist_with_3_checkpoints(self):
        # Does recovery so returns true
        # Create the 3 checkpoints
        self.create_checkpoints_at_directory(self.pr.recovery_directory_hostname, 3)
        self.pr._number_of_other_workbench_processes = mock.MagicMock(return_value=2)

        self.assertTrue(self.pr.check_for_recover_checkpoint())

    def test_remove_oldest_checkpoints_when_no_errors_present(self):
        self.pr._recovery_directory_pid = self.working_directory
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()

        for ii in range(0, self.pr.maximum_num_checkpoints+1):
            os.mkdir(os.path.join(self.working_directory, "dir"+str(ii)))
            time.sleep(0.01)

        self.pr.remove_oldest_checkpoints()

        # Now should have had a call made to delete working_directory + dir0
        self.pr._remove_directory_and_directory_trees.assert_called_with(os.path.join(self.working_directory, "dir0"))

    @mock.patch('workbench.projectrecovery.projectrecovery.logger')
    def test_remove_oldest_checkpoints_logs_when_directory_not_removable(self, logger_mock):
        self.pr._recovery_directory_pid = self.working_directory
        self.pr._remove_directory_and_directory_trees = mock.MagicMock(side_effect=OSError("Error removing directory"))

        for ii in range(0, self.pr.maximum_num_checkpoints+1):
            os.mkdir(os.path.join(self.working_directory, "dir"+str(ii)))
            time.sleep(0.01)

        self.pr.remove_oldest_checkpoints()

        # Now should have had a call made to delete working_directory + dir0
        self.pr._remove_directory_and_directory_trees.assert_called_with(os.path.join(self.working_directory, "dir0"))
        logger_mock.warning.assert_called_once()

    def test_clear_all_unused_checkpoints_called_with_none_and_only_one_user(self):
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        os.makedirs(self.pr.recovery_directory_hostname)

        self.pr.clear_all_unused_checkpoints()

        self.pr._remove_directory_and_directory_trees.assert_called_with(self.pr.recovery_directory_hostname,
                                                                         ignore_errors=True)

    def test_clear_all_unused_checkpoints_called_with_none_and_multiple_users(self):
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        os.makedirs(self.pr.recovery_directory_hostname)
        user = os.path.join(self.pr.recovery_directory, "dimitar")
        if os.path.exists(user):
            shutil.rmtree(user)
        os.makedirs(user)

        self.pr.clear_all_unused_checkpoints()

        self.pr._remove_directory_and_directory_trees.assert_called_with(self.pr.recovery_directory_hostname,
                                                                         ignore_errors=True)

    def test_clear_all_unused_checkpoints_called_with_not_none(self):
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        os.makedirs(self.pr.recovery_directory_hostname)

        self.pr.clear_all_unused_checkpoints(pid_dir=self.pr.recovery_directory_hostname)

        self.pr._remove_directory_and_directory_trees.assert_called_with(self.pr.recovery_directory_hostname,
                                                                         ignore_errors=True)

    def _repair_checkpoint_checkpoints_setup(self, checkpoint1, checkpoint2, pid, pid2):
        if os.path.exists(pid):
            shutil.rmtree(pid)
        os.makedirs(pid)
        os.makedirs(checkpoint1)
        if os.path.exists(pid2):
            shutil.rmtree(pid2)
        os.makedirs(pid2)
        os.makedirs(checkpoint2)

        # Add a lock file to checkpoint 1
        open(os.path.join(checkpoint1, self.pr.lock_file_name), 'a').close()

        # Add one workspace to the checkpoint and change modified dates to older than a month
        os.utime(pid2, (1, 1))

    def _repair_checkpoints_assertions(self, checkpoint1, checkpoint2, pid, pid2):
        # None of the checkpoints should exist after the call. Thus the PID folder should be deleted and thus ignored.
        directory_removal_calls = [mock.call(os.path.join(self.pr.recovery_directory_hostname, '200000'),
                                             ignore_errors=True),
                                   mock.call(os.path.join(self.pr.recovery_directory_hostname, "1000000", "check1"),
                                             ignore_errors=True)]

        self.pr._remove_directory_and_directory_trees.assert_has_calls(directory_removal_calls)

        empty_file_calls = [mock.call(self.pr.recovery_directory_hostname)]
        self.pr._remove_empty_folders_from_dir.assert_has_calls(empty_file_calls)

        self.assertTrue(os.path.exists(checkpoint1))
        self.assertTrue(os.path.exists(pid2))
        self.assertTrue(os.path.exists(checkpoint2))
        self.assertTrue(os.path.exists(pid))

    def test_repair_checkpoints(self):
        pid = os.path.join(self.pr.recovery_directory_hostname, "1000000")
        checkpoint1 = os.path.join(pid, "check1")
        pid2 = os.path.join(self.pr.recovery_directory_hostname, "200000")
        checkpoint2 = os.path.join(pid, "check3")
        self._repair_checkpoint_checkpoints_setup(checkpoint1, checkpoint2, pid, pid2)
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        self.pr._remove_empty_folders_from_dir = mock.MagicMock()

        self.pr.repair_checkpoints()

        self._repair_checkpoints_assertions(checkpoint1, checkpoint2, pid, pid2)

        self.pr._remove_empty_folders_from_dir(self.pr.recovery_directory_hostname)

    def test_find_checkpoints_older_than_a_month(self):
        pid = os.path.join(self.pr.recovery_directory_hostname, "1000000")
        if os.path.exists(pid):
            shutil.rmtree(pid)

        os.makedirs(pid)
        os.utime(pid, (1, 1))

        self.assertEqual([pid], self.pr._find_checkpoints_older_than_a_month([pid]))

    def test_find_checkpoints_which_are_locked(self):
        pid = os.path.join(self.pr.recovery_directory_hostname, "1000000")
        if os.path.exists(pid):
            shutil.rmtree(pid)
        checkpoint1 = os.path.join(pid, "check1")

        os.makedirs(pid)
        os.makedirs(checkpoint1)
        # Add a lock file to checkpoint 1
        open(os.path.join(checkpoint1, self.pr.lock_file_name), 'a').close()

        self.assertEqual([checkpoint1], self.pr._find_checkpoints_which_are_locked([pid]))
class ProjectRecoverySaverTest(unittest.TestCase):
    def setUp(self):
        self.working_directory = tempfile.mkdtemp()

        self.pr = ProjectRecovery(None)
        self.pr_saver = self.pr.saver

    def tearDown(self):
        ADS.clear()
        if os.path.exists(self.pr.recovery_directory_hostname):
            shutil.rmtree(self.pr.recovery_directory_hostname)

        if os.path.exists(self.working_directory):
            shutil.rmtree(self.working_directory)

    def test_add_lock_file(self):
        self.pr_saver._add_lock_file(self.working_directory)
        self.assertTrue(os.path.exists(os.path.join(self.working_directory, self.pr.lock_file_name)))

    def test_remove_lock_file(self):
        self.pr_saver._add_lock_file(self.working_directory)
        self.assertTrue(os.path.exists(os.path.join(self.working_directory, self.pr.lock_file_name)))

        self.pr_saver._remove_lock_file(self.working_directory)
        self.assertTrue(not os.path.exists(os.path.join(self.working_directory, self.pr.lock_file_name)))

    def test_recovery_save_when_nothing_is_present(self):
        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.assertIsNone(self.pr.recovery_save())

    def test_recovery_save_with_just_workspaces(self):
        CreateSampleWorkspace(OutputWorkspace="ws")
        self.pr_saver._spin_off_another_time_thread = mock.MagicMock()

        # Do save
        self.pr_saver.recovery_save()

        # Check 0.py was made
        checkpoint = self.pr.listdir_fullpath(self.pr.recovery_directory_pid)[0]
        self.assertTrue(os.path.exists(os.path.join(checkpoint, "0.py")))
        self.assertEqual(self.pr_saver._spin_off_another_time_thread.call_count, 1)

    @mock.patch('workbench.projectrecovery.projectrecoverysaver.find_all_windows_that_are_savable')
    def test_recovery_save_with_just_interfaces(self, windows_that_are_savable):
        CreateSampleWorkspace(OutputWorkspace="ws")
        # Return a FakeEncoder object that will return an empty dictionary
        windows_that_are_savable.return_value = [[FakeEncoder(), FakeEncoder()]]
        self.pr_saver._spin_off_another_time_thread = mock.MagicMock()
        ADS.clear()

        self.pr_saver.gfm = mock.MagicMock()
        self.pr_saver.gfm.figs = {}

        self.pr_saver.recovery_save()

        # Check no 0.py was made
        checkpoint = self.pr.listdir_fullpath(self.pr.recovery_directory_pid)[0]
        self.assertTrue(not os.path.exists(os.path.join(checkpoint, "0.py")))
        self.assertEqual(self.pr_saver._spin_off_another_time_thread.call_count, 1)

        # Read the .json file and check nothing is in workspace and something is in the interfaces dictionary
        with open(os.path.join(checkpoint, (os.path.basename(checkpoint) + self.pr.recovery_file_ext))) as f:
            dictionary = json.load(f)
            self.assertEqual(len(dictionary["interfaces"]), 1)
            self.assertEqual(len(dictionary["workspaces"]), 0)

    def test_empty_group_workspaces(self):
        CreateSampleWorkspace(OutputWorkspace="ws")
        CreateSampleWorkspace(OutputWorkspace="ws1")
        GroupWorkspaces(OutputWorkspace="Group", InputWorkspaces="ws,ws1")
        group_workspace = ADS.retrieve("Group")
        group_workspace.remove("ws")
        group_workspace.remove("ws1")

        self.assertTrue(self.pr_saver._empty_group_workspace(group_workspace))

    @mock.patch('workbench.projectrecovery.projectrecoverysaver.Timer')
    def test_spin_of_another_thread(self, timer):
        self.pr_saver._spin_off_another_time_thread()
        timer.assert_has_calls([mock.call().start()])

    def test_save_workspaces(self):
        CreateSampleWorkspace(OutputWorkspace="ws1")
        CreateSampleWorkspace(OutputWorkspace="ws2")

        self.pr_saver._save_workspaces(self.working_directory)

        # Assert that the 0.py and 1.py that are expected are made
        self.assertTrue(os.path.exists(os.path.join(self.working_directory, "0.py")))
        self.assertTrue(os.path.exists(os.path.join(self.working_directory, "1.py")))

    def test_save_project(self):
        self.pr_saver.gfm = mock.MagicMock()
        self.pr_saver.gfm.figs = {}

        self.pr_saver._save_project(self.working_directory)

        project_file = os.path.join(self.working_directory, (os.path.basename(self.working_directory) + self.pr.recovery_file_ext))
        self.assertTrue(os.path.exists(project_file))
        with open(project_file) as f:
            dictionary = json.load(f)
            self.assertEqual(len(dictionary["interfaces"]), 0)
            self.assertEqual(len(dictionary["workspaces"]), 0)

    @mock.patch('workbench.projectrecovery.projectrecoverysaver.find_all_windows_that_are_savable')
    def test_recovery_save_with_both_workspace_and_interfaces(self, windows_that_are_savable):
        CreateSampleWorkspace(OutputWorkspace="ws")
        # Return a FakeEncoder object that will return an empty dictionary
        windows_that_are_savable.return_value = [[FakeEncoder(), FakeEncoder()]]
        self.pr_saver._spin_off_another_time_thread = mock.MagicMock()

        self.pr_saver.gfm = mock.MagicMock()
        self.pr_saver.gfm.figs = {}

        self.pr_saver.recovery_save()

        # Check 0.py was made
        checkpoint = self.pr.listdir_fullpath(self.pr.recovery_directory_pid)[0]
        self.assertTrue(os.path.exists(os.path.join(checkpoint, "0.py")))
        self.assertEqual(self.pr_saver._spin_off_another_time_thread.call_count, 1)

        # Read the .json file and check nothing is in workspace and something is in the interfaces dictionary
        with open(os.path.join(checkpoint, (os.path.basename(checkpoint) + self.pr.recovery_file_ext))) as f:
            dictionary = json.load(f)
            self.assertEqual(len(dictionary["interfaces"]), 1)
            self.assertEqual(len(dictionary["workspaces"]), 1)

    def test_start_recovery_thread_if_thread_on_is_false(self):
        self.pr_saver._timer_thread = mock.MagicMock()
        self.pr_saver.thread_on = False
        self.pr_saver.recovery_enabled = True

        self.pr_saver.start_recovery_thread()

        self.assertEqual(self.pr_saver._timer_thread.start.call_count, 1)

    def test_stop_recovery_thread(self):
        self.pr_saver._timer_thread = mock.MagicMock()

        self.pr_saver.stop_recovery_thread()

        self.assertEqual(self.pr_saver._timer_thread.cancel.call_count, 1)
class ProjectRecoveryLoaderTest(unittest.TestCase):
    def setUp(self):
        self.working_directory = tempfile.mkdtemp()

        self.pr = ProjectRecovery(None)
        self.pr_loader = self.pr.loader

    def tearDown(self):
        ADS.clear()
        if os.path.exists(self.pr.recovery_directory_hostname):
            shutil.rmtree(self.pr.recovery_directory_hostname)

        if os.path.exists(self.working_directory):
            shutil.rmtree(self.working_directory)

    @mock.patch('workbench.projectrecovery.projectrecoveryloader.ProjectRecoveryPresenter')
    def test_attempt_recovery_and_recovery_passes(self, presenter):
        presenter.return_value.start_recovery_view.return_value = True
        presenter.return_value.start_recovery_failure.return_value = True
        add_main_window_mock(self.pr.loader)
        self.pr.clear_all_unused_checkpoints = mock.MagicMock()
        self.pr.start_recovery_thread = mock.MagicMock()

        self.pr.attempt_recovery()

        self.assertEqual(presenter.return_value.start_recovery_view.call_count, 1)
        self.assertEqual(presenter.return_value.start_recovery_failure.call_count, 0)
        self.assertEqual(self.pr.clear_all_unused_checkpoints.call_count, 1)
        self.assertEqual(self.pr.start_recovery_thread.call_count, 1)
        self.pr.loader.main_window.algorithm_selector.block_progress_widget_updates.assert_context_triggered()

    @mock.patch('workbench.projectrecovery.projectrecoveryloader.ProjectRecoveryPresenter')
    def test_attempt_recovery_and_recovery_fails_first_time_but_is_successful_on_failure_view(self, presenter):
        presenter.return_value.start_recovery_view.return_value = False
        presenter.return_value.start_recovery_failure.return_value = True
        add_main_window_mock(self.pr.loader)
        self.pr.clear_all_unused_checkpoints = mock.MagicMock()
        self.pr.start_recovery_thread = mock.MagicMock()

        self.pr.attempt_recovery()

        self.assertEqual(presenter.return_value.start_recovery_view.call_count, 1)
        self.assertEqual(presenter.return_value.start_recovery_failure.call_count, 1)
        self.assertEqual(self.pr.clear_all_unused_checkpoints.call_count, 1)
        self.assertEqual(self.pr.start_recovery_thread.call_count, 1)
        self.pr.loader.main_window.algorithm_selector.block_progress_widget_updates.assert_context_triggered()

    @mock.patch('workbench.projectrecovery.projectrecoveryloader.ProjectLoader')
    def test_load_project_interfaces_call(self, loader):
        loader.return_value.load_project.return_value = True

        self.pr_loader._load_project_interfaces("")

        self.assertEqual(loader.return_value.load_project.call_args, mock.call(file_name=self.pr.recovery_file_ext,
                                                                               load_workspaces=False))

    def test_compile_recovery_script(self):
        # make sure to clear out the script if it exists
        if os.path.exists(self.pr.recovery_order_workspace_history_file):
            os.remove(self.pr.recovery_order_workspace_history_file)

        # Create checkpoint
        CreateSampleWorkspace(OutputWorkspace="ws")
        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.pr.recovery_save()

        # Find the checkpoint
        checkpoints = os.listdir(self.pr.recovery_directory_pid)
        checkpoint = os.path.join(self.pr.recovery_directory_pid, checkpoints[0])

        self.pr_loader._compile_recovery_script(checkpoint)

        self.assertTrue(os.path.exists(self.pr.recovery_order_workspace_history_file))

        # Confirm contents is correct
        with open(self.pr.recovery_order_workspace_history_file, 'r') as f:
            actual_file_contents = f.read()

        file_contents = ""
        # Strip out the time
        for ii in actual_file_contents:
            if ii == '#':
                break
            file_contents += ii

        self.assertEqual(file_contents,
                         "from mantid.simpleapi import *\n\nCreateSampleWorkspace(OutputWorkspace='ws') ")

    def test_load_checkpoint(self):
        # Create the checkpoint
        CreateSampleWorkspace(OutputWorkspace="ws")
        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.pr.recovery_save()

        # Mock out excess function calls
        self.pr_loader.recovery_presenter = mock.MagicMock()
        self.pr_loader._open_script_in_editor_call = mock.MagicMock()
        self.pr_loader._run_script_in_open_editor = mock.MagicMock()

        # Find the checkpoint
        checkpoints = os.listdir(self.pr.recovery_directory_pid)
        checkpoint = os.path.join(self.pr.recovery_directory_pid, checkpoints[0])

        self.pr_loader.load_checkpoint(checkpoint)

        # Test the calls are made properly
        self.assertEqual(self.pr_loader._open_script_in_editor_call.call_count, 1)
        self.assertEqual(self.pr_loader._run_script_in_open_editor.call_count, 1)

    def test_open_script_in_editor(self):
        self.pr_loader.recovery_presenter = mock.MagicMock()
        self.pr_loader._open_script_in_editor_call = mock.MagicMock()

        # Ensure a script file exists
        script = os.path.join(self.working_directory, "script")
        open(script, 'a').close()

        self.pr_loader._open_script_in_editor(script)

        self.assertEqual(self.pr_loader._open_script_in_editor_call.call_count, 1)
        self.assertEqual(self.pr_loader.recovery_presenter.set_up_progress_bar.call_count, 1)
        self.assertEqual(self.pr_loader.recovery_presenter.set_up_progress_bar.call_args, mock.call(0))
class ProjectRecoveryTest(unittest.TestCase):
    def setUp(self):
        self.multifileinterpreter = mock.MagicMock()
        self.pr = ProjectRecovery(self.multifileinterpreter)
        self.working_directory = tempfile.mkdtemp()

    def tearDown(self):
        ADS.clear()
        if os.path.exists(self.pr.recovery_directory_hostname):
            shutil.rmtree(self.pr.recovery_directory_hostname)

        if os.path.exists(self.working_directory):
            shutil.rmtree(self.working_directory)

    def test_constructor_settings_are_set(self):
        # Test the paths set in the constructor that are generated.
        self.assertEqual(self.pr.recovery_directory,
                         os.path.join(ConfigService.getAppDataDirectory(), "workbench-recovery"))
        self.assertEqual(self.pr.recovery_directory_hostname,
                         os.path.join(ConfigService.getAppDataDirectory(), "workbench-recovery", socket.gethostname()))
        self.assertEqual(self.pr.recovery_directory_pid,
                         os.path.join(ConfigService.getAppDataDirectory(), "workbench-recovery", socket.gethostname(),
                                      str(os.getpid())))
        self.assertEqual(self.pr.recovery_order_workspace_history_file,
                         os.path.join(ConfigService.getAppDataDirectory(), "ordered_recovery.py"))

        # Test config service values
        self.assertEqual(self.pr.time_between_saves, int(ConfigService[SAVING_TIME_KEY]))
        self.assertEqual(self.pr.maximum_num_checkpoints, int(ConfigService[NO_OF_CHECKPOINTS_KEY]))
        self.assertEqual(self.pr.recovery_enabled, ("true" == ConfigService[RECOVERY_ENABLED_KEY].lower()))

    def test_start_recovery_thread_if_thread_on_is_true(self):
        self.pr._timer_thread = mock.MagicMock()
        self.pr.thread_on = True
        self.pr.recovery_enabled = True

        self.pr.start_recovery_thread()

        self.assertEqual(self.pr._timer_thread.start.call_count, 0)

    def test_remove_empty_dir(self):
        if not os.path.exists(self.pr.recovery_directory):
            os.makedirs(self.pr.recovery_directory)

        empty = os.path.join(self.pr.recovery_directory, "empty")
        os.mkdir(os.path.join(self.pr.recovery_directory, "empty"))

        # Feed parent to temp directory here, on Linux = '/tmp'
        parent_path, _ = os.path.split(empty)
        self.pr._remove_empty_folders_from_dir(parent_path)

        self.assertTrue(not os.path.exists(empty))

    def test_remove_empty_dir_throws_outside_of_workbench_directory(self):
        os.mkdir(os.path.join(self.working_directory, "temp"))
        self.assertRaises(RuntimeError, self.pr._remove_empty_folders_from_dir, self.working_directory)

    def test_remove_all_folders_from_dir_raises_outside_of_mantid_dir(self):
        self.assertRaises(RuntimeError, self.pr._remove_directory_and_directory_trees, self.working_directory)

    def test_remove_all_folders_from_dir_doesnt_raise_inside_of_mantid_dir(self):
        temp_dir = os.path.join(self.pr.recovery_directory, "tempDir")
        os.mkdir(temp_dir)

        self.pr._remove_directory_and_directory_trees(temp_dir)

        self.assertTrue(not os.path.exists(temp_dir))

    @unittest.skipIf(is_macOS(), "Can be unreliable on macOS and is a test of logic not OS capability")
    def test_sort_paths_by_last_modified(self):
        # Make sure there is actually a different modified time on the files by using sleeps
        first = tempfile.mkdtemp()
        time.sleep(0.5)
        second = tempfile.mkdtemp()
        time.sleep(0.5)
        third = tempfile.mkdtemp()
        paths = [second, third, first]
        paths = self.pr.sort_by_last_modified(paths)

        self.assertListEqual(paths, [first, second, third])

    def test_get_pid_folder_to_be_used_to_load_a_checkpoint_from(self):
        self.pr._make_process_from_pid = mock.MagicMock()
        self.pr._is_mantid_workbench_process = mock.MagicMock(return_value=True)
        # Needs to be a high number outside of possible pids
        one = os.path.join(self.pr.recovery_directory_hostname, "10000000")
        two = os.path.join(self.pr.recovery_directory_hostname, "20000000")

        # Make the first pid folder, neither pid will be in use but it should select the oldest first
        os.makedirs(one)
        time.sleep(0.01)
        os.makedirs(two)

        result = self.pr.get_pid_folder_to_load_a_checkpoint_from()
        self.assertEqual(one, result)

    def test_list_dir_full_path(self):
        one = os.path.join(self.working_directory, "10000000")
        two = os.path.join(self.working_directory, "20000000")
        os.makedirs(one)
        os.makedirs(two)

        # There is no concern for list order in this equality assertion
        if sys.version_info.major < 3:
            # Python 2.7 way of doing it
            self.assertItemsEqual([one, two], self.pr.listdir_fullpath(self.working_directory))
        else:
            # Python 3.2+ way of doing it
            self.assertCountEqual([one, two], self.pr.listdir_fullpath(self.working_directory))

    def test_recovery_save_when_nothing_is_present(self):
        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.assertIsNone(self.pr.recovery_save())

    def test_check_for_recovery_where_no_checkpoints_exist(self):
        self.assertTrue(not self.pr.check_for_recover_checkpoint())

    @staticmethod
    def create_checkpoints_at_directory(directory, number):
        for ii in range(0, number):
            os.makedirs(os.path.join(directory, (str(ii) + "00000000"), "spare_dir"))

    def test_check_for_recovery_when_2_instances_exist_with_2_checkpoints(self):
        # Doesn't do recovery so returns false

        # Create the 2 checkpoints
        self.create_checkpoints_at_directory(self.pr.recovery_directory_hostname, 2)
        self.pr._number_of_other_workbench_processes = mock.MagicMock(return_value=2)

        self.assertTrue(not self.pr.check_for_recover_checkpoint())

    def test_check_for_recovery_when_2_instances_exist_with_3_checkpoints(self):
        # Does recovery so returns true
        # Create the 3 checkpoints
        self.create_checkpoints_at_directory(self.pr.recovery_directory_hostname, 3)
        self.pr._number_of_other_workbench_processes = mock.MagicMock(return_value=2)

        self.assertTrue(self.pr.check_for_recover_checkpoint())

    def test_remove_oldest_checkpoints(self):
        self.pr._recovery_directory_pid = self.working_directory
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()

        for ii in range(0, self.pr.maximum_num_checkpoints+1):
            os.mkdir(os.path.join(self.working_directory, "dir"+str(ii)))
            time.sleep(0.01)

        self.pr.remove_oldest_checkpoints()

        # Now should have had a call made to delete working_directory + dir0
        self.pr._remove_directory_and_directory_trees.assert_called_with(os.path.join(self.working_directory, "dir0"))

    def test_clear_all_unused_checkpoints_called_with_none_and_only_one_user(self):
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        os.makedirs(self.pr.recovery_directory_hostname)

        self.pr.clear_all_unused_checkpoints()

        self.pr._remove_directory_and_directory_trees.assert_called_with(self.pr.recovery_directory_hostname)

    def test_clear_all_unused_checkpoints_called_with_none_and_multiple_users(self):
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        os.makedirs(self.pr.recovery_directory_hostname)
        user = os.path.join(self.pr.recovery_directory, "dimitar")
        if os.path.exists(user):
            shutil.rmtree(user)
        os.makedirs(user)

        self.pr.clear_all_unused_checkpoints()

        self.pr._remove_directory_and_directory_trees.assert_called_with(self.pr.recovery_directory_hostname)

    def test_clear_all_unused_checkpoints_called_with_not_none(self):
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        os.makedirs(self.pr.recovery_directory_hostname)

        self.pr.clear_all_unused_checkpoints(pid_dir=self.pr.recovery_directory_hostname)

        self.pr._remove_directory_and_directory_trees.assert_called_with(self.pr.recovery_directory_hostname)

    def _repair_checkpoint_checkpoints_setup(self, checkpoint1, checkpoint2, pid, pid2):
        if os.path.exists(pid):
            shutil.rmtree(pid)
        os.makedirs(pid)
        os.makedirs(checkpoint1)
        if os.path.exists(pid2):
            shutil.rmtree(pid2)
        os.makedirs(pid2)
        os.makedirs(checkpoint2)

        # Add a lock file to checkpoint 1
        open(os.path.join(checkpoint1, self.pr.lock_file_name), 'a').close()

        # Add one workspace to the checkpoint and change modified dates to older than a month
        os.utime(pid2, (1, 1))

    def _repair_checkpoints_assertions(self, checkpoint1, checkpoint2, pid, pid2):
        # None of the checkpoints should exist after the call. Thus the PID folder should be deleted and thus ignored.
        directory_removal_calls = [mock.call(os.path.join(self.pr.recovery_directory_hostname, '200000')),
                                   mock.call(os.path.join(self.pr.recovery_directory_hostname, "1000000", "check1"))]

        self.pr._remove_directory_and_directory_trees.assert_has_calls(directory_removal_calls)

        empty_file_calls = [mock.call(self.pr.recovery_directory_hostname)]
        self.pr._remove_empty_folders_from_dir.assert_has_calls(empty_file_calls)

        self.assertTrue(os.path.exists(checkpoint1))
        self.assertTrue(os.path.exists(pid2))
        self.assertTrue(os.path.exists(checkpoint2))
        self.assertTrue(os.path.exists(pid))

    def test_repair_checkpoints(self):
        pid = os.path.join(self.pr.recovery_directory_hostname, "1000000")
        checkpoint1 = os.path.join(pid, "check1")
        pid2 = os.path.join(self.pr.recovery_directory_hostname, "200000")
        checkpoint2 = os.path.join(pid, "check3")
        self._repair_checkpoint_checkpoints_setup(checkpoint1, checkpoint2, pid, pid2)
        self.pr._remove_directory_and_directory_trees = mock.MagicMock()
        self.pr._remove_empty_folders_from_dir = mock.MagicMock()

        self.pr.repair_checkpoints()

        self._repair_checkpoints_assertions(checkpoint1, checkpoint2, pid, pid2)

        self.pr._remove_empty_folders_from_dir(self.pr.recovery_directory_hostname)

    def test_find_checkpoints_older_than_a_month(self):
        pid = os.path.join(self.pr.recovery_directory_hostname, "1000000")
        if os.path.exists(pid):
            shutil.rmtree(pid)

        os.makedirs(pid)
        os.utime(pid, (1, 1))

        self.assertEqual([pid], self.pr._find_checkpoints_older_than_a_month([pid]))

    def test_find_checkpoints_which_are_locked(self):
        pid = os.path.join(self.pr.recovery_directory_hostname, "1000000")
        if os.path.exists(pid):
            shutil.rmtree(pid)
        checkpoint1 = os.path.join(pid, "check1")

        os.makedirs(pid)
        os.makedirs(checkpoint1)
        # Add a lock file to checkpoint 1
        open(os.path.join(checkpoint1, self.pr.lock_file_name), 'a').close()

        self.assertEqual([checkpoint1], self.pr._find_checkpoints_which_are_locked([pid]))
class ProjectRecoveryModelTest(unittest.TestCase):
    def setUp(self):
        self.pr = ProjectRecovery(multifileinterpreter=None)

        # Make absolutely sure that the workbench-recovery directory is cleared.
        if os.path.exists(self.pr.recovery_directory):
            shutil.rmtree(self.pr.recovery_directory)

        # Set up some checkpoints
        self.setup_some_checkpoints()

        self.pr._make_process_from_pid = mock.MagicMock()
        self.pr._is_mantid_workbench_process = mock.MagicMock(return_value=True)
        self.prm = ProjectRecoveryModel(self.pr, mock.MagicMock())

    def tearDown(self):
        # Make sure to clear the hostname layer between tests
        ADS.clear()

        if os.path.exists(self.pid):
            shutil.rmtree(self.pid)

    def setup_some_checkpoints(self):
        self.pr._spin_off_another_time_thread = mock.MagicMock()
        directory = self.pr.recovery_directory_hostname

        # Add a numbered folder for the pid
        self.pid = os.path.join(directory, "3000000")
        if not os.path.exists(self.pid):
            os.makedirs(self.pid)
        self.pr._recovery_directory_pid = self.pid

        # Add 5 workspaces
        for ii in range(0, 5):
            CreateSampleWorkspace(OutputWorkspace=str(ii))

        self.pr.saver._spin_off_another_time_thread = mock.MagicMock()
        self.pr.recovery_save()

    def test_find_number_of_workspaces_in_directory(self):
        # Expect 0 as just checkpoints
        self.assertEqual(self.prm.find_number_of_workspaces_in_directory(self.pid), 0)

        self.assertTrue(os.path.exists(self.pid))
        list_dir = os.listdir(self.pid)
        list_dir.sort()
        self.assertEqual(self.prm.find_number_of_workspaces_in_directory(os.path.join(self.pid, list_dir[0])), 5)

    def test_get_row_as_string(self):
        row = self.prm.rows[0]
        self.assertEqual(self.prm.get_row(row[0]), row)

    def test_get_row_as_int(self):
        row = self.prm.rows[0]
        self.assertEqual(self.prm.get_row(0), row)

    def test_get_row_as_string_not_found(self):
        row = ["", "", ""]
        self.assertEqual(self.prm.get_row("asdadasdasd"), row)

    def test_start_mantid_normally(self):
        self.prm.start_mantid_normally()
        self.assertEqual(self.prm.presenter.close_view.call_count, 1)

    def test_recover_selected_checkpoint(self):
        checkpoint = os.listdir(self.pid)[0]
        self.prm._start_recovery_of_checkpoint = mock.MagicMock()
        self.prm.recover_selected_checkpoint(checkpoint)

        self.assertEqual(1, self.prm.presenter.change_start_mantid_to_cancel_label.call_count)
        self.assertEqual(1, self.prm._start_recovery_of_checkpoint.call_count)

    def test_open_selected_in_editor(self):
        checkpoint = os.listdir(self.pid)[0]
        self.prm.project_recovery.open_checkpoint_in_script_editor = mock.MagicMock()
        self.prm.open_selected_in_editor(checkpoint)

        self.assertEqual(1, self.prm.project_recovery.open_checkpoint_in_script_editor.call_count)
        self.assertEqual(self.prm.project_recovery.open_checkpoint_in_script_editor.call_args,
                         mock.call(os.path.join(self.pid, checkpoint)))

    def test_decide_last_checkpoint(self):
        CreateSampleWorkspace(OutputWorkspace="6")
        self.pr.recovery_save()

        checkpoints = os.listdir(self.pid)
        checkpoints.sort()

        last_checkpoint = self.prm.decide_last_checkpoint()
        self.assertEqual(checkpoints[-1], os.path.basename(last_checkpoint))

    def test_fill_rows(self):
        # wait a second so that we can add a second checkpoint with a different name, because the checkpoints differ at
        # most by a second.
        time.sleep(1)

        CreateSampleWorkspace(OutputWorkspace="6")
        self.pr.recovery_save()

        self.prm.fill_rows()

        checkpoints = os.listdir(self.pid)
        checkpoints.sort()

        self.assertEqual(["", "", ""], self.prm.rows[2])
        self.assertEqual([checkpoints[0].replace("T", " "), "5", "No"], self.prm.rows[1])
        self.assertEqual([checkpoints[1].replace("T", " "), "6", "No"], self.prm.rows[0])

    def test_get_number_of_checkpoints(self):
        self.assertEqual(int(ConfigService.getString(NO_OF_CHECKPOINTS_KEY)),
                         self.prm.get_number_of_checkpoints())

    def test_update_checkpoint_tried(self):
        checkpoints = os.listdir(self.pid)

        self.assertEqual(self.prm.rows[0][2], "No")
        self.prm._update_checkpoint_tried(os.path.join(self.pid, checkpoints[0]))
        self.assertEqual(self.prm.rows[0][2], "Yes")