Example #1
0
    def runnable_searcher(abs_path_to_search: str = None,
                          file_filter: Callable[[str, str], bool] = lambda folder, file: True) \
            -> Tuple[str, str]:

        if abs_path_to_search is None:
            abs_path_to_search = os.getcwd()

        curr_folder = os.path.abspath(abs_path_to_search)

        for folder, dirs, files in walk_user_files(curr_folder):

            files = [f for f in files if f.endswith('.go') and file_filter(folder, f)]

            if len(files) == 0:
                continue

            if len(files) == 1:
                return folder, files[0]

            contents = {}

            for file in files:
                path = os.path.abspath(os.path.join(folder, file))
                if path in file_contents_cached:
                    contents[file] = file_contents_cached[path]
                elif os.path.exists(path):
                    with open(path) as f:
                        file_content = f.read()
                        contents[file] = file_content
                        file_contents_cached[path] = contents[file]

            has_main = {f: False for f in files}

            for file in files:
                source = contents[file]

                if GoRunnableFile.main_searcher.search(source) is not None:
                    has_main[file] = True

            candidates = [f for f in files if has_main[f]]

            if len(candidates) == 1:
                return folder, candidates[0]

            if len(candidates) > 1:
                str_files = ', '.join(f'"{f}"' for f in candidates)
                raise ErrorWithFeedback(
                    f'Cannot decide which file to run out of the following: {str_files}\n'
                    'They all have "func main()". Leave one file with main function.')

        raise ErrorWithFeedback(
            'Cannot find a file with main function.\n'
            f'Are your project files located at \"{curr_folder}\"?')
    def launch_django_application(self, test_case: TestCase):
        if not isinstance(test_case.attach, DjangoSettings):
            raise UnexpectedError(
                f'Django tests should have DjangoSettings class as an attach, '
                f'found {type(test_case.attach)}')

        source = test_case.source_name

        if source is None:
            source = 'manage'

        full_source = source.replace('.', os.sep) + '.py'
        full_path = os.path.abspath(full_source)

        if not os.path.exists(full_path):
            raise ErrorWithFeedback(
                f'Cannot find file named "{os.path.basename(full_path)}" '
                f'in folder "{os.path.dirname(full_path)}". '
                f'Check if you deleted it.')

        self.full_path = full_path
        self.port = self.__find_free_port(test_case.attach.tryout_ports)

        if test_case.attach.use_database:
            self.__prepare_database(test_case.attach.test_database)

        self.process = PopenWrapper(sys.executable, self.full_path,
                                    'runserver', self.port, '--noreload')

        i: int = 100
        search_phrase = 'Starting development server at'
        while i:
            if search_phrase in self.process.stdout:
                break
            i -= 1

            if self.process.is_error_happened():
                i = 0
            else:
                sleep(0.1)
        else:
            stdout = self.process.stdout.strip()
            stderr = self.process.stderr.strip()

            error_info = f'Cannot start Django server because cannot find "{search_phrase}" in process\' output'
            if len(stdout):
                error_info += '\n\nstdout:\n' + stdout
            if len(stderr):
                error_info += '\n\nstderr:\n' + stderr

            raise ErrorWithFeedback(error_info)
Example #3
0
    def tear_down():
        if current_thread() != SystemHandler.__locker_thread:
            raise ErrorWithFeedback(
                "Cannot tear down the testing process from the other thread")

        with SystemHandler.__lock:
            if not SystemHandler.__locked:
                raise ErrorWithFeedback(
                    "Cannot tear down the testing process more than once")
            SystemHandler.__locked = False
            SystemHandler.__locker_thread = None

        OutputHandler.revert_stdout()
        InputHandler.revert_input()
        ExitHandler.revert_exit()
    def execute(self, stdin: str) -> str:
        if self.is_finished():
            from hstest.stage_test import StageTest
            StageTest.curr_test_run.set_error_in_test(
                ErrorWithFeedback(
                    f"The program {self} has unexpectedly terminated.\n" +
                    "It finished execution too early, should continue running."
                ))
            raise TestedProgramFinishedEarly()

        if stdin is None:
            self.stop_input()
            return ""

        if not self.is_waiting_input():
            raise UnexpectedError(
                f"Program {self} is not waiting for the input " +
                f"(state == \"{self._machine.state}\")")

        if self.__no_more_input:
            raise UnexpectedError(
                f"Can't pass input to the program {self} - input was prohibited."
            )

        self._input = stdin
        if self.__in_background:
            self._machine.set_state(ProgramState.RUNNING)
            return ""

        # suspends thread while the program is executing,
        # waits for non-RUNNING state to be reached
        self._machine.set_and_wait(ProgramState.RUNNING)
        return self.__get_execution_output()
