Exemplo n.º 1
0
    def perform_step(
        self, operations: List[Union[UpdateFile, DeleteFile]], server: Server,
        options: Options, build_options: Options, testcase: DataDrivenTestCase,
        main_src: str, step: int, num_regular_incremental_steps: int
    ) -> Tuple[List[str], List[List[str]]]:
        """Perform one fine-grained incremental build step (after some file updates/deletions).

        Return (mypy output, triggered targets).
        """
        perform_file_operations(operations)
        sources = self.parse_sources(main_src, step, options)

        if step <= num_regular_incremental_steps:
            new_messages = self.build(build_options, sources)
        else:
            new_messages = self.run_check(server, sources)

        updated: List[str] = []
        changed: List[str] = []
        targets: List[str] = []
        triggered = []
        if server.fine_grained_manager:
            if CHECK_CONSISTENCY:
                check_consistency(server.fine_grained_manager)
            triggered.append(server.fine_grained_manager.triggered)

            updated = server.fine_grained_manager.updated_modules
            changed = [
                mod
                for mod, file in server.fine_grained_manager.changed_modules
            ]
            targets = server.fine_grained_manager.processed_targets

        expected_stale = testcase.expected_stale_modules.get(step - 1)
        if expected_stale is not None:
            assert_module_equivalence('stale' + str(step - 1), expected_stale,
                                      changed)

        expected_rechecked = testcase.expected_rechecked_modules.get(step - 1)
        if expected_rechecked is not None:
            assert_module_equivalence('rechecked' + str(step - 1),
                                      expected_rechecked, updated)

        expected = testcase.expected_fine_grained_targets.get(step)
        if expected:
            assert_target_equivalence('targets' + str(step), expected, targets)

        new_messages = normalize_messages(new_messages)

        a = new_messages
        assert testcase.tmpdir
        a.extend(
            self.maybe_suggest(step, server, main_src, testcase.tmpdir.name))

        return a, triggered
Exemplo n.º 2
0
    def run_case(self, testcase: DataDrivenTestCase) -> None:
        if self.should_skip(testcase):
            pytest.skip()
            return

        main_src = '\n'.join(testcase.input)
        main_path = os.path.join(test_temp_dir, 'main')
        with open(main_path, 'w') as f:
            f.write(main_src)

        options = self.get_options(main_src, testcase, build_cache=False)
        for name, _ in testcase.files:
            if 'mypy.ini' in name:
                config = name  # type: Optional[str]
                break
        else:
            config = None
        if config:
            parse_config_file(options, config)
        server = Server(options, alt_lib_path=test_temp_dir)

        step = 1
        sources = self.parse_sources(main_src, step, options)
        if self.use_cache:
            build_options = self.get_options(main_src,
                                             testcase,
                                             build_cache=True)
            if config:
                parse_config_file(build_options, config)
            messages = self.build(build_options, sources)
        else:
            messages = self.run_check(server, sources)

        a = []
        if messages:
            a.extend(normalize_messages(messages))

        if server.fine_grained_manager:
            if CHECK_CONSISTENCY:
                check_consistency(server.fine_grained_manager)

        steps = testcase.find_steps()
        all_triggered = []

        for operations in steps:
            step += 1
            for op in operations:
                if isinstance(op, UpdateFile):
                    # Modify/create file
                    copy_and_fudge_mtime(op.source_path, op.target_path)
                else:
                    # Delete file
                    os.remove(op.path)
            sources = self.parse_sources(main_src, step, options)
            new_messages = self.run_check(server, sources)

            updated = []  # type: List[str]
            changed = []  # type: List[str]
            if server.fine_grained_manager:
                if CHECK_CONSISTENCY:
                    check_consistency(server.fine_grained_manager)
                all_triggered.append(server.fine_grained_manager.triggered)

                updated = server.fine_grained_manager.updated_modules
                changed = [
                    mod for mod, file in
                    server.fine_grained_manager.changed_modules
                ]

            assert_module_equivalence(
                'stale' + str(step - 1),
                testcase.expected_stale_modules.get(step - 1), changed)
            assert_module_equivalence(
                'rechecked' + str(step - 1),
                testcase.expected_rechecked_modules.get(step - 1), updated)

            new_messages = normalize_messages(new_messages)

            a.append('==')
            a.extend(new_messages)

        # Normalize paths in test output (for Windows).
        a = [line.replace('\\', '/') for line in a]

        assert_string_arrays_equal(
            testcase.output, a,
            'Invalid output ({}, line {})'.format(testcase.file,
                                                  testcase.line))

        if testcase.triggered:
            assert_string_arrays_equal(
                testcase.triggered, self.format_triggered(all_triggered),
                'Invalid active triggers ({}, line {})'.format(
                    testcase.file, testcase.line))
