#!/usr/bin/env python
"""Test suite for :py:mod:`plastid.bin.crossmap`"""
import tempfile
import os
import subprocess

from nose.plugins.attrib import attr
from pkg_resources import resource_filename, cleanup_resources
from plastid.test.functional.base import execute_helper
from plastid.test.ref_files import RPATH, REF_FILES, \
                                             COUNT_OPTIONS, \
                                             ANNOTATION_OPTIONS, \
                                             MASK_OPTIONS  
from plastid.bin.crossmap import main
from plastid.util.services.decorators import catch_stderr


#===============================================================================
# INDEX: global constants used by tests
#===============================================================================

test_info = {
    "test_method"    : catch_stderr()(main),
    "module_name"    : "plastid.bin.crossmap",
    "ref_file_path"  : resource_filename("plastid","test/data/command_line"),
    "temp_file_path" : tempfile.mkdtemp(prefix="crossmap"),
}
"""Dictionary of global variables used in this module"""

# path to bowtie index, which will be built
_ebwt = os.path.join(test_info["temp_file_path"],"yeast_ebwt")

# equality options used by tests
_eq_ops = "--no_header --sort_keys 3"

# command-line string used to execute crossmap script
# substitute: ebwt, outbase, sequence file, offset, mismatches
_exec_str   = "%s %s --sequence_file %s --offset %s --mismatches %s -k 26 -p %s --save_kmers"

# basename for output files for a test run
# substitute: offset, mismatches
_outbase_str = "yeast_crossmap_o%s"

# kmer file basename, generated by first test and used by remaining tests
_kmers = os.path.join(test_info["temp_file_path"],((_outbase_str % 0) +"_26_0"))


#===============================================================================
# INDEX: definition of tests
#        The first we specify explicitly,
#        the remaining we generate programmatically
#===============================================================================

# first test generates kmer file
offset = mismatches = 0
outbase = _outbase_str % offset
crossmap_tests = [
    (_exec_str % (_ebwt,
                  os.path.join(test_info["temp_file_path"],outbase),
                  REF_FILES["yeast_fasta"],
                  offset,
                  mismatches,
                  1
                  ),
     [REF_FILES[outbase + "_26_%s" % mismatches]],
     [os.path.join(test_info["temp_file_path"],outbase + ("_26_%s_crossmap.bed" % mismatches) )],
     [_eq_ops]),
]
"""Tests to execute, as 4-tuples of:

    - command-line arguments to pass to :py:mod:`plastid.bin.crossmap`
    - a list of reference files corresponding to reference output
    - a list of output files made by running :py:mod:`plastid.bin.crossmap`
    - a list of equality options under which comparisons between the output
      and reference files should be evaluated
"""

# mismatch, offset pairs for remaining tests
_tuples = [(0,12),
           (2,12)]

# generate remaining tests programmatically using kmer file from first run
processes = 1
for mismatches, offset in _tuples:
    outbase = _outbase_str % offset
    test_str = (_exec_str + " --have_kmers") % (
                                                os.path.join(test_info["temp_file_path"],_ebwt),
                                                os.path.join(test_info["temp_file_path"],outbase),
                                                os.path.join(test_info["temp_file_path"],_kmers),
                                                offset,
                                                mismatches,
                                                processes)
    ref_file  = REF_FILES[outbase + "_26_%s" % mismatches]
    test_file = os.path.join(test_info["temp_file_path"],outbase + ("_26_%s_crossmap.bed" % mismatches))
    crossmap_tests.append([test_str,[ref_file],[test_file],[_eq_ops]])


# multiprocess test
mismatches = 0
processes = 4
test_str = (_exec_str + " --have_kmers") % (
                                            os.path.join(test_info["temp_file_path"],_ebwt),
                                            os.path.join(test_info["temp_file_path"],outbase+"_p4"),
                                            os.path.join(test_info["temp_file_path"],_kmers),
                                            offset,
                                            mismatches,
                                            processes)
ref_file  = REF_FILES[outbase + "_26_%s" % mismatches]
test_file = os.path.join(test_info["temp_file_path"],outbase + "_p4"  +  ("_26_%s_crossmap.bed" % mismatches))
crossmap_tests.append([test_str,[ref_file],[test_file],[_eq_ops]])



#===============================================================================
# INDEX: Helper functions to run tests
#===============================================================================

@attr(test="functional")
@attr(speed="slow")
def do_test():
    """Perform functional test for plastid.bin.crossmap"""
    for x in execute_helper(test_info,crossmap_tests):
        yield x

def setUpModule():
    """Create bowtie index before running tests"""
    args = ["bowtie-build",REF_FILES["yeast_fasta"],_ebwt]
    with open(os.devnull,"a") as null:
        try:
            subprocess.check_call(args,stdout=null,stderr=subprocess.STDOUT)
        except CalledProcessError as e:
            print("Could not build bowtie index for crossmap test.")
            raise e

