def test_upgrade_project( self, fix, run_global_version_update, gather, get_errors, remove_version, subprocess, ) -> None: arguments = MagicMock() arguments.lint = False gather.return_value = [] upgrade.run_fixme_all(arguments) fix.assert_not_called() subprocess.assert_not_called() errors = [ { "line": 2, "column": 4, "path": "local.py", "code": 7, "name": "Kind", "concise_description": "Error", "inference": {}, "ignore_error": False, "external_to_global_root": False, } ] get_errors.return_value = errors configuration = upgrade.Configuration( "/root/local/.pyre_configuration.local", {"version": 123} ) configuration.get_path() upgrade._upgrade_project(arguments, configuration, "/root") run_global_version_update.assert_not_called() fix.called_once_with(arguments, upgrade.sort_errors(errors)) subprocess.assert_called_once_with( ["hg", "commit", "--message", upgrade._commit_message("local")] ) # Test with lint subprocess.reset_mock() fix.reset_mock() arguments.lint = True upgrade._upgrade_project(arguments, configuration, "/root") run_global_version_update.assert_not_called() fix.called_once_with(arguments, upgrade.sort_errors(errors)) calls = [ call(["arc", "lint", "--never-apply-patches", "--output", "none"]), call().__bool__(), call(["arc", "lint", "--apply-patches", "--output", "none"]), call(["hg", "commit", "--message", upgrade._commit_message("local")]), ] subprocess.assert_has_calls(calls)
def test_run_fixme_single(self, fix, get_errors, remove_version, find_configuration, subprocess) -> None: arguments = MagicMock() arguments.sandcastle = None arguments.submit = True arguments.path = Path("local") get_errors.return_value = [] configuration_contents = '{"targets":[]}' with patch("builtins.open", mock_open(read_data=configuration_contents)): upgrade.run_fixme_single(arguments) fix.assert_not_called() subprocess.assert_not_called() configuration_contents = '{"version": 123}' with patch("builtins.open", mock_open(read_data=configuration_contents)): upgrade.run_fixme_single(arguments) fix.assert_not_called() calls = [ call([ "hg", "commit", "--message", upgrade._commit_message("local") ]), call(["jf", "submit", "--update-fields"]), ] subprocess.assert_has_calls(calls) fix.reset_mock() subprocess.reset_mock() pyre_errors = [{ "line": 2, "column": 4, "path": "local.py", "code": 7, "name": "Kind", "concise_description": "Error", "inference": {}, "ignore_error": False, "external_to_global_root": False, }] get_errors.return_value = pyre_errors with patch("builtins.open", mock_open(read_data=configuration_contents)): upgrade.run_fixme_single(arguments) fix.called_once_with(arguments, errors.sort_errors(pyre_errors)) calls = [ call([ "hg", "commit", "--message", upgrade._commit_message("local") ]), call(["jf", "submit", "--update-fields"]), ] call.assert_has_calls(calls)
def test_run_global_version_update( self, open_mock, gather_local_configurations, find_project_configuration, subprocess, ) -> None: arguments = MagicMock() arguments.hash = "abcd" arguments.push_blocking_only = False with patch("json.dump") as dump: mocks = [ mock_open(read_data='{"version": "old"}').return_value, mock_open(read_data="{}").return_value, mock_open(read_data='{"push_blocking": false}').return_value, mock_open(read_data="{}").return_value, mock_open(read_data='{"push_blocking": true}').return_value, mock_open(read_data="{}").return_value, ] open_mock.side_effect = mocks upgrade.run_global_version_update(arguments) dump.assert_has_calls( [ call({"version": "abcd"}, mocks[1], indent=2, sort_keys=True), call( {"push_blocking": False, "version": "old"}, mocks[3], indent=2, sort_keys=True, ), call( {"push_blocking": True, "version": "old"}, mocks[5], indent=2, sort_keys=True, ), ] ) subprocess.assert_called_once_with( [ "hg", "commit", "--message", upgrade._commit_message( "global configuration", summary_override="Automatic upgrade to hash `abcd`", ), ] ) # Push blocking argument: Since the push blocking only argument is only used when # gathering local configurations (mocked here), this is a no-op. Documents it. subprocess.reset_mock() arguments.push_blocking_only = True arguments.submit = True with patch("json.dump") as dump: mocks = [ mock_open(read_data='{"version": "old"}').return_value, mock_open(read_data="{}").return_value, mock_open(read_data='{"push_blocking": false}').return_value, mock_open(read_data="{}").return_value, mock_open(read_data='{"push_blocking": true}').return_value, mock_open(read_data="{}").return_value, ] open_mock.side_effect = mocks upgrade.run_global_version_update(arguments) dump.assert_has_calls( [ call({"version": "abcd"}, mocks[1], indent=2, sort_keys=True), call( {"push_blocking": False, "version": "old"}, mocks[3], indent=2, sort_keys=True, ), call( {"push_blocking": True, "version": "old"}, mocks[5], indent=2, sort_keys=True, ), ] ) calls = [ call( [ "hg", "commit", "--message", upgrade._commit_message( "global configuration", summary_override="Automatic upgrade to hash `abcd`", ), ] ), call(["jf", "submit", "--update-fields", "--no-deps"]), ] subprocess.assert_has_calls(calls)
def test_run_fixme( self, stdin_errors, run_errors, path_read_text, subprocess ) -> None: arguments = MagicMock() arguments.comment = None arguments.max_line_length = 88 arguments.run = False arguments.truncate = True stdin_errors.return_value = [] run_errors.return_value = [] upgrade.run_fixme(arguments) # Test single error. with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [1]: description", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = " 1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( " # pyre-fixme[1]: description\n 1\n2" ) # Generated files. with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 2, "concise_description": "Error [1]: description", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "# @" "generated\n1\n2\n" upgrade.run_fixme(arguments) path_write_text.assert_not_called() # Test single error with lint. arguments.run = True arguments.lint = True with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [1]: description", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = " 1\n2" upgrade.run_fixme(arguments) calls = [ call(" # pyre-fixme[1]: description\n 1\n2"), call(" # pyre-fixme[1]: description\n 1\n2"), ] path_write_text.assert_has_calls(calls) calls = [ call(["arc", "lint", "--never-apply-patches", "--output", "none"]), call().__bool__(), call(["arc", "lint", "--apply-patches", "--output", "none"]), ] subprocess.assert_has_calls(calls) arguments.run = False arguments.lint = False # Test error with comment. with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [1]: description", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = " 1\n2" arguments.comment = "T1234" upgrade.run_fixme(arguments) arguments.comment = None path_write_text.assert_called_once_with(" # pyre-fixme[1]: T1234\n 1\n2") # Test multiple errors and multiple lines. with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [1]: description", }, { "path": "path.py", "line": 2, "concise_description": "Error [1]: description", }, { "path": "path.py", "line": 2, "concise_description": "Error [2]: description", }, ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( "# pyre-fixme[1]: description\n" "1\n" "# pyre-fixme[1]: description\n" "# pyre-fixme[2]: description\n" "2" ) with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 2, "concise_description": "Error [10]: Description one.", }, { "path": "path.py", "line": 2, "concise_description": "Error [11]: Description two.", }, ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( "1\n" "# pyre-fixme[10]: Description one.\n" "# pyre-fixme[11]: Description two.\n" "2" ) arguments.max_line_length = 40 with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 2, "concise_description": "Error [1]: Description one.", }, { "path": "path.py", "line": 2, "concise_description": "Error [2]: Very long description two.", }, { "path": "path.py", "line": 2, "concise_description": "Error [3]: Very long description three.", }, { "path": "path.py", "line": 2, "concise_description": "Error [4]: Description four.", }, ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( "1\n" "# pyre-fixme[1]: Description one.\n" "# pyre-fixme[2]: Very long descriptio...\n" "# pyre-fixme[3]: Very long descriptio...\n" "# pyre-fixme[4]: Description four.\n" "2" ) arguments.max_line_length = 36 arguments.truncate = False with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [2]: Maximum characters.", }, { "path": "path.py", "line": 2, "concise_description": "Error [2]: Too many characters.", }, ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( "# pyre-fixme[2]: Maximum characters.\n" "1\n" "# pyre-fixme[2]: Too many\n" "# characters.\n" "2" ) arguments.max_line_length = 40 arguments.truncate = False with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 2, "concise_description": "Error [1]: Description one.", }, { "path": "path.py", "line": 2, "concise_description": "Error [2]: Very long description two.", }, { "path": "path.py", "line": 2, "concise_description": "Error [3]: Very long description three.", }, { "path": "path.py", "line": 2, "concise_description": "Error [4]: Description four.", }, ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( "1\n" "# pyre-fixme[1]: Description one.\n" "# pyre-fixme[2]: Very long\n" "# description two.\n" "# pyre-fixme[3]: Very long\n" "# description three.\n" "# pyre-fixme[4]: Description four.\n" "2" ) arguments.truncate = True # Test errors in multiple files. arguments.max_line_length = 88 with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [1]: description", }, { "path": "other.py", "line": 2, "concise_description": "Error [2]: description", }, ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "1\n2" upgrade.run_fixme(arguments) path_write_text.has_calls( [ call("# pyre-fixme[1]: description\n1\n2"), call("1\n#pyre-fixme[2]: description\n2"), ] ) # Test removal of extraneous ignore. with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [0]: extraneous ignore", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = " # pyre-ignore[0]: [1, 2, 3]\n2" upgrade.run_fixme(arguments) arguments.comment = None path_write_text.assert_called_once_with("2") # Test removal of extraneous ignore. with patch.object(pathlib.Path, "write_text") as path_write_text: arguments.max_line_length = 30 errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [0]: extraneous", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = ( " # pyre-ignore[0]: [1, 2, 3]\n# continuation comment\n2" ) upgrade.run_fixme(arguments) arguments.comment = None arguments.truncate = True path_write_text.assert_called_once_with("2") # We don't remove legitimate comments. with patch.object(pathlib.Path, "write_text") as path_write_text: arguments.max_line_length = 30 errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [0]: extraneous", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = ( " # pyre-ignore[0]: [1, 2, 3]\n# user comment\n2" ) upgrade.run_fixme(arguments) arguments.comment = None arguments.truncate = True path_write_text.assert_called_once_with("# user comment\n2") with patch.object(pathlib.Path, "write_text") as path_write_text: arguments.max_line_length = 30 errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [0]: extraneous ignore that's " "quite long", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = ( " # pyre-ignore[0]:\n# comment that doesn't fit on one line\n" "# pyre-ignore[1]:\n2" ) upgrade.run_fixme(arguments) arguments.comment = None arguments.truncate = True path_write_text.assert_called_once_with("# pyre-ignore[1]:\n2") with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [0]: extraneous ignore", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "# pyre-fixme[1]\n# pyre-fixme[2]\n2" upgrade.run_fixme(arguments) arguments.comment = None path_write_text.assert_called_once_with("# pyre-fixme[2]\n2") # Test removal of extraneous ignore (trailing comment). with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [0]: extraneous ignore", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "1# pyre-ignore[0]: [1, 2, 3]\n2" upgrade.run_fixme(arguments) arguments.comment = None path_write_text.assert_called_once_with("1\n2") # Test long lines. with patch.object(pathlib.Path, "write_text") as path_write_text: arguments_short = MagicMock() arguments_short.comment = None arguments_short.max_line_length = 35 arguments_short.run = False errors = [ { "path": "path.py", "line": 1, "concise_description": "Error [1]: description one, " "that has a pretty verbose text", }, { "path": "path.py", "line": 2, "concise_description": "Error [2]: description-that-will-not-break-" "even-when-facing-adversities", }, ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = "line 1\nline 2\nline 3" upgrade.run_fixme(arguments_short) path_write_text.assert_called_once_with( """# FIXME[1]: description one... line 1 # FIXME[2]: description-tha... line 2 line 3""".replace( " ", "" ).replace( "FIXME", "pyre-fixme" ) ) # Fall back to normal description for backwards compatibility. with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "description": "Error [1]: description", "concise_description": "", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = " 1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( " # pyre-fixme[1]: description\n 1\n2" ) # Ensure that we prefer concise descriptions. with patch.object(pathlib.Path, "write_text") as path_write_text: errors = [ { "path": "path.py", "line": 1, "description": "Error [1]: description", "concise_description": "Error[1]: Concise.", } ] stdin_errors.return_value = errors run_errors.return_value = errors path_read_text.return_value = " 1\n2" upgrade.run_fixme(arguments) path_write_text.assert_called_once_with( " # pyre-fixme[1]: Concise.\n 1\n2" )
def test_run_fixme_all( self, fix, run_global_version_update, get_errors, remove_version, find_configuration, gather, subprocess, ) -> None: arguments = MagicMock() arguments.lint = False gather.return_value = [ upgrade.Configuration("local/.pyre_configuration.local", {"version": 123}) ] get_errors.return_value = [] upgrade.run_fixme_all(arguments) run_global_version_update.assert_not_called() fix.assert_not_called() subprocess.assert_called_once_with( ["hg", "commit", "--message", upgrade._commit_message("local")] ) fix.reset_mock() subprocess.reset_mock() errors = [ { "line": 2, "column": 4, "path": "local.py", "code": 7, "name": "Kind", "concise_description": "Error", "inference": {}, "ignore_error": False, "external_to_global_root": False, } ] get_errors.return_value = errors upgrade.run_fixme_all(arguments) run_global_version_update.assert_not_called() fix.called_once_with(arguments, upgrade.sort_errors(errors)) subprocess.assert_called_once_with( ["hg", "commit", "--message", upgrade._commit_message("local")] ) # Test configuraton with no version set fix.reset_mock() subprocess.reset_mock() gather.return_value = [ upgrade.Configuration("local/.pyre_configuration.local", {}) ] upgrade.run_fixme_all(arguments) fix.assert_not_called() subprocess.assert_not_called() # Test with given hash fix.reset_mock() subprocess.reset_mock() gather.return_value = [ upgrade.Configuration("local/.pyre_configuration.local", {"version": 123}) ] arguments.hash = "abc" arguments.submit = True upgrade.run_fixme_all(arguments) run_global_version_update.assert_called_once_with(arguments) fix.called_once_with(arguments, upgrade.sort_errors(errors)) calls = [ call(["hg", "commit", "--message", upgrade._commit_message("local")]), call(["jf", "submit", "--update-fields", "--no-deps"]), ] subprocess.assert_has_calls(calls) # Test with linting fix.reset_mock() subprocess.reset_mock() run_global_version_update.reset_mock() arguments.lint = True upgrade.run_fixme_all(arguments) run_global_version_update.assert_called_once_with(arguments) fix.called_once_with(arguments, upgrade.sort_errors(errors)) calls = [ call(["hg", "commit", "--message", upgrade._commit_message("local")]), call(["jf", "submit", "--update-fields", "--no-deps"]), ] subprocess.assert_has_calls(calls)
def test_run_global_version_update( self, open_mock, gather_local_configurations, find_project_configuration, submit_changes, subprocess, ) -> None: arguments = MagicMock() arguments.submit = False arguments.hash = "abcd" arguments.paths = [] with patch("json.dump") as dump: mocks = [ mock_open(read_data='{"version": "old"}').return_value, mock_open(read_data="{}").return_value, mock_open( read_data='{"use_buck_builder": false}').return_value, mock_open(read_data="{}").return_value, mock_open(read_data='{"use_buck_builder": true}').return_value, mock_open(read_data="{}").return_value, ] open_mock.side_effect = mocks GlobalVersionUpdate(arguments, repository).run() dump.assert_has_calls([ call({"version": "abcd"}, mocks[1], indent=2, sort_keys=True), call( { "use_buck_builder": False, "version": "old" }, mocks[3], indent=2, sort_keys=True, ), call( { "use_buck_builder": True, "version": "old" }, mocks[5], indent=2, sort_keys=True, ), ]) submit_changes.assert_called_once_with( commit=True, submit=False, title="Update pyre global configuration version", summary="Automatic upgrade to hash `abcd`", ignore_failures=True, ) # paths passed from arguments will override the local configuration list # Therefore, we only read the first json configuration. subprocess.reset_mock() arguments.paths = [Path("foo/bar")] arguments.submit = False with patch("json.dump") as dump: mocks = [ mock_open(read_data='{"version": "old"}').return_value, mock_open(read_data="{}").return_value, mock_open(read_data="{}").return_value, mock_open(read_data="{}").return_value, mock_open(read_data="{}").return_value, mock_open(read_data="{}").return_value, ] open_mock.side_effect = mocks GlobalVersionUpdate(arguments, repository).run() dump.assert_has_calls([ call({"version": "abcd"}, mocks[1], indent=2, sort_keys=True) ]) subprocess.assert_has_calls([])
def test_run_fixme_targets_file(self, suppress_errors, subprocess) -> None: arguments = MagicMock() arguments.subdirectory = None arguments.no_commit = False arguments.lint = False buck_return = MagicMock() buck_return.returncode = 1 buck_return.stderr = b"stderr" subprocess.return_value = buck_return FixmeTargets(arguments, repository)._run_fixme_targets_file( Path("."), "a/b", [ Target("derp", strict=False, pyre=True), Target("herp", strict=False, pyre=True), ], ) subprocess.assert_called_once_with( [ "buck", "test", "--show-full-json-output", "a/b:derp-pyre-typecheck", "a/b:herp-pyre-typecheck", "--", "--run-disabled", ], stdout=-1, stderr=-1, ) suppress_errors.assert_not_called() buck_return.returncode = 0 subprocess.return_value = buck_return FixmeTargets(arguments, repository)._run_fixme_targets_file( Path("."), "a/b", [ Target("derp", strict=False, pyre=True), Target("herp", strict=False, pyre=True), ], ) suppress_errors.assert_not_called() buck_return.returncode = 32 buck_return.stdout = b""" Discovering tests Running 1 tests Started new test run: https://url a/b:c-typecheck - a.b.c (c.TypeCheck) 57.547 1/1 (failed) Test output: > All OK. > WARNING: Invoking pyre through buck TARGETS may... > See `https://wiki/configuration/` to set up Pyre for your project. > > a/b/x.py:278:28 %s > a/b/x.py:325:41 %s > a/b/y.py:86:26 %s a/b:c-typecheck - main 0.000 (passed) Finished test run: https://url Summary (total time 58.75s): PASS: 1 FAIL: 1 a/b:c-typecheck - a.b.c (c.TypeCheck) SKIP: 0 FATAL: 0 TIMEOUT: 0 OMIT: 0 """ % ( b"Undefined attribute [16]: `Optional` has no attribute `derp`.", b"Undefined attribute [16]: `Optional` has no attribute `herp`.", b"Incompatible parameter type [6]: Expected `str` for 1st positional only " + b"parameter to call `merp` but got `Optional[str]`.", ) subprocess.return_value = buck_return expected_errors = errors.Errors([ { "line": 278, "column": 28, "path": Path("a/b/x.py"), "code": "16", "description": "Undefined attribute [16]: `Optional` has no " + "attribute `derp`.", "concise_description": "Undefined attribute [16]: `Optional` has " + "no attribute `derp`.", }, { "line": 325, "column": 41, "path": Path("a/b/x.py"), "code": "16", "description": "Undefined attribute [16]: `Optional` has no " + "attribute `herp`.", "concise_description": "Undefined attribute [16]: `Optional` has " + "no attribute `herp`.", }, { "line": 86, "column": 26, "path": Path("a/b/y.py"), "code": "6", "description": "Incompatible parameter type [6]: Expected `str` " + "for 1st positional only parameter to call `merp` but got " + "`Optional[str]`.", "concise_description": "Incompatible parameter type [6]: Expected " + "`str` for 1st positional only parameter to call `merp` but got " + "`Optional[str]`.", }, ]) FixmeTargets(arguments, repository)._run_fixme_targets_file( Path("."), "a/b", [ Target("derp", strict=False, pyre=True), Target("herp", strict=False, pyre=True), ], ) suppress_errors.assert_called_once_with(expected_errors) # Test fallback to type check targets with modified names subprocess.reset_mock() suppress_errors.reset_mock() failed_buck_return = MagicMock() failed_buck_return.returncode = 5 failed_buck_return.stdout = b"" buck_query_return = MagicMock() buck_query_return.stdout = b"//target/to:retry-pyre-typecheck" subprocess.side_effect = [ failed_buck_return, buck_query_return, buck_return ] FixmeTargets(arguments, repository)._run_fixme_targets_file( Path("."), "a/b", [Target("derp", strict=False, pyre=True)]) subprocess.assert_has_calls([ call( [ "buck", "test", "--show-full-json-output", "a/b:derp-pyre-typecheck", "--", "--run-disabled", ], stdout=-1, stderr=-1, ), call(["buck", "query", "a/b:derp-pyre-typecheck"], stdout=-1, stderr=-1), call( [ "buck", "test", "--show-full-json-output", "//target/to:retry-pyre-typecheck", "--", "--run-disabled", ], stdout=-1, stderr=-1, ), ]) suppress_errors.assert_called_once_with(expected_errors) subprocess.reset_mock() suppress_errors.reset_mock() failed_buck_return = MagicMock() failed_buck_return.returncode = 5 failed_buck_return.stdout = b"" buck_query_return = MagicMock() buck_query_return.stdout = b"" buck_query_return.stderr = b""" Error in preloading targets. The rule //a/b:derp-pyre-typecheck could \ not be found. Please check the spelling and whether it is one of the 10 targets in \ /a/b/TARGETS. (1000 bytes) 2 similar targets in \ /data/users/szhu/fbsource/fbcode/tools/build/test/TARGETS are: //target/to:retry-pyre-typecheck //target/to:retry_non_typecheck """ subprocess.side_effect = [ failed_buck_return, buck_query_return, buck_return ] FixmeTargets(arguments, repository)._run_fixme_targets_file( Path("."), "a/b", [Target("derp", strict=False, pyre=True)]) subprocess.assert_has_calls([ call( [ "buck", "test", "--show-full-json-output", "a/b:derp-pyre-typecheck", "--", "--run-disabled", ], stdout=-1, stderr=-1, ), call(["buck", "query", "a/b:derp-pyre-typecheck"], stdout=-1, stderr=-1), call( [ "buck", "test", "--show-full-json-output", "//target/to:retry-pyre-typecheck", "--", "--run-disabled", ], stdout=-1, stderr=-1, ), ]) suppress_errors.assert_called_once_with(expected_errors)
def test_upgrade_project( self, fix, run_global_version_update, errors_from_stdin, gather, get_errors, remove_version, subprocess, ) -> None: arguments = MagicMock() arguments.lint = False arguments.from_stdin = False gather.return_value = [] upgrade.run_fixme_all(arguments) fix.assert_not_called() subprocess.assert_not_called() pyre_errors = [{ "line": 2, "column": 4, "path": "local.py", "code": 7, "name": "Kind", "concise_description": "Error", "inference": {}, "ignore_error": False, "external_to_global_root": False, }] get_errors.return_value = pyre_errors configuration = upgrade.Configuration( Path("/root/local/.pyre_configuration.local"), {"version": 123}) configuration.get_path() upgrade._upgrade_project(arguments, configuration, Path("/root")) run_global_version_update.assert_not_called() fix.called_once_with(arguments, errors.sort_errors(pyre_errors)) subprocess.assert_called_once_with( ["hg", "commit", "--message", upgrade._commit_message("local")]) # Test with lint subprocess.reset_mock() fix.reset_mock() arguments.from_stdin = False arguments.lint = True upgrade._upgrade_project(arguments, configuration, Path("/root")) errors_from_stdin.assert_not_called() run_global_version_update.assert_not_called() fix.called_once_with(arguments, errors.sort_errors(pyre_errors)) calls = [ call([ "arc", "lint", "--never-apply-patches", "--enforce-lint-clean", "--output", "none", ]), call().__bool__(), call(["arc", "lint", "--apply-patches", "--output", "none"]), call([ "hg", "commit", "--message", upgrade._commit_message("local") ]), ] subprocess.assert_has_calls(calls) # Test with from_stdin and lint subprocess.reset_mock() fix.reset_mock() get_errors.reset_mock() arguments.from_stdin = True arguments.lint = True errors_from_stdin.return_value = pyre_errors get_errors.return_value = pyre_errors upgrade._upgrade_project(arguments, configuration, Path("/root")) # Called in the first round to get initial errors errors_from_stdin.assert_called() # Called in the second round to get new errors after applying lint. get_errors.assert_called_once() run_global_version_update.assert_not_called() fix.called_once_with(arguments, errors.sort_errors(pyre_errors)) calls = [ call([ "arc", "lint", "--never-apply-patches", "--enforce-lint-clean", "--output", "none", ]), call().__bool__(), call(["arc", "lint", "--apply-patches", "--output", "none"]), call([ "hg", "commit", "--message", upgrade._commit_message("local") ]), ] subprocess.assert_has_calls(calls)
def test_run_global_version_update( self, open_mock, run_fixme, gather_local_configurations, configuration_write, configuration_set_version, find_project_configuration, submit_changes, subprocess, ) -> None: arguments = MagicMock() arguments.submit = False arguments.hash = "abcd" arguments.paths = [] with patch("json.dump"): mocks = [ mock_open(read_data='{"version": "old"}').return_value, mock_open(read_data='{"use_buck_builder": false}').return_value, mock_open(read_data='{"use_buck_builder": true}').return_value, ] open_mock.side_effect = mocks GlobalVersionUpdate.from_arguments(arguments, repository).run() configuration_set_version.assert_has_calls( [call("abcd"), call("old"), call("old")] ) configuration_write.assert_has_calls([call(), call(), call()]) submit_changes.assert_called_once_with( commit=True, submit=False, title="Update pyre global configuration version", summary="Automatic upgrade to hash `abcd`", ignore_failures=True, ) # Paths passed from arguments will override the local configuration list # Therefore, we only read the first json configuration. subprocess.reset_mock() configuration_set_version.reset_mock() configuration_write.reset_mock() arguments.paths = [Path("foo/bar")] with patch("json.dump"): mocks = [ mock_open(read_data='{"version": "old"}').return_value, mock_open(read_data="{}").return_value, ] open_mock.side_effect = mocks GlobalVersionUpdate.from_arguments(arguments, repository).run() configuration_set_version.assert_has_calls([call("abcd"), call("old")]) configuration_write.assert_has_calls([call(), call()]) subprocess.assert_has_calls([]) # Run fixme if global version has sources. subprocess.reset_mock() configuration_set_version.reset_mock() configuration_write.reset_mock() submit_changes.reset_mock() arguments.paths = [] with patch("json.dump"): mocks = [ mock_open( read_data='{"version": "old", "source_directories": ["source"]}' ).return_value, mock_open(read_data='{"use_buck_builder": false}').return_value, mock_open(read_data='{"use_buck_builder": true}').return_value, ] open_mock.side_effect = mocks GlobalVersionUpdate.from_arguments(arguments, repository).run() configuration_set_version.assert_has_calls( [call("abcd"), call("old"), call("old")] ) configuration_write.assert_has_calls([call(), call(), call()]) run_fixme.assert_called_once() submit_changes.assert_called_once_with( commit=True, submit=False, title="Update pyre global configuration version", summary="Automatic upgrade to hash `abcd`", ignore_failures=True, )