Merge lp:~bregma/evemu/lp-731678 into lp:evemu

Proposed by Stephen M. Webb
Status: Merged
Merged at revision: 44
Proposed branch: lp:~bregma/evemu/lp-731678
Merge into: lp:evemu
Diff against target: 1466 lines (+1291/-5)
23 files modified
.bzrignore (+1/-0)
Makefile.am (+5/-1)
configure.ac (+2/-0)
data/3m.prop (+1/-1)
data/bcm5974.prop (+1/-1)
data/ntrig-dell-xt2.event (+146/-0)
data/ntrig-dell-xt2.prop (+31/-0)
data/ntrig-lenovo-T410s.prop (+24/-0)
data/synaptics.prop (+32/-0)
data/wetab.prop (+1/-1)
python/Makefile.am (+37/-0)
python/evemu-test-runner.in (+4/-0)
python/evemu/__init__.py (+271/-0)
python/evemu/base.py (+43/-0)
python/evemu/const.py (+235/-0)
python/evemu/exception.py (+22/-0)
python/evemu/testing/mocker.py (+5/-0)
python/evemu/testing/result.py (+48/-0)
python/evemu/testing/runner.py (+63/-0)
python/evemu/testing/testcase.py (+70/-0)
python/evemu/tests/test_base.py (+25/-0)
python/evemu/tests/test_device.py (+223/-0)
src/evemu.c (+1/-1)
To merge this branch: bzr merge lp:~bregma/evemu/lp-731678
Reviewer Review Type Date Requested Status
Open Input Framework Team Pending
Review via email: mp+84009@code.launchpad.net

Description of the change

This MR takes Duncan McGreggor's original Python bindings branch, fixes all the unit tests, and refactors into a single Device class with a clear invariant and a simpler API. A few bits of the original remain.

The unit tests exercise pretty much all of the C API, so that takes care of unit testing for this library.

The unit tests need to be run as root because they require write access to kernel devices. They may also exercise race condition bugs in desktop software (eg. utouch-geis, GTK+, x.org) and may randomly require you to restart all or part of your system. Bugs have been filed upstream in those projects. Do not say you have not been warned.

To post a comment you must log in.
Revision history for this message
Chase Douglas (chasedouglas) wrote :

Everything looks sane to me, though I don't know python very well. I'm fine with merging it anyways though. Unit testing is good, creating new bindings is better, and unit testing through the new bindings is awesome :).

The only question I have is whether the python/Makefile.original and python/TODO files were meant to be included. If so, why are they there?

lp:~bregma/evemu/lp-731678 updated
121. By Stephen M. Webb