Example #5
0
    def __prepare_database(self, test_database: str):
        os.environ['HYPERSKILL_TEST_DATABASE'] = test_database
        with open(test_database, 'w'):
            pass
        migrate = ProcessWrapper(sys.executable,
                                 self.full_path,
                                 'migrate',
                                 check_early_finish=True)

        while not migrate.is_finished() and len(migrate.stderr) == 0:
            sleep(0.01)

        if len(migrate.stderr) != 0:
            migrate.wait_output()

            stdout = migrate.stdout
            stderr = migrate.stderr

            error_info = 'Cannot apply migrations to an empty database.'
            if len(stdout):
                error_info += '\n\nstdout:\n' + stdout.strip()
            if len(stderr):
                error_info += '\n\nstderr:\n' + stderr.strip()

            raise ErrorWithFeedback(error_info)
Example #6
0
 def __find_free_port(self, ports: List[int]) -> int:
     for port in ports:
         if not is_port_in_use(port):
             return port
     raise ErrorWithFeedback(
         'Cannot find a port to start Django application '
         f'(tried ports form {ports[0]} to {ports[-1]})')
Example #7
0
    def set_up():
        with SystemHandler.__lock:
            if SystemHandler.__locked:
                raise ErrorWithFeedback(
                    "Cannot start the testing process more than once")
            SystemHandler.__locked = True
            SystemHandler.__locker_thread = current_thread()

        OutputHandler.replace_stdout()
        InputHandler.replace_input()
        ExitHandler.replace_exit()
