#!/usr/bin/env python
# encoding: utf-8
"""
Run compliance tests for a BagIt command-line tool

This requires a tool which behaves like a standard Unix tool and returns 0 for
success and 1 for failures. This tool will check stderr and report its contents
for the warning test bags.

Example usage:

    %(prog)s -- bagit.py --validate --quiet

"""

from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import logging
import os
import platform
import subprocess
import sys

BASE_DIR = os.path.dirname(__file__)

SYSTEM = platform.system().lower()

DEFAULT_ENCODING = sys.stdout.encoding
if DEFAULT_ENCODING != 'UTF-8':
    print('Default encoding is %s: use with caution, UTF-8 is recommended', file=sys.stderr)


def run_version_suite(test_program_argv, version_dir):
    spec_version = os.path.basename(version_dir)

    for test_category in os.listdir(version_dir):
        test_subsuite_directory = os.path.join(version_dir, test_category)
        if not os.path.isdir(test_subsuite_directory):
            continue

        if test_category.endswith('-only') and not test_category.startswith(SYSTEM):
            logging.info('Skipping %s tests as we are running on %s', test_category, SYSTEM)
            continue

        logging.info('Running version %s %s tests', spec_version, test_category)

        for test_bag in os.listdir(test_subsuite_directory):
            if not os.path.isdir(os.path.join(test_subsuite_directory, test_bag)):
                continue

            proc = subprocess.Popen(test_program_argv + [test_bag],
                                    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                    cwd=test_subsuite_directory)
            rc = proc.wait()
            stdout, stderr = proc.communicate()

            # We'll select the log level based on whether we expected an error:
            log_f = logging.info
            dump_stderr = False
            if test_category == 'valid':
                if rc != 0:
                    log_f = logging.error
                    dump_stderr = True
            elif test_category == 'invalid':
                if rc == 0:
                    log_f = logging.error
            elif test_category == 'warning':
                if rc == 0 and not stderr:
                    log_f = logging.warning

            log_f('Expected %s test %s: rc=%d, stdout=%d bytes, stderr=%d bytes',
                  test_category, test_bag, rc, len(stdout), len(stderr))

            log_captured_output(logging.info, 'stdout', stdout)
            log_captured_output(log_f if dump_stderr else logging.info, 'stderr', stderr)


def log_captured_output(logger, stream_name, output):
    output = output.decode(DEFAULT_ENCODING).strip()
    if output:
        logger('%s:\n\t%%s' % stream_name, output.replace('\n', '\n\t'))


def configure_logging(verbosity=0):
    if verbosity > 1:
        desired_level = logging.DEBUG
    elif verbosity > 0:
        desired_level = logging.INFO
    else:
        desired_level = logging.WARNING

    try:
        import coloredlogs
        coloredlogs.install(level=desired_level, reconfigure=True)
        return
    except ImportError:
        pass

    if verbosity:
        stdout_handler = logging.StreamHandler(stream=sys.stdout)
        stdout_handler.setLevel(desired_level)
        logging.getLogger().addHandler(stdout_handler)
    else:
        logging.basicConfig(level=logging.WARNING, stream=sys.stderr)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__.strip(),
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('--verbosity', '-v', action='count', default=0)
    parser.add_argument('test_program', metavar='TEST_PROGRAM', help='Path to an executable')
    parser.add_argument('test_program_arguments', nargs='*',
                        help='Arguments for the test executable - e.g. --validate for bagit.py.'
                             ' Note that on many shells you may need to follow the common'
                             ' convention of placing -- before the start of arguments which are'
                             ' intended for the test executable rather than this program.')
    args = parser.parse_args()

    configure_logging(args.verbosity)

    test_prog = [args.test_program]
    test_prog.extend(args.test_program_arguments)

    for i in os.listdir(os.path.normpath(BASE_DIR)):
        if not os.path.isdir(i):
            continue
        if not i.startswith('v'):
            continue

        version_dir = os.path.join(BASE_DIR, i)

        run_version_suite(test_prog, version_dir)
