142 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Python
		
	
	
	
| from __future__ import generators
 | |
| import py
 | |
| from py.__.test.session import Session
 | |
| from py.__.test.terminal.out import getout
 | |
| from py.__.test.outcome import Failed, Passed, Skipped
 | |
| 
 | |
| 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(4.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._makerepr(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(Failed)
 | |
|     failures = [config.get_collector_trail(item) for item,_ in failures]
 | |
|     channel.send(failures)
 |