#!/usr/bin/env python

####################################################################
# Run the Sage doctest system on the collection of files and
# directories specified on the command line.

####################################################################

import os, signal, sys, time


argv = sys.argv

opts = ' '.join([X for X in argv if X[0] == '-'])
argv = [X for X in argv if X[0] != '-']

t0 = time.time()

def abspath(x):
    return strip_automount_prefix(os.path.abspath(x))

def strip_automount_prefix(filename):
    """
    Strip prefixes added on automounted filesystems in some cases,
    which make the absolute path appear hidden.
    
    AUTHOR:
        -- Kate Minola
    """
    sep = os.path.sep
    str = filename.split(sep,2)
    if len(str) < 2:
        new = sep
    else:
        new = sep + str[1]
    if os.path.exists(new):
        inode1 = os.stat(filename)[1]
        inode2 = os.stat(new)[1]
        if inode1 == inode2:
            filename = new
    return filename


SAGE_ROOT = os.environ['SAGE_ROOT']
SAGE_SITE = os.path.realpath(os.path.join(os.environ['SAGE_LOCAL'],
                                          'lib', 'python', 'site-packages'))

if 'SAGE_TESTDIR' not in os.environ:
    os.environ['SAGE_TESTDIR'] = os.path.join(SAGE_ROOT, "tmp")
TMP = os.path.join(os.environ['SAGE_TESTDIR'], "tmp")
if not os.path.exists(TMP):
    os.makedirs(TMP)

def sage_test_command(f):
    g = abspath(f)
    if SAGE_ROOT in g:
        f = g[len(SAGE_ROOT)+1:]
    return 'sage -t %s "%s"'%(opts, f)

def skip(F):
    G = abspath(F)
    i = G.rfind(os.sep)
    if os.path.exists(os.path.join(G[:i], 'nodoctest.py')):
        print "%s (skipping) -- nodoctest.py file in directory"%sage_test_command(F)
        return True

    if 'nodoctest' in open(G).read()[:50]:
        return True
    if G.find(os.path.join('doc', 'output')) != -1:
        return True

    sys.stdout.write("%-60s"%sage_test_command(F)+"\n")
    sys.stdout.flush()
    return False

failed = []

def test(F, cmd):
    t = time.time()
    if skip(F):
        return 0
    s = os.path.join(SAGE_ROOT, 'local', 'bin', 'sage-%s' % cmd) + ' "%s"' % F
    err = os.system(s)
    # On unix systems, the return value of os.system has the process return
    # value in the second byte.
    err = err // 256

    # Check the process exit code that sage-doctest returns

    if err == 1: # process exit code 1: File not found
        failed.append(sage_test_command(F)+" # File not found")
    elif err == 2: # process exit code 2: KeyboardInterrupt
        failed.append(sage_test_command(F)+" # KeyboardInterrupt")
        raise KeyboardInterrupt
    elif err == 3: # process exit code 3: Terminated by signal
        failed.append(sage_test_command(F)+" # Killed/crashed")
    elif err == 4: # process exit code 4: Unhandled doctest exception
        failed.append(sage_test_command(F)+" # Exception from doctest framework")
    elif err == 100: # process exit code 100: Regular doctest failures
        failed.append(sage_test_command(F))
    elif err != 0:
        failed.append(sage_test_command(F))
    os.system("mv -f __test* %s 2>/dev/null"%(TMP))
    print "\t [%.1f s]"%(time.time() - t)
    return err


def test_file(F):
    if not os.path.exists(F):
        if os.path.exists(os.path.join(SAGE_ROOT, F)):
            F = os.path.join(SAGE_ROOT, F)
    if not os.path.exists(F):
        if F[:6] != "__test" and not F.endswith('.png'):
            print "ERROR: File %s is missing" % os.path.join(os.curdir, F)
            failed.append(os.path.join(os.curdir, F) + " # File not found")
        return 1

    extra_opts = ''
    if SAGE_SITE in os.path.realpath(F) and not '-force_lib' in opts:
        extra_opts = ' -force_lib'

    base, ext = os.path.splitext(F)
    if ext in ['.py', '.spyx', '.pyx', '.tex', '.pxi', '.sage', '.rst']:
        test(F, 'doctest ' + opts + extra_opts)
    elif os.path.isdir(F) and not (F[:1] == '.') \
            and not '#' in F and not os.sep + 'notes' in F:
        ld = os.listdir(F)
        if not ('__nodoctest__' in ld):
            for L in ld:
                k = test_file(os.path.join(F, L))
    return 0

files = argv[1:]

if '-sagenb' in opts:
    opts = opts.replace('--sagenb', '').replace('-sagenb', '')

    # Find SageNB's home.
    from pkg_resources import Requirement, working_set
    sagenb_loc = working_set.find(Requirement.parse('sagenb')).location

    # In case we're using setuptools' "develop" mode.
    if not SAGE_SITE in sagenb_loc:
        opts += ' -force_lib'

    files.append(os.path.join(sagenb_loc, 'sagenb'))

if len(files) == 0:
    print "Usage: sage -t [-verbose] [-long] [-optional] [-only-optional=list,of,tags] <files or directories>"
    print "Test the docstrings in each listed Python or Cython file, and "
    print "if a directory is given, test the docstrings in all files in"
    print "all subdirectories of that directory."
    print ""
    print "OPTIONS:"
    print "     -force_lib     -- assume all files are Sage library files,"
    print "                       regardless of location"
    print "     -long          -- include lines with the phrase 'long time'"
    print "     -verbose       -- print debugging output during the test"
    print "     -optional      -- also test all #optional examples"
    print "     -only-optional=list,of,tags -- only run doctests with "
    print "                       #optional <nonempty subset of tags>"
    print "      if the list of tags is omitted, run all optional doctests"
    print "     -randorder     -- if given, randomize *order* of tests"
    print "     -randorder=seed-- use seed to get same random order"
    print "     -sagenb        -- test all sagenb files"
    
    sys.exit(1)

files.sort()
        
for F in files:
    try:
        test_file(F)
    except KeyboardInterrupt:
        print "Aborting further tests."
        break
    
print " "
print "-"*int(70)

if len(failed) == 0:
    print "All tests passed!"
else:
    print "The following tests failed:\n"
    print "\n\t" + "\n\t".join(failed)

print "Total time for all tests: %.1f seconds"%(time.time() - t0)
