"""
Data-driven tests for the GA4GH reference implementation. A data
driven test applies a given test method to a data file, and
each applicaton is an independent test case under nose.
"""
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import contextlib
import os
import inspect

import ga4gh.avrotools as avrotools


def _wrapTestMethod(method):
    """
    Takes an instance method of a DataDrivenTest subclass, and returns a
    function that can be used in a test generator in nose. This entails
    updating the description attribute so that we can see details of
    the tests being run in nose.
    """
    instance = method.im_self
    cls = instance.__class__

    def testFunction():
        method()
    testFunction.description = "{}.{}.{}:{}".format(
        method.__module__, cls.__name__, method.__name__,
        instance.getSetId())
    return testFunction


def makeTests(testDataDir, testClass):
    """
    Top-level entry point for data driven tests. For every subdirectory
    in testDataDir, create an instance of testClass and then yield
    each of its testMethods in a format suitable for use with nose
    test generators.
    """
    for testSetId in os.listdir(testDataDir):
        tester = testClass(testSetId, testDataDir)
        for name, _ in inspect.getmembers(testClass):
            if name.startswith("test"):
                yield _wrapTestMethod(getattr(tester, name))


class TestCase(object):
    """
    Base class for datadriven test classes.
    Contains assert methods.
    """
    def assertEqual(self, a, b):
        """
        Tests if a an b are equal. If not, output an error and raise
        an assertion error.
        """
        if a != b:
            raise AssertionError("{} != {}".format(a, b))

    def assertNotEqual(self, a, b):
        """
        Tests if a and b are not equal.  If they are equal, raise
        an assertion error.
        """
        if a == b:
            raise AssertionError("{} == {}".format(a, b))

    def assertTrue(self, x):