def test_status_checker_until_complete_infinite(
    get_test_data_path: Path,
    get_test_db_copy: Callable[[str], DatabaseConnector],
    copy_test_case_log_file: Callable[[str], None],
) -> None:
    """
    Test the infinite loop of StatusChecker.

    Parameters
    ----------
    get_test_data_path : Path
        Path to the test data
    get_test_db_copy : function
        Function which returns a DatabaseConnector connected to a copy of test.db
    copy_test_case_log_file : function
        Return the function for copying the test case log files
    """
    test_case = "infinite_log_file_pid_started_ended_no_mock_pid_complete"

    project_path = get_test_data_path
    db_connector = get_test_db_copy(test_case)
    copy_test_case_log_file(test_case)

    # Remove row which has status running (as it will always have
    # this status)
    db_connector.execute_statement("DELETE FROM run WHERE name = 'testdata_5'")

    db_reader = DatabaseReader(db_connector)

    status_checker = StatusChecker(db_connector, project_path)
    status_checker.check_and_update_until_complete()

    query = status_checker.get_query_string_for_non_errored_runs()
    assert len(db_reader.query(query).index) == 0
예제 #2
0
    def __run_status_checker(self, node_name: str) -> None:
        """
        Run the StatusChecker.

        Parameters
        ----------
        node_name : str
            Name of node to run the status checker for

        Raises
        ------
        RuntimeError
            If the types of self.__run_graph[node_name]["db_connector"] or
            self.__run_graph[node_name]["project_path"] are unexpected
        """
        db_connector = self.__run_graph[node_name]["db_connector"]
        if not isinstance(db_connector, DatabaseConnector):
            raise RuntimeError(
                f"The db_connector of the '{node_name}' node was expected "
                f"to be of type 'DatabaseConnector', but got "
                f"'{type(db_connector)}' instead")
        project_path = self.__run_graph[node_name]["project_path"]
        if not isinstance(project_path, Path):
            raise RuntimeError(
                f"The project_path of the '{node_name}' node was expected "
                f"to be of type 'Path', but got '{type(project_path)}' "
                f"instead")
        StatusChecker(db_connector, project_path).check_and_update_status()
def test_status_checker_run_time_error(
        make_test_database: Callable[[Optional[str]],
                                     DatabaseConnector]) -> None:
    """
    Test that the status checker raises RuntimeError without tables.

    Parameters
    ----------
    make_test_database : DatabaseConnector
        Connection to the test database
    """
    db_connector = make_test_database("status_checker_no_table")
    status_checker = StatusChecker(db_connector, Path())

    with pytest.raises(RuntimeError):
        status_checker.check_and_update_status()
