# -*- python -*-
##
## SConscript  -  SCons buildscript for the Testsuite (called by SConstruct)
##

import os
from os import path
from glob import glob

from Buildhelper import srcSubtree
from Buildhelper import scanSubtree
from Buildhelper import globRootdirs
from Buildhelper import createPlugins

Import('env core_lib app_lib backend_lib guimodule tools config')
env = env.Clone()
env.Append(CPPPATH='include')  # additional headers for tests

# test classes of subcomponents linked in shared objects
sharedTestLibs = {}



def linkContext(id):
    if id.startswith('lib'):
        return app_lib              # tests in 'lib*' subdirs only linked against application framework
    elif id.startswith('back'):
        return backend_lib          # tests in 'back*' subdirs only linked against backend layer
    elif id.startswith('gui'):
        return guimodule+core_lib   # tests in 'gui*' are additionally linked against the GTK GUI module
    else:
        return core_lib             # all other tests linked against complete application core


def exeTestName(srcName):
    name = path.basename(path.splitext(srcName)[0])
    if not name.startswith('test-'):
        name = 'test-'+name
    return name


def testCases(env,dir):
    """ visit a source subtree and declare all testcases.
        Test classes will be linked into shared libraries,
        while plain-C tests end up as standalone test-XXX.
        @note: using the tree root as additional includepath
        @note: linking scope chosen based on the name-prefix 
    """
    env = env.Clone()
    env.Append(CPPPATH=dir) # add subdir to Includepath
    
    # pick up all test classes and link them shared
    testlib = []
    testClasses = list(scanSubtree(dir,['*.cpp']))
    if testClasses:
        testlib = sharedTestLibs[dir] = env.SharedLibrary('test-'+dir, testClasses, addLibs=linkContext(dir))  # note: linking dependencies according to application layers
    
    # pick up standalone plain-C tests
    standaloneTests = list(scanSubtree(dir,['test-*.c']))
    simpletests = [env.Program(exeTestName(p), [p]+linkContext(dir)) for p in standaloneTests]
    
    return testlib + simpletests




# Specific linker treatment for the testsuite:
# force explicit linking against all dependencies to ensure auto-registration of
# any test-class on startup, even if no code dependency is visible for the linker 
envSuite = env.Clone()
envSuite.Append(LINKFLAGS='-Wl,--no-as-needed')


# have to treat some subdirs separately.
specialDirs = ['plugin','tool','include']
testSrcDirs = globRootdirs('*')


testcases  = [testCases(env, dir)  for dir in testSrcDirs if not dir in specialDirs]
testLibs   = sharedTestLibs.values()
testrunner = envSuite.Program("test-suite", ["testrunner.cpp"]+testLibs+core_lib)


testsuite = ( testcases
            + testrunner
            + createPlugins(env, 'plugin', addLibs=core_lib)
            + env.File(glob('*.tests'))     # depend explicitly on the test definition files for test.sh
            + config
            )
Export('testsuite')




# for creating a Valgrind-Suppression file
vgsuppression = envSuite.Program('vgsuppression',['tool/vgsuppression.c']+testLibs+core_lib)   ## for suppressing false valgrind alarms
tools  += [vgsuppression]


#
# actually run the Testsuite
#
#  - the product of running the Testsuite is the ",testlog"
#  - it depends on all artifacts defined as "testsuite" above
#  - including the tests/*.tests (suite definition files)
#  - if not set via options switch, the environment variables TESTMODE,
#    TESTSUITES and VALGRINDFLAGS are explicitly propagated to test.sh
#
testEnv = env.Clone()

valgrind = os.environ.get('VALGRINDFLAGS', '') # unset if not defined
if not valgrind and not env['VALGRIND']:
    valgrind = 'DISABLE'

testEnv.Append(ENV = { 'VALGRINDFLAGS' : valgrind
                     , 'LUMIERA_CONFIG_PATH' : './'
                     , 'TEST_CONF' : env.File("test.conf").abspath
                     })

def propagateSetting(env, key):
    setting = key in env and env[key] or os.environ.get(key)
    if setting:
        env['ENV'][key] = setting


propagateSetting(testEnv, 'TESTSUITES')
propagateSetting(testEnv, 'TESTMODE')
propagateSetting(testEnv, 'LUMIERA_PLUGIN_PATH')

testDir = env.Dir('#$TARGDIR')
runTest = env.File("test.sh").abspath

runTests = testEnv.Command('#$TARGDIR/,testlog', testsuite, runTest,  chdir=testDir)
Depends(runTests,vgsuppression)



#
# define Phony targets
#  - 'scons testcode' triggers building of the Testsuite
#  - 'scons check' triggers building and running
#
env.Alias('testcode', testsuite )
env.Alias('check',    runTests )

# allow tempfiles of test.sh to be cleaned  
env.Clean ('check', [',testlog',',testlog.pre',',expect_stdout',',stdout',',stderr',',testtmp','.libs'])
