141 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Python
		
	
	
	
from __future__ import generators
 | 
						|
import py
 | 
						|
from py.__.test.session import Session
 | 
						|
from py.__.test.terminal.out import getout 
 | 
						|
 | 
						|
def checkpyfilechange(rootdir, statcache={}):
 | 
						|
    """ wait until project files are changed. """
 | 
						|
    def fil(p): 
 | 
						|
        return p.ext in ('.py', '.c', '.h')
 | 
						|
    #fil = lambda x: x.check(fnmatch='*.py')
 | 
						|
    def rec(p):
 | 
						|
        return p.check(dotfile=0)
 | 
						|
    changed = False
 | 
						|
    for path in rootdir.visit(fil, rec):
 | 
						|
        oldstat = statcache.get(path, None)
 | 
						|
        try:
 | 
						|
            statcache[path] = curstat = path.stat()
 | 
						|
        except py.error.ENOENT:
 | 
						|
            if oldstat:
 | 
						|
                del statcache[path]
 | 
						|
                print "# WARN: race condition on", path
 | 
						|
        else:
 | 
						|
            if oldstat:
 | 
						|
               if oldstat.mtime != curstat.mtime or \
 | 
						|
                  oldstat.size != curstat.size:
 | 
						|
                    changed = True
 | 
						|
                    print "# MODIFIED", path
 | 
						|
            else:
 | 
						|
                changed = True
 | 
						|
    return changed
 | 
						|
 | 
						|
def getfailureitems(failures): 
 | 
						|
    l = []
 | 
						|
    for rootpath, names in failures:
 | 
						|
        root = py.path.local(rootpath)
 | 
						|
        if root.check(dir=1):
 | 
						|
            current = py.test.collect.Directory(root).Directory(root)
 | 
						|
        elif root.check(file=1):
 | 
						|
            current = py.test.collect.Module(root).Module(root)
 | 
						|
        # root is fspath of names[0] -> pop names[0]
 | 
						|
        # slicing works with empty lists
 | 
						|
        names = names[1:]
 | 
						|
        while names: 
 | 
						|
            name = names.pop(0) 
 | 
						|
            try: 
 | 
						|
                current = current.join(name)
 | 
						|
            except NameError: 
 | 
						|
                print "WARNING: could not find %s on %r" %(name, current) 
 | 
						|
                break 
 | 
						|
        else: 
 | 
						|
            l.append(current) 
 | 
						|
    return l
 | 
						|
 | 
						|
class RemoteTerminalSession(Session):
 | 
						|
    def __init__(self, config, file=None):
 | 
						|
        super(RemoteTerminalSession, self).__init__(config=config)
 | 
						|
        self._setexecutable()
 | 
						|
        if file is None:
 | 
						|
            file = py.std.sys.stdout 
 | 
						|
        self._file = file
 | 
						|
        self.out = getout(file)
 | 
						|
 | 
						|
    def _setexecutable(self):
 | 
						|
        name = self.config.option.executable
 | 
						|
        if name is None:
 | 
						|
            executable = py.std.sys.executable 
 | 
						|
        else:
 | 
						|
            executable = py.path.local.sysfind(name)
 | 
						|
            assert executable is not None, executable 
 | 
						|
        self.executable = executable 
 | 
						|
 | 
						|
    def main(self):
 | 
						|
        rootdir = self.config.topdir 
 | 
						|
        wasfailing = False 
 | 
						|
        failures = []
 | 
						|
        while 1:
 | 
						|
            if self.config.option.looponfailing and (failures or not wasfailing): 
 | 
						|
                while not checkpyfilechange(rootdir):
 | 
						|
                    py.std.time.sleep(2.4)
 | 
						|
            wasfailing = len(failures)
 | 
						|
            failures = self.run_remote_session(failures)
 | 
						|
            if not self.config.option.looponfailing: 
 | 
						|
                break
 | 
						|
            print "#" * 60
 | 
						|
            print "# looponfailing: mode: %d failures args" % len(failures)
 | 
						|
            for root, names in failures:
 | 
						|
                name = "/".join(names) # XXX
 | 
						|
                print "Failure at: %r" % (name,) 
 | 
						|
            print "#    watching py files below %s" % rootdir
 | 
						|
            print "#                           ", "^" * len(str(rootdir))
 | 
						|
        return failures
 | 
						|
 | 
						|
    def _initslavegateway(self):
 | 
						|
        print "* opening PopenGateway: ", self.executable 
 | 
						|
        topdir = self.config.topdir
 | 
						|
        return py.execnet.PopenGateway(self.executable), topdir
 | 
						|
 | 
						|
    def run_remote_session(self, failures):
 | 
						|
        gw, topdir = self._initslavegateway()
 | 
						|
        channel = gw.remote_exec("""
 | 
						|
            from py.__.test.terminal.remote import slaverun_TerminalSession
 | 
						|
            slaverun_TerminalSession(channel) 
 | 
						|
        """, stdout=self.out, stderr=self.out) 
 | 
						|
        try:
 | 
						|
            print "MASTER: initiated slave terminal session ->"
 | 
						|
            repr = self.config.make_repr(conftestnames=[])
 | 
						|
            channel.send((str(topdir), repr, failures))
 | 
						|
            print "MASTER: send start info, topdir=%s" % (topdir,)
 | 
						|
            try:
 | 
						|
                return channel.receive()
 | 
						|
            except channel.RemoteError, e:
 | 
						|
                print "*" * 70
 | 
						|
                print "ERROR while waiting for proper slave startup"
 | 
						|
                print "*" * 70
 | 
						|
                print e
 | 
						|
                return []
 | 
						|
        finally:
 | 
						|
            gw.exit()
 | 
						|
 | 
						|
def slaverun_TerminalSession(channel):
 | 
						|
    """ we run this on the other side. """
 | 
						|
    print "SLAVE: initializing ..."
 | 
						|
    topdir, repr, failures = channel.receive()
 | 
						|
    print "SLAVE: received configuration, using topdir:", topdir
 | 
						|
    config = py.test.config 
 | 
						|
    config.initdirect(topdir, repr, failures)
 | 
						|
    config.option.session = None
 | 
						|
    config.option.looponfailing = False 
 | 
						|
    config.option.usepdb = False 
 | 
						|
    config.option.executable = None
 | 
						|
    if failures:
 | 
						|
        config.option.verbose = True
 | 
						|
 | 
						|
    session = config.initsession()
 | 
						|
    session.shouldclose = channel.isclosed 
 | 
						|
    print "SLAVE: starting session.main()"
 | 
						|
    session.main()
 | 
						|
    failures = session.getitemoutcomepairs(py.test.Item.Failed)
 | 
						|
    failures = [config.get_collector_trail(item) for item,_ in failures]
 | 
						|
    channel.send(failures)
 |