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
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))
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)
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))
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/')
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))
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
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
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)
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
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)
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)