Exemplo n.º 3
0
    def run_case_once(self,
                      testcase: DataDrivenTestCase,
                      incremental_step: int = 0) -> None:
        find_module_clear_caches()
        original_program_text = '\n'.join(testcase.input)
        module_data = self.parse_module(original_program_text,
                                        incremental_step)

        if incremental_step:
            if incremental_step == 1:
                # In run 1, copy program text to program file.
                for module_name, program_path, program_text in module_data:
                    if module_name == '__main__':
                        with open(program_path, 'w') as f:
                            f.write(program_text)
                        break
            elif incremental_step > 1:
                # In runs 2+, copy *.[num] files to * files.
                for dn, dirs, files in os.walk(os.curdir):
                    for file in files:
                        if file.endswith('.' + str(incremental_step)):
                            full = os.path.join(dn, file)
                            target = full[:-2]
                            copy_and_fudge_mtime(full, target)
                # Delete files scheduled to be deleted in [delete <path>.num] sections.
                for path in testcase.deleted_paths.get(incremental_step,
                                                       set()):
                    # Use retries to work around potential flakiness on Windows (AppVeyor).
                    retry_on_error(lambda: os.remove(path))

        # Parse options after moving files (in case mypy.ini is being moved).
        options = parse_options(original_program_text, testcase,
                                incremental_step)
        options.use_builtins_fixtures = True
        options.show_traceback = True
        if 'optional' in testcase.file:
            options.strict_optional = True
        if incremental_step:
            options.incremental = True
        else:
            options.cache_dir = os.devnull  # Don't waste time writing cache

        sources = []
        for module_name, program_path, program_text in module_data:
            # Always set to none so we're forced to reread the module in incremental mode
            sources.append(
                BuildSource(program_path, module_name,
                            None if incremental_step else program_text))

        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental_step == 0:
            # Not incremental
            msg = 'Unexpected type checker output ({}, line {})'
            output = testcase.output
        elif incremental_step == 1:
            msg = 'Unexpected type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental_step > 1:
            msg = ('Unexpected type checker output in incremental, run {}'.
                   format(incremental_step) + ' ({}, line {})')
            output = testcase.output2.get(incremental_step, [])
        else:
            raise AssertionError()

        if output != a and self.update_data:
            update_testcase_output(testcase, a)
        assert_string_arrays_equal(output, a,
                                   msg.format(testcase.file, testcase.line))

        if incremental_step and res:
            if options.follow_imports == 'normal' and testcase.output is None:
                self.verify_cache(module_data, a, res.manager)
            if incremental_step > 1:
                suffix = '' if incremental_step == 2 else str(
                    incremental_step - 1)
                assert_module_equivalence(
                    'rechecked' + suffix,
                    testcase.expected_rechecked_modules.get(incremental_step -
                                                            1),
                    res.manager.rechecked_modules)
                assert_module_equivalence(
                    'stale' + suffix,
                    testcase.expected_stale_modules.get(incremental_step - 1),
                    res.manager.stale_modules)
Exemplo n.º 4
0
    def run_case(self, testcase: DataDrivenTestCase) -> None:
        if self.should_skip(testcase):
            pytest.skip()
            return

        main_src = '\n'.join(testcase.input)
        main_path = os.path.join(test_temp_dir, 'main')
        with open(main_path, 'w', encoding='utf8') as f:
            f.write(main_src)

        options = self.get_options(main_src, testcase, build_cache=False)
        build_options = self.get_options(main_src, testcase, build_cache=True)
        server = Server(options, DEFAULT_STATUS_FILE)

        num_regular_incremental_steps = self.get_build_steps(main_src)
        step = 1
        sources = self.parse_sources(main_src, step, options)
        if step <= num_regular_incremental_steps:
            messages = self.build(build_options, sources)
        else:
            messages = self.run_check(server, sources)

        a = []
        if messages:
            a.extend(normalize_messages(messages))

        assert testcase.tmpdir
        a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir.name))

        if server.fine_grained_manager:
            if CHECK_CONSISTENCY:
                check_consistency(server.fine_grained_manager)

        steps = testcase.find_steps()
        all_triggered = []

        for operations in steps:
            step += 1
            for op in operations:
                if isinstance(op, UpdateFile):
                    # Modify/create file
                    copy_and_fudge_mtime(op.source_path, op.target_path)
                else:
                    # Delete file
                    os.remove(op.path)
            sources = self.parse_sources(main_src, step, options)

            if step <= num_regular_incremental_steps:
                new_messages = self.build(build_options, sources)
            else:
                new_messages = self.run_check(server, sources)

            updated = []  # type: List[str]
            changed = []  # type: List[str]
            targets = []  # type: List[str]
            if server.fine_grained_manager:
                if CHECK_CONSISTENCY:
                    check_consistency(server.fine_grained_manager)
                all_triggered.append(server.fine_grained_manager.triggered)

                updated = server.fine_grained_manager.updated_modules
                changed = [mod for mod, file in server.fine_grained_manager.changed_modules]
                targets = server.fine_grained_manager.processed_targets

            expected_stale = testcase.expected_stale_modules.get(step - 1)
            if expected_stale is not None:
                assert_module_equivalence(
                    'stale' + str(step - 1),
                    expected_stale, changed)

            expected_rechecked = testcase.expected_rechecked_modules.get(step - 1)
            if expected_rechecked is not None:
                assert_module_equivalence(
                    'rechecked' + str(step - 1),
                    expected_rechecked, updated)

            expected = testcase.expected_fine_grained_targets.get(step)
            if expected:
                assert_target_equivalence(
                    'targets' + str(step),
                    expected, targets)

            new_messages = normalize_messages(new_messages)

            a.append('==')
            a.extend(new_messages)
            assert testcase.tmpdir
            a.extend(self.maybe_suggest(step, server, main_src, testcase.tmpdir.name))

        # Normalize paths in test output (for Windows).
        a = [line.replace('\\', '/') for line in a]

        assert_string_arrays_equal(
            testcase.output, a,
            'Invalid output ({}, line {})'.format(
                testcase.file, testcase.line))

        if testcase.triggered:
            assert_string_arrays_equal(
                testcase.triggered,
                self.format_triggered(all_triggered),
                'Invalid active triggers ({}, line {})'.format(testcase.file,
                                                               testcase.line))
