def test_refresh_for_unknown_key(self): device = '9876 name' # this test only makes sense if this device is unknown yet self.assertIsNone(groups.find(name=device)) self.daemon = Daemon() # make sure the devices are populated groups.refresh() self.daemon.refresh() fixtures[self.new_fixture_path] = { 'capabilities': { evdev.ecodes.EV_KEY: [evdev.ecodes.KEY_A] }, 'phys': '9876 phys', 'info': evdev.device.DeviceInfo(4, 5, 6, 7), 'name': device } self.daemon._autoload('25v7j9q4vtj') # this is unknown, so the daemon will scan the devices again # test if the injector called groups.refresh successfully self.assertIsNotNone(groups.find(name=device))
def _handle_commands(self): """Handle all unread commands.""" # wait for something to do select.select([self._commands], [], []) while self._commands.poll(): cmd = self._commands.recv() logger.debug('Received command "%s"', cmd) if cmd == TERMINATE: logger.debug('Helper terminates') sys.exit(0) if cmd == REFRESH_GROUPS: groups.refresh() self._send_groups() continue group = groups.find(key=cmd) if group is None: groups.refresh() group = groups.find(key=cmd) if group is not None: self.group = group continue logger.error('Received unknown command "%s"', cmd)
def test_reads_joysticks(self): # if their purpose is "buttons" custom_mapping.set('gamepad.joystick.left_purpose', BUTTONS) push_events('gamepad', [ new_event(EV_ABS, ABS_Y, MAX_ABS), # the value of that one is interpreted as release, because # it is too small new_event(EV_ABS, ABS_X, MAX_ABS // 10) ]) self.create_helper() reader.start_reading(groups.find(name='gamepad')) time.sleep(0.2) self.assertEqual(reader.read(), (EV_ABS, ABS_Y, 1)) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 1) reader._unreleased = {} custom_mapping.set('gamepad.joystick.left_purpose', MOUSE) push_events('gamepad', [ new_event(EV_ABS, ABS_Y, MAX_ABS) ]) self.create_helper() reader.start_reading(groups.find(name='gamepad')) time.sleep(0.1) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 0)
def on_select_device(self, dropdown): """List all presets, create one if none exist yet.""" self.save_preset() if self.group and dropdown.get_active_id() == self.group.key: return # selecting a device will also automatically select a different # preset. Prevent another unsaved-changes dialog to pop up custom_mapping.changed = False group_key = dropdown.get_active_id() if group_key is None: return logger.debug('Selecting device "%s"', group_key) self.group = groups.find(key=group_key) self.preset_name = None self.populate_presets() reader.start_reading(groups.find(key=group_key)) self.show_device_mapping_status()
def test_change_device(self): push_events('Foo Device 2', [ new_event(EV_KEY, 1, 1), ] * 100) push_events('Bar Device', [ new_event(EV_KEY, 2, 1), ] * 100) self.create_helper() reader.start_reading(groups.find(key='Foo Device 2')) time.sleep(0.1) self.assertEqual(reader.read(), Key(EV_KEY, 1, 1)) reader.start_reading(groups.find(name='Bar Device')) # it's plausible that right after sending the new read command more # events from the old device might still appear. Give the helper # some time to handle the new command. time.sleep(0.1) reader.clear() time.sleep(0.1) self.assertEqual(reader.read(), Key(EV_KEY, 2, 1))
def test_refresh_on_start(self): if os.path.exists(get_config_path('xmodmap.json')): os.remove(get_config_path('xmodmap.json')) ev = (EV_KEY, 9) keycode_to = 100 group_name = '9876 name' # expected key of the group group_key = group_name group = groups.find(name=group_name) # this test only makes sense if this device is unknown yet self.assertIsNone(group) custom_mapping.change(Key(*ev, 1), 'a') system_mapping.clear() system_mapping._set('a', keycode_to) # make the daemon load the file instead with open(get_config_path('xmodmap.json'), 'w') as file: json.dump(system_mapping._mapping, file, indent=4) system_mapping.clear() preset = 'foo' custom_mapping.save(get_preset_path(group_name, preset)) config.set_autoload_preset(group_key, preset) push_events(group_key, [new_event(*ev, 1)]) self.daemon = Daemon() # make sure the devices are populated groups.refresh() # the daemon is supposed to find this device by calling refresh fixtures[self.new_fixture_path] = { 'capabilities': { evdev.ecodes.EV_KEY: [ev[1]] }, 'phys': '9876 phys', 'info': evdev.device.DeviceInfo(4, 5, 6, 7), 'name': group_name } self.daemon.set_config_dir(get_config_path()) self.daemon.start_injecting(group_key, preset) # test if the injector called groups.refresh successfully group = groups.find(key=group_key) self.assertEqual(group.name, group_name) self.assertEqual(group.key, group_key) time.sleep(0.1) self.assertTrue(uinput_write_history_pipe[0].poll()) event = uinput_write_history_pipe[0].recv() self.assertEqual(event.t, (EV_KEY, keycode_to, 1)) self.daemon.stop_injecting(group_key) self.assertEqual(self.daemon.get_state(group_key), STOPPED)
def test_skip_camera(self): fixtures['/foo/bar'] = { 'name': 'camera', 'phys': 'abcd1', 'info': evdev.DeviceInfo(1, 2, 3, 4), 'capabilities': { evdev.ecodes.EV_KEY: [evdev.ecodes.KEY_CAMERA] } } groups.refresh() self.assertIsNone(groups.find(name='camera')) self.assertIsNotNone(groups.find(name='gamepad'))
def test_clear(self): push_events('Foo Device 2', [ new_event(EV_KEY, CODE_1, 1), new_event(EV_KEY, CODE_2, 1), new_event(EV_KEY, CODE_3, 1) ] * 15) self.create_helper() reader.start_reading(groups.find(key='Foo Device 2')) time.sleep(START_READING_DELAY + EVENT_READ_TIMEOUT * 3) reader.read() self.assertEqual(len(reader._unreleased), 3) self.assertIsNotNone(reader.previous_event) self.assertIsNotNone(reader.previous_result) # make the helper send more events to the reader time.sleep(EVENT_READ_TIMEOUT * 2) self.assertTrue(reader._results.poll()) reader.clear() self.assertFalse(reader._results.poll()) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 0) self.assertIsNone(reader.get_unreleased_keys()) self.assertIsNone(reader.previous_event) self.assertIsNone(reader.previous_result) self.tearDown()
def test_combine_triggers(self): reader.start_reading(groups.find(key='Foo Device 2')) i = 0 def next_timestamp(): nonlocal i i += 1 return time.time() + i # based on an observed bug send_event_to_reader(new_event(3, 1, 0, next_timestamp())) send_event_to_reader(new_event(3, 0, 0, next_timestamp())) send_event_to_reader(new_event(3, 2, 1, next_timestamp())) self.assertEqual(reader.read(), (EV_ABS, ABS_Z, 1)) send_event_to_reader(new_event(3, 0, 0, next_timestamp())) send_event_to_reader(new_event(3, 5, 1, next_timestamp())) self.assertEqual(reader.read(), ((EV_ABS, ABS_Z, 1), (EV_ABS, ABS_RZ, 1))) send_event_to_reader(new_event(3, 5, 0, next_timestamp())) send_event_to_reader(new_event(3, 0, 0, next_timestamp())) send_event_to_reader(new_event(3, 1, 0, next_timestamp())) self.assertEqual(reader.read(), None) send_event_to_reader(new_event(3, 2, 1, next_timestamp())) send_event_to_reader(new_event(3, 1, 0, next_timestamp())) send_event_to_reader(new_event(3, 0, 0, next_timestamp())) # due to not properly handling the duplicate down event it cleared # the combination and returned it. Instead it should report None # and by doing that keep the previous combination. self.assertEqual(reader.read(), None)
def test_store_permutations_for_macros(self): mapping = Mapping() ev_1 = (EV_KEY, 41, 1) ev_2 = (EV_KEY, 42, 1) ev_3 = (EV_KEY, 43, 1) # a combination mapping.change(Key(ev_1, ev_2, ev_3), 'k(a)') self.injector = Injector(groups.find(key='Foo Device 2'), mapping) history = [] class Stop(Exception): pass def _construct_capabilities(*args): history.append(args) # avoid going into any mainloop raise Stop() with mock.patch.object(self.injector, '_construct_capabilities', _construct_capabilities): try: self.injector.run() except Stop: pass # one call self.assertEqual(len(history), 1) # first argument of the first call macros = self.injector.context.macros self.assertEqual(len(macros), 2) self.assertEqual(macros[(ev_1, ev_2, ev_3)].code, 'k(a)') self.assertEqual(macros[(ev_2, ev_1, ev_3)].code, 'k(a)')
def test_gamepad_forward_joysticks(self): push_events( 'gamepad', [ # should forward them unmodified new_event(EV_ABS, ABS_X, 10), new_event(EV_ABS, ABS_Y, 20), new_event(EV_ABS, ABS_X, -30), new_event(EV_ABS, ABS_Y, -40), new_event(EV_KEY, BTN_A, 1), new_event(EV_KEY, BTN_A, 0) ] * 2) custom_mapping.set('gamepad.joystick.left_purpose', NONE) custom_mapping.set('gamepad.joystick.right_purpose', NONE) # BTN_A -> 77 custom_mapping.change(Key((1, BTN_A, 1)), 'b') system_mapping._set('b', 77) self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.start() # wait for the injector to start sending, at most 1s uinput_write_history_pipe[0].poll(1) time.sleep(0.2) # convert the write history to some easier to manage list history = read_write_history_pipe() self.assertEqual(history.count((EV_ABS, ABS_X, 10)), 2) self.assertEqual(history.count((EV_ABS, ABS_Y, 20)), 2) self.assertEqual(history.count((EV_ABS, ABS_X, -30)), 2) self.assertEqual(history.count((EV_ABS, ABS_Y, -40)), 2) self.assertEqual(history.count((EV_KEY, 77, 1)), 2) self.assertEqual(history.count((EV_KEY, 77, 0)), 2)
def test_reading_2(self): # a combination of events push_events('Foo Device 2', [ new_event(EV_KEY, CODE_1, 1, 10000.1234), new_event(EV_KEY, CODE_3, 1, 10001.1234), new_event(EV_ABS, ABS_HAT0X, -1, 10002.1234) ]) pipe = multiprocessing.Pipe() def refresh(): # from within the helper process notify this test that # refresh was called as expected pipe[1].send('refreshed') with mock.patch.object(groups, 'refresh', refresh): self.create_helper() reader.start_reading(groups.find(key='Foo Device 2')) # sending anything arbitrary does not stop the helper reader._commands.send(856794) time.sleep(0.2) # but it makes it look for new devices because maybe its list of # groups is not up-to-date self.assertTrue(pipe[0].poll()) self.assertEqual(pipe[0].recv(), 'refreshed') self.assertEqual(reader.read(), ( (EV_KEY, CODE_1, 1), (EV_KEY, CODE_3, 1), (EV_ABS, ABS_HAT0X, -1) )) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 3)
def test_start_stop(self): group = groups.find(key='Foo Device 2') preset = 'preset9' daemon = Daemon() start_history = [] stop_history = [] stop_all_history = [] daemon.start_injecting = lambda *args: start_history.append(args) daemon.stop_injecting = lambda *args: stop_history.append(args) daemon.stop_all = lambda *args: stop_all_history.append(args) communicate( options('start', None, preset, group.paths[0], False, False, False), daemon) self.assertEqual(len(start_history), 1) self.assertEqual(start_history[0], (group.key, preset)) communicate( options('stop', None, None, group.paths[1], False, False, False), daemon) self.assertEqual(len(stop_history), 1) # provided any of the groups paths as --device argument, figures out # the correct group.key to use here self.assertEqual(stop_history[0], (group.key, )) communicate(options('stop-all', None, None, None, False, False, False), daemon) self.assertEqual(len(stop_all_history), 1) self.assertEqual(stop_all_history[0], ())
def test_gamepad_trigger(self): # map one of the triggers to BTN_NORTH, while the other one # should be forwarded unchanged value = MAX_ABS // 2 push_events('gamepad', [ new_event(EV_ABS, ABS_Z, value), new_event(EV_ABS, ABS_RZ, value), ]) # ABS_Z -> 77 # ABS_RZ is not mapped custom_mapping.change(Key((EV_ABS, ABS_Z, 1)), 'b') system_mapping._set('b', 77) self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.start() # wait for the injector to start sending, at most 1s uinput_write_history_pipe[0].poll(1) time.sleep(0.2) # convert the write history to some easier to manage list history = read_write_history_pipe() self.assertEqual(history.count((EV_KEY, 77, 1)), 1) self.assertEqual(history.count((EV_ABS, ABS_RZ, value)), 1)
def test_gamepad_to_buttons_event_producer(self): custom_mapping.set('gamepad.joystick.left_purpose', BUTTONS) custom_mapping.set('gamepad.joystick.right_purpose', BUTTONS) self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.stop_injecting() self.injector.run() self.assertIsNone(self.injector._event_producer.abs_range)
def do_stuff(): if self.injector is not None: # discard the previous injector self.injector.stop_injecting() time.sleep(0.1) while uinput_write_history_pipe[0].poll(): uinput_write_history_pipe[0].recv() push_events('gamepad', [ new_event(*w_down), new_event(*d_down), new_event(*w_up), new_event(*d_up), ]) self.injector = Injector(groups.find(name='gamepad'), custom_mapping) # the injector will otherwise skip the device because # the capabilities don't contain EV_TYPE input = InputDevice('/dev/input/event30') self.injector._grab_device = lambda *args: input self.injector.start() uinput_write_history_pipe[0].poll(timeout=1) time.sleep(EVENT_READ_TIMEOUT * 10) return read_write_history_pipe()
def test_gamepad_purpose_none_2(self): # forward abs joystick events for the left joystick only custom_mapping.set('gamepad.joystick.left_purpose', NONE) config.set('gamepad.joystick.right_purpose', MOUSE) self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.context = Context(custom_mapping) path = '/dev/input/event30' device = self.injector._grab_device(path) # the right joystick maps as mouse, so it is grabbed # even with an empty mapping self.assertIsNotNone(device) gamepad = classify(device) == GAMEPAD self.assertTrue(gamepad) capabilities = self.injector._construct_capabilities(gamepad) self.assertNotIn(EV_ABS, capabilities) self.assertIn(EV_REL, capabilities) custom_mapping.change(Key(EV_KEY, BTN_A, 1), 'a') device = self.injector._grab_device(path) gamepad = classify(device) == GAMEPAD self.assertIsNotNone(device) self.assertTrue(gamepad) capabilities = self.injector._construct_capabilities(gamepad) self.assertNotIn(EV_ABS, capabilities) self.assertIn(EV_REL, capabilities) self.assertIn(EV_KEY, capabilities)
def test_autoload_other_path(self): device_names = ['Foo Device', 'Bar Device'] groups_ = [groups.find(name=name) for name in device_names] presets = ['bar123', 'bar2'] config_dir = os.path.join(tmp, 'qux', 'quux') paths = [ os.path.join(config_dir, 'presets', device_names[0], presets[0] + '.json'), os.path.join(config_dir, 'presets', device_names[1], presets[1] + '.json') ] Mapping().save(paths[0]) Mapping().save(paths[1]) daemon = Daemon() start_history = [] daemon.start_injecting = lambda *args: start_history.append(args) config.path = os.path.join(config_dir, 'config.json') config.load_config() config.set_autoload_preset(device_names[0], presets[0]) config.set_autoload_preset(device_names[1], presets[1]) config.save_config() communicate( options('autoload', config_dir, None, None, False, False, False), daemon) self.assertEqual(len(start_history), 2) self.assertEqual(start_history[0], (groups_[0].key, presets[0])) self.assertEqual(start_history[1], (groups_[1].key, presets[1]))
def select_newest_preset(self): """Find and select the newest preset (and its device).""" device, preset = find_newest_preset() group = groups.find(name=device) if device is not None: self.get('device_selection').set_active_id(group.key) if preset is not None: self.get('preset_selection').set_active_id(preset)
def test_autoload(self): preset = 'preset7' group = groups.find(key='Foo Device 2') daemon = Daemon() self.daemon = daemon self.daemon.set_config_dir(get_config_path()) mapping = Mapping() mapping.change(Key(3, 2, 1), 'a') mapping.save(group.get_preset_path(preset)) # no autoloading is configured yet self.daemon._autoload(group.key) self.assertNotIn(group.key, daemon.autoload_history._autoload_history) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) config.set_autoload_preset(group.key, preset) config.save_config() self.daemon.set_config_dir(get_config_path()) len_before = len(self.daemon.autoload_history._autoload_history) # now autoloading is configured, so it will autoload self.daemon._autoload(group.key) len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual( daemon.autoload_history._autoload_history[group.key][1], preset) self.assertFalse( daemon.autoload_history.may_autoload(group.key, preset)) injector = daemon.injectors[group.key] self.assertEqual(len_before + 1, len_after) # calling duplicate _autoload does nothing self.daemon._autoload(group.key) self.assertEqual( daemon.autoload_history._autoload_history[group.key][1], preset) self.assertEqual(injector, daemon.injectors[group.key]) self.assertFalse( daemon.autoload_history.may_autoload(group.key, preset)) # explicit start_injecting clears the autoload history self.daemon.start_injecting(group.key, preset) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) # calling autoload for (yet) unknown devices does nothing len_before = len(self.daemon.autoload_history._autoload_history) self.daemon._autoload('unknown-key-1234') len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual(len_before, len_after) # autoloading key-mapper devices does nothing len_before = len(self.daemon.autoload_history._autoload_history) self.daemon.autoload_single('Bar Device') len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual(len_before, len_after)
def test_device1_event_producer(self): custom_mapping.set('gamepad.joystick.left_purpose', MOUSE) custom_mapping.set('gamepad.joystick.right_purpose', WHEEL) self.injector = Injector(groups.find(key='Foo Device 2'), custom_mapping) self.injector.stop_injecting() self.injector.run() # not a gamepad, so _event_producer is not initialized for that. # it can still debounce stuff though self.assertIsNone(self.injector._event_producer.abs_range)
def test_duplicate_device(self): fixtures['/dev/input/event20']['name'] = 'Foo Device' groups.refresh() group1 = groups.find(key='Foo Device') group2 = groups.find(key='Foo Device 2') group3 = groups.find(key='Foo Device 3') self.assertIn('/dev/input/event1', group1.paths) self.assertIn('/dev/input/event10', group2.paths) self.assertIn('/dev/input/event20', group3.paths) self.assertEqual(group1.key, 'Foo Device') self.assertEqual(group2.key, 'Foo Device 2') self.assertEqual(group3.key, 'Foo Device 3') self.assertEqual(group1.name, 'Foo Device') self.assertEqual(group2.name, 'Foo Device') self.assertEqual(group3.name, 'Foo Device')
def test_skip_unused_device(self): # skips a device because its capabilities are not used in the mapping custom_mapping.change(Key(EV_KEY, 10, 1), 'a') self.injector = Injector(groups.find(key='Foo Device 2'), custom_mapping) self.injector.context = Context(custom_mapping) path = '/dev/input/event11' device = self.injector._grab_device(path) self.assertIsNone(device) self.assertEqual(self.failed, 0)
def test_reading_1(self): # a single event push_events('Foo Device 2', [new_event(EV_ABS, ABS_HAT0X, 1)]) push_events('Foo Device 2', [new_event(EV_ABS, REL_X, 1)]) # mouse movements are ignored self.create_helper() reader.start_reading(groups.find(key='Foo Device 2')) time.sleep(0.2) self.assertEqual(reader.read(), (EV_ABS, ABS_HAT0X, 1)) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 1)
def test_wrong_device(self): push_events('Foo Device 2', [ new_event(EV_KEY, CODE_1, 1), new_event(EV_KEY, CODE_2, 1), new_event(EV_KEY, CODE_3, 1) ]) self.create_helper() reader.start_reading(groups.find(name='Bar Device')) time.sleep(EVENT_READ_TIMEOUT * 5) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 0)
def test_grab_device_1(self): custom_mapping.change(Key(EV_ABS, ABS_HAT0X, 1), 'a') self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.context = Context(custom_mapping) _grab_device = self.injector._grab_device # doesn't have the required capability self.assertIsNone(_grab_device('/dev/input/event10')) # according to the fixtures, /dev/input/event30 can do ABS_HAT0X self.assertIsNotNone(_grab_device('/dev/input/event30')) # this doesn't exist self.assertIsNone(_grab_device('/dev/input/event1234'))
def test_switch_device(self): push_events('Bar Device', [new_event(EV_KEY, CODE_1, 1)]) push_events('Foo Device 2', [new_event(EV_KEY, CODE_3, 1)]) self.create_helper() reader.start_reading(groups.find(name='Bar Device')) self.assertFalse(reader._results.poll()) self.assertEqual(reader.group.name, 'Bar Device') time.sleep(EVENT_READ_TIMEOUT * 5) self.assertTrue(reader._results.poll()) reader.start_reading(groups.find(key='Foo Device 2')) self.assertEqual(reader.group.name, 'Foo Device') self.assertFalse(reader._results.poll()) # pipe resets time.sleep(EVENT_READ_TIMEOUT * 5) self.assertTrue(reader._results.poll()) self.assertEqual(reader.read(), (EV_KEY, CODE_3, 1)) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 1)
def test_capabilities_and_uinput_presence(self, ungrab_patch): custom_mapping.change(Key(EV_KEY, KEY_A, 1), 'c') custom_mapping.change(Key(EV_REL, REL_HWHEEL, 1), 'k(b)') self.injector = Injector(groups.find(key='Foo Device 2'), custom_mapping) self.injector.stop_injecting() self.injector.run() self.assertEqual( self.injector.context.mapping.get_symbol(Key(EV_KEY, KEY_A, 1)), 'c') self.assertEqual( self.injector.context.key_to_code[((EV_KEY, KEY_A, 1), )], KEY_C) self.assertEqual( self.injector.context.mapping.get_symbol(Key( EV_REL, REL_HWHEEL, 1)), 'k(b)') self.assertEqual( self.injector.context.macros[((EV_REL, REL_HWHEEL, 1), )].code, 'k(b)') self.assertListEqual( sorted(uinputs.keys()), sorted([ # reading and preventing original events from reaching the # display server 'key-mapper Foo Device foo forwarded', 'key-mapper Foo Device forwarded', # injection 'key-mapper Foo Device 2 mapped' ])) forwarded_foo = uinputs.get('key-mapper Foo Device foo forwarded') forwarded = uinputs.get('key-mapper Foo Device forwarded') mapped = uinputs.get('key-mapper Foo Device 2 mapped') self.assertIsNotNone(forwarded_foo) self.assertIsNotNone(forwarded) self.assertIsNotNone(mapped) # puts the needed capabilities into the new key-mapper device self.assertIn(EV_KEY, mapped.capabilities()) self.assertEqual(len(mapped.capabilities()[EV_KEY]), 2) self.assertIn(KEY_C, mapped.capabilities()[EV_KEY]) self.assertIn(KEY_B, mapped.capabilities()[EV_KEY]) # not a gamepad that maps joysticks to mouse movements self.assertNotIn(EV_REL, mapped.capabilities()) # copies capabilities for all other forwarded devices self.assertIn(EV_REL, forwarded_foo.capabilities()) self.assertIn(EV_KEY, forwarded.capabilities()) self.assertEqual(sorted(forwarded.capabilities()[EV_KEY]), keyboard_keys) self.assertEqual(ungrab_patch.call_count, 2)
def test_device_with_only_ev_abs(self): # could be anything, a lot of devices have ABS_X capabilities, # so it is not treated as gamepad joystick and since it also # doesn't have key capabilities, there is nothing to map. fixtures['/foo/bar'] = { 'name': 'qux', 'phys': 'abcd2', 'info': evdev.DeviceInfo(1, 2, 3, 4), 'capabilities': { evdev.ecodes.EV_ABS: [evdev.ecodes.ABS_X] } } groups.refresh() self.assertIsNotNone(groups.find(name='gamepad')) self.assertIsNone(groups.find(name='qux')) # verify this test even works at all fixtures['/foo/bar']['capabilities'][EV_KEY] = [KEY_A] groups.refresh() self.assertIsNotNone(groups.find(name='qux'))
def test_reading_ignore_up(self): push_events('Foo Device 2', [ new_event(EV_KEY, CODE_1, 0, 10), new_event(EV_KEY, CODE_2, 1, 11), new_event(EV_KEY, CODE_3, 0, 12), ]) self.create_helper() reader.start_reading(groups.find(key='Foo Device 2')) time.sleep(0.1) self.assertEqual(reader.read(), (EV_KEY, CODE_2, 1)) self.assertEqual(reader.read(), None) self.assertEqual(len(reader._unreleased), 1)