rendered paste body#!/usr/bin/env python
#############################################################################
#
# Linux Desktop Testing Project http://ldtp.freedesktop.org
#
# Author:
# S.Theyagarajan (theyaga@gmail.com)
# K.Harishankaran (sp2hari@gmail.com)
#
# Copyright 2004 - 2006 Novell, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
#Example Usage python record.py start gedit all
#Example Usage pythin record.py stop <filepath>
#The filename.py will be the resultant Python script generated which can be used to play back
#
#############################################################################
from optparse import OptionParser
#Defining the command line arguments
usage = "Usage:\n %prog -c start [-a application] [-p prefs] [-t]\n %prog -c stop -f file"
parser = OptionParser (usage)
parser.add_option ("-c", "--command", dest="command",
help="Command to start or stop the server", metavar="start/stop")
parser.add_option ("-a", "--application", dest="application", default="all",
help="Application to be recorded", metavar="all/application")
parser.add_option ("-p", "--prefs", dest="prefs",
help="Preferences while recording ", metavar="all/mouse/keybrd", default="all")
parser.add_option ("-f", "--file", dest="filename", metavar="filepath",
help="File where the recorded script is written")
parser.add_option ("-t","--timer", action="store_true",
help="Record time delays also")
(options, args) = parser.parse_args ()
#Checking for the command line arguments
if options.command == None :
parser.error ("Command start or stop must be provided")
if options.command != "start" and options.command != "stop":
parser.error ("Invalid command . Please give start or stop")
if options.command == "stop" and options.filename == None :
parser.error ("File name must be given for stop command")
#Assigning the variables to be used in the file
if options.command == "start" :
actionName = 'startrecord'
elif options.command == "stop" :
actionName = 'stoprecord'
filename = options.filename
arguments = options.prefs
applicationName = options.application
from ldtpcodegen import *
from xml.dom.minidom import *
from xml.parsers.expat import ExpatError
import socket, os, sys, struct, time, threading, re, random, thread, traceback
ldtpDebug = os.getenv ('LDTP_DEBUG')
class ConnectionLost (Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr (self.value)
# generate XML content
def generatexml (actionName, _requestId, application = None, argument = None):
_xml = '<?xml version=\"1.0\"?>'
_xml += '<REQUEST>'
_xml += '<RECORD>'
# Fill action name
_xml = _xml + '<ID>' + _requestId + '</ID>'
_xml = _xml + '<ACTION>' + actionName + '</ACTION>'
if application != None:
_xml = _xml + '<APPLICATION>' + application + '</APPLICATION>'
_xml = _xml + '<ARGUMENTS>'
_xml = _xml + '<ARGUMENT>ALL</ARGUMENT>'
_xml = _xml + '</ARGUMENTS>'
_xml = _xml + '</RECORD>'
_xml += '</REQUEST>'
return _xml
# Send given packet to server
def sendpacket (msg):
try:
# Get client socket fd based on thread id
client = _sockFdPool.get (threading.currentThread ())
# Pack length (integer value) in network byte order
msglen = struct.pack ('!i', len (msg))
#FIXME::msglen not taken using struct.pack as in ldtp.py
# Send message length
client.send (msglen)
# Send message
client.send (msg)
# print len (msg), msg
if ldtpDebug != None and ldtpDebug == '2':
print 'send packet', msg
except socket.error, msg:
raise LdtpExecutionError ('Server aborted')
def recvpacket (peek_flag = 0, sockfd = None):
#print 'recv packet'
flag = False
try:
flag = True
client = None
# Get client socket fd based on thread id
if sockfd == None:
client = _sockFdPool.get (threading.currentThread ())
else:
client = sockfd
_responsePacket = None
client.settimeout (5.0)
# Hardcoded 4 bytes, as the server sends 4 bytes as packet length
data = client.recv (4, peek_flag)
if data == '' or data == None:
return None
_packetSize, = struct.unpack('!i', data)
if peek_flag == 0 and ldtpDebug != None and ldtpDebug == '2':
print 'Received packet size', _packetSize
# MSG_PEEK
# This flag causes the receive operation to return data from the beginning
# of the receive queue without removing that data from the queue.
# Thus, a subsequent receive call will return the same data.
if peek_flag != 0:
# MSG_PEEK
_responsePacket = client.recv (4 + _packetSize, peek_flag)
else:
_responsePacket = client.recv (_packetSize, peek_flag)
if peek_flag != 0:
_pattern = re.compile ('\<\?xml')
_searchObj = re.search (_pattern, _responsePacket)
_finalPacket = _responsePacket[_searchObj.start () :]
_responsePacket = _finalPacket
if peek_flag == 0 and ldtpDebug != None and ldtpDebug == '2':
print 'Received response Packet', _responsePacket
return _responsePacket
except struct.error, msg:
raise LdtpExecutionError ('Invalid packet length ' + str (msg))
except AttributeError, msg:
raise LdtpExecutionError ('Error while receiving packet ' + str (msg))
except socket.timeout:
if ldtpDebug != None and ldtpDebug == '2':
print 'Timeout'
return ''
except socket.error, msg:
raise LdtpExecutionError ('Error while receiving packet ' + str (msg))
except MemoryError, msg:
raise LdtpExecutionError ('Error while receiving packet ' + str (msg))
except:
raise LdtpExecutionError ('Error while receiving packet')
def peekresponse ():
return recvpacket (socket.MSG_PEEK)
def getresponse (packetId):
setflag = True
while setflag:
peekResponsePacket = peekresponse ()
if peekResponsePacket == None:
continue
_responseType, _responseStatus, _responseData , _serverResponseLen, _requestFilePath = parsexml (peekResponsePacket)
# For precautions, we are just checking, whether the packet is notification or not
setflag = False
if _responseType == None:
continue
if _responseStatus[2] == packetId:
if _responseStatus[0] != 0 and ldtpDebug != None:
print '*** ' + _responseStatus[1]
packet = recvpacket ()
# As we have already parsed the packet,
# we are just returning the parsed packet
return _responseStatus, _responseData
socketpath = '/tmp/ldtp-record-' + os.getenv ('USER') + '-' + os.getenv ('DISPLAY')
# Socket fd pool
_sockFdPool = {}
if socketpath == '':
raise ConnectionLost ('Server not running')
try:
# Create a client socket
mainsock = socket.socket (socket.AF_UNIX, socket.SOCK_STREAM)
except socket.error,msg:
raise LdtpExecutionError ('Error while creating UNIX socket ' + str (msg))
# Let us retry connecting to the server for 3 times
retryCount = 0
while True:
try:
try:
# Connect to server socket
mainsock.connect (socketpath)
break
except TypeError:
raise ConnectionLost ('Environment LDTP_AUTH_SOCK variable not set')
except socket.error, msg:
if retryCount == 3:
raise ConnectionLost ('Could not establish connection ' + str (msg))
retryCount += 1
_pid = os.fork ()
if _pid == 0:
try:
os.execvpe ('ldtp', (), os.environ)
except OSError:
raise LdtpExecutionError ('ldtp executable not in PATH')
else:
# Let us wait for 1 second, let the server starts
time.sleep (1)
_sockFdPool[threading.currentThread ()] = mainsock
def shutdown ():
if threading.activeCount () > 1:
thread.exit ()
def getText (nodelist):
rc = ""
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
rc = rc + node.data
return rc
def getCData (nodelist):
rc = ""
for node in nodelist:
if node.nodeType == node.CDATA_SECTION_NODE:
rc = rc + node.data
return rc
#This function generates the run.xml used as input to gldap.py
def generaterunxml (filename):
_runxml = '<?xml version=\"1.0\"?>'
_runxml += '<ldtp>'
_runxml += '<logfileoverwrite> 1 </logfileoverwrite>'
_runxml += '<logfile>'
_runxml += os.getcwd() + '/log.xml'
_runxml += '</logfile>'
# Fill action name
_runxml = _runxml + '<group>'
_runxml = _runxml + '<script>'
_runxml = _runxml + '<name>' + filename +".py" '</name>'
_runxml = _runxml + '</script>'
_runxml = _runxml + '</group>'
_runxml = _runxml + '</ldtp>'
#print _runxml
output_file = file("run.xml","w")
output_file.write( _runxml)
output_file.close()
if ldtpDebug != None and ldtpDebug == '2':
print "Run.xml created ..."
def parsexml (xmldata):
"""Returns the value obtained from the server's return LDTP packet"""
_statusMsg = None
_statusCode = None
_serverResponse = None
_responseType = None
_requestId = None
_requestFilePath = None
_serverResponse = None
_responseObj = None
_serverResponseLen = 0
try:
if ldtpDebug != None and ldtpDebug == '2':
print xmldata
dom = parseString (xmldata)
try:
_responseObj = dom.getElementsByTagName ('RESPONSE')[0]
_responseType = 'record'
except IndexError:
raise LdtpExecutionError ('Invalid response')
try:
_responseStatusObj = _responseObj.getElementsByTagName ('STATUS')[0]
_statusCode = int (getText (_responseStatusObj.getElementsByTagName ('CODE')[0].childNodes))
_statusMsg = getText (_responseStatusObj.getElementsByTagName ('MESSAGE')[0].childNodes)
except ValueError:
raise LdtpExecutionError ('Invalid Status')
except IndexError:
raise LdtpExecutionError ('Invalid Status')
try:
_requestId = getText (_responseObj.getElementsByTagName ('ID')[0].childNodes)
except IndexError:
# On notification _requestId will be empty
pass
try:
data = _responseObj.getElementsByTagName ('DATA')[0]
_serverResponse = getCData (data.getElementsByTagName ('VALUE')[0].childNodes).encode ('utf-8')
_serverResponseLen = int (getText (data.getElementsByTagName ('LENGTH')[0].childNodes))
except ValueError:
raise LdtpExecutionError ('Invalid Data Length')
except IndexError:
_serverResponse = None
# Data tag may not be present
pass
try:
_requestFilePath = getText (_responseObj.getElementsByTagName ('FILEPATH')[0].childNodes)
except IndexError:
_requestFilePath = None
# On notification _requestId will be empty
pass
except ExpatError, msg:
if xml.parsers.expat.ErrorString (msg.code) == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
return None
raise LdtpExecutionError ('Parsing XML error: ' + str (msg))
# Return all the respective values, let the calling function decide what to do with the values
if ldtpDebug != None and ldtpDebug == '2':
print "Response Length:", _serverResponseLen
print "Status", _statusCode, _statusMsg
if _requestFilePath != None:
print "XML File from server:", _requestFilePath #, "- Note: When filing bug attach this file, apart from steps to reproduce the bug"
print "Created python file path ", filename + ".py"
print "Created XML file path path ", filename + ".xml"
if (_statusCode == 0):
generate_python_code (_requestFilePath, filename)
generate_data_xml (_requestFilePath, filename)
return _responseType, (_statusCode, _statusMsg, _requestId), (_serverResponseLen, _serverResponse), _requestFilePath, _serverResponseLen
def startrecord (applicationName = None, arguments = None):
try:
_requestId = str (random.randint (0, sys.maxint))
_message = generatexml ('startrecord', _requestId, applicationName, arguments)
sendpacket (_message)
except LdtpExecutionError, msg:
pass
except LdtpExecutionError, msg:
raise LdtpExecutionError (str (msg))
def stoprecord (filename):
try:
_requestId = str (random.randint (0, sys.maxint))
_message = generatexml ('stoprecord', _requestId)
sendpacket (_message)
_responseStatus, _responseData = getresponse (_requestId)
except LdtpExecutionError, msg:
raise LdtpExecutionError (str (msg) + str (traceback.format_exc ()))
if actionName == "startrecord" :
startrecord (applicationName, arguments)
else :
stoprecord (filename)