Exemplo n.º 5
0
    def run_case_once(self,
                      testcase: DataDrivenTestCase,
                      operations: List[FileOperation] = [],
                      incremental_step: int = 0) -> None:
        original_program_text = '\n'.join(testcase.input)
        module_data = self.parse_module(original_program_text,
                                        incremental_step)

        # Unload already loaded plugins, they may be updated.
        for file, _ in testcase.files:
            module = module_from_path(file)
            if module.endswith('_plugin') and module in sys.modules:
                del sys.modules[module]
        if incremental_step == 0 or incremental_step == 1:
            # In run 1, copy program text to program file.
            for module_name, program_path, program_text in module_data:
                if module_name == '__main__':
                    with open(program_path, 'w', encoding='utf8') as f:
                        f.write(program_text)
                    break
        elif incremental_step > 1:
            # In runs 2+, copy *.[num] files to * files.
            for op in operations:
                if isinstance(op, UpdateFile):
                    # Modify/create file
                    copy_and_fudge_mtime(op.source_path, op.target_path)
                else:
                    # Delete file
                    # Use retries to work around potential flakiness on Windows (AppVeyor).
                    path = op.path
                    retry_on_error(lambda: os.remove(path))

        # Parse options after moving files (in case mypy.ini is being moved).
        options = parse_options(original_program_text, testcase,
                                incremental_step)
        options.use_builtins_fixtures = True
        options.show_traceback = True

        # Enable some options automatically based on test file name.
        if 'optional' in testcase.file:
            options.strict_optional = True
        if 'columns' in testcase.file:
            options.show_column_numbers = True
        if 'errorcodes' in testcase.file:
            options.show_error_codes = True

        if incremental_step and options.incremental:
            # Don't overwrite # flags: --no-incremental in incremental test cases
            options.incremental = True
        else:
            options.incremental = False
            # Don't waste time writing cache unless we are specifically looking for it
            if not testcase.writescache:
                options.cache_dir = os.devnull

        sources = []
        for module_name, program_path, program_text in module_data:
            # Always set to none so we're forced to reread the module in incremental mode
            sources.append(
                BuildSource(program_path, module_name,
                            None if incremental_step else program_text))

        plugin_dir = os.path.join(test_data_prefix, 'plugins')
        sys.path.insert(0, plugin_dir)

        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        finally:
            assert sys.path[0] == plugin_dir
            del sys.path[0]

        if testcase.normalize_output:
            a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental_step == 0:
            # Not incremental
            msg = 'Unexpected type checker output ({}, line {})'
            output = testcase.output
        elif incremental_step == 1:
            msg = 'Unexpected type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental_step > 1:
            msg = ('Unexpected type checker output in incremental, run {}'.
                   format(incremental_step) + ' ({}, line {})')
            output = testcase.output2.get(incremental_step, [])
        else:
            raise AssertionError()

        if output != a and testcase.config.getoption('--update-data', False):
            update_testcase_output(testcase, a)
        assert_string_arrays_equal(output, a,
                                   msg.format(testcase.file, testcase.line))

        if res:
            if options.cache_dir != os.devnull:
                self.verify_cache(module_data, res.errors, res.manager,
                                  res.graph)

            name = 'targets'
            if incremental_step:
                name += str(incremental_step + 1)
            expected = testcase.expected_fine_grained_targets.get(
                incremental_step + 1)
            actual = res.manager.processed_targets
            # Skip the initial builtin cycle.
            actual = [
                t for t in actual if not any(
                    t.startswith(mod)
                    for mod in core_modules + ['mypy_extensions'])
            ]
            if expected is not None:
                assert_target_equivalence(name, expected, actual)
            if incremental_step > 1:
                suffix = '' if incremental_step == 2 else str(
                    incremental_step - 1)
                expected_rechecked = testcase.expected_rechecked_modules.get(
                    incremental_step - 1)
                if expected_rechecked is not None:
                    assert_module_equivalence('rechecked' + suffix,
                                              expected_rechecked,
                                              res.manager.rechecked_modules)
                expected_stale = testcase.expected_stale_modules.get(
                    incremental_step - 1)
                if expected_stale is not None:
                    assert_module_equivalence('stale' + suffix, expected_stale,
                                              res.manager.stale_modules)

        if testcase.output_files:
            check_test_output_files(testcase,
                                    incremental_step,
                                    strip_prefix='tmp/')
