#!/usr/bin/env python3

#
# Copyright © 2021 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# This software product is a proprietary product of Nvidia Corporation and its affiliates
# (the "Company") and all right, title, and interest in and to the software
# product, including all associated intellectual property rights, are and
# shall remain exclusively with the Company.
#
# This software product is governed by the End User License Agreement
# provided with the software product. 
"""
Check if a GitLab merge request is ready for merge.

Creates a local clone of the merge request merge preview in a temporary
directory and runs the full test suite in VX.

For example, to check MR !2237, use

    mrcheck 2237
"""


# standard libraries
import sys
from argparse import (
    ArgumentParser,
    RawDescriptionHelpFormatter,
)
from subprocess import CalledProcessError

# third party libraries
# NOTE: This is coming from the local scripts/ directory.
from repo_tools import (
    cleanup,
    echo,
    init_repo,
    run,
)


def test_static_analysis(repo_helper):
    echo("Running flake8...")
    repo_helper.pytest(
        "./cue/tests/test_static_analysis.py",
        # Don't collect coverage
        "-oaddopts=",
        # PERF: Only run tests that cache results.
        "-k", "flake8",
    )


def test_specs(repo_helper):
    echo("Building specs...")
    repo_helper.pytest(
        *repo_helper.glob("cue_*/tests/test_specs.py"),
        "-k", "test_verify_spec", "-oaddopts=",
    )


def test_all(repo_helper):
    echo("Running unit test suite...")
    component_test_selector = "needs_cl or destructive"
    # The unit test suite should have 100% code coverage.
    repo_helper.pytest("-m", f"not ({component_test_selector})")

    echo("Running component test suite...")
    component_pytest_args = [
        # Run the component tests
        "-m", f"({component_test_selector}) and not packaging",
        # Don't fail on lack of coverage
        "-oaddopts=",
        # Keep skipping the slow stuff though.
        "--benchmark-skip",
    ]
    try:
        repo_helper.pytest(*component_pytest_args)
    except CalledProcessError:
        echo("Rerunning failed component test...")
        repo_helper.pytest("--last-failed", '-vv', *component_pytest_args)


def run_tests(repo_helper):
    """
    Run all the tests in an order that gives quick/likely failures first.
    """
    # Static analysis is most likely to fail and quickest run.
    test_static_analysis(repo_helper)
    # Specs are not as likely to fail, but we cache the results, so we're not
    # losing much time by running them up front.
    # For the PRs that touch the specs, it'll be nice to have the failures
    # quickly.
    test_specs(repo_helper)
    # Finally, run the full test suite. This will take a while.
    test_all(repo_helper)


def print_mr(mr_number, mr_branchname):
    mr_link = (
        "https://gitlab-master.nvidia.com/nbu-sws/nvue/nvue/-"
        + f"/merge_requests/{mr_number}"
    )
    echo("")
    echo("Check out changes locally using the following tag:")
    echo("")
    echo(f"    {mr_branchname}")
    echo("")
    echo("Follow up on the merge request here:")
    echo("")
    echo(f"    {mr_link}")
    echo("")


def main():
    parser = ArgumentParser(
        description=__doc__,
        formatter_class=RawDescriptionHelpFormatter,
    )

    parser.add_argument(
        "mr_number",
        action="store",
        metavar="<mr-number>",
        type=int,
        help="The numeric id of the MR to test"
    )

    args = parser.parse_args()

    # Fetch the MR's merge preview into a local tag.
    mr_branchname = f"merge-requests/{args.mr_number}/merge"
    refspec = f"+refs/{mr_branchname}:refs/tags/{mr_branchname}"
    echo(f"Fetching {mr_branchname!r}")
    run(['git', 'fetch', 'origin', refspec])

    # Clone the local tag into a tmp repo.
    repo_helper = init_repo(
        repository=".",
        revision=mr_branchname,
    )
    try:
        run_tests(repo_helper)
    finally:
        cleanup(repo_helper)
        print_mr(args.mr_number, mr_branchname)


if __name__ == "__main__":
    try:
        main()
    except Exception as err:
        sys.stderr.write(f"Error: {err}")
        sys.stderr.write('\n')
        sys.exit(1)
