#!/usr/bin/env python3

#
# This is a TAP compiler for python unittest
#
# It hooks into python's standard unittest module, and produces TAP output.
#
# This produces the Test Anything Protocol (ie: TAP)
# https://metacpan.org/pod/release/PETDANCE/Test-Harness-2.64/lib/Test/Harness/TAP.pod
#
# Based on code from here:
# https://github.com/vit1251/unittest-tap-reporting
# 

import argparse
import imp
import os
import sys
import time
import traceback
import unittest

def write_line(format, *args):
    sys.stdout.write(format % args)
    sys.stdout.write("\n")
    sys.stdout.flush()

class TAPTestResult(unittest.result.TestResult):
    def __init__(self):
        unittest.result.TestResult.__init__(self)
        self.number = 0

    def addSuccess(self, test):
        self.number += 1
        write_line("ok %d %s", self.number, test.id())

    def addSkip(self, test, reason):
        self.number += 1
        write_line("not ok %d # skip %s", self.number, test.id())
        write_line("# %s", reason)

    def addError(self, test, exc):
        (etype, evalue, etraceback) = exc
        traceback.print_exception(etype, evalue, etraceback, file=sys.stderr)
        self.number += 1
        write_line("not ok %d %s", self.number, test.id())

    def addFailure(self, test, err):
        (etype, evalue, etraceback) = exc
        traceback.print_exception(etype, evalue, etraceback, limit=1, file=sys.stderr)
        self.number += 1
        write_line("not ok %d %s", self.number, test.id())

class TAPTestRunner(object):
    def __init__(self):
        pass

    def run(self, test):
        write_line("1..%d", test.countTestCases())
        result = TAPTestResult()
        startTestRun = getattr(result, 'startTestRun', lambda : None)
        startTestRun()
        try:
            test(result)
        finally:
            stopTestRun = getattr(result, 'stopTestRun', lambda : None)
            stopTestRun()
        return result

def main(argv):
    parser = argparse.ArgumentParser(description='Python unittest TAP driver')
    parser.add_argument('module', nargs=1,
                        help="A unittest test module to run")
    args = parser.parse_args(argv[1:])

    (name, ext) = os.path.splitext(os.path.basename(args.module[0]))
    module = imp.load_source(name, args.module[0])
    loader = unittest.TestLoader()
    tests = loader.loadTestsFromModule(module)
    runner = TAPTestRunner()
    runner.run(tests)

if __name__ == "__main__":
    sys.exit(main(sys.argv))