Exemplo n.º 6
0
    def run_case(self, testcase: DataDrivenTestCase) -> None:
        if self.should_skip(testcase):
            pytest.skip()
            return

        main_src = '\n'.join(testcase.input)
        main_path = os.path.join(test_temp_dir, 'main')
        with open(main_path, 'w', encoding='utf8') as f:
            f.write(main_src)

        options = self.get_options(main_src, testcase, build_cache=False)
        build_options = self.get_options(main_src, testcase, build_cache=True)
        server = Server(options, DEFAULT_STATUS_FILE)

        num_regular_incremental_steps = self.get_build_steps(main_src)
        step = 1
        sources = self.parse_sources(main_src, step, options)
        if step <= num_regular_incremental_steps:
            messages = self.build(build_options, sources)
        else:
            messages = self.run_check(server, sources)

        a = []
        if messages:
            a.extend(normalize_messages(messages))

        if server.fine_grained_manager:
            if CHECK_CONSISTENCY:
                check_consistency(server.fine_grained_manager)

        steps = testcase.find_steps()
        all_triggered = []

        for operations in steps:
            step += 1
            for op in operations:
                if isinstance(op, UpdateFile):
                    # Modify/create file
                    copy_and_fudge_mtime(op.source_path, op.target_path)
                else:
                    # Delete file
                    os.remove(op.path)
            sources = self.parse_sources(main_src, step, options)

            if step <= num_regular_incremental_steps:
                new_messages = self.build(build_options, sources)
            else:
                new_messages = self.run_check(server, sources)

            updated = []  # type: List[str]
            changed = []  # type: List[str]
            targets = []  # type: List[str]
            if server.fine_grained_manager:
                if CHECK_CONSISTENCY:
                    check_consistency(server.fine_grained_manager)
                all_triggered.append(server.fine_grained_manager.triggered)

                updated = server.fine_grained_manager.updated_modules
                changed = [mod for mod, file in server.fine_grained_manager.changed_modules]
                targets = server.fine_grained_manager.processed_targets

            assert_module_equivalence(
                'stale' + str(step - 1),
                testcase.expected_stale_modules.get(step - 1),
                changed)
            assert_module_equivalence(
                'rechecked' + str(step - 1),
                testcase.expected_rechecked_modules.get(step - 1),
                updated)
            assert_target_equivalence(
                'targets' + str(step),
                testcase.expected_fine_grained_targets.get(step),
                targets)

            new_messages = normalize_messages(new_messages)

            a.append('==')
            a.extend(new_messages)

        # Normalize paths in test output (for Windows).
        a = [line.replace('\\', '/') for line in a]

        assert_string_arrays_equal(
            testcase.output, a,
            'Invalid output ({}, line {})'.format(
                testcase.file, testcase.line))

        if testcase.triggered:
            assert_string_arrays_equal(
                testcase.triggered,
                self.format_triggered(all_triggered),
                'Invalid active triggers ({}, line {})'.format(testcase.file,
                                                               testcase.line))
