From Python to silicon
 

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

users:george_pantazopoulos:demo_2 [2006/10/14 06:22] (current)
themaxx created
Line 1: Line 1:
 +<code python>
 +#! /usr/bin/python
  
 +""" 
 + -----------------------------------------------------------------------------
 +     __  ___      __  ______  __       ____                   __            
 +    /  |/  /_  __/ / / / __ \/ /      / __ )____  ____  _____/ /____  _____ 
 +   / /|_/ / / / / /_/ / / / / /      / __  / __ \/ __ \/ ___/ __/ _ \/ ___/ 
 +  / /  / / /_/ / __  / /_/ / /___   / /_/ / /_/ / /_/ (__  ) /_/  __/ /     
 + /_/  /_/\__, /_/ /_/_____/_____/  /_____/\____/\____/____/\__/\___/_/      
 +        /____/                                                            
 +                                                                                                                                                    #
 + MyHDL Booster Library                                                      
 +                                                                            
 + George Pantazopoulos                                                       
 + http://www.gammaburst.net                                                  
 +     
 + -----------------------------------------------------------------------------
 + 
 + MyHDL Regression test script
 + ----------------------------
 + 
 + - Built-in alternative to py.test
 + 
 + - Recurses through directories and runs all functions starting with "test"
 +   as testbenches in a MyHDL Simulation
 +   
 + - functions are run in the order that they are declared in the module file, 
 +   just like py.test
 + 
 + Usage:
 +     If started with no arguments, it will recurse into subdirectories
 +     starting at the current dir
 +     
 +     If a directory is specified, it will recurse into that path
 +     
 +     If a python filename is specified, it will only operate on that file
 + 
 + Current limitations:
 + - Functions must not take any arguments
 + - A running total of tests passed/failed is needed
 + - test classes not supported yet
 + 
 +"""
 +
 +import compiler
 +import compiler.visitor
 +from   compiler.visitor import ASTVisitor
 +
 +import sys
 +import pprint
 +import os.path
 +
 +from myhdl import Simulation, SimulationError
 +
 +__title__    =  "myhdl.test"
 +__author__   =  "George Pantazopoulos http://www.gammaburst.net"
 +
 +__descr__    =  "Automated regression test runner for MyHDL.\n" \
 +                "Part of the MyHDL Booster package"
 +
 +__version__  =  "0.2.0"
 +__date__     =  "14 Oct 2006"
 +
 +# Walk through a directory structure and execute all test functions 
 +# in each python file in the order they are declared in the file.
 +
 +# For each python file, use the compiler module to process it into 
 +# an ast tree. Then, walk the ast tree and collect the function instances
 +
 +# Grab the function instance names.
 +
 +# Then, import the python file as a module and 
 +# call functions in that module (no args)
 +
 +# References:
 +# http://www.python.org/doc/2.4.3/lib/module-compiler.ast.html
 +# http://www.velocityreviews.com/forums/t371535-compile-ast-to-bytecode.html
 +
 +def getFunctions(filename):
 +    """
 +    Parses a file and extracts AST nodes that are Functions
 +    Returns them in a list
 +    """
 +
 +    class FuncCollector(ASTVisitor):
 +        def __init__(self):
 +            ASTVisitor.__init__(self)
 +            self.funcs = []
 +            
 +        def visitFunction(self, node):
 +            self.funcs.append(node)
 +
 +        # We don't want class methods to end up in our function name list
 +        # So when we visit a class we just do nothing
 +        def visitClass(self, node):
 +            pass
 +
 +        def get_funcs(self):
 +            return self.funcs
 +
 +    # Parse the file into an AST object
 +    ast = compiler.parseFile(filename)
 +    
 +    # Instantiate the ASTVisitor that will collect the function definitions
 +    fc = FuncCollector()
 +    
 +    # Walk through the AST tree and collect the function definitions
 +    compiler.walk(ast, fc)
 +        
 +    return fc.get_funcs()
 +
 +# ----------------------------------------------------------------------------
 +
 +def getFuncNames(funcnodes):
 +    """
 +    Given a list of AST Function nodes,
 +    extract the function names into a list.
 +    """
 +    
 +    funcnames = []
 +    
 +    for node in funcnodes:
 +        funcnames.append(node.name)
 +        
 +    return funcnames
 +
 +# ----------------------------------------------------------------------------
 +
 +def importFromFile(pathname):
 +    """
 +    Loads a python file and imports it as a module.
 +    Returns the module object
 +    """
 +    
 +    # Add the module's dirname to sys.path so it can be imported later
 +    sys.path.append(os.path.dirname(pathname))
 +
 +    # Get the basename of the python module file
 +    module_basename = os.path.basename(pathname)
 +    
 +    # Make sure the file extension is ".py"
 +    file_ext = '.py'
 +    
 +    if os.path.splitext(module_basename)[1] != file_ext:
 +        raise Exception, \
 +        "Filename " + module_basename + " lacks a '" \
 +        + file_ext + "' extension."
 +    
 +    # Strip off the .py extension of the filename
 +    module_name = os.path.splitext(module_basename)[0]
 +    
 +    # Import the module represented by this Python source file
 +    module = __import__(module_name)
 +    
 +    return module
 +
 +# ----------------------------------------------------------------------------
 +
 +def execTestFuncsInFile(pathname, keep_going=False):
 +    """
 +    Identifies the test functions in a given python file and executes
 +    them as a testbench in a MyHDL Simulation.
 +    
 +    The functions are executed in the order they are defined in the module
 +    """
 +    
 +    
 +    module = importFromFile(pathname)
 +    #print "imported module " + "'" + module.__name__ + "'"
 +    #print
 +   
 +    # Get the function names we extracted from the file
 +    funcnames = getFuncNames(getFunctions(pathname))
 +    
 +
 +    # Filter out the test function names
 +    testfuncnames = []
 +    
 +    for name in funcnames:
 +        if name.startswith('test'):
 +            testfuncnames.append(name)
 +    
 +    for name in testfuncnames:
 +        
 +        try:
 +            # Use that test function as the testbench in a Simulation
 +            sim = Simulation(module.__dict__[name].__call__())
 +            sim.run()
 +            
 +            # If there's no exception, then the test must have passed.
 +            print (name + '()').ljust(60,'.') + " PASS".ljust(15)
 +            
 +        # ASCII Timing Spec code throws a generic 'Exception'
 +        except (Exception, SimulationError), info:
 +            print (name + '()').ljust(60,'.') + " FAIL".ljust(15)
 +            print " |"
 +            print " ---> " + str(info)
 +            print 
 +
 +            if keep_going == False:
 +                return False
 +            
 +            
 +    return True
 +            
 +# ----------------------------------------------------------------------------
 +
 +def execTestFuncsInDirTree(path, keep_going=False):
 +    """
 +    Recurse into subdirectories starting in path 
 +    and execute tests in any .py files.
 +    """
 +    
 +    # Start in the given path and recurse into subdirs
 +    for root, dirs, filenames in os.walk(path):
 +        
 +        # At each subdir, we're provided with a list of filenames
 +        for filename in filenames:
 +            
 +            # Only operate on Python source files (.py extension)
 +            if os.path.splitext(filename)[1] == '.py':
 +                
 +                # Reconstruct the full pathname
 +                pathname = os.path.join(root, filename)
 +                
 +                # Execute all the test functions in this file
 +                if execTestFuncsInFile(pathname=pathname, 
 +                                       keep_going=keep_going) == False:
 +                    if keep_going == False:
 +                        return
 +            
 +# ----------------------------------------------------------------------------
 +
 +if __name__ == '__main__':
 +    
 +    title    = globals()['__title__']
 +    descr    = globals()['__descr__']
 +    author   = globals()['__author__']
 +    version  = globals()['__version__']
 +    date     = globals()['__date__']
 +    
 +    def print_author():
 +        print "Author: " + author
 +    
 +    def print_version():
 +        print title + " " + version
 +    
 +    def print_usage():        
 +        print
 +        print_version()
 +        print date
 +        print
 +        print descr
 +        print
 +        print "Usage: " + sys.argv[0] + " [options] [dir | path-to-.py-file]"
 +        print
 +        print "options:"
 +        
 +        print " -h, --help.......... print this help screen"
 +        print " -v  --version....... show version info and exit"
 +        print " -k, --keep-going.... continue running tests even if there were failures" 
 +                
 +        print
 +        print_author()
 +        
 +    def is_dir_or_file(str):
 +        return os.path.isdir(str) or os.path.isfile(str)
 +    
 +    keep_going   = False
 +    path         = None
 +    
 +    # If this module is called as a script with no args
 +    # then recurse through the subdirs starting from the current dir
 +    if len(sys.argv) == 1:
 +        path = '.'
 +    
 +    # One argument
 +    elif len(sys.argv) == 2:
 +
 +        # The single arg is not a file or dir. 
 +        # Maybe it's an option with no args following it...
 +        if not is_dir_or_file(sys.argv[1]):
 +            
 +            # Help request.
 +            if sys.argv[1] in ['help', '-h', '-help', '--h', '--help']:
 +                print_usage()
 +                sys.exit(0)
 +            
 +            elif sys.argv[1] in ['-v', '--version']:
 +                print_version()
 +                sys.exit(0)
 +            
 +            # User specified -k with no args following it.
 +            elif sys.argv[1] in ['-k', '--keep-going']:
 +                keep_going = True
 +                path = '.'
 +            
 +            else:
 +                print "Unrecognized option: " + sys.argv[1]
 +                print_usage()
 +                sys.exit(1)
 +            
 +        else:
 +            # A path or directory was specified as the single argument
 +            keep_going = False
 +            path = sys.argv[1]
 +
 +    # Two args
 +    elif len(sys.argv) == 3:
 +        
 +        # This should be an option with a path or filename following it.
 +        if not is_dir_or_file(sys.argv[1]):
 +        
 +            if sys.argv[1] in ['-k', '--keep-going']:
 +                keep_going == True
 +
 +            else:
 +                print "Unrecognized option: " + sys.argv[1]
 +                sys.exit(1)
 +                
 +            if not is_dir_or_file(sys.argv[2]):
 +                print "Second argument must be directory or filename"
 +                print_usage()
 +                sys.exit(1)
 +            else:
 +                path = sys.argv[2]
 +        
 +    # User parameter collection is complete here.
 +        
 +    if os.path.isdir(path):
 +        # Operate recursively starting with the specified dir
 +        execTestFuncsInDirTree(path=path, keep_going=keep_going)
 +        
 +    elif os.path.isfile(path):
 +        # Operate just on the file specified as the first argument
 +        execTestFuncsInFile(path=path, keep_going=keep_going)
 +
 +</code>
users/george_pantazopoulos/demo_2.txt ยท Last modified: 2006/10/14 06:22 by themaxx
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki