"""

reftool module "tests"

Tracks information about the tests we found.  Each test is basically a
pair of a .out and .ref file that we uncover from the filesystem.  In
addition, other files may be associated; typically a .py file
containing sample code.  That .py file may be associated with many tests.

As an example, the file 003.py is used to generate a number of files
from different points during the compilation and for different
backends.  As an example for PPC:

all/tests/003.basic.ppc.{out,ref}
all/tests/003.refine.ppc.{out,ref}
all/tests/003.lowlevel.ppc.{out,ref}
all/tests/003.codegen.ppc.{out,ref}

However, other tests have simply things like:

stacksim/tests/001.{out,ref}

The way we handle this is that we parse the name into a set of data
separated by periods.  If a name has no particular field, such as
001.ref, then its data is just "".

We associate all files with a test that share the initial prefix and
have an extension other than out/ref.

"""

from oath.progress import indefinite_progress_meter
import difflib

class Test:

    def __init__ (self, outpath, refpath):

        assert outpath.exists ()

        self.out_path = outpath
        self.ref_path = refpath

        # The name of the .out file will be something like
        # 001.lowlevel.ppc.out.  Determine the various columns of
        # categorization data.
        self.categories = outpath.namebase.split ('.')
        assert len (self.categories) >= 1

        # Find other associated files.
        self.associated_paths = [
            p for p in outpath.parent.glob (self.categories[0]+"*")
            if p.ext != ".out" and p.ext != ".ref" and p.ext != ".pyc"]

        # Initially no action is associated with this test
        self.action = None

        pass

    def column (self, index):

        """ Returns the data for category #index, substituting "" if
        this test does not have that many categorizations """
        
        if index >= len (self.categories):
            return ""
        return self.categories[index]

    def set_action (self, action):

        """ Modifies the action associated with this test; this will
        be an instance of something from the module 'actions' """
        
        self.action = action
        pass

    def compute_diff (self):

        outlines = self.out_path.lines ()

        if self.ref_path.exists(): reflines = self.ref_path.lines ()
        else: reflines = ()

        lines = difflib.unified_diff (reflines,
                                      outlines,
                                      self.ref_path,
                                      self.out_path,
                                      None,
                                      None)

        return "".join (lines)

    pass

def _gather_tests (result, onlydiffs, basepath):

    # Scan for '.out' files:
    for outpath in basepath.walkfiles ():

        # Create new tests for each .out file
        if outpath.ext == ".out":

            # construct corresponding reference name
            refpath = outpath.parent / outpath.namebase + ".ref"

            # if the reference exists, check it is different from the
            # output or else ignore it
            if (not onlydiffs or
                not refpath.exists() or
                outpath.bytes() != refpath.bytes()):
                
                result.append (Test (outpath, refpath))
                pass
            
            pass

        # Yield to update the progress meter
        yield 1
        
        pass

    pass

def gather_tests (scr, onlydiffs, basepath):

    """ Returns an array of Test objects found by walking the file system """

    result = []
    indefinite_progress_meter (scr, "Gathering test files",
                                             _gather_tests (result,
                                                            onlydiffs,
                                                            basepath))
    return result