Exemplo n.º 7
0
    def run_case_step(self, testcase: DataDrivenTestCase,
                      incremental_step: int) -> None:
        bench = testcase.config.getoption(
            '--bench', False) and 'Benchmark' in testcase.name

        options = Options()
        options.use_builtins_fixtures = True
        options.show_traceback = True
        options.strict_optional = True
        # N.B: We try to (and ought to!) run with the current
        # version of python, since we are going to link and run
        # against the current version of python.
        # But a lot of the tests use type annotations so we can't say it is 3.5.
        options.python_version = max(sys.version_info[:2], (3, 6))
        options.export_types = True
        options.preserve_asts = True
        options.incremental = False

        # Avoid checking modules/packages named 'unchecked', to provide a way
        # to test interacting with code we don't have types for.
        options.per_module_options['unchecked.*'] = {'follow_imports': 'error'}

        source = build.BuildSource('native.py', 'native', None)
        sources = [source]
        module_names = ['native']
        module_paths = ['native.py']

        # Hard code another module name to compile in the same compilation unit.
        to_delete = []
        for fn, text in testcase.files:
            fn = os.path.relpath(fn, test_temp_dir)

            if os.path.basename(fn).startswith('other') and fn.endswith('.py'):
                name = os.path.basename(fn).split('.')[0]
                module_names.append(name)
                sources.append(build.BuildSource(fn, name, None))
                to_delete.append(fn)
                module_paths.append(fn)

                shutil.copyfile(
                    fn,
                    os.path.join(os.path.dirname(fn),
                                 name + '_interpreted.py'))

        for source in sources:
            options.per_module_options.setdefault(source.module,
                                                  {})['mypyc'] = True

        separate = (self.get_separate('\n'.join(
            testcase.input), incremental_step) if self.separate else False)

        groups = construct_groups(sources, separate, len(module_names) > 1)

        try:
            result = emitmodule.parse_and_typecheck(sources=sources,
                                                    options=options,
                                                    alt_lib_path='.')
            errors = Errors()
            compiler_options = CompilerOptions(multi_file=self.multi_file,
                                               separate=self.separate)
            ir, cfiles = emitmodule.compile_modules_to_c(
                result,
                compiler_options=compiler_options,
                errors=errors,
                groups=groups,
            )
            if errors.num_errors:
                errors.flush_errors()
                assert False, "Compile error"
        except CompileError as e:
            for line in e.messages:
                print(line)
            assert False, 'Compile error'

        # Check that serialization works on this IR
        check_serialization_roundtrip(ir)

        setup_file = os.path.abspath(os.path.join(WORKDIR, 'setup.py'))
        # We pass the C file information to the build script via setup.py unfortunately
        with open(setup_file, 'w', encoding='utf-8') as f:
            f.write(
                setup_format.format(module_paths, separate, cfiles,
                                    self.multi_file))

        if not run_setup(setup_file, ['build_ext', '--inplace']):
            if testcase.config.getoption('--mypyc-showc'):
                show_c(cfiles)
            assert False, "Compilation failed"

        # Assert that an output file got created
        suffix = 'pyd' if sys.platform == 'win32' else 'so'
        assert glob.glob('native.*.{}'.format(suffix))

        driver_path = 'driver.py'
        env = os.environ.copy()
        env['MYPYC_RUN_BENCH'] = '1' if bench else '0'

        # XXX: This is an ugly hack.
        if 'MYPYC_RUN_GDB' in os.environ:
            if platform.system() == 'Darwin':
                subprocess.check_call(
                    ['lldb', '--', sys.executable, driver_path], env=env)
                assert False, (
                    "Test can't pass in lldb mode. (And remember to pass -s to "
                    "pytest)")
            elif platform.system() == 'Linux':
                subprocess.check_call(
                    ['gdb', '--args', sys.executable, driver_path], env=env)
                assert False, (
                    "Test can't pass in gdb mode. (And remember to pass -s to "
                    "pytest)")
            else:
                assert False, 'Unsupported OS'

        proc = subprocess.Popen([sys.executable, driver_path],
                                stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT,
                                env=env)
        output = proc.communicate()[0].decode('utf8')
        outlines = output.splitlines()

        if testcase.config.getoption('--mypyc-showc'):
            show_c(cfiles)
        if proc.returncode != 0:
            print()
            print('*** Exit status: %d' % proc.returncode)

        # Verify output.
        if bench:
            print('Test output:')
            print(output)
        else:
            if incremental_step == 1:
                msg = 'Invalid output'
                expected = testcase.output
            else:
                msg = 'Invalid output (step {})'.format(incremental_step)
                expected = testcase.output2.get(incremental_step, [])

            assert_test_output(testcase, outlines, msg, expected)

        if incremental_step > 1 and options.incremental:
            suffix = '' if incremental_step == 2 else str(incremental_step - 1)
            expected_rechecked = testcase.expected_rechecked_modules.get(
                incremental_step - 1)
            if expected_rechecked is not None:
                assert_module_equivalence('rechecked' + suffix,
                                          expected_rechecked,
                                          result.manager.rechecked_modules)
            expected_stale = testcase.expected_stale_modules.get(
                incremental_step - 1)
            if expected_stale is not None:
                assert_module_equivalence('stale' + suffix, expected_stale,
                                          result.manager.stale_modules)

        assert proc.returncode == 0
Exemplo n.º 8
0
    def perform_step(
        self, operations: List[Union[UpdateFile, DeleteFile]], server: Server,
        options: Options, build_options: Options, testcase: DataDrivenTestCase,
        main_src: str, step: int, num_regular_incremental_steps: int
    ) -> Tuple[List[str], List[List[str]]]:
        """Perform one fine-grained incremental build step (after some file updates/deletions).

        Return (mypy output, triggered targets).
        """
        for op in operations:
            if isinstance(op, UpdateFile):
                # Modify/create file
                copy_and_fudge_mtime(op.source_path, op.target_path)
            else:
                # Delete file/directory
                if os.path.isdir(op.path):
                    # Sanity check to avoid unexpected deletions
                    assert op.path.startswith('tmp')
                    shutil.rmtree(op.path)
                else:
                    os.remove(op.path)
        sources = self.parse_sources(main_src, step, options)

        if step <= num_regular_incremental_steps:
            new_messages = self.build(build_options, sources)
        else:
            new_messages = self.run_check(server, sources)

        updated = []  # type: List[str]
        changed = []  # type: List[str]
        targets = []  # type: List[str]
        triggered = []
        if server.fine_grained_manager:
            if CHECK_CONSISTENCY:
                check_consistency(server.fine_grained_manager)
            triggered.append(server.fine_grained_manager.triggered)

            updated = server.fine_grained_manager.updated_modules
            changed = [
                mod
                for mod, file in server.fine_grained_manager.changed_modules
            ]
            targets = server.fine_grained_manager.processed_targets

        expected_stale = testcase.expected_stale_modules.get(step - 1)
        if expected_stale is not None:
            assert_module_equivalence('stale' + str(step - 1), expected_stale,
                                      changed)

        expected_rechecked = testcase.expected_rechecked_modules.get(step - 1)
        if expected_rechecked is not None:
            assert_module_equivalence('rechecked' + str(step - 1),
                                      expected_rechecked, updated)

        expected = testcase.expected_fine_grained_targets.get(step)
        if expected:
            assert_target_equivalence('targets' + str(step), expected, targets)

        new_messages = normalize_messages(new_messages)

        a = new_messages
        assert testcase.tmpdir
        a.extend(
            self.maybe_suggest(step, server, main_src, testcase.tmpdir.name))

        return a, triggered
