Merge lp:~bregma/evemu/lp-731678 into lp:evemu
- lp-731678
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Open Input Framework Team | Pending | ||
Review via email: mp+84009@code.launchpad.net |
Commit message
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 : | # |
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); |
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?