#!/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
"""


from argparse import ArgumentParser, RawDescriptionHelpFormatter
import sys
from subprocess import CalledProcessError

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


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)