Exemplo n.º 9
0
    def run_case_once(self,
                      testcase: DataDrivenTestCase,
                      operations: List[FileOperation] = [],
                      incremental_step: int = 0) -> None:
        original_program_text = '\n'.join(testcase.input)
        module_data = self.parse_module(original_program_text,
                                        incremental_step)

        if incremental_step == 0 or incremental_step == 1:
            # In run 1, copy program text to program file.
            for module_name, program_path, program_text in module_data:
                if module_name == '__main__':
                    with open(program_path, 'w') as f:
                        f.write(program_text)
                    break
        elif incremental_step > 1:
            # In runs 2+, copy *.[num] files to * files.
            for op in operations:
                if isinstance(op, UpdateFile):
                    # Modify/create file
                    copy_and_fudge_mtime(op.source_path, op.target_path)
                else:
                    # Delete file
                    # Use retries to work around potential flakiness on Windows (AppVeyor).
                    path = op.path
                    retry_on_error(lambda: os.remove(path))

        # Parse options after moving files (in case mypy.ini is being moved).
        options = parse_options(original_program_text, testcase,
                                incremental_step)
        options.use_builtins_fixtures = True
        options.show_traceback = True
        if 'optional' in testcase.file:
            options.strict_optional = True
        if incremental_step and options.incremental:
            # Don't overwrite # flags: --no-incremental in incremental test cases
            options.incremental = True
        else:
            options.incremental = False
            # Don't waste time writing cache unless we are specifically looking for it
            if 'writescache' not in testcase.name.lower():
                options.cache_dir = os.devnull

        sources = []
        for module_name, program_path, program_text in module_data:
            # Always set to none so we're forced to reread the module in incremental mode
            sources.append(
                BuildSource(program_path, module_name,
                            None if incremental_step else program_text))

        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental_step == 0:
            # Not incremental
            msg = 'Unexpected type checker output ({}, line {})'
            output = testcase.output
        elif incremental_step == 1:
            msg = 'Unexpected type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental_step > 1:
            msg = ('Unexpected type checker output in incremental, run {}'.
                   format(incremental_step) + ' ({}, line {})')
            output = testcase.output2.get(incremental_step, [])
        else:
            raise AssertionError()

        if output != a and testcase.config.getoption('--update-data', False):
            update_testcase_output(testcase, a)
        assert_string_arrays_equal(output, a,
                                   msg.format(testcase.file, testcase.line))

        if res:
            if options.cache_dir != os.devnull:
                self.verify_cache(module_data, res.errors, res.manager,
                                  res.graph)

            if incremental_step > 1:
                suffix = '' if incremental_step == 2 else str(
                    incremental_step - 1)
                assert_module_equivalence(
                    'rechecked' + suffix,
                    testcase.expected_rechecked_modules.get(incremental_step -
                                                            1),
                    res.manager.rechecked_modules)
                assert_module_equivalence(
                    'stale' + suffix,
                    testcase.expected_stale_modules.get(incremental_step - 1),
                    res.manager.stale_modules)
