> On Feb 17, 2005, at 9:52 AM, Davide Rossetti wrote:
> > I remember I read on this mlist about a testing tool. a script or 
> > something which took a source file in input and tried to swap lines 
> > and compile it, then reported results... can't google it exacly.. any 
> > help ??

Here's something vaguely related: a quick hack for shrinking testcases,
when you're looking for a cutdown that still exhibits some
still-mysterious failure.

Its stupidity makes it slow. But you can run it while you're asleep.



#! /usr/bin/env python

"""
thinloop.py  <infile> <tester> <successfile>

Expects infile is C source.
Loops, writing successively "thinned" version of infile to tmpfiles.
        After each tmpfile is written, does system("<tester> <tmpfile>"),
        with a 15 second timeout. If that doesn't time out, examines
        <successfile>. If it's zero length, assumes the thinned version
        was a failure, and tries a different version. If it's nonzero,
        assumes the thinned version was a success, and bases the next
        thinning on that version.
Runs forever, or until 100 fails in a row.
Prints "+" or "_" (success or fail) on each iteration.
The most recent successful output is kept as file "<infile>.keep".
The most recent file that hung the tester is file "<infile>.hung".

Thinning algorithm:
        Deletes one randomly chosen source line, under the rules:
        1) doesn't delete lines containing "{" or "}"
        2) finds a text line containing ";"
        3) deletes that line, and all preceding lines up-to the 
                preceding candidate line (as defined by rules 1 and 2).

"""
from __future__ import nested_scopes

import sys,os
import os.path
import random
import tempfile
import copy
import signal

pid = 0
#-------------
def handlerfunc( signum, obj ):
        """Post this as the signal handler.
           Expects pid is valid.
        """
        global pid
        signal.alarm( 0 )
        if signum == signal.SIGALRM:
                # Caught a timeout signal, so kill the pid.
                os.kill( pid, signal.SIGKILL )

signal.signal( signal.SIGCHLD, handlerfunc)
signal.signal( signal.SIGALRM, handlerfunc)

#-------------
def tag_of( line ):
        """Given a text lines, returns:
                0 - preserve (contains '{' or '}'
                1 - target (contains ';')
                2 - freefire (not either of above)
        """
        if '{' in line: return 0
        if '}' in line: return 0
        if ';' in line: return 1
        return 2
#-------------
def slim_list( line_list):
        """ Returns a slimmed copy of line_list"""

        # Get a shallow copy (ie same strings, but new ptrs)
        line_list = copy.copy( line_list )

        # Get a random in the range 0..N-1
        target = random.choice(xrange(len(line_list)))

        try:    
                # If we run off the end of arrays, just punt,
                # and make no change.

                # Search forward from random startpoint for a semicolon
                while ';' not in line_list[target]:
                        target += 1

                # Search backward for all freefire's leading up to target
                freefire = target
                while tag_of( line_list[freefire-1]) == 2:
                        freefire -= 1

                # delete all lines freefire upthu target
                # (Note each del renumbers them.)
                while freefire <= target:
                        del line_list[freefire]
                        target -= 1

        except:
                pass
        return line_list

#-------------
def main( infile_name, tester, success_file ):

        global pid
        line_list = open(infile_name).readlines()
        fail_count = 0;
        while 1:

                # Get a shortened file
                short_list = slim_list( line_list )

                # Emit the remaining lines to a tmpfile
                outfile_name = tempfile.mktemp(".c")
                outfile = open( outfile_name, "w")
                outfile.writelines(short_list)
                outfile.close()

                # Invoke the tester, after setting timeout
                signal.alarm( 15 )
                pid = os.spawnv( os.P_NOWAIT, tester, [tester,outfile_name] )

                # Wait for the tester to finish, turning any error from a KILL
                # into a bool. See handlerfunc, who expects we've set the pid.
                ok = 1
                try:
                        os.wait()
                except OSError:
                        ok = 0

                # Check the tester's success criteria, maybe keep the change
                if ok and os.path.getsize( success_file ) != 0:
                        # Success: the change was good.
                        line_list = short_list
                        os.system( "/bin/cp -f %s %s.keep" % 
                                ( outfile_name, infile_name))
                        sys.stderr.write("+")
                        fail_count = 0
                else:
                        if not ok:
                                # Can't use rename, won't cross disks
                                os.system( "/bin/cp -f %s %s.hung" %
                                        ( outfile_name, infile_name ))
                        sys.stderr.write("_")
                        fail_count += 1
                        if fail_count >= 100:
                                sys.exit("Making no headway, stopping.")
                os.remove( outfile_name )
#-------------
if __name__ == '__main__':
        if len(sys.argv) != 4:
                sys.exit("TRY: thinloop.py  <infile> <tester> <successfile>" )
        infile_name     = sys.argv[1]
        tester          = sys.argv[2]
        success_file    = sys.argv[3]
        main( infile_name, tester, success_file )

Reply via email to