Example #8
0
 def readline(self) -> str:
     line = self.handler.eject_next_line()
     if line is None:
         if not Settings.allow_out_of_input:
             from hstest import StageTest
             StageTest.curr_test_run.set_error_in_test(ErrorWithFeedback(
                 "Program ran out of input. You tried to read more, than expected."))
             raise ExitException()
         else:
             raise EOFError('EOF when reading a line')
     return line
    def __prepare_database(self, test_database: str):
        os.environ['HYPERSKILL_TEST_DATABASE'] = test_database
        with open(test_database, 'w'):
            pass
        migrate = subprocess.Popen([sys.executable, self.full_path, 'migrate'],
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
        exit_code = migrate.wait()
        if exit_code != 0:
            stdout = migrate.stdout.read().decode().strip()
            stderr = migrate.stderr.read().decode().strip()

            error_info = 'Cannot apply migrations to an empty database.'
            if len(stdout):
                error_info += '\n\nstdout:\n' + stdout.strip()
            if len(stderr):
                error_info += '\n\nstderr:\n' + stderr.strip()

            raise ErrorWithFeedback(error_info)
Example #10
0
 def check(self, reply: str, attach: Any):
     raise ErrorWithFeedback(
         f"The program has unexpectedly terminated.\n" +
         "It finished execution too early, should continue running.")
Example #11
0
def runnable_searcher(
    abs_path_to_search: str,
    file_filter: Callable[[str, str], bool] = lambda x, y: True
) -> Tuple[str, str]:
    curr_folder = os.path.abspath(abs_path_to_search)
    test_folder = os.path.join(curr_folder, 'test')

    for folder, dirs, files in os.walk(curr_folder):
        if folder.startswith(test_folder):
            continue

        if folder == curr_folder:
            for file in 'test.py', 'tests.py':
                if file in files:
                    files.remove(file)

        files = [
            f for f in files if f.endswith('.py') and file_filter(folder, f)
        ]

        if len(files) == 0:
            continue

        if len(files) == 1:
            return folder, files[0]

        contents = {}

        for file in files:
            path = os.path.abspath(os.path.join(folder, file))
            if path in _contents_cached:
                contents[file] = _contents_cached[path]
            elif os.path.exists(path):
                with open(path) as f:
                    file_content = f.read()
                    contents[file] = [
                        file_content,
                        re.compile(
                            rf'(^|\n)import +[\w., ]*\b{file[:-3]}\b[\w., ]*',
                            re.M),
                        re.compile(
                            rf'(^|\n)from +\.? *\b{file[:-3]}\b +import +',
                            re.M)
                    ]
                    _contents_cached[path] = contents[file]

        is_imported = {f: False for f in files}
        has_name_main = {f: False for f in files}

        for file in files:
            source = contents[file][0]
            if '__name__' in source and '__main__' in source:
                has_name_main[file] = True

            for f, (s, r1, r2) in contents.items():
                if r1.search(source) is not None or r2.search(
                        source) is not None:
                    is_imported[f] = True

        candidates_by_import = [f for f in files if not is_imported[f]]

        if len(candidates_by_import) == 1:
            return folder, candidates_by_import[0]

        candidates_by_name_main = [f for f in files if has_name_main[f]]

        if len(candidates_by_name_main) == 1:
            return folder, candidates_by_name_main[0]

        candidates_import_main = [
            f for f in candidates_by_import if has_name_main[f]
        ]

        if len(candidates_import_main) == 1:
            return folder, candidates_import_main[0]

        if len(candidates_import_main) > 1:
            str_files = ', '.join(f'"{f}"' for f in candidates_import_main)
            raise ErrorWithFeedback(
                f'Cannot decide which file to run out of the following: {str_files}\n'
                'They all have "if __name__ == \'__main__\'". Leave one file with this line.'
            )

        str_files = ', '.join(f'"{f}"' for f in (
            candidates_by_import if len(candidates_by_import) else files))

        raise ErrorWithFeedback(
            f'Cannot decide which file to run out of the following: {str_files}\n'
            'Write "if __name__ == \'__main__\'" in one of them to mark it as an entry point.'
        )

    raise ErrorWithFeedback(
        'Cannot find a file to import and run your code.\n'
        f'Are your project files located at \"{curr_folder}\"?')
Example #12
0
 def _check_errors(self):
     if self.process.is_error_happened():
         self.process.terminate()
         raise ErrorWithFeedback(self.process.stderr)
Example #13
0
 def test(self):
     status, feedback = TestRunTestInsideTest('main').run_tests()
     if status != 0:
         raise ErrorWithFeedback(feedback)
     return CheckResult.correct()
 def _check_errors(self):
     for process_item in self.processes:
         filename, process = process_item
         if process.is_error_happened():
             raise ErrorWithFeedback(
                 f'Error running "{filename}"\n\n{process.stderr}')
    def launch_flask_applications(self, test_case: TestCase):
        if not isinstance(test_case.attach, FlaskSettings):
            raise UnexpectedError(
                f'Flask tests should have FlaskSettings class as an attach, '
                f'found {type(test_case.attach)}')

        sources = test_case.attach.sources

        if len(sources) == 0:
            raise UnexpectedError(
                f'Cannot find Flask applications to run, no sources were defined in tests'
            )

        new_sources = []

        for source in sources:
            filename, port = source

            full_source = filename.replace('.', os.sep) + '.py'
            full_path = os.path.abspath(full_source)

            if not os.path.exists(full_path):
                raise ErrorWithFeedback(
                    f'Cannot find file named "{os.path.basename(full_path)}" '
                    f'in folder "{os.path.dirname(full_path)}". '
                    f'Check if you deleted it.')

            if port is None:
                port = self.__find_free_port(test_case.attach.tryout_ports)

            process = PopenWrapper(sys.executable, full_path,
                                   f'localhost:{port}')
            self.processes += [(full_source, process)]

            i: int = 100
            search_phrase = '(Press CTRL+C to quit)'
            while i:
                if search_phrase in process.stderr:
                    break
                i -= 1

                if process.is_error_happened():
                    i = 0
                else:
                    sleep(0.1)
            else:
                stdout = process.stdout.strip()
                stderr = process.stderr.strip()

                error_info = (
                    f'Cannot start Flask server {full_source} '
                    f'because cannot find "{search_phrase}" in process\' output'
                )

                if len(stdout):
                    error_info += '\n\nstdout:\n' + stdout
                if len(stderr):
                    error_info += '\n\nstderr:\n' + stderr

                raise ErrorWithFeedback(error_info)

            new_sources += [(filename, port)]

        test_case.attach.sources = new_sources