Exemplo n.º 10
0
    def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> None:
        bench = testcase.config.getoption('--bench', False) and 'Benchmark' in testcase.name

        options = Options()
        options.use_builtins_fixtures = True
        options.show_traceback = True
        options.strict_optional = True
        options.python_version = sys.version_info[:2]
        options.export_types = True
        options.preserve_asts = True
        options.incremental = self.separate

        # Avoid checking modules/packages named 'unchecked', to provide a way
        # to test interacting with code we don't have types for.
        options.per_module_options['unchecked.*'] = {'follow_imports': 'error'}

        source = build.BuildSource('native.py', 'native', None)
        sources = [source]
        module_names = ['native']
        module_paths = ['native.py']

        # Hard code another module name to compile in the same compilation unit.
        to_delete = []
        for fn, text in testcase.files:
            fn = os.path.relpath(fn, test_temp_dir)

            if os.path.basename(fn).startswith('other') and fn.endswith('.py'):
                name = fn.split('.')[0].replace(os.sep, '.')
                module_names.append(name)
                sources.append(build.BuildSource(fn, name, None))
                to_delete.append(fn)
                module_paths.append(fn)

                shutil.copyfile(fn,
                                os.path.join(os.path.dirname(fn), name + '_interpreted.py'))

        for source in sources:
            options.per_module_options.setdefault(source.module, {})['mypyc'] = True

        separate = (self.get_separate('\n'.join(testcase.input), incremental_step) if self.separate
                    else False)

        groups = construct_groups(sources, separate, len(module_names) > 1)

        try:
            compiler_options = CompilerOptions(multi_file=self.multi_file, separate=self.separate)
            result = emitmodule.parse_and_typecheck(
                sources=sources,
                options=options,
                compiler_options=compiler_options,
                groups=groups,
                alt_lib_path='.')
            errors = Errors()
            ir, cfiles = emitmodule.compile_modules_to_c(
                result,
                compiler_options=compiler_options,
                errors=errors,
                groups=groups,
            )
            if errors.num_errors:
                errors.flush_errors()
                assert False, "Compile error"
        except CompileError as e:
            for line in e.messages:
                print(fix_native_line_number(line, testcase.file, testcase.line))
            assert False, 'Compile error'

        # Check that serialization works on this IR. (Only on the first
        # step because the the returned ir only includes updated code.)
        if incremental_step == 1:
            check_serialization_roundtrip(ir)

        opt_level = int(os.environ.get('MYPYC_OPT_LEVEL', 0))
        debug_level = int(os.environ.get('MYPYC_DEBUG_LEVEL', 0))

        setup_file = os.path.abspath(os.path.join(WORKDIR, 'setup.py'))
        # We pass the C file information to the build script via setup.py unfortunately
        with open(setup_file, 'w', encoding='utf-8') as f:
            f.write(setup_format.format(module_paths,
                                        separate,
                                        cfiles,
                                        self.multi_file,
                                        opt_level,
                                        debug_level))

        if not run_setup(setup_file, ['build_ext', '--inplace']):
            if testcase.config.getoption('--mypyc-showc'):
                show_c(cfiles)
            assert False, "Compilation failed"

        # Assert that an output file got created
        suffix = 'pyd' if sys.platform == 'win32' else 'so'
        assert glob.glob(f'native.*.{suffix}') or glob.glob(f'native.{suffix}')

        driver_path = 'driver.py'
        if not os.path.isfile(driver_path):
            # No driver.py provided by test case. Use the default one
            # (mypyc/test-data/driver/driver.py) that calls each
            # function named test_*.
            default_driver = os.path.join(
                os.path.dirname(__file__), '..', 'test-data', 'driver', 'driver.py')
            shutil.copy(default_driver, driver_path)
        env = os.environ.copy()
        env['MYPYC_RUN_BENCH'] = '1' if bench else '0'

        # XXX: This is an ugly hack.
        if 'MYPYC_RUN_GDB' in os.environ:
            if platform.system() == 'Darwin':
                subprocess.check_call(['lldb', '--', sys.executable, driver_path], env=env)
                assert False, ("Test can't pass in lldb mode. (And remember to pass -s to "
                               "pytest)")
            elif platform.system() == 'Linux':
                subprocess.check_call(['gdb', '--args', sys.executable, driver_path], env=env)
                assert False, ("Test can't pass in gdb mode. (And remember to pass -s to "
                               "pytest)")
            else:
                assert False, 'Unsupported OS'

        proc = subprocess.Popen([sys.executable, driver_path], stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT, env=env)
        output = proc.communicate()[0].decode('utf8')
        outlines = output.splitlines()

        if testcase.config.getoption('--mypyc-showc'):
            show_c(cfiles)
        if proc.returncode != 0:
            print()
            print('*** Exit status: %d' % proc.returncode)

        # Verify output.
        if bench:
            print('Test output:')
            print(output)
        else:
            if incremental_step == 1:
                msg = 'Invalid output'
                expected = testcase.output
            else:
                msg = f'Invalid output (step {incremental_step})'
                expected = testcase.output2.get(incremental_step, [])

            if not expected:
                # Tweak some line numbers, but only if the expected output is empty,
                # as tweaked output might not match expected output.
                outlines = [fix_native_line_number(line, testcase.file, testcase.line)
                            for line in outlines]
            assert_test_output(testcase, outlines, msg, expected)

        if incremental_step > 1 and options.incremental:
            suffix = '' if incremental_step == 2 else str(incremental_step - 1)
            expected_rechecked = testcase.expected_rechecked_modules.get(incremental_step - 1)
            if expected_rechecked is not None:
                assert_module_equivalence(
                    'rechecked' + suffix,
                    expected_rechecked, result.manager.rechecked_modules)
            expected_stale = testcase.expected_stale_modules.get(incremental_step - 1)
            if expected_stale is not None:
                assert_module_equivalence(
                    'stale' + suffix,
                    expected_stale, result.manager.stale_modules)

        assert proc.returncode == 0