예제 #4
0
    def __monitor_runs(
        self,
        submitter_dict: Dict[
            str,
            Dict[
                str, Union[Optional[AbstractSubmitter], Union[DatabaseConnector, Path]]
            ],
        ],
        raise_errors: bool,
        wait_time: int,
    ) -> None:
        """
        Monitor the runs belonging to the same order.

        Parameters
        ----------
        submitter_dict : dict
            Dict containing the the node names as keys and a new dict as values
            The new dict contains the keywords 'submitter' with value AbstractSubmitter
            If the submitter contains a bout run, the new dict will also contain the
            keyword 'db_connector' with the value DatabaseConnector and the keyword
            'project_path' with the value Path which will be used in the StatusChecker
        raise_errors : bool
            If True the program will raise any error caught when during the running
            of the nodes
            If False the program will continue execution, but all nodes depending on
            the errored node will be marked as errored and not submitted
        wait_time : int
            Time to wait before checking if a job has completed

        Raises
        ------
        RuntimeError
            If the types in the dict are unexpected
        """
        node_names = list(
            node_name
            for node_name in submitter_dict.keys()
            if submitter_dict[node_name]["submitter"] is not None
        )

        while len(node_names) != 0:
            for node_name in node_names:
                submitter = submitter_dict[node_name]["submitter"]
                if not isinstance(submitter, AbstractSubmitter):
                    raise RuntimeError(
                        f"The submitter of the '{node_name}' node was expected to be "
                        f"of type 'AbstractSubmitter', but got '{type(submitter)}' "
                        f"instead"
                    )

                if submitter.completed():
                    if submitter.errored():
                        self.__run_graph.change_status_node_and_dependencies(node_name)
                        if raise_errors:
                            submitter.raise_error()

                    node_names.remove(node_name)
                else:
                    logging.debug(
                        "pid=%s found, %s seems to be running", submitter.pid, node_name
                    )

                if node_name.startswith("bout_run"):
                    db_connector = submitter_dict[node_name]["db_connector"]
                    if not isinstance(db_connector, DatabaseConnector):
                        raise RuntimeError(
                            f"The db_connector of the '{node_name}' node was expected "
                            f"to be of type 'DatabaseConnector', but got "
                            f"'{type(db_connector)}' instead"
                        )

                    project_path = submitter_dict[node_name]["project_path"]
                    if not isinstance(project_path, Path):
                        raise RuntimeError(
                            f"The project_path of the '{node_name}' node was expected "
                            f"to be of type 'Path', but got '{type(project_path)}' "
                            f"instead"
                        )
                    StatusChecker(db_connector, project_path).check_and_update_status()

            sleep(wait_time)
def test_status_checker(
    test_case: str,
    get_test_data_path: Path,
    get_test_db_copy: Callable[[str], DatabaseConnector],
    mock_pid_exists: Callable[[str], None],
    copy_test_case_log_file: Callable[[str], None],
) -> None:
    """
    Test the StatusChecker exhaustively (excluding raises and loop).

    Parameters
    ----------
    test_case : str
        Description of the test on the form

        >>> ('<log_file_present>_<pid_present_in_log>_'
        ...  '<started_time_present_in_log>_<ended_time_present_in_log>'
        ...  '_<whether_pid_exists>_<new_status>')

    get_test_data_path : Path
        Path to test data
    get_test_db_copy : function
        Function which returns a a database connector to the copy of the
        test database
    mock_pid_exists : function
        Function which sets up a monkeypatch for psutil.pid_exist
    copy_test_case_log_file : function
        Function which copies log files according to the test_case
    """
    project_path = get_test_data_path
    db_connector = get_test_db_copy(test_case)
    mock_pid_exists(test_case)
    copy_test_case_log_file(test_case)

    db_reader = DatabaseReader(db_connector)

    status_checker = StatusChecker(db_connector, project_path)
    status_checker.check_and_update_status()

    # Check that the correct status has been assigned to "running"
    # pylint: disable=no-member
    result = db_reader.query("SELECT latest_status FROM run WHERE name = "
                             "'testdata_5'").loc[0, "latest_status"]
    assert result == "running"

    # Check that the correct status has been assigned to "submitted"
    expected = test_case.split("_")[-1]
    # pylint: disable=no-member
    result = db_reader.query("SELECT latest_status FROM run WHERE name = "
                             "'testdata_6'").loc[0, "latest_status"]
    assert result == expected

    # Check that correct start_time has been set
    if "not_started" not in test_case:
        expected = str(datetime(2020, 5, 1, 17, 7, 10))
        # pylint: disable=no-member
        result = db_reader.query("SELECT start_time FROM run WHERE name = "
                                 "'testdata_6'").loc[0, "start_time"]
        assert expected == result

    # Check that correct end_time has been set
    if "not_ended" not in test_case and "complete" in test_case:
        expected = str(datetime(2020, 5, 1, 17, 7, 14))
        # pylint: disable=no-member
        result = db_reader.query("SELECT stop_time FROM run WHERE name = "
                                 "'testdata_6'").loc[0, "stop_time"]
        assert expected == result