require("awful._compat") local runner = require( "_runner" ) local placement = require( "awful.placement" ) local gtable = require( "gears.table" ) local test_client = require( "_client" ) local akeyboard = require( "awful.keyboard" ) local amouse = require( "awful.mouse" ) local module = { key = require( "awful.key" ), button = require( "awful.button" ) } local steps = {} local second = { key = { "a", "b", "c", "d" }, button = { 1 , 2 , 3 , 4 }, } local function send_events(event_type, step) root.fake_input("key_press" , "Alt_L") root.fake_input(event_type.."_press" , second[event_type][step]) root.fake_input(event_type.."_release", second[event_type][step]) root.fake_input("key_release", "Alt_L") end -- Test the shared API between the keys and buttons for _, type_name in ipairs { "key", "button" } do local objects = {} local pressed, released = {}, {} table.insert(steps, function() objects[1] = module[type_name]( {"Mod1"}, second[type_name][1], function() pressed[1] = true end, function() released[1] = true end ) assert(objects[1].has_root_binding == false) assert(not pressed [1]) assert(not released[1]) root["_append_"..type_name](objects[1]) -- It is async, the result need to be verified later return true end) table.insert(steps, function() assert(objects[1].has_root_binding) -- Test "artificial" execution objects[1]:trigger() assert(pressed [1]) assert(released[1]) -- Test adding and removing in the same iteration root["_remove_"..type_name](objects[1]) assert(second[type_name][2]) -- Use the multiple parameters syntax. objects[2] = module[type_name]( {"Mod1"}, second[type_name][2], function() pressed[2] = true end, function() released[2] = true end ) root["_append_"..type_name](objects[2]) return true end) table.insert(steps, function() assert(objects[2].has_root_binding == true ) assert(objects[1].has_root_binding == false) -- Make sure the cursor is not over the wibar placement.centered(mouse) assert(second[type_name][2]) send_events(type_name, 2) return true end) -- Wait until the events are registered. table.insert(steps, function() if (not pressed[2]) or (not released[2]) then return end -- Spawn a client. test_client("myclient") return true end) --FIXME it works when manually tested, but automated testing is too flacky -- to enable... if type_name == "key" then table.insert(steps, function() if #mouse.screen.clients ~= 1 then return end placement.maximize(mouse.screen.clients[1]) return true end) local o1, o2 = nil table.insert(steps, function() local c = mouse.screen.clients[1] -- This time, use the `args` syntax. local args = { modifiers = {"Mod1"}, on_press = function() pressed [3] = true end, on_release = function() released[3] = true end } args[type_name] = second[type_name][3] -- This time, use the `args` syntax. o1 = module[type_name](args) -- Test the old API. c[type_name.."s"](c, gtable.join(o1)) return true end) -- This wont work until the client buttons/keys are updated, there is no -- way to know ahead of time. table.insert(steps, function(count) if count < 5 then awesome.sync(); return end send_events(type_name, 3) return true end) table.insert(steps, function() if (not pressed[3]) or (not released[3]) then return end local c = mouse.screen.clients[1] -- Unset the events to make sure keys can be removed. pressed[3], released[3] = false, false o2 = module[type_name]( {"Mod1"}, second[type_name][4], function() pressed[4] = true end, function() released[4] = true end ) -- Test the new API c[type_name.."s"] = {o2} return true end) table.insert(steps, function(count) if count < 5 then awesome.sync(); return end send_events(type_name, 3) send_events(type_name, 4) return true end) table.insert(steps, function() if (not pressed[4]) or (not released[4]) then return end assert(not pressed [3]) assert(not released[3]) local c = mouse.screen.clients[1] -- Make sure mixing the 2 syntaxes doesn't create a chimera state pressed[4], released[4] = false, false -- This *will* happen with older configs because `awful.rules` will -- loop the properties, find the old capi list and fail to understand -- that the content should use the legacy API. local joined = gtable.join(o1) assert(#joined == 4) c[type_name.."s"] = joined -- It should have been converted to the new format. assert(#c[type_name.."s"] == 1) return true end) table.insert(steps, function(count) if count < 5 then awesome.sync(); return end send_events(type_name, 3) send_events(type_name, 4) return true end) table.insert(steps, function() if (not pressed[3]) or (not released[3]) then return end assert(not pressed [4]) assert(not released[4]) local c = mouse.screen.clients[1] assert(#c[type_name.."s"] == 1) -- Test setting the object to `false` to simulate an inline Lua -- expression gone wrong. This used to work and is rather -- convenient, so lets not break it even if it is technically a bug. c[type_name.."s"] = false assert(#c[type_name.."s"] == 0) c[type_name.."s"] = {o1} assert(#c[type_name.."s"] == 1) -- Test removing the objects using `nil`. c[type_name.."s"] = nil assert(#c[type_name.."s"] == 0) c[type_name.."s"] = {o1} assert(#c[type_name.."s"] == 1) -- Test removing using `{}` c[type_name.."s"] = {} assert(#c[type_name.."s"] == 0) c:kill() return true end) -- Cleanup (otherwise there is a race with the root.buttons tests) table.insert(steps, function(count) if #mouse.screen.clients ~= 0 then if count > 5 then -- It's stuck, kill it. test_client.terminate() else for _, c in pairs(client.get()) do c:kill() end end return end return true end) end end local exe1, exe2, exe3, exe4 = false, false, false, false local new1, new2 = nil, nil -- Check that you can add new default key/mousebindings at any time. table.insert(steps, function() if #mouse.screen.clients > 0 then return end new1 = module.key { key = "a", modifiers = {}, on_press = function() exe1 = true end } new2 = module.button { button = 8, modifiers = {}, on_press = function() exe2 = true end } akeyboard.append_client_keybinding(new1) amouse.append_client_mousebinding(new2) -- Spawn a client. test_client("myclient") return true end) table.insert(steps, function() if #mouse.screen.clients == 0 then return end client.focus = mouse.screen.clients[1] -- That should trigger the newly added keybinding. root.fake_input("key_press" , "a") root.fake_input("key_release", "a") -- Move the mouse over the client so mousebindings work. placement.centered(mouse, {parent=client.focus}) awesome.sync() -- Same thing for the mouse. root.fake_input("button_press" , 8) root.fake_input("button_release", 8) return true end) table.insert(steps, function() if (not exe1) or (not exe2) then return end akeyboard.remove_client_keybinding(new1) amouse.remove_client_mousebinding(new2) exe1, exe2 = false, false return true end) -- Removing is async, so wait until the next loop. table.insert(steps, function() root.fake_input("key_press" , "a") root.fake_input("key_release", "a") root.fake_input("button_press" , 8) root.fake_input("button_release", 8) awesome.sync() return true end) -- Try adding key/mousebindings to existing clients. table.insert(steps, function(count) if count ~= 3 then return end assert(not exe1) assert(not exe2) -- Append both types at the same time to make sure nothing overwrite -- the list. akeyboard.append_client_keybinding(new1) amouse.append_client_mousebinding(new2) new1 = module.key { key = "b", modifiers = {}, on_press = function() exe3 = true end } new2 = module.button { button = 7, modifiers = {}, on_press = function() exe4 = true end } -- Append directly on the client. client.focus:append_keybinding(new1) client.focus:append_mousebinding(new2) return true end) -- Removing is async, so wait until the next loop. table.insert(steps, function() root.fake_input("key_press" , "b") root.fake_input("key_release", "b") root.fake_input("button_press" , 7) root.fake_input("button_release", 7) return true end) table.insert(steps, function() if (not exe3) or (not exe4) then return end -- Note, there is no point to try remove_keybinding, it was already called -- (indirectly) 4 steps ago. return true end) table.insert(steps, test_client.terminate) runner.run_steps(steps) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80