Removed python/Makefile.original and python/TODO

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-06-17 01:54:02 +0000
3+++ .bzrignore 2011-11-30 21:40:28 +0000
4@@ -11,6 +11,7 @@
5 config-aux
6 config.*
7 configure
8+evemu-test-runner
9 libtool
10 stamp-*
11 tools/evemu-describe
12
13=== modified file 'Makefile.am'
14--- Makefile.am 2011-04-19 09:30:21 +0000
15+++ Makefile.am 2011-11-30 21:40:28 +0000
16@@ -1,4 +1,4 @@
17-SUBDIRS = src tools
18+SUBDIRS = src tools python
19
20 pkgconfigdir = $(libdir)/pkgconfig
21 pkgconfig_DATA = utouch-evemu.pc
22@@ -9,6 +9,8 @@
23 INSTALL:
24 $(INSTALL_CMD)
25
26+EXTRA_DIST = data
27+
28 ChangeLog:
29 @if test -d ".bzr"; then \
30 cmd=bzr; \
31@@ -20,3 +22,5 @@
32 $${cmd} log > ChangeLog;
33
34 dist-hook: ChangeLog INSTALL
35+
36+DISTCLEANFILES = ChangeLog
37
38=== modified file 'configure.ac'
39--- configure.ac 2011-11-02 13:27:23 +0000
40+++ configure.ac 2011-11-30 21:40:28 +0000
41@@ -22,6 +22,7 @@
42 # Checks for programs.
43 AC_PROG_CC
44 AC_PROG_INSTALL
45+AM_PATH_PYTHON([2.6])
46
47 # man page generation
48 AC_ARG_VAR([XMLTO], [Path to xmlto command])
49@@ -39,6 +40,7 @@
50
51 AC_CONFIG_FILES([Makefile
52 src/Makefile
53+ python/Makefile
54 tools/Makefile
55 utouch-evemu.pc])
56 AC_OUTPUT
57
58=== modified file 'data/3m.prop'
59--- data/3m.prop 2010-09-19 08:43:49 +0000
60+++ data/3m.prop 2011-11-30 21:40:28 +0000
61@@ -1,4 +1,4 @@
62-N: 3M-3M-MicroTouch-USB-controller
63+N: 3M-3M-MicroTouch-USB-controller Virtual Device
64 I: 0003 0596 0502 0110
65 B: 00 0b 00 00 00 00 00 00 00
66 B: 01 00 00 00 00 00 00 00 00
67
68=== modified file 'data/bcm5974.prop'
69--- data/bcm5974.prop 2010-09-18 17:10:11 +0000
70+++ data/bcm5974.prop 2011-11-30 21:40:28 +0000
71@@ -1,4 +1,4 @@
72-N: bcm5974
73+N: bcm5974 Virtual Device
74 I: 0003 05ac 0223 0000
75 B: 00 0b 00 00 00 00 00 00 00
76 B: 01 00 00 00 00 00 00 00 00
77
78=== added file 'data/ntrig-dell-xt2.event'
79--- data/ntrig-dell-xt2.event 1970-01-01 00:00:00 +0000
80+++ data/ntrig-dell-xt2.event 2011-11-30 21:40:28 +0000
81@@ -0,0 +1,146 @@
82+E: 1299660667.063211 0003 0035 7411
83+E: 1299660667.063229 0003 0036 4677
84+E: 1299660667.063232 0003 0034 1
85+E: 1299660667.063236 0003 0030 462
86+E: 1299660667.063238 0003 0031 360
87+E: 1299660667.063242 0000 0002 0
88+E: 1299660667.063260 0003 0035 7361
89+E: 1299660667.063263 0003 0036 3291
90+E: 1299660667.063266 0003 0034 1
91+E: 1299660667.063269 0003 0030 462
92+E: 1299660667.063271 0003 0031 360
93+E: 1299660667.063275 0000 0002 0
94+E: 1299660667.063280 0003 0035 5912
95+E: 1299660667.063283 0003 0036 1483
96+E: 1299660667.063286 0003 0034 0
97+E: 1299660667.063289 0003 0030 540
98+E: 1299660667.063292 0003 0031 462
99+E: 1299660667.063295 0000 0002 0
100+E: 1299660667.063299 0001 014a 1
101+E: 1299660667.063302 0003 0000 7411
102+E: 1299660667.063306 0003 0001 4677
103+E: 1299660667.063311 0000 0000 0
104+E: 1299660667.081029 0003 0035 7380
105+E: 1299660667.081039 0003 0036 4674
106+E: 1299660667.081043 0003 0034 1
107+E: 1299660667.081045 0003 0030 462
108+E: 1299660667.081048 0003 0031 360
109+E: 1299660667.081051 0000 0002 0
110+E: 1299660667.081064 0003 0035 7401
111+E: 1299660667.081067 0003 0036 3263
112+E: 1299660667.081070 0003 0034 0
113+E: 1299660667.081073 0003 0030 360
114+E: 1299660667.081076 0003 0031 308
115+E: 1299660667.081079 0000 0002 0
116+E: 1299660667.081082 0003 0035 5887
117+E: 1299660667.081085 0003 0036 1484
118+E: 1299660667.081088 0003 0034 0
119+E: 1299660667.081091 0003 0030 540
120+E: 1299660667.081094 0003 0031 308
121+E: 1299660667.081097 0000 0002 0
122+E: 1299660667.081106 0000 0000 0
123+E: 1299660667.097214 0003 0035 7379
124+E: 1299660667.097237 0003 0036 4678
125+E: 1299660667.097240 0003 0034 0
126+E: 1299660667.097243 0003 0030 360
127+E: 1299660667.097246 0003 0031 308
128+E: 1299660667.097249 0000 0002 0
129+E: 1299660667.097267 0003 0035 7371
130+E: 1299660667.097270 0003 0036 3262
131+E: 1299660667.097273 0003 0034 1
132+E: 1299660667.097276 0003 0030 462
133+E: 1299660667.097279 0003 0031 360
134+E: 1299660667.097282 0000 0002 0
135+E: 1299660667.097286 0003 0035 5901
136+E: 1299660667.097290 0003 0036 1488
137+E: 1299660667.097293 0003 0034 0
138+E: 1299660667.097296 0003 0030 540
139+E: 1299660667.097299 0003 0031 462
140+E: 1299660667.097302 0000 0002 0
141+E: 1299660667.097312 0000 0000 0
142+E: 1299660667.113209 0003 0035 7382
143+E: 1299660667.113228 0003 0036 4680
144+E: 1299660667.113231 0003 0034 0
145+E: 1299660667.113234 0003 0030 360
146+E: 1299660667.113237 0003 0031 308
147+E: 1299660667.113240 0000 0002 0
148+E: 1299660667.113256 0003 0035 7399
149+E: 1299660667.113259 0003 0036 3253
150+E: 1299660667.113262 0003 0034 0
151+E: 1299660667.113265 0003 0030 360
152+E: 1299660667.113268 0003 0031 308
153+E: 1299660667.113271 0000 0002 0
154+E: 1299660667.113275 0003 0035 5886
155+E: 1299660667.113277 0003 0036 1489
156+E: 1299660667.113280 0003 0034 1
157+E: 1299660667.113283 0003 0030 462
158+E: 1299660667.113286 0003 0031 360
159+E: 1299660667.113289 0000 0002 0
160+E: 1299660667.113293 0003 0035 6837
161+E: 1299660667.113296 0003 0036 2669
162+E: 1299660667.113298 0003 0034 1
163+E: 1299660667.113301 0003 0030 462
164+E: 1299660667.113304 0003 0031 360
165+E: 1299660667.113307 0000 0002 0
166+E: 1299660667.113316 0000 0000 0
167+E: 1299660667.129014 0003 0035 7375
168+E: 1299660667.129022 0003 0036 4685
169+E: 1299660667.129025 0003 0034 0
170+E: 1299660667.129028 0003 0030 360
171+E: 1299660667.129031 0003 0031 308
172+E: 1299660667.129034 0000 0002 0
173+E: 1299660667.129043 0003 0035 7396
174+E: 1299660667.129046 0003 0036 3254
175+E: 1299660667.129049 0003 0034 0
176+E: 1299660667.129052 0003 0030 360
177+E: 1299660667.129055 0003 0031 308
178+E: 1299660667.129058 0000 0002 0
179+E: 1299660667.129062 0003 0035 5892
180+E: 1299660667.129064 0003 0036 1503
181+E: 1299660667.129067 0003 0034 0
182+E: 1299660667.129070 0003 0030 540
183+E: 1299660667.129073 0003 0031 462
184+E: 1299660667.129076 0000 0002 0
185+E: 1299660667.129080 0003 0035 6829
186+E: 1299660667.129083 0003 0036 2671
187+E: 1299660667.129086 0003 0034 1
188+E: 1299660667.129089 0003 0030 462
189+E: 1299660667.129091 0003 0031 360
190+E: 1299660667.129094 0000 0002 0
191+E: 1299660667.129103 0000 0000 0
192+E: 1299660667.145205 0003 0035 7378
193+E: 1299660667.145221 0003 0036 4687
194+E: 1299660667.145224 0003 0034 0
195+E: 1299660667.145232 0003 0030 360
196+E: 1299660667.145235 0003 0031 308
197+E: 1299660667.145238 0000 0002 0
198+E: 1299660667.145254 0003 0035 7403
199+E: 1299660667.145257 0003 0036 3252
200+E: 1299660667.145260 0003 0034 0
201+E: 1299660667.145263 0003 0030 360
202+E: 1299660667.145266 0003 0031 308
203+E: 1299660667.145269 0000 0002 0
204+E: 1299660667.145272 0003 0035 5894
205+E: 1299660667.145275 0003 0036 1508
206+E: 1299660667.145278 0003 0034 0
207+E: 1299660667.145281 0003 0030 540
208+E: 1299660667.145284 0003 0031 462
209+E: 1299660667.145287 0000 0002 0
210+E: 1299660667.145290 0003 0035 6853
211+E: 1299660667.145293 0003 0036 2668
212+E: 1299660667.145296 0003 0034 0
213+E: 1299660667.145299 0003 0030 360
214+E: 1299660667.145302 0003 0031 154
215+E: 1299660667.145305 0000 0002 0
216+E: 1299660667.145314 0000 0000 0
217+E: 1299660667.169030 0003 0035 5897
218+E: 1299660667.169040 0003 0036 1513
219+E: 1299660667.169044 0003 0034 0
220+E: 1299660667.169047 0003 0030 540
221+E: 1299660667.169049 0003 0031 308
222+E: 1299660667.169053 0000 0002 0
223+E: 1299660667.169066 0003 0000 5897
224+E: 1299660667.169069 0003 0001 1513
225+E: 1299660667.169074 0000 0000 0
226+E: 1299660667.181005 0001 014a 0
227+E: 1299660667.181013 0000 0000 0
228
229=== added file 'data/ntrig-dell-xt2.prop'
230--- data/ntrig-dell-xt2.prop 1970-01-01 00:00:00 +0000
231+++ data/ntrig-dell-xt2.prop 2011-11-30 21:40:28 +0000
232@@ -0,0 +1,31 @@
233+N: N-Trig-MultiTouch-Virtual-Device
234+I: 0003 1b96 0001 0110
235+P: 00 00 00 00 00 00 00 00
236+B: 00 0b 00 00 00 00 00 00 00
237+B: 01 00 00 00 00 00 00 00 00
238+B: 01 00 00 00 00 00 00 00 00
239+B: 01 00 00 00 00 00 00 00 00
240+B: 01 00 00 00 00 00 00 00 00
241+B: 01 00 00 00 00 00 00 00 00
242+B: 01 00 04 00 00 00 00 00 00
243+B: 01 00 00 00 00 00 00 00 00
244+B: 01 00 00 00 00 00 00 00 00
245+B: 01 00 00 00 00 00 00 00 00
246+B: 01 00 00 00 00 00 00 00 00
247+B: 01 00 00 00 00 00 00 00 00
248+B: 01 00 00 00 00 00 00 00 00
249+B: 02 00 00 00 00 00 00 00 00
250+B: 03 03 00 00 00 00 00 73 00
251+B: 04 00 00 00 00 00 00 00 00
252+B: 05 00 00 00 00 00 00 00 00
253+B: 11 00 00 00 00 00 00 00 00
254+B: 12 00 00 00 00 00 00 00 00
255+B: 15 00 00 00 00 00 00 00 00
256+B: 15 00 00 00 00 00 00 00 00
257+A: 00 0 9600 75 0
258+A: 01 0 7200 78 0
259+A: 30 0 9600 200 0
260+A: 31 0 7200 150 0
261+A: 34 0 1 0 0
262+A: 35 0 9600 75 0
263+A: 36 0 7200 78 0
264
265=== added file 'data/ntrig-lenovo-T410s.event'
266=== added file 'data/ntrig-lenovo-T410s.prop'
267--- data/ntrig-lenovo-T410s.prop 1970-01-01 00:00:00 +0000
268+++ data/ntrig-lenovo-T410s.prop 2011-11-30 21:40:28 +0000
269@@ -0,0 +1,24 @@
270+N: N-Trig-MultiTouch Virtual Device
271+I: 0003 1b96 0001 0110
272+P: 00 00 00 00 00 00 00 00
273+B: 00 01 00 00 00 00 00 00 00
274+B: 01 00 00 00 00 00 00 00 00
275+B: 01 00 00 00 00 00 00 00 00
276+B: 01 00 00 00 00 00 00 00 00
277+B: 01 00 00 00 00 00 00 00 00
278+B: 01 00 00 00 00 00 00 00 00
279+B: 01 00 00 00 00 00 00 00 00
280+B: 01 00 00 00 00 00 00 00 00
281+B: 01 00 00 00 00 00 00 00 00
282+B: 01 00 00 00 00 00 00 00 00
283+B: 01 00 00 00 00 00 00 00 00
284+B: 01 00 00 00 00 00 00 00 00
285+B: 01 00 00 00 00 00 00 00 00
286+B: 02 00 00 00 00 00 00 00 00
287+B: 03 00 00 00 00 00 00 00 00
288+B: 04 00 00 00 00 00 00 00 00
289+B: 05 00 00 00 00 00 00 00 00
290+B: 11 00 00 00 00 00 00 00 00
291+B: 12 00 00 00 00 00 00 00 00
292+B: 15 00 00 00 00 00 00 00 00
293+B: 15 00 00 00 00 00 00 00 00
294
295=== added file 'data/synaptics.prop'
296--- data/synaptics.prop 1970-01-01 00:00:00 +0000
297+++ data/synaptics.prop 2011-11-30 21:40:28 +0000
298@@ -0,0 +1,32 @@
299+N: SynPS/2 Synaptics TouchPad
300+I: 0011 0002 0007 01b1
301+P: 09 00 00 00 00 00 00 00
302+B: 00 0b 00 00 00 00 00 00 00
303+B: 01 00 00 00 00 00 00 00 00
304+B: 01 00 00 00 00 00 00 00 00
305+B: 01 00 00 00 00 00 00 00 00
306+B: 01 00 00 00 00 00 00 00 00
307+B: 01 00 00 03 00 00 00 00 00
308+B: 01 20 64 00 00 00 00 00 00
309+B: 01 00 00 00 00 00 00 00 00
310+B: 01 00 00 00 00 00 00 00 00
311+B: 01 00 00 00 00 00 00 00 00
312+B: 01 00 00 00 00 00 00 00 00
313+B: 01 00 00 00 00 00 00 00 00
314+B: 01 00 00 00 00 00 00 00 00
315+B: 02 00 00 00 00 00 00 00 00
316+B: 03 03 00 00 11 00 80 60 02
317+B: 04 00 00 00 00 00 00 00 00
318+B: 05 00 00 00 00 00 00 00 00
319+B: 11 00 00 00 00 00 00 00 00
320+B: 12 00 00 00 00 00 00 00 00
321+B: 15 00 00 00 00 00 00 00 00
322+B: 15 00 00 00 00 00 00 00 00
323+A: 00 1472 5888 0 0
324+A: 01 1408 4820 0 0
325+A: 18 0 255 0 0
326+A: 1c 0 15 0 0
327+A: 2f 0 1 0 0
328+A: 35 1472 5888 0 0
329+A: 36 1408 4820 0 0
330+A: 39 0 65535 0 0
331
332=== modified file 'data/wetab.prop'
333--- data/wetab.prop 2010-11-18 13:56:30 +0000
334+++ data/wetab.prop 2011-11-30 21:40:28 +0000
335@@ -1,4 +1,4 @@
336-N: eGalax-Inc.-USB-TouchController
337+N: eGalax-Inc.-USB-TouchController Virtual Device
338 I: 0003 0eef 72a1 0210
339 B: 00 0b 00 00 00 00 00 00 00
340 B: 01 00 00 00 00 00 00 00 00
341
342=== added directory 'python'
343=== added file 'python/Makefile.am'
344--- python/Makefile.am 1970-01-01 00:00:00 +0000
345+++ python/Makefile.am 2011-11-30 21:40:28 +0000
346@@ -0,0 +1,37 @@
347+#
348+# @file python/Makefile.am
349+# @brief automake recipe for the uTouch evemu Python bindings
350+#
351+# Copyright 2011 Canonical, Ltd.
352+#
353+# This program is free software: you can redistribute it and/or modify it
354+# under the terms of the GNU General Public License version 3, as published
355+# by the Free Software Foundation.
356+#
357+# This program is distributed in the hope that it will be useful, but
358+# WITHOUT ANY WARRANTY; without even the implied warranties of
359+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
360+# PURPOSE. See the GNU General Public License for more details.
361+#
362+# You should have received a copy of the GNU General Public License along
363+# with this program. If not, see <http://www.gnu.org/licenses/>.
364+
365+nobase_python_PYTHON = \
366+ $(wildcard evemu/*.py)
367+
368+check_SCRIPTS = evemu-test-runner
369+
370+TESTS = $(check_SCRIPTS)
371+
372+evemu-test-runner: evemu-test-runner.in Makefile
373+ $(AM_V_GEN)$(SED) \
374+ -e 's,[@]builddir[@],$(builddir),g' \
375+ -e 's,[@]top_builddir[@],$(top_builddir),g' \
376+ -e 's,[@]srcdir[@],$(srcdir),g' \
377+ -e 's,[@]python[@],$(PYTHON),g' \
378+ $< >$@
379+ chmod +x $@
380+
381+BUILT_SOURCES = evemu-test-runner
382+EXTRA_DIST = evemu-test-runner.in $(wildcard evemu/test*)
383+CLEANFILES = $(BUILT_SOURCES)
384
385=== added directory 'python/evemu'
386=== added file 'python/evemu-test-runner.in'
387--- python/evemu-test-runner.in 1970-01-01 00:00:00 +0000
388+++ python/evemu-test-runner.in 2011-11-30 21:40:28 +0000
389@@ -0,0 +1,4 @@
390+#!/bin/sh
391+
392+PYTHONPATH=@builddir@:@srcdir@ LD_LIBRARY_PATH=@top_builddir@/src/.libs @python@ -m evemu.testing.runner
393+
394
395=== added file 'python/evemu/__init__.py'
396--- python/evemu/__init__.py 1970-01-01 00:00:00 +0000
397+++ python/evemu/__init__.py 2011-11-30 21:40:28 +0000
398@@ -0,0 +1,271 @@
399+"""
400+The evemu module provides the Python interface to the kernel-level input device
401+raw events.
402+"""
403+
404+# Copyright 2011 Canonical Ltd.
405+#
406+# This program is free software: you can redistribute it and/or modify it
407+# under the terms of the GNU General Public License version 3, as published
408+# by the Free Software Foundation.
409+#
410+# This program is distributed in the hope that it will be useful, but
411+# WITHOUT ANY WARRANTY; without even the implied warranties of
412+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
413+# PURPOSE. See the GNU General Public License for more details.
414+#
415+# You should have received a copy of the GNU General Public License along
416+# with this program. If not, see <http://www.gnu.org/licenses/>.
417+
418+from ctypes.util import find_library
419+
420+import ctypes
421+import evemu.base
422+import evemu.const
423+import glob
424+import os
425+import re
426+import stat
427+
428+__all__ = ["Device"]
429+
430+
431+class Device(object):
432+ """
433+ Encapsulates a raw kernel input event device, either an existing one as
434+ reported by the kernel or a pseudodevice as created through a .prop file.
435+ """
436+
437+ def __init__(self, f):
438+ """
439+ Initializas an evemu Device.
440+
441+ args:
442+ f -- a file object or filename string for either an existing input
443+ device node (/dev/input/eventNN) or an evemu prop file that can be used
444+ to create a pseudo-device node.
445+ """
446+
447+ if type(f).__name__ == 'str':
448+ self._file = open(f, 'r+b')
449+ elif type(f).__name__ == 'file':
450+ self._file = f
451+ else:
452+ raise TypeError("expected file or file name")
453+
454+ self._is_propfile = True
455+ if stat.S_ISCHR(os.fstat(self._file.fileno()).st_mode):
456+ self._is_propfile = False
457+ elif self._file.read(3) == 'N: ':
458+ self._file.seek(0)
459+ else:
460+ raise TypeError("file must be a device special or prop file")
461+
462+ self._evemu = evemu.base.EvEmuBase(find_library(evemu.const.LIB))
463+ self._uinput = None
464+
465+ libevemu_new = self._evemu.get_lib().evemu_new
466+ libevemu_new.restype = ctypes.c_void_p
467+ self._evemu_device = libevemu_new("")
468+
469+ if self._is_propfile:
470+ fs = self._evemu._call0(self._evemu.get_c_lib().fdopen,
471+ self._file.fileno(),
472+ 'r')
473+ self._evemu._call(self._evemu.get_lib().evemu_read,
474+ self._evemu_device,
475+ fs)
476+ self._uinput = os.open(evemu.const.UINPUT_NODE, os.O_WRONLY)
477+ self._file = self._create_devnode()
478+ else:
479+ self._evemu._call(self._evemu.get_lib().evemu_extract,
480+ self._evemu_device,
481+ self._file.fileno())
482+
483+ def __del__(self):
484+ if self._is_propfile:
485+ self._file.close()
486+ self._evemu._call(self._evemu.get_lib().evemu_destroy,
487+ self._uinput)
488+
489+ def _create_devnode(self):
490+ self._evemu._call(self._evemu.get_lib().evemu_create,
491+ self._evemu_device,
492+ self._uinput)
493+ return open(self._find_newest_devnode(self.name), 'r+')
494+
495+ def _find_newest_devnode(self, target_name):
496+ newest_node = (None, float(0))
497+ for sysname in glob.glob("/sys/class/input/event*/device/name"):
498+ with open(sysname) as f:
499+ name = f.read().rstrip()
500+ if name == target_name:
501+ ev = re.search("(event\d+)", sysname)
502+ if ev:
503+ devname = os.path.join("/dev/input", ev.group(1))
504+ ctime = os.stat(devname).st_ctime
505+ if ctime > newest_node[1]:
506+ newest_node = (devname, ctime)
507+ return newest_node[0]
508+
509+ def describe(self, prop_file):
510+ """
511+ Gathers information about the input device and prints it
512+ to prop_file. This information can be parsed later when constructing
513+ a Device to create a virtual input device with the same properties.
514+
515+ Scripts that use this method need to be run as root.
516+ """
517+ if type(prop_file).__name__ != 'file':
518+ raise TypeError("expected file")
519+
520+ fs = self._evemu._call0(self._evemu.get_c_lib().fdopen,
521+ prop_file.fileno(),
522+ "w")
523+ self._evemu._call(self._evemu.get_lib().evemu_write,
524+ self._evemu_device,
525+ fs)
526+ self._evemu.get_c_lib().fflush(fs)
527+
528+ def play(self, events_file):
529+ """
530+ Replays an event sequence, as provided by the events_file,
531+ through the input device. The event sequence must be in
532+ the form created by the record method.
533+
534+ Scripts that use this method need to be run as root.
535+ """
536+ if type(events_file).__name__ != 'file':
537+ raise TypeError("expected file")
538+
539+ fs = self._evemu._call0(self._evemu.get_c_lib().fdopen,
540+ events_file.fileno(),
541+ "r")
542+ self._evemu._call(self._evemu.get_lib().evemu_play,
543+ fs,
544+ self._file.fileno())
545+
546+ def record(self, events_file, timeout=10000):
547+ """
548+ Captures events from the input device and prints them to the
549+ events_file. The events can be parsed by the play method,
550+ allowing a virtual input device to emit the exact same event
551+ sequence.
552+
553+ Scripts that use this method need to be run as root.
554+ """
555+ if type(events_file).__name__ != 'file':
556+ raise TypeError("expected file")
557+
558+ fs = self._evemu._call0(self._evemu.get_c_lib().fdopen,
559+ events_file.fileno(),
560+ "w")
561+ self._evemu._call(self._evemu.get_lib().evemu_record,
562+ fs,
563+ self._file.fileno(),
564+ timeout)
565+ self._evemu.get_c_lib().fflush(fs)
566+
567+ @property
568+ def version(self):
569+ """
570+ Gets the version of the evemu library used to create the Device.
571+ """
572+ return self._evemu._call(self._evemu.get_lib().evemu_get_version,
573+ self._evemu_device)
574+
575+ @property
576+ def devnode(self):
577+ """
578+ Gets the name of the /dev node of the input device.
579+ """
580+ return self._file.name
581+
582+ @property
583+ def name(self):
584+ """
585+ Gets the name of the input device (as reported by the device).
586+ """
587+ func = self._evemu.get_lib().evemu_get_name
588+ func.restype = ctypes.c_char_p
589+ return self._evemu._call(func, self._evemu_device)
590+
591+ @property
592+ def id_bustype(self):
593+ """
594+ Identifies the kernel device bustype.
595+ """
596+ return self._evemu._call(self._evemu.get_lib().evemu_get_id_bustype,
597+ self._evemu_device)
598+
599+ @property
600+ def id_vendor(self):
601+ """
602+ Identifies the kernel device vendor.
603+ """
604+ return self._evemu._call(self._evemu.get_lib().evemu_get_id_vendor,
605+ self._evemu_device)
606+
607+ @property
608+ def id_product(self):
609+ """
610+ Identifies the kernel device product.
611+ """
612+ return self._evemu._call(self._evemu.get_lib().evemu_get_id_product,
613+ self._evemu_device)
614+
615+ @property
616+ def id_version(self):
617+ """
618+ Identifies the kernel device version.
619+ """
620+ return self._evemu._call(self._evemu.get_lib().evemu_get_id_version,
621+ self._evemu_device)
622+
623+ def get_abs_minimum(self, event_code):
624+ return self._evemu._call(self._evemu.get_lib().evemu_get_abs_minimum,
625+ self._evemu_device,
626+ int(event_code))
627+
628+ def get_abs_maximum(self, event_code):
629+ return self._evemu._call(self._evemu.get_lib().evemu_get_abs_maximum,
630+ self._evemu_device,
631+ event_code)
632+
633+ def get_abs_fuzz(self, event_code):
634+ return self._evemu._call(self._evemu.get_lib().evemu_get_abs_fuzz,
635+ self._evemu_device,
636+ event_code)
637+
638+ def get_abs_flat(self, event_code):
639+ return self._evemu._call(self._evemu.get_lib().evemu_get_abs_flat,
640+ self._evemu_device,
641+ event_code)
642+
643+ def get_abs_resolution(self, event_code):
644+ return self._evemu._call(self._evemu.get_lib().evemu_get_abs_resolution,
645+ self._evemu_device,
646+ event_code)
647+
648+ def has_prop(self, event_code):
649+ return self._evemu._call(self._evemu.get_lib().evemu_has_prop,
650+ self._evemu_device,
651+ event_code)
652+
653+ def has_event(self, event_type, event_code):
654+ """
655+ This method's 'even_type' parameter is expected to mostly take the
656+ value for EV_ABS (i.e., 0x03), but may on occasion EV_KEY (i.e., 0x01).
657+ If the former, then the even_code parameter will take the same values
658+ as the methods above (ABS_*). However, if the latter, then the legal
659+ values will be BTN_*.
660+
661+ The reason for including the button data, is that buttons are sometimes
662+ used to simulate gestures for a higher number of touches than are
663+ possible with just 2-touch hardware.
664+ """
665+ return self._evemu._call(self._evemu.get_lib().evemu_has_event,
666+ self._evemu_device,
667+ event_type,
668+ event_code)
669+
670
671=== added file 'python/evemu/base.py'
672--- python/evemu/base.py 1970-01-01 00:00:00 +0000
673+++ python/evemu/base.py 2011-11-30 21:40:28 +0000
674@@ -0,0 +1,43 @@
675+import ctypes
676+from ctypes.util import find_library
677+import os
678+
679+from evemu import const
680+from evemu import exception
681+
682+
683+class EvEmuBase(object):
684+ """
685+ A base wrapper class for the evemu functions, accessed via ctypes.
686+ """
687+ def __init__(self, library=""):
688+ if not library:
689+ library = const.LIB
690+ self._lib = ctypes.CDLL(library, use_errno=True)
691+ self._libc = ctypes.CDLL(find_library("c"), use_errno=True)
692+
693+ def _call0(self, api_call, *parameters):
694+ result = api_call(*parameters)
695+ if result == 0 and self.get_c_errno() != 0:
696+ raise exception.ExecutionError, "%s: %s" % (
697+ api_call.__name__, self.get_c_error())
698+ return result
699+
700+ def _call(self, api_call, *parameters):
701+ result = api_call(*parameters)
702+ if result < 0 and self.get_c_errno() != 0:
703+ raise exception.ExecutionError, "%s: %s" % (
704+ api_call.__name__, self.get_c_error())
705+ return result
706+
707+ def get_c_errno(self):
708+ return ctypes.get_errno()
709+
710+ def get_c_error(self):
711+ return os.strerror(ctypes.get_errno())
712+
713+ def get_c_lib(self):
714+ return self._libc
715+
716+ def get_lib(self):
717+ return self._lib
718
719=== added file 'python/evemu/const.py'
720--- python/evemu/const.py 1970-01-01 00:00:00 +0000
721+++ python/evemu/const.py 2011-11-30 21:40:28 +0000
722@@ -0,0 +1,235 @@
723+LIB = "utouch-evemu"
724+DEFAULT_LIB = "/usr/lib/libutouch-evemu.so"
725+LOCAL_LIB = "../src/.libs/libutouch-evemu.so"
726+UINPUT_NODE = "/dev/uinput"
727+MAX_EVENT_NODE = 32
728+UINPUT_MAX_NAME_SIZE = 80 # defined in linux/uinput.h
729+DEVICE_PATH_TEMPLATE = "/dev/input/event%d"
730+DEVICE_NAME_PATH_TEMPLATE = "/sys/class/input/event%d/device/name"
731+# The following should be examined every release of evemu
732+API = [
733+ "evemu_new",
734+ "evemu_delete",
735+ "evemu_extract",
736+ "evemu_write",
737+ "evemu_read",
738+ "evemu_write_event",
739+ "evemu_record",
740+ "evemu_read_event",
741+ "evemu_play",
742+ "evemu_create",
743+ "evemu_destroy",
744+ # Device settrs
745+ "evemu_set_name",
746+ # Device gettrs
747+ "evemu_get_version",
748+ "evemu_get_name",
749+ "evemu_get_id_bustype",
750+ "evemu_get_id_vendor",
751+ "evemu_get_id_product",
752+ "evemu_get_id_version",
753+ "evemu_get_abs_minimum",
754+ "evemu_get_abs_maximum",
755+ "evemu_get_abs_fuzz",
756+ "evemu_get_abs_flat",
757+ "evemu_get_abs_resolution",
758+ # Device hasers
759+ "evemu_has_prop",
760+ "evemu_has_event",
761+ ]
762+
763+event_types = {
764+ "EV_SYN": 0x00,
765+ "EV_KEY": 0x01,
766+ "EV_REL": 0x02,
767+ "EV_ABS": 0x03,
768+ "EV_MSC": 0x04,
769+ "EV_SW": 0x05,
770+ "EV_LED": 0x11,
771+ "EV_SND": 0x12,
772+ "EV_REP": 0x14,
773+ "EV_FF": 0x15,
774+ "EV_PWR": 0x16,
775+ "EV_FF_STATUS": 0x17,
776+ "EV_MAX": 0x1f,
777+ }
778+event_types["EV_CNT"] = event_types["EV_MAX"] + 1,
779+
780+event_names = {
781+ "EV_SYN": "Sync",
782+ "EV_KEY": "Keys or Buttons",
783+ "EV_REL": "Relative Axes",
784+ "EV_ABS": "Absolute Axes",
785+ "EV_MSC": "Miscellaneous",
786+ "EV_SW": "Switches",
787+ "EV_LED": "Leds",
788+ "EV_SND": "Sound",
789+ "EV_REP": "Repeat",
790+ "EV_FF": "Force Feedback",
791+ "EV_PWR": "Power Management",
792+ "EV_FF_STATUS": "Force Feedback Status",
793+}
794+
795+absolute_axes = {
796+ "ABS_X": 0x00,
797+ "ABS_Y": 0x01,
798+ "ABS_Z": 0x02,
799+ "ABS_RX": 0x03,
800+ "ABS_RY": 0x04,
801+ "ABS_RZ": 0x05,
802+ "ABS_THROTTLE": 0x06,
803+ "ABS_RUDDER": 0x07,
804+ "ABS_WHEEL": 0x08,
805+ "ABS_GAS": 0x09,
806+ "ABS_BRAKE": 0x0a,
807+ "ABS_HAT0X": 0x10,
808+ "ABS_HAT0Y": 0x11,
809+ "ABS_HAT1X": 0x12,
810+ "ABS_HAT1Y": 0x13,
811+ "ABS_HAT2X": 0x14,
812+ "ABS_HAT2Y": 0x15,
813+ "ABS_HAT3X": 0x16,
814+ "ABS_HAT3Y": 0x17,
815+ "ABS_PRESSURE": 0x18,
816+ "ABS_DISTANCE": 0x19,
817+ "ABS_TILT_X": 0x1a,
818+ "ABS_TILT_Y": 0x1b,
819+ "ABS_TOOL_WIDTH": 0x1c,
820+ "ABS_VOLUME": 0x20,
821+ "ABS_MISC": 0x28,
822+ "ABS_MT_SLOT": 0x2f, # MT slot being modified
823+ "ABS_MT_TOUCH_MAJOR": 0x30, # Major axis of touching ellipse
824+ "ABS_MT_TOUCH_MINOR": 0x31, # Minor axis (omit if circular)
825+ "ABS_MT_WIDTH_MAJOR": 0x32, # Major axis of approaching ellipse
826+ "ABS_MT_WIDTH_MINOR": 0x33, # Minor axis (omit if circular)
827+ "ABS_MT_ORIENTATION": 0x34, # Ellipse orientation
828+ "ABS_MT_POSITION_X": 0x35, # Center X ellipse position
829+ "ABS_MT_POSITION_Y": 0x36, # Center Y ellipse position
830+ "ABS_MT_TOOL_TYPE": 0x37, # Type of touching device
831+ "ABS_MT_BLOB_ID": 0x38, # Group a set of packets as a blob
832+ "ABS_MT_TRACKING_ID": 0x39, # Unique ID of initiated contact
833+ "ABS_MT_PRESSURE": 0x3a, # Pressure on contact area
834+ "ABS_MT_DISTANCE": 0x3b, # Contact hover distance
835+ "ABS_MAX": 0x3f,
836+ }
837+# XXX ABS_CNT doesn't always give the same value from test data; disabling it
838+# for now.
839+#absolute_axes["ABS_CNT"] = absolute_axes["ABS_MAX"] + 1
840+
841+buttons = {
842+ "BTN_MISC": 0x100,
843+ "BTN_0": 0x100,
844+ "BTN_1": 0x101,
845+ "BTN_2": 0x102,
846+ "BTN_3": 0x103,
847+ "BTN_4": 0x104,
848+ "BTN_5": 0x105,
849+ "BTN_6": 0x106,
850+ "BTN_7": 0x107,
851+ "BTN_8": 0x108,
852+ "BTN_9": 0x109,
853+
854+ "BTN_MOUSE": 0x110,
855+ "BTN_LEFT": 0x110,
856+ "BTN_RIGHT": 0x111,
857+ "BTN_MIDDLE": 0x112,
858+ "BTN_SIDE": 0x113,
859+ "BTN_EXTRA": 0x114,
860+ "BTN_FORWARD": 0x115,
861+ "BTN_BACK": 0x116,
862+ "BTN_TASK": 0x117,
863+
864+ "BTN_JOYSTICK": 0x120,
865+ "BTN_TRIGGER": 0x120,
866+ "BTN_THUMB": 0x121,
867+ "BTN_THUMB2": 0x122,
868+ "BTN_TOP": 0x123,
869+ "BTN_TOP2": 0x124,
870+ "BTN_PINKIE": 0x125,
871+ "BTN_BASE": 0x126,
872+ "BTN_BASE2": 0x127,
873+ "BTN_BASE3": 0x128,
874+ "BTN_BASE4": 0x129,
875+ "BTN_BASE5": 0x12a,
876+ "BTN_BASE6": 0x12b,
877+ "BTN_DEAD": 0x12f,
878+
879+ "BTN_GAMEPAD": 0x130,
880+ "BTN_A": 0x130,
881+ "BTN_B": 0x131,
882+ "BTN_C": 0x132,
883+ "BTN_X": 0x133,
884+ "BTN_Y": 0x134,
885+ "BTN_Z": 0x135,
886+ "BTN_TL": 0x136,
887+ "BTN_TR": 0x137,
888+ "BTN_TL2": 0x138,
889+ "BTN_TR2": 0x139,
890+ "BTN_SELECT": 0x13a,
891+ "BTN_START": 0x13b,
892+ "BTN_MODE": 0x13c,
893+ "BTN_THUMBL": 0x13d,
894+ "BTN_THUMBR": 0x13e,
895+
896+ "BTN_DIGI": 0x140,
897+ "BTN_TOOL_PEN": 0x140,
898+ "BTN_TOOL_RUBBER": 0x141,
899+ "BTN_TOOL_BRUSH": 0x142,
900+ "BTN_TOOL_PENCIL": 0x143,
901+ "BTN_TOOL_AIRBRUSH": 0x144,
902+ "BTN_TOOL_FINGER": 0x145,
903+ "BTN_TOOL_MOUSE": 0x146,
904+ "BTN_TOOL_LENS": 0x147,
905+ "BTN_TOUCH": 0x14a,
906+ "BTN_STYLUS": 0x14b,
907+ "BTN_STYLUS2": 0x14c,
908+ "BTN_TOOL_DOUBLETAP": 0x14d,
909+ "BTN_TOOL_TRIPLETAP": 0x14e,
910+ "BTN_TOOL_QUADTAP": 0x14f, # Four fingers on trackpad
911+
912+ "BTN_WHEEL": 0x150,
913+ "BTN_GEAR_DOWN": 0x150,
914+ "BTN_GEAR_UP": 0x151,
915+
916+ "BTN_TRIGGER_HAPPY": 0x2c0,
917+ "BTN_TRIGGER_HAPPY1": 0x2c0,
918+ "BTN_TRIGGER_HAPPY2": 0x2c1,
919+ "BTN_TRIGGER_HAPPY3": 0x2c2,
920+ "BTN_TRIGGER_HAPPY4": 0x2c3,
921+ "BTN_TRIGGER_HAPPY5": 0x2c4,
922+ "BTN_TRIGGER_HAPPY6": 0x2c5,
923+ "BTN_TRIGGER_HAPPY7": 0x2c6,
924+ "BTN_TRIGGER_HAPPY8": 0x2c7,
925+ "BTN_TRIGGER_HAPPY9": 0x2c8,
926+ "BTN_TRIGGER_HAPPY10": 0x2c9,
927+ "BTN_TRIGGER_HAPPY11": 0x2ca,
928+ "BTN_TRIGGER_HAPPY12": 0x2cb,
929+ "BTN_TRIGGER_HAPPY13": 0x2cc,
930+ "BTN_TRIGGER_HAPPY14": 0x2cd,
931+ "BTN_TRIGGER_HAPPY15": 0x2ce,
932+ "BTN_TRIGGER_HAPPY16": 0x2cf,
933+ "BTN_TRIGGER_HAPPY17": 0x2d0,
934+ "BTN_TRIGGER_HAPPY18": 0x2d1,
935+ "BTN_TRIGGER_HAPPY19": 0x2d2,
936+ "BTN_TRIGGER_HAPPY20": 0x2d3,
937+ "BTN_TRIGGER_HAPPY21": 0x2d4,
938+ "BTN_TRIGGER_HAPPY22": 0x2d5,
939+ "BTN_TRIGGER_HAPPY23": 0x2d6,
940+ "BTN_TRIGGER_HAPPY24": 0x2d7,
941+ "BTN_TRIGGER_HAPPY25": 0x2d8,
942+ "BTN_TRIGGER_HAPPY26": 0x2d9,
943+ "BTN_TRIGGER_HAPPY27": 0x2da,
944+ "BTN_TRIGGER_HAPPY28": 0x2db,
945+ "BTN_TRIGGER_HAPPY29": 0x2dc,
946+ "BTN_TRIGGER_HAPPY30": 0x2dd,
947+ "BTN_TRIGGER_HAPPY31": 0x2de,
948+ "BTN_TRIGGER_HAPPY32": 0x2df,
949+ "BTN_TRIGGER_HAPPY33": 0x2e0,
950+ "BTN_TRIGGER_HAPPY34": 0x2e1,
951+ "BTN_TRIGGER_HAPPY35": 0x2e2,
952+ "BTN_TRIGGER_HAPPY36": 0x2e3,
953+ "BTN_TRIGGER_HAPPY37": 0x2e4,
954+ "BTN_TRIGGER_HAPPY38": 0x2e5,
955+ "BTN_TRIGGER_HAPPY39": 0x2e6,
956+ "BTN_TRIGGER_HAPPY40": 0x2e7,
957+ }
958
959=== added file 'python/evemu/exception.py'
960--- python/evemu/exception.py 1970-01-01 00:00:00 +0000
961+++ python/evemu/exception.py 2011-11-30 21:40:28 +0000
962@@ -0,0 +1,22 @@
963+class EvEmuError(Exception):
964+ pass
965+
966+
967+class WrapperError(EvEmuError):
968+ pass
969+
970+
971+class ExecutionError(EvEmuError):
972+ pass
973+
974+
975+class TestError(EvEmuError):
976+ pass
977+
978+
979+class NullFileHandleError(EvEmuError):
980+ pass
981+
982+
983+class SkipTest(Exception):
984+ pass
985
986=== added directory 'python/evemu/testing'
987=== added file 'python/evemu/testing/__init__.py'
988=== added file 'python/evemu/testing/mocker.py'
989--- python/evemu/testing/mocker.py 1970-01-01 00:00:00 +0000
990+++ python/evemu/testing/mocker.py 2011-11-30 21:40:28 +0000
991@@ -0,0 +1,5 @@
992+"""
993+This module is for use by unit tests in order to mock the uinput device. This
994+alleviates the need for root access, and thus makes the unit tests something
995+that can be run by packaging software.
996+"""
997
998=== added file 'python/evemu/testing/result.py'
999--- python/evemu/testing/result.py 1970-01-01 00:00:00 +0000
1000+++ python/evemu/testing/result.py 2011-11-30 21:40:28 +0000
1001@@ -0,0 +1,48 @@
1002+import unittest
1003+try:
1004+ # Python 2.7
1005+ from unittest import TextTestResult
1006+except ImportError:
1007+ # Python 2.4, 2.5, 2.6
1008+ from unittest import _TextTestResult as TextTestResult
1009+
1010+
1011+def get_test_directory():
1012+ from evemu import tests
1013+ return tests.__path__[0]
1014+
1015+
1016+def get_test_module():
1017+ return get_test_directory().replace("/", ".")
1018+
1019+
1020+class CustomTestResult(TextTestResult):
1021+
1022+ def __init__(self, *args, **kwds):
1023+ super(CustomTestResult, self).__init__(*args, **kwds)
1024+ self.current_module = ""
1025+ self.last_module = ""
1026+ self.current_class = ""
1027+ self.last_class = ""
1028+
1029+ def startTest(self, test):
1030+ unittest.TestResult.startTest(self, test)
1031+ if not self.showAll:
1032+ return
1033+ self.last_module = self.current_module
1034+ self.last_class = self.current_class
1035+ method = test._testMethodName
1036+ module_and_class = test.id().rsplit(method)[0][:-1]
1037+ this_module = ".".join(module_and_class.split(".")[:-1])
1038+ self.current_module = this_module
1039+ this_class = module_and_class.split(".")[-1]
1040+ self.current_class = this_class
1041+ if self.last_module != self.current_module:
1042+ heading = "\n%s.%s" % (get_test_module(), this_module)
1043+ self.stream.writeln(heading)
1044+ if self.last_class != self.current_class:
1045+ self.stream.writeln(" %s" % this_class)
1046+ self.stream.write(" %s " % method.ljust(50, "."))
1047+ self.stream.write(" ")
1048+ self.stream.flush()
1049+
1050
1051=== added file 'python/evemu/testing/runner.py'
1052--- python/evemu/testing/runner.py 1970-01-01 00:00:00 +0000
1053+++ python/evemu/testing/runner.py 2011-11-30 21:40:28 +0000
1054@@ -0,0 +1,63 @@
1055+import os
1056+import unittest
1057+
1058+from evemu.testing import result
1059+
1060+
1061+def get_test_directory():
1062+ from evemu import tests
1063+ return tests.__path__[0]
1064+
1065+
1066+class CustomTestRunner(unittest.TextTestRunner):
1067+ """
1068+ This is only needed for Python 2.6 and lower.
1069+ """
1070+ def _makeResult(self):
1071+ return result.CustomTestResult(
1072+ self.stream, self.descriptions, self.verbosity)
1073+
1074+
1075+def get_suite(loader, top_level_directory):
1076+ if hasattr(loader, "discover"):
1077+ # Python 2.7
1078+ suite = loader.discover(top_level_directory)
1079+ else:
1080+ # Python 2.4, 2.5, 2.6
1081+ names = []
1082+ def _path_to_module(path):
1083+ # generate dotted names for file paths
1084+ path = path.replace(".py", "")
1085+ return path.replace("/", ".")
1086+
1087+ # walk the directory
1088+ for dirpath, dirnames, filenames in os.walk(top_level_directory):
1089+ modules = [
1090+ _path_to_module(os.path.join(dirpath, x)) for x in filenames
1091+ if x.startswith("test_") and x.endswith(".py")]
1092+ if not modules:
1093+ continue
1094+ names.extend(modules)
1095+ suite = loader.loadTestsFromNames(names)
1096+ return suite
1097+
1098+
1099+def get_runner():
1100+ try:
1101+ # Python 2.7
1102+ runner = unittest.TextTestRunner(
1103+ verbosity=2, resultclass=result.CustomTestResult)
1104+ except TypeError:
1105+ # Python 2.4, 2.5, 2.6
1106+ runner = CustomTestRunner(verbosity=2)
1107+ return runner
1108+
1109+
1110+def run_tests():
1111+ loader = unittest.TestLoader()
1112+ suite = get_suite(loader, get_test_directory())
1113+ get_runner().run(suite)
1114+
1115+
1116+if __name__ == "__main__":
1117+ run_tests()
1118
1119=== added file 'python/evemu/testing/testcase.py'
1120--- python/evemu/testing/testcase.py 1970-01-01 00:00:00 +0000
1121+++ python/evemu/testing/testcase.py 2011-11-30 21:40:28 +0000
1122@@ -0,0 +1,70 @@
1123+from ctypes.util import find_library
1124+import os
1125+import unittest
1126+
1127+from evemu import const
1128+from evemu import exception
1129+
1130+
1131+def get_top_directory():
1132+ import evemu
1133+ return evemu.__path__[0]
1134+
1135+
1136+def skip(message):
1137+ try:
1138+ return unittest.skip(message)
1139+ except AttributeError:
1140+ def _skip(message):
1141+ def decorator(test_item):
1142+ def skip_wrapper(*args, **kwds):
1143+ raise exception.SkipTest(message)
1144+ return skip_wrapper
1145+ return decorator
1146+ return _skip(message)
1147+
1148+
1149+class Non26BaseTestCase(unittest.TestCase):
1150+ """
1151+ This is to provide methods that aren't in 2.6 and below, but are in 2.7 and
1152+ above.
1153+ """
1154+ def __init__(self, *args, **kwds):
1155+ super(Non26BaseTestCase, self).__init__(*args, **kwds)
1156+ if not hasattr(unittest.TestCase, "assertIn"):
1157+ self.assertIn = self._assertIn26
1158+
1159+ def _assertIn26(self, member, container, msg=None):
1160+ """Just like self.assertTrue(a in b), but with a nicer default message."""
1161+ if member not in container:
1162+ standardMsg = '%s not found in %s' % (repr(member),
1163+ repr(container))
1164+ self.fail(msg or standardMsg)
1165+
1166+
1167+class BaseTestCase(unittest.TestCase):
1168+
1169+ def setUp(self):
1170+ super(BaseTestCase, self).setUp()
1171+ library = find_library(const.LIB)
1172+ if not library:
1173+ if os.path.exists(const.DEFAULT_LIB):
1174+ library = const.DEFAULT_LIB
1175+ else:
1176+ library = const.LOCAL_LIB
1177+ self.library = library
1178+ basedir = get_top_directory()
1179+ self.data_dir = os.path.join(basedir, "..", "..", "data")
1180+ self.device = None
1181+
1182+ def tearDown(self):
1183+ if self.device:
1184+ self.device.destroy()
1185+ super(BaseTestCase, self).tearDown()
1186+
1187+ def get_device_file(self):
1188+ return os.path.join(self.data_dir, "ntrig-dell-xt2.prop")
1189+
1190+ def get_events_file(self):
1191+ return os.path.join(self.data_dir, "ntrig-dell-xt2.event")
1192+
1193
1194=== added directory 'python/evemu/tests'
1195=== added file 'python/evemu/tests/__init__.py'
1196=== added file 'python/evemu/tests/test_base.py'
1197--- python/evemu/tests/test_base.py 1970-01-01 00:00:00 +0000
1198+++ python/evemu/tests/test_base.py 2011-11-30 21:40:28 +0000
1199@@ -0,0 +1,25 @@
1200+import unittest
1201+
1202+from evemu import const
1203+from evemu.base import EvEmuBase
1204+from evemu.testing import testcase
1205+
1206+
1207+class EvEmuBaseTestCase(testcase.BaseTestCase):
1208+
1209+ def test_so_library_found(self):
1210+ wrapper = EvEmuBase(self.library)
1211+ # Make sure that the library loads
1212+ self.assertNotEqual(
1213+ wrapper._lib._name.find("libutouch-evemu"), -1)
1214+
1215+ def test_c_symbols_found(self):
1216+ # Make sure that the expected functions are present
1217+ wrapper = EvEmuBase(self.library)
1218+ for function_name in const.API:
1219+ function = getattr(wrapper._lib, function_name)
1220+ self.assertTrue(function is not None)
1221+
1222+
1223+if __name__ == "__main__":
1224+ unittest.main()
1225
1226=== added file 'python/evemu/tests/test_device.py'
1227--- python/evemu/tests/test_device.py 1970-01-01 00:00:00 +0000
1228+++ python/evemu/tests/test_device.py 2011-11-30 21:40:28 +0000
1229@@ -0,0 +1,223 @@
1230+
1231+from evemu.testing import testcase
1232+from multiprocessing import Process, Queue, Event
1233+
1234+import evemu
1235+import os
1236+import re
1237+import tempfile
1238+import unittest
1239+
1240+
1241+def record(recording_started, device_node, q):
1242+ """
1243+ Runs the recorder in a separate process because the evemu API is a
1244+ blocking API.
1245+ """
1246+ device = evemu.Device(device_node)
1247+ with tempfile.TemporaryFile() as event_file:
1248+ recording_started.set()
1249+ device.record(event_file, 1000)
1250+ event_file.flush()
1251+ event_file.seek(0)
1252+ outdata = event_file.readlines()
1253+ q.put(outdata)
1254+
1255+
1256+class DeviceActionTestCase(testcase.BaseTestCase):
1257+ """
1258+ Verifies the high-level Device functions (create, describe, play, record).
1259+ """
1260+
1261+ def test_construct_from_dev_node_name(self):
1262+ """
1263+ Verifies a Device can be constructed from an existing input device node
1264+ name.
1265+ """
1266+ d = evemu.Device("/dev/input/event10")
1267+
1268+ def test_construct_from_dev_node_file(self):
1269+ """
1270+ Verifies a Device can be constructed from an existing input device node
1271+ file object.
1272+ """
1273+ d = evemu.Device(open("/dev/input/event10"))
1274+
1275+ def test_construct_from_prop_file_name(self):
1276+ """
1277+ Verifies a device can be constructed from an evemu prop file name.
1278+ """
1279+ d = evemu.Device(self.get_device_file())
1280+
1281+ def test_construct_from_prop_file_file(self):
1282+ """
1283+ Verifies a device can be constructed from an evemu prop file file
1284+ object.
1285+ """
1286+ d = evemu.Device(open(self.get_device_file()))
1287+
1288+ def test_describe(self):
1289+ """
1290+ Verifies that a device description can be correctly extracted from a
1291+ Device.
1292+ """
1293+ # Get original description
1294+ with open(self.get_device_file()) as f:
1295+ data = f.readlines()
1296+
1297+ # Create a pseudo device with that description
1298+ d = evemu.Device(self.get_device_file())
1299+
1300+ # get the description to a temporary file
1301+ with tempfile.TemporaryFile() as t:
1302+ d.describe(t)
1303+
1304+ # read in the temporary file and compare to the original
1305+ t.flush()
1306+ t.seek(0)
1307+ newdata = t.readlines()
1308+ self.assertEquals(data, newdata)
1309+
1310+ def test_play_and_record(self):
1311+ """
1312+ Verifies that a Device and play back prerecorded events.
1313+ """
1314+ device = evemu.Device(self.get_device_file())
1315+ devnode = device.devnode
1316+ events_file = self.get_events_file()
1317+ with open(events_file) as e:
1318+ indata = e.readlines()
1319+
1320+ recording_started = Event()
1321+ q = Queue()
1322+ record_process = Process(target=record,
1323+ args=(recording_started, devnode, q))
1324+ record_process.start()
1325+ recording_started.wait(100)
1326+ device.play(open(events_file))
1327+
1328+ outdata = q.get()
1329+ record_process.join()
1330+
1331+ self.assertEquals(len(indata), len(outdata))
1332+ fuzz = re.compile("E: \d+\.\d+ (.*)")
1333+ for i in range(len(indata)):
1334+ lhs = fuzz.match(indata[i])
1335+ self.assertTrue(lhs)
1336+ rhs = fuzz.match(outdata[i])
1337+ self.assertTrue(rhs)
1338+ self.assertEquals(lhs.group(1), rhs.group(1))
1339+
1340+
1341+class DevicePropertiesTestCase(testcase.BaseTestCase):
1342+ """
1343+ Verifies the workings of the various device property accessors.
1344+ """
1345+
1346+ def setUp(self):
1347+ super(DevicePropertiesTestCase, self).setUp()
1348+ self._device = evemu.Device(self.get_device_file())
1349+
1350+ def tearDown(self):
1351+ del self._device
1352+ super(DevicePropertiesTestCase, self).tearDown()
1353+
1354+ def test_version(self):
1355+ self.assertEqual(self._device.version, 0)
1356+
1357+ def test_name(self):
1358+ self.assertEqual(self._device.name, "N-Trig-MultiTouch-Virtual-Device")
1359+
1360+ def test_id_bustype(self):
1361+ self.assertEqual(self._device.id_bustype, 3)
1362+
1363+ def test_id_vendor(self):
1364+ self.assertEqual(hex(self._device.id_vendor), "0x1b96")
1365+
1366+ def test_id_product(self):
1367+ self.assertEqual(self._device.id_product, 1)
1368+
1369+ def test_id_version(self):
1370+ self.assertEqual(self._device.id_version, 272)
1371+
1372+ def test_get_abs_minimum(self):
1373+ expected = [
1374+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1375+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1376+ ]
1377+ results = [self._device.get_abs_minimum(x)
1378+ for x in evemu.const.absolute_axes.values()]
1379+ self.assertEqual(results, expected)
1380+
1381+ def test_get_abs_maximum(self):
1382+ expected = [
1383+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9600, 7200, 0, 0, 0, 0,
1384+ 7200, 1, 7200, 0, 9600, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9600,
1385+ 0, 0,
1386+ ]
1387+ # Skipping the entry for ABS_CNT; some times it's 0, sometimes a very
1388+ # large negative number.
1389+ results = [self._device.get_abs_maximum(x)
1390+ for x in evemu.const.absolute_axes.values()]
1391+ self.assertEqual(results[:-1], expected[:-1])
1392+
1393+ def test_get_abs_fuzz(self):
1394+ expected = [
1395+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 78, 0, 0, 0, 0, 150,
1396+ 0, 78, 0, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0,
1397+ ]
1398+ results = [self._device.get_abs_fuzz(x)
1399+ for x in evemu.const.absolute_axes.values()]
1400+ self.assertEqual(results, expected)
1401+
1402+ def test_get_abs_flat(self):
1403+ expected = [
1404+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1405+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1406+ ]
1407+ results = [self._device.get_abs_flat(x)
1408+ for x in evemu.const.absolute_axes.values()]
1409+ self.assertEqual(results, expected)
1410+
1411+ def test_get_abs_resolution(self):
1412+ expected = [
1413+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1414+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1415+ ]
1416+ results = [self._device.get_abs_resolution(x)
1417+ for x in evemu.const.absolute_axes.values()]
1418+ self.assertEqual(results, expected)
1419+
1420+ def test_has_prop(self):
1421+ expected = [
1422+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1423+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1424+ ]
1425+ results = [self._device.has_prop(x)
1426+ for x in evemu.const.absolute_axes.values()]
1427+ self.assertEqual(results, expected)
1428+
1429+ def test_has_event_ev_abs(self):
1430+ expected = [
1431+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1,
1432+ 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1433+ ]
1434+ results = [self._device.has_event(evemu.const.event_types["EV_ABS"], x)
1435+ for x in evemu.const.absolute_axes.values()]
1436+ self.assertEqual(results, expected)
1437+
1438+ def test_has_event_ev_key(self):
1439+ expected = [
1440+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
1441+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1442+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1443+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1444+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1445+ ]
1446+ results = [self._device.has_event(evemu.const.event_types["EV_KEY"], x)
1447+ for x in evemu.const.buttons.values()]
1448+ self.assertEqual(results, expected)
1449+
1450+
1451+if __name__ == "__main__":
1452+ unittest.main()
1453
1454=== added file 'python/setup.py'
1455=== modified file 'src/evemu.c'
1456--- src/evemu.c 2011-11-02 13:49:33 +0000
1457+++ src/evemu.c 2011-11-30 21:40:28 +0000
1458@@ -337,7 +337,7 @@
1459
1460 memset(dev, 0, sizeof(*dev));
1461
1462- ret = fscanf(fp, "N: %ms\n", &devname);
1463+ ret = fscanf(fp, "N: %m[^\n]\n", &devname);
1464 if (ret <= 0) {
1465 if (devname != NULL)
1466 free(devname);

Subscribers

People subscribed via source and target branches