#!/usr/bin/env python3

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# Based heavily on subunit2junit filter script from the upstream subunit python
# package, https://github.com/testing-cabal/subunit/ just adapted to run on
# python3 more reliably.

"""Script to convert subunit stream returned by stestr to junitxml for processing by azure-pipelines."""

import argparse
import sys

from junitxml import JUnitXmlResult
from subunit.filters import run_tests_from_stream
from testtools import StreamToExtendedDecorator


def filter_by_result(
    result_factory,
    output_path,
    passthrough,
    forward,
    input_stream=sys.stdin,
    protocol_version=1,
    passthrough_subunit=True,
):
    """Filter an input stream using a test result.

    :param callable result_factory: A callable that when passed an output stream
        returns a TestResult.  It is expected that this result will output
        to the given stream.
    :param str output_path: A path send output to.  If None, output will be go
        to ``sys.stdout``.
    :param bool passthrough: If True, all non-subunit input will be sent to
        ``sys.stdout``.  If False, that input will be discarded.
    :param bool forward: If True, all subunit input will be forwarded directly to
        ``sys.stdout`` as well as to the ``TestResult``.
    :param file input_stream: The source of subunit input.  Defaults to
        ``sys.stdin``.
    :param int protocol_version: The subunit protocol version to expect.
    :param bool passthrough_subunit: If True, passthrough should be as subunit.

    :return: A test result with the results of the run.
    :rtype: JUnitXmlResult
    """
    if protocol_version == 1:
        sys.stderr.write("Subunit protocol version 2 must be used")
        sys.exit(1)

    if passthrough:
        passthrough_stream = sys.stdout
    else:
        passthrough_stream = None

    if forward:
        forward_stream = sys.stdout
    else:
        forward_stream = None

    if output_path is None:
        output_to = sys.stdout
    else:
        output_to = open(output_path, "w")  # pylint: disable=consider-using-with

    try:
        result = result_factory(output_to)
        run_tests_from_stream(
            input_stream,
            result,
            passthrough_stream,
            forward_stream,
            protocol_version=protocol_version,
            passthrough_subunit=passthrough_subunit,
        )
    finally:
        if output_path:
            output_to.close()
    return result


def run_filter_script(
    result_factory, description, post_run_hook=None, protocol_version=1, passthrough_subunit=True
):
    """Main function for simple subunit filter scripts.

    Many subunit filter scripts take a stream of subunit input and use a
    TestResult to handle the events generated by that stream.  This function
    wraps a lot of the boiler-plate around that by making a script with
    options for handling passthrough information and stream forwarding

    :param callable result_factory: A callable that takes an output stream and returns
        a test result that outputs to that stream.
    :param str description: A description of the filter script.
    :param callable post_run_hook: A callback function that runs after the test run
        finishes. It will be passed a single positional argument the result
        object returned by the run.
    :param int protocol_version: What protocol version to consume/emit.
    :param bool passthrough_subunit: If True, passthrough should be as subunit.
    """
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument(
        "--no-passthrough",
        action="store_true",
        help="Hide all non subunit input.",
        default=False,
        dest="no_passthrough",
    )
    parser.add_argument("-o", "--output-to", help="Send the output to this path rather than stdout")
    parser.add_argument(
        "-f",
        "--forward",
        action="store_true",
        default=False,
        help=(
            "Forward subunit stream on stdout. When set, "
            "received non-subunit output will be encapsulated"
            " in subunit."
        ),
    )
    args = parser.parse_args()
    result = filter_by_result(
        result_factory,
        args.output_to,
        not args.no_passthrough,
        args.forward,
        protocol_version=protocol_version,
        passthrough_subunit=passthrough_subunit,
        input_stream=sys.stdin,
    )
    if post_run_hook:
        post_run_hook(result)
    if not hasattr(result, "wasSuccessful"):
        result = result.decorated


def _main():
    run_filter_script(
        lambda output: StreamToExtendedDecorator(JUnitXmlResult(output)),
        "Convert to junitxml",
        protocol_version=2,
    )


if __name__ == "__main__":
    _main()
