Always use display cursor (Bug #488796)
[gcalctool.git] / test / play_keystrokes.py
blobc5709aac12f756e12fe275cd54b638afd4296174
1 #!/bin/python
3 # Gcalctool Automated Tests
5 # $Header$
7 # Copyright (c) 1987-2007 Sun Microsystems, Inc.
8 # All Rights Reserved.
11 """Gcalctool Automated Tests. This standalone script talks
12 directly with the AT-SPI Registry via its IDL interfaces.
14 It's based on the Orca tool play_keystrokes.py.
16 It will read gcalctool test calculations (read from standard input)
17 and generate keyboard events for the gcalctool application.
19 To perform the gcalctool automated tests, follow these steps:
21 1) Run the runtests.py script in a terminal window.
22 Results are written to standard output. For example:
24 runtests.py > output.txt
26 2) Start the play_keystrokes.py script in a terminal window.
27 The input file should be provided on standard input. For example:
29 play_keystrokes.py < input.txt
31 3) Run gcalctool.
33 4) Give focus to gcalctool.
35 That's it! The tests will now be automatically run and automatically
36 terminate when the last line from the input file is read.
37 """
39 import os
40 import signal
41 import sys
42 import time
43 import bonobo
44 import ORBit
45 import gtk
47 ORBit.load_typelib("Accessibility")
48 ORBit.CORBA.ORB_init()
50 import Accessibility
51 import Accessibility__POA
53 listeners = []
54 keycodeCache = {}
56 registry = bonobo.get_object("OAFIID:Accessibility_Registry:1.0",
57 "Accessibility/Registry")
59 applicationName = "gcalctool"
60 debug = False
62 eventTypes = [
63 "focus:"
67 ########################################################################
68 # #
69 # Event listener class for global events #
70 # #
71 ########################################################################
73 class EventListener(Accessibility__POA.EventListener):
74 """Registers a callback directly with the AT-SPI Registry for the
75 given event type. Most users of this module will not use this
76 class directly, but will instead use the addEventListener method.
77 """
79 def __init__(self, registry, callback, eventType):
80 self.registry = registry
81 self.callback = callback
82 self.eventType = eventType
83 self.register()
85 def ref(self): pass
87 def unref(self): pass
89 def queryInterface(self, repo_id):
90 thiz = None
91 if repo_id == "IDL:Accessibility/EventListener:1.0":
92 thiz = self._this()
93 return thiz
95 def register(self):
96 self._default_POA().the_POAManager.activate()
97 self.registry.registerGlobalEventListener(self._this(),
98 self.eventType)
99 self.__registered = True
100 return self.__registered
102 def deregister(self):
103 if not self.__registered:
104 return
105 self.registry.deregisterGlobalEventListener(self._this(),
106 self.eventType)
107 self.__registered = False
109 def notifyEvent(self, event):
110 self.callback(event)
112 def __del__(self):
113 self.deregister()
116 ########################################################################
118 # Testing functions. #
120 ########################################################################
122 def start():
123 """Starts event notification with the AT-SPI Registry. This method
124 only returns after 'stop' has been called.
127 bonobo.main()
130 def stop():
131 """Stop event notification with the AT-SPI Registry.
134 bonobo.main_quit()
137 def registerEventListener(callback, eventType):
138 """Registers the given eventType and callback with the Registry.
140 Arguments:
141 - callback: function to call with an AT-SPI event instance
142 - eventType: string representing the type of event
145 listener = EventListener(registry, callback, eventType)
146 listeners.append(listener)
149 def shutdownAndExit(signum=None, frame=None):
150 stop()
153 def isApplication(event, appName):
154 """Check to see if this event is for the desired application, by
155 getting the component at the top of the object hierarchy (which
156 should have a role of "application", and comparing its name against
157 the one given.
159 Arguments:
160 - event: the event to process
161 - appName: the application name to test against
164 parent = event.source
165 while parent:
166 if parent.getRoleName() == "application":
167 break
168 parent = parent.parent
169 if parent and parent.name == appName:
170 return True
172 return False
175 def getKeycode(keysym):
176 """Converts an XKeysym string (e.g., 'KP_Enter') to a keycode that
177 should match the event.hw_code for key events.
179 Arguments:
180 - keysym: a string that is a valid representation of an XKeysym.
182 Returns an integer representing a key code that should match the
183 event.hw_code for key events.
186 if debug:
187 sys.stderr.write("getKeycode: keysym: %s\n" % keysym)
189 if not keycodeCache.has_key(keysym):
190 keymap = gtk.gdk.keymap_get_default()
191 entries = keymap.get_entries_for_keyval(
192 gtk.gdk.keyval_from_name(keysym))
193 if entries:
194 keycodeCache[keysym] = entries[0][0]
195 else:
196 keycodeCache[keysym] = 0
197 return keycodeCache[keysym]
200 def generateEvents(d, hw_code):
201 """Converts an XKeysym event string to its hardware code and generates
202 two keyboard events (pressed and released) for it.
204 Arguments:
205 - d: a handle to the Registry device event controller
206 - hw_code: the hardware key code.
209 if debug:
210 sys.stderr.write("generateEvents: hw_code: %d\n" % hw_code)
211 d.generateKeyboardEvent(hw_code, "", 0)
212 d.generateKeyboardEvent(hw_code, "", 1)
215 def sendKey(d, token):
216 """Converts an XKeysym event string to its hardware code and generates
217 two keyboard events (pressed and released) for it. Look for tokens
218 starting with "Control-" and "Alt-" and send two sets of events.
220 Arguments:
221 - d: a handle to the Registry device event controller
222 - token: an XKeysym string containing the event type.
225 if debug:
226 sys.stderr.write("sendKey: token: %s\n" % token)
228 if token.startswith("Control-"):
229 generateEvents(d, getKeycode("Control_L"))
230 generateEvents(d, getKeycode(token[len(token)-1]))
232 elif token.startswith("Alt-"):
233 generateEvents(d, getKeycode("Alt_L"))
234 generateEvents(d, getKeycode(token[len(token)-1]))
236 else:
237 generateEvents(d, getKeycode(token))
240 def readAndSendInput():
241 """Keep reading a line of text from standard input (which contains
242 a single gcalctool test), until all lines have been read. Comment
243 (lines starting with "#") and blank lines are thrown away. For each
244 remaining line, it splits it into tokens, each of which is an XKeysym
245 event type. Two keyboard events are generated from each of these
246 ("pressed" and "released") until a Return event token has been found.
247 After that has been sent, a pair of Delete events are generated to
248 clear the display before the next line is read from standard input.
250 Note that this routine throws a EOFError exception when there is no
251 more input to read.
254 d = registry.getDeviceEventController()
256 while True:
257 line = raw_input()
258 if (len(line) == 0) or (line[0] == '#'):
259 continue
261 tokens = line.split()
262 for i in range(0, len(tokens)):
263 sendKey(d, tokens[i])
264 if debug:
265 sys.stderr.write("readAndSendInput: sleeping...\n")
267 time.sleep(0.5)
268 if debug:
269 sys.stderr.write("readAndSendInput: waking...\n")
271 if tokens[i] == "Return":
272 break
275 def notifyEvent(event):
276 if event.type == "focus:":
277 if isApplication(event, applicationName):
278 sys.stderr.write("Starting keyboard event generation.\n")
280 try:
281 readAndSendInput()
282 except EOFError:
283 pass
285 sys.stderr.write("Completing keyboard event generation.\n")
286 shutdownAndExit()
289 def test():
290 for eventType in eventTypes:
291 registerEventListener(notifyEvent, eventType)
292 start()
295 if __name__ == "__main__":
296 import signal
297 signal.signal(signal.SIGINT, shutdownAndExit)
298 signal.signal(signal.SIGQUIT, shutdownAndExit)
299 test()