Exemplo n.º 11
0
    def run_case_once(self, testcase: DataDrivenTestCase,
                      operations: List[FileOperation] = [],
                      incremental_step: int = 0) -> None:
        original_program_text = '\n'.join(testcase.input)
        module_data = self.parse_module(original_program_text, incremental_step)

        # Unload already loaded plugins, they may be updated.
        for file, _ in testcase.files:
            module = module_from_path(file)
            if module.endswith('_plugin') and module in sys.modules:
                del sys.modules[module]
        if incremental_step == 0 or incremental_step == 1:
            # In run 1, copy program text to program file.
            for module_name, program_path, program_text in module_data:
                if module_name == '__main__':
                    with open(program_path, 'w', encoding='utf8') as f:
                        f.write(program_text)
                    break
        elif incremental_step > 1:
            # In runs 2+, copy *.[num] files to * files.
            for op in operations:
                if isinstance(op, UpdateFile):
                    # Modify/create file
                    copy_and_fudge_mtime(op.source_path, op.target_path)
                else:
                    # Delete file
                    # Use retries to work around potential flakiness on Windows (AppVeyor).
                    path = op.path
                    retry_on_error(lambda: os.remove(path))

        # Parse options after moving files (in case mypy.ini is being moved).
        options = parse_options(original_program_text, testcase, incremental_step)
        options.use_builtins_fixtures = True
        options.show_traceback = True
        if 'optional' in testcase.file:
            options.strict_optional = True
        if 'newsemanal' in testcase.file:
            options.new_semantic_analyzer = True
        if incremental_step and options.incremental:
            # Don't overwrite # flags: --no-incremental in incremental test cases
            options.incremental = True
        else:
            options.incremental = False
            # Don't waste time writing cache unless we are specifically looking for it
            if not testcase.writescache:
                options.cache_dir = os.devnull

        sources = []
        for module_name, program_path, program_text in module_data:
            # Always set to none so we're forced to reread the module in incremental mode
            sources.append(BuildSource(program_path, module_name,
                                       None if incremental_step else program_text))

        plugin_dir = os.path.join(test_data_prefix, 'plugins')
        sys.path.insert(0, plugin_dir)

        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        finally:
            assert sys.path[0] == plugin_dir
            del sys.path[0]

        if testcase.normalize_output:
            a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental_step == 0:
            # Not incremental
            msg = 'Unexpected type checker output ({}, line {})'
            output = testcase.output
        elif incremental_step == 1:
            msg = 'Unexpected type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental_step > 1:
            msg = ('Unexpected type checker output in incremental, run {}'.format(
                incremental_step) + ' ({}, line {})')
            output = testcase.output2.get(incremental_step, [])
        else:
            raise AssertionError()

        if output != a and testcase.config.getoption('--update-data', False):
            update_testcase_output(testcase, a)
        assert_string_arrays_equal(output, a, msg.format(testcase.file, testcase.line))

        if res:
            if options.cache_dir != os.devnull:
                self.verify_cache(module_data, res.errors, res.manager, res.graph)

            if incremental_step > 1:
                suffix = '' if incremental_step == 2 else str(incremental_step - 1)
                assert_module_equivalence(
                    'rechecked' + suffix,
                    testcase.expected_rechecked_modules.get(incremental_step - 1),
                    res.manager.rechecked_modules)
                assert_module_equivalence(
                    'stale' + suffix,
                    testcase.expected_stale_modules.get(incremental_step - 1),
                    res.manager.stale_modules)
Exemplo n.º 12
0
    def run_case_once(self, testcase: DataDrivenTestCase, incremental_step: int = 0) -> None:
        original_program_text = '\n'.join(testcase.input)
        module_data = self.parse_module(original_program_text, incremental_step)

        if incremental_step == 0 or incremental_step == 1:
            # In run 1, copy program text to program file.
            for module_name, program_path, program_text in module_data:
                if module_name == '__main__':
                    with open(program_path, 'w') as f:
                        f.write(program_text)
                    break
        elif incremental_step > 1:
            # In runs 2+, copy *.[num] files to * files.
            for dn, dirs, files in os.walk(os.curdir):
                for file in files:
                    if file.endswith('.' + str(incremental_step)):
                        full = os.path.join(dn, file)
                        target = full[:-2]
                        copy_and_fudge_mtime(full, target)
            # Delete files scheduled to be deleted in [delete <path>.num] sections.
            for path in testcase.deleted_paths.get(incremental_step, set()):
                # Use retries to work around potential flakiness on Windows (AppVeyor).
                retry_on_error(lambda: os.remove(path))

        # Parse options after moving files (in case mypy.ini is being moved).
        options = parse_options(original_program_text, testcase, incremental_step)
        options.use_builtins_fixtures = True
        options.show_traceback = True
        if 'optional' in testcase.file:
            options.strict_optional = True
        if incremental_step:
            options.incremental = True

        sources = []
        for module_name, program_path, program_text in module_data:
            # Always set to none so we're forced to reread the module in incremental mode
            sources.append(BuildSource(program_path, module_name,
                                       None if incremental_step else program_text))

        res = None
        try:
            res = build.build(sources=sources,
                              options=options,
                              alt_lib_path=test_temp_dir)
            a = res.errors
        except CompileError as e:
            a = e.messages
        a = normalize_error_messages(a)

        # Make sure error messages match
        if incremental_step == 0:
            # Not incremental
            msg = 'Unexpected type checker output ({}, line {})'
            output = testcase.output
        elif incremental_step == 1:
            msg = 'Unexpected type checker output in incremental, run 1 ({}, line {})'
            output = testcase.output
        elif incremental_step > 1:
            msg = ('Unexpected type checker output in incremental, run {}'.format(
                incremental_step) + ' ({}, line {})')
            output = testcase.output2.get(incremental_step, [])
        else:
            raise AssertionError()

        if output != a and self.update_data:
            update_testcase_output(testcase, a)
        assert_string_arrays_equal(output, a, msg.format(testcase.file, testcase.line))

        if res:
            self.verify_cache(module_data, res.errors, res.manager, res.graph)

            if incremental_step > 1:
                suffix = '' if incremental_step == 2 else str(incremental_step - 1)
                assert_module_equivalence(
                    'rechecked' + suffix,
                    testcase.expected_rechecked_modules.get(incremental_step - 1),
                    res.manager.rechecked_modules)
                assert_module_equivalence(
                    'stale' + suffix,
                    testcase.expected_stale_modules.get(incremental_step - 1),
                    res.manager.stale_modules)