#
# Copyright (c) 2009-2011, ETH Zurich.
# All rights reserved.
#
# This file is distributed under the terms in the attached LICENSE file.
# If you do not find this file, copies can be found by writing to:
# ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
#

import os
import types
import string
import datetime
import debug
import re

RAW_FILE_NAME = 'raw.txt'
MENU_LST_FILE_NAME = 'menu.lst'
BOOT_FILE_NAME = 'bootlog.txt'
TERM_FILTER = re.compile("\[\d\d?m")

def _clean_line(line):
    # filter output line of control characters
    filtered_out = filter(lambda c: c in string.printable, line.rstrip())
    # Delete terminal color codes from output
    filtered_out = TERM_FILTER.sub('', filtered_out)
    return filtered_out

def _write_menu_lst_debug(test, build, machine, path):
    menu_lst_file_name = os.path.join(path, MENU_LST_FILE_NAME)
    debug.verbose("Writing menu.lst to %s" % menu_lst_file_name)
    out = open(menu_lst_file_name, "w")
    out.write( test.get_modules(build, machine).get_menu_data("/") )
    out.close()


def run_test(build, machine, test, path):
    # Open files for raw output from the victim and log data from the test
    raw_file_name = os.path.join(path, RAW_FILE_NAME)
    debug.verbose('open %s for raw output' % raw_file_name)
    raw_file = open(raw_file_name, 'w')

    # run the test, dumping the output to the raw file as we go
    try:
        debug.verbose('harness: setup test')
        test.setup(build, machine, path)
        _write_menu_lst_debug(test, build, machine, path)
        debug.verbose('harness: run test')
        starttime = datetime.datetime.now()
        for out in test.run(build, machine, path):
            # timedelta for the time this line was emitted from the start of the run
            timestamp = datetime.datetime.now() - starttime
            # format as string, discarding sub-second precision
            timestr = str(timestamp).split('.', 1)[0]
            debug.debug('[%s] %s' % (timestr, _clean_line(out)))
            # log full raw line (without timestamp) to output file
            raw_file.write(out)
        debug.verbose('harness: output complete')
    except KeyboardInterrupt:
        # let the user know that we are on our way out
        debug.error('Interrupted! Performing cleanup...')
        raise
    finally:
        raw_file.close()
        debug.verbose('harness: cleanup test')
        test.cleanup(machine)

def process_output(test, path):
    """Process raw.txt and return array of output lines that begins with grubs
    output, avoids having encoding issues when generating other report files"""

    raw_file_name = os.path.join(path, RAW_FILE_NAME)

    if os.path.exists(raw_file_name):
        idx = 0
        with open(raw_file_name, 'r') as rf:
            lines = rf.readlines()
            for idx, line in enumerate(lines):
                if line.strip() == "root (nd)" or \
                   line.strip().startswith("Kernel starting at address"):
                    break
            if idx == len(lines)-1:
                debug.verbose('magic string "root (nd)" or "Kernel starting at address" not found, assuming no garbage in output')
                idx=0

        return [ unicode(_clean_line(l), errors='replace') for l in lines[idx:] ]

    # file did not exist
    return ["could not open %s to process test output" % raw_file_name]

def extract_errors(test, path):
    raw_file_name = os.path.join(path, RAW_FILE_NAME)
    debug.verbose('open %s for raw input' % raw_file_name)
    raw_file = open(raw_file_name, 'r')

    try:
        results = test.process_data(path, raw_file)
    finally:
        raw_file.close()

    try:
        return results.errors
    except:
        return None


def process_results(test, path):
    # open raw file for input processing
    raw_file_name = os.path.join(path, RAW_FILE_NAME)
    debug.verbose('open %s for raw input' % raw_file_name)
    raw_file = open(raw_file_name, 'r')

    try:
        results = test.process_data(path, raw_file)
    finally:
        raw_file.close()
    if not results:
        debug.verbose('no results')
        return True  # no results, assume success

    retval = True  # everything OK

    # Process raw.txt and make a bootlog.txt that begins with grubs or
    # Barrelfish's output, avoids having encoding issues when viewing logfiles
    boot_file_name = os.path.join(path, BOOT_FILE_NAME)
    if os.path.exists(raw_file_name):
        idx = 0
        with open(raw_file_name, 'r') as rf:
            lines = rf.readlines()
            for idx, line in enumerate(lines):
                if line.strip() == "root (nd)" or \
                   line.strip().startswith("Kernel starting at address"):
                    break
        if idx > 0:
            with open(boot_file_name, 'w') as wf:
                wf.writelines(lines[idx:])
        else:
            debug.verbose('Magic string root (nd) not found, do not write bootlog.txt')
    else:
        debug.verbose('No file named %s exists. Do not create bootlog.txt.' % raw_file_name)

    # if a single result, turn it into a list
    if not isinstance(results, types.ListType):
        results = [results]
    for result in results:
        # see if it passed
        try:
            passed = result.passed()
        except NotImplementedError:
            passed = None
        if passed is False:
            debug.log('Test %s FAILED %s' % (test.name, '(' + result.reason() + ')') )
            retval = False
        elif passed:
            debug.verbose('Test %s PASSED' % test.name)

        # write it to a file
        name = result.name if result.name else 'results'
        data_file_name = os.path.join(path, name + '.dat')
        debug.verbose('create %s for processed output' % data_file_name)
        data_file = open(data_file_name, 'w')
        try:
            result.to_file(data_file)
            data_file.close()
        except NotImplementedError:
            debug.verbose('no processed output, remove %s' % data_file_name)
            data_file.close()
            os.remove(data_file_name)

    return retval

@@ -801,24 +801,122 @@
 ine, path):%0A
+    # Ignore for tests that do not implement get_modules%0A    if hasattr(test, %22get_modules%22):%0A    
     menu_lst
@@ -963,32 +963,36 @@
 _FILE_NAME)%0A    
+  
+  
 debug.verbose(%22W
@@ -1032,24 +1032,28 @@
 _file_name)%0A
+    
     out = op
@@ -1080,24 +1080,28 @@
 e, %22w%22)%0A    
+ 
+   
 out.write( t
@@ -1149,24 +1149,28 @@
 data(%22/%22) )%0A
+    
     out.clos

Fix for tests without get_modules.