#!/usr/bin/env python
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#

import os
import sys
import tempfile

from subprocess import call
from shutil     import rmtree

import logging
import unittest


def banner():
    """
    Display interpreter and system info for the test env
    """
    print '*' * 75
    cmd = os.path.basename(__file__)
    print "%s: python version is %s" % (cmd, sys.version)
    print '*' * 75


def myopts(vals):
    """
    Storage for extra command line args passed.

    Returns:
    hash - argparse::Namespace object values
    """

    if not hasattr(myopts, 'vals'):
        if 'argparse' in sys.modules:
            tmp = { } # key existance enables unittest module debug
        else:
            tmp = { 'debug': False, 'verbose': False }

        for k in dir(vals):
            if k[0:1] == '_':
                continue
            tmp[k] = getattr(vals, k)
        myopts.vals = tmp

    return myopts.vals
    

def path2posix(src):
    """
    Normalize directory path syntax

    Keyword arguments:
    src - path to normalize

    Returns:
    scalar - a file path with drive separators and windows slashes removed

    Todo:
    move to {build,config,tools,toolkit}/python for use in a library
    """

    ## (drive, tail) = os.path.splitdrive(src)
    ## Support path testing on all platforms
    drive = ''
    winpath = src.find(':')
    if -1 != winpath and 10 > winpath:
        (drive, tail) = src.split(':', 1)

    if drive:
        todo = [ '', drive.rstrip(':').lstrip('/').lstrip('\\') ]
        todo.extend( tail.lstrip('/').lstrip('\\').split('\\') ) # c:\a => [a]
    else: # os.name == 'posix'
        todo = src.split('\\')

    dst = '/'.join(todo)
    return dst
    

def checkMkdir(work, debug=False):
    """
    Verify arg permutations for directory mutex creation.

    Keyword arguments:
    None

    Returns:
    Exception on error

    Note:
    Exception() rather than self.assertTrue() is used in this test
    function to enable scatch cleanup on test exit/failure conditions.
    Not guaranteed by python closures on early exit.
    """

    logging.debug("Testing: checkMkdir")

    # On Windows, don't convert paths to POSIX
    skipposix = sys.platform == "win32"
    if skipposix:
        path = os.path.abspath(__file__)
        dirname_fun = os.path.dirname
    else:
        path = path2posix(os.path.abspath(__file__))
        import posixpath
        dirname_fun = posixpath.dirname

    src = dirname_fun(path)
    # root is 5 directories up from path
    root = reduce(lambda x, _: dirname_fun(x), xrange(5), path)

    rootP = path2posix(root)
    srcP  = path2posix(src)
    workP = path2posix(work)

    # C:\foo -vs- /c/foo
    # [0] command paths use /c/foo
    # [1] os.path.exists() on mingw() requires C:\
    paths = [
        "mkdir_bycall", # function generated
        "mkdir_bydep",  # explicit dependency
        "mkdir_bygen",  # by GENERATED_DIRS macro
    ]

    ## Use make from the parent "make check" call when available
    cmd = { 'make': 'make' }
    shell0 = os.environ.get('MAKE')
    if shell0:
        shell = os.path.splitext(shell0)[0] # strip: .exe, .py
        if -1 != shell.find('make'):
            print "MAKE COMMAND FOUND: %s" % (shell0)
            cmd['make'] = shell0 if skipposix else path2posix(shell0)

    args = []
    args.append('%s' % (cmd['make']))
    args.append('-C %s'                % (work if skipposix else workP))
    args.append("-f %s/testor.tmpl"    % (src if skipposix else srcP))
    args.append('topsrcdir=%s'         % (root if skipposix else rootP))
    args.append('deps_mkdir_bycall=%s' % paths[0])
    args.append('deps_mkdir_bydep=%s'  % paths[1])
    args.append('deps_mkdir_bygen=%s'  % paths[2])
    args.append('checkup') # target

    # Call will fail on mingw with output redirected ?!?
    if debug:
        pass
    if False: # if not debug:
        args.append('>/dev/null')

    cmd = '%s' % (' '.join(args))
    logging.debug("Running: %s" % (cmd))
    rc = call(cmd, shell=True)
    if rc:
        raise Exception("make failed ($?=%s): cmd=%s" % (rc, cmd))

    for i in paths:
        path = os.path.join(work, i)
        logging.debug("Did testing mkdir(%s) succeed?" % (path))
        if not os.path.exists(path):
            raise Exception("Test path %s does not exist" % (path))


def parseargs():
    """
    Support additional command line arguments for testing
    
    Returns:
    hash - arguments of interested parsed from the command line
    """

    opts = None
    try:
        import argparse2
        parser = argparse.ArgumentParser()
        parser.add_argument('--debug',
                            action="store_true",
                            default=False,
                            help='Enable debug mode')
        # Cannot overload verbose, Verbose: False enables debugging
        parser.add_argument('--verbose',
                            action="store_true",
                            default=False,
                            help='Enable verbose mode')
        parser.add_argument('unittest_args',
                            nargs='*'
                            # help='Slurp/pass remaining args to unittest'
                            )
        opts = parser.parse_args()

    except ImportError:
        pass

    return opts


class TestMakeLogic(unittest.TestCase):
    """
    Test suite used to validate makefile library rules and macros
    """

    def setUp(self):
        opts = myopts(None) # NameSpace object not hash
        self.debug   = opts['debug']
        self.verbose = opts['verbose']

        if self.debug:
            logging.basicConfig(level=logging.DEBUG)

        if self.verbose:
            print
            print "ENVIRONMENT DUMP:"
            print '=' * 75
            for k,v in os.environ.items():
                print "env{%s} => %s" % (k, v)
            print


    def test_path2posix(self):

        todo = {
            '/dev/null'     : '/dev/null',
            'A:\\a\\b\\c'   : '/A/a/b/c',
            'B:/x/y'        : '/B/x/y',
            'C:/x\\y/z'     : '/C/x/y/z',
            '//FOO/bar/tans': '//FOO/bar/tans',
            '//X\\a/b\\c/d' : '//X/a/b/c/d',
            '\\c:mozilla\\sandbox': '/c/mozilla/sandbox',
        }

        for val,exp in todo.items():
            found = path2posix(val)
            tst = "posix2path(%s): %s != %s)" % (val, exp, found)
            self.assertEqual(exp, found, "%s: invalid path detected" % (tst))


    def test_mkdir(self):
        """
        Verify directory creation rules and macros
        """

        failed = True

        # Exception handling is used to cleanup scratch space on error
        try:
            work = tempfile.mkdtemp()
            checkMkdir(work, self.debug)
            failed = False
        finally:
            if os.path.exists(work):
                rmtree(work)

        self.assertFalse(failed, "Unit test failure detected")


if __name__ == '__main__':
    banner()
    opts = parseargs()
    myopts(opts)

    if opts:
        if hasattr(opts, 'unittest_args'):
            sys.argv[1:] = opts.unittest_args
        else:
            sys.argv[1:] = []

    unittest.main()