Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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"
            )
Esempio n. 5
0
    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)
Esempio n. 6
0
    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([])
Esempio n. 7
0
    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)
Esempio n. 8
0
    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)
Esempio n. 9
0
    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,
            )