1156 lines
32 KiB
Lua
1156 lines
32 KiB
Lua
-- This test suite tries to prevent the legacy notification popups from
|
|
-- regressing as the new notification API is improving.
|
|
local spawn = require("awful.spawn")
|
|
local naughty = require("naughty" )
|
|
local gdebug = require("gears.debug")
|
|
local gtable = require("gears.table")
|
|
local cairo = require("lgi" ).cairo
|
|
local beautiful = require("beautiful")
|
|
local Gio = require("lgi" ).Gio
|
|
local GLib = require("lgi" ).GLib
|
|
local gpcall = require("gears.protected_call")
|
|
local dwidget = require("naughty.widget._default")
|
|
|
|
-- Bypass the new rc.lua and force the legacy mode again.
|
|
naughty._reset_display_handlers()
|
|
|
|
-- This module test deprecated APIs
|
|
require("gears.debug").deprecate = function() end
|
|
|
|
local dbus_connection = assert(Gio.bus_get_sync(Gio.BusType.SESSION))
|
|
|
|
local function send_notify(app, id, icon, summary, body, actions, hints, timeout, callback)
|
|
local parameters = GLib.Variant("(susssasa{sv}i)", {
|
|
app, id, icon, summary, body, actions, hints, timeout
|
|
})
|
|
local reply_type = GLib.VariantType.new("(u)")
|
|
|
|
local function invoke_callback(conn, result)
|
|
local new_id = conn:call_finish(result).value[1]
|
|
if callback then
|
|
callback(new_id)
|
|
end
|
|
end
|
|
dbus_connection:call("org.freedesktop.Notifications",
|
|
"/org/freedesktop/Notifications", "org.freedesktop.Notifications",
|
|
"Notify", parameters, reply_type, Gio.DBusCallFlags.NO_AUTO_START, -1,
|
|
nil, invoke_callback)
|
|
end
|
|
|
|
local steps = {}
|
|
|
|
local has_cmd_notify = false
|
|
|
|
-- Use `notify-send` instead of the shimmed version to better test the dbus
|
|
-- to notification code.
|
|
local function check_cmd()
|
|
local path = os.getenv("PATH")
|
|
local pos = 1
|
|
while path:find(":", pos) do
|
|
local np = path:find(":", pos)
|
|
local p = path:sub(pos, np-1).."/"
|
|
|
|
pos = np+1
|
|
|
|
local f = io.open(p.."notify-send")
|
|
if f then
|
|
f:close()
|
|
has_cmd_notify = true
|
|
end
|
|
|
|
if has_cmd_notify then return end
|
|
end
|
|
end
|
|
|
|
check_cmd()
|
|
|
|
if not has_cmd_notify then
|
|
require("gears.debug").print_warning("Did not find notify-send, skipping some tests")
|
|
end
|
|
|
|
local active, destroyed, reasons, added_counter = {}, {}, {}, 0
|
|
|
|
local default_width, default_height = 0, 0
|
|
|
|
local function added_callback(n)
|
|
table.insert(active, n)
|
|
added_counter = added_counter + 1
|
|
end
|
|
naughty.connect_signal("added", added_callback)
|
|
|
|
local function destroyed_callback(n, reason)
|
|
local found = false
|
|
|
|
for k, n2 in ipairs(active) do
|
|
if n2 == n then
|
|
found = true
|
|
table.remove(active, k)
|
|
end
|
|
end
|
|
|
|
assert(found)
|
|
|
|
if reason then
|
|
reasons[reason] = reasons[reason] and reasons[reason] + 1 or 1
|
|
end
|
|
|
|
table.insert(destroyed, n)
|
|
end
|
|
naughty.connect_signal("destroyed", destroyed_callback)
|
|
|
|
-- Test vertical overlapping
|
|
local function test_overlap()
|
|
local wa = mouse.screen.workarea
|
|
|
|
for _, n1 in ipairs(active) do
|
|
assert(n1.box)
|
|
assert(n1.id)
|
|
|
|
-- Check for overlapping the workarea
|
|
assert(n1.box.y+default_height < wa.y+wa.height)
|
|
assert(n1.box.y >= wa.y)
|
|
|
|
-- Check for overlapping each other
|
|
for _, n2 in ipairs(active) do
|
|
assert(n2.box)
|
|
if n1 ~= n2 then
|
|
local geo1, geo2 = n1.box:geometry(), n2.box:geometry()
|
|
assert(geo1.height == geo2.height)
|
|
assert(geo1.height + 2*n1.box.border_width + naughty.config.spacing
|
|
== default_height)
|
|
|
|
if n1.position == n2.position then
|
|
assert(
|
|
geo1.y >= geo2.y+default_height or
|
|
geo2.y >= geo1.y+default_height
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Test default size, and title/message.
|
|
table.insert(steps, function()
|
|
|
|
local n = naughty.notification {
|
|
title = "title",
|
|
message = "message"
|
|
}
|
|
|
|
assert(n.box)
|
|
local offset = 2*n.box.border_width
|
|
default_width = n.box.width+offset
|
|
default_height = n.box.height + offset + naughty.config.spacing
|
|
|
|
assert(default_width > 0)
|
|
assert(default_height > 0)
|
|
|
|
-- Test title/message behavior.
|
|
assert(n.textbox:get_markup() == "<b>title</b>\nmessage", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "title\nmessage", n.textbox:get_text())
|
|
|
|
n.title = nil
|
|
assert(n.textbox:get_markup() == "message", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "message", n.textbox:get_text())
|
|
n.title = ""
|
|
assert(n.textbox:get_markup() == "message", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "message", n.textbox:get_text())
|
|
|
|
n.message = "<i>only</i> message"
|
|
assert(n.textbox:get_markup() == "<i>only</i> message", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "only message", n.textbox:get_text())
|
|
|
|
n.message = nil
|
|
assert(n.textbox:get_markup() == "", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "", n.textbox:get_text())
|
|
n.message = ""
|
|
assert(n.textbox:get_markup() == "", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "", n.textbox:get_text())
|
|
|
|
-- Only title (with markup).
|
|
n.title = "<i>only</i> title"
|
|
assert(n.textbox:get_markup() == "<b><i>only</i> title</b>", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "<i>only</i> title", n.textbox:get_text())
|
|
|
|
-- Markup with only interpreting "<br>".
|
|
n.title = ""
|
|
n.message = "<3"
|
|
assert(n.textbox:get_markup() == "<3", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == "<3", n.textbox:get_text())
|
|
n.title = ">3"
|
|
assert(n.textbox:get_markup() == "<b>>3</b>\n<3", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == ">3\n<3", n.textbox:get_text())
|
|
n.message = nil
|
|
assert(n.textbox:get_markup() == "<b>>3</b>", n.textbox:get_markup())
|
|
assert(n.textbox:get_text() == ">3", n.textbox:get_text())
|
|
|
|
-- Invalid utf8.
|
|
local invalid_title = "title" .. string.char(0x80) .. "X"
|
|
local invalid_message = "message" .. string.char(0x80) .. "X"
|
|
n.title = invalid_title
|
|
n.message = invalid_message
|
|
assert(n.textbox:get_markup() == nil, n.textbox:get_markup())
|
|
local expected_text = invalid_title .. "\n" .. invalid_message
|
|
expected_text = expected_text:gsub(string.char(0x80), string.char(0xff))
|
|
assert(n.textbox:get_text() == expected_text, n.textbox:get_text())
|
|
|
|
n:destroy()
|
|
|
|
assert(added_counter == 1)
|
|
added_counter = 0
|
|
|
|
return true
|
|
end)
|
|
|
|
|
|
if has_cmd_notify then
|
|
table.insert(steps, function()
|
|
spawn{ 'notify-send', 'title', 'message', '-t', '25000' }
|
|
|
|
return true
|
|
end)
|
|
|
|
table.insert(steps, function()
|
|
if #active ~= 1 then return end
|
|
|
|
local n = active[1]
|
|
|
|
assert(n.box)
|
|
|
|
-- Make sure the expiration timer is started
|
|
assert(n.timer)
|
|
assert(n.timer.started)
|
|
assert(n.is_expired == false)
|
|
|
|
n:destroy()
|
|
|
|
assert(#active == 0)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test pausing incoming notifications.
|
|
table.insert(steps, function()
|
|
assert(not naughty.suspended)
|
|
|
|
naughty.suspended = true
|
|
|
|
-- There is some magic behind this, check it works
|
|
assert(naughty.suspended)
|
|
|
|
spawn{ 'notify-send', 'title', 'message', '-t', '25000' }
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test resuming incoming notifications.
|
|
table.insert(steps, function(count)
|
|
if count ~= 4 then return end
|
|
|
|
assert(#active == 0)
|
|
assert(#naughty.notifications.suspended == 1)
|
|
assert(naughty.notifications.suspended[1]:get_suspended())
|
|
|
|
naughty.resume()
|
|
|
|
assert(not naughty.suspended)
|
|
assert(#naughty.notifications.suspended == 0)
|
|
assert(#active == 1)
|
|
|
|
active[1]:destroy()
|
|
assert(#active == 0)
|
|
|
|
spawn{ 'notify-send', 'title', 'message', '-t', '1' }
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test automatic expiration.
|
|
table.insert(steps, function()
|
|
if added_counter ~= 3 then return end
|
|
|
|
return true
|
|
end)
|
|
|
|
table.insert(steps, function()
|
|
if #active > 0 then return end
|
|
|
|
-- It expired after one milliseconds, so it should be gone as soon as
|
|
-- it is registered.
|
|
assert(#active == 0)
|
|
|
|
assert(not naughty.expiration_paused)
|
|
naughty.expiration_paused = true
|
|
|
|
-- There is some magic behind this, make sure it works
|
|
assert(naughty.expiration_paused)
|
|
|
|
spawn{ 'notify-send', 'title', 'message', '-t', '1' }
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test disabling automatic expiration.
|
|
table.insert(steps, function()
|
|
if added_counter ~= 4 then return end
|
|
|
|
-- It should not expire by itself, so that should always be true
|
|
assert(#active == 1)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Wait long enough to avoid races.
|
|
table.insert(steps, function(count)
|
|
if count ~= 4 then return end
|
|
|
|
assert(#active == 1)
|
|
assert(active[1].is_expired)
|
|
|
|
naughty.expiration_paused = false
|
|
assert(not naughty.expiration_paused)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Make sure enabling expiration process the expired queue.
|
|
table.insert(steps, function()
|
|
-- Right now this doesn't require a step for itself, but this could change
|
|
-- so better not "document" the instantaneous clearing of the queue.
|
|
if #active > 0 then return end
|
|
|
|
spawn{ 'notify-send', 'low', 'message', '-t', '25000', '-u', 'low' }
|
|
spawn{ 'notify-send', 'normal', 'message', '-t', '25000', '-u', 'normal' }
|
|
spawn{ 'notify-send', 'critical', 'message', '-t', '25000', '-u', 'critical' }
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test the urgency level and default preset.
|
|
table.insert(steps, function()
|
|
if added_counter ~= 7 then return end
|
|
|
|
while #active > 0 do
|
|
active[1]:destroy()
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test what happens when the screen has the maximum number of notification it
|
|
-- can display at one.
|
|
table.insert(steps, function()
|
|
local wa = mouse.screen.workarea
|
|
local max_notif = math.floor(wa.height/default_height)
|
|
|
|
-- Everything should fit, otherwise the math is wrong in
|
|
-- `neughty.layout.legacy` and its a regression.
|
|
for i=1, max_notif do
|
|
spawn{ 'notify-send', 'notif '..i, 'message', '-t', '25000', '-u', 'low' }
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Check the lack of overlapping and the presence of the expected content.
|
|
table.insert(steps, function()
|
|
local wa = mouse.screen.workarea
|
|
local max_notif = math.floor(wa.height/default_height)
|
|
if added_counter ~= 7 + max_notif then return end
|
|
|
|
assert(#active == max_notif)
|
|
|
|
test_overlap()
|
|
|
|
-- Now add even more!
|
|
for i=1, 5 do
|
|
spawn{ 'notify-send', 'notif '..i, 'message', '-t', '25000', '-u', 'low' }
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test the code to hide the older notifications when there is too many for the
|
|
-- screen.
|
|
table.insert(steps, function()
|
|
local wa = mouse.screen.workarea
|
|
local max_notif = math.floor(wa.height/default_height)
|
|
if added_counter ~= 7 + max_notif + 5 then return end
|
|
|
|
-- The other should have been hidden
|
|
assert(#active == max_notif)
|
|
|
|
assert(reasons[naughty.notification_closed_reason.too_many_on_screen] == 5)
|
|
|
|
test_overlap()
|
|
|
|
while #active > 0 do
|
|
active[1]:destroy()
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
end
|
|
|
|
local positions = {
|
|
"top_left" , "top_middle" , "top_right" ,
|
|
"bottom_left" , "bottom_middle" , "bottom_right" ,
|
|
"middle" ,
|
|
}
|
|
|
|
-- Test each corners.
|
|
table.insert(steps, function()
|
|
for _, pos in ipairs(positions) do
|
|
for i=1, 3 do
|
|
-- Skip dbus for this one.
|
|
naughty.notification {
|
|
position = pos,
|
|
title = "At "..pos.." "..i,
|
|
message = "some message",
|
|
timeout = 25000,
|
|
}
|
|
end
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
table.insert(steps, function()
|
|
if #active ~= #positions*3 then return end
|
|
|
|
test_overlap()
|
|
|
|
while #active > 0 do
|
|
active[1]:destroy()
|
|
end
|
|
|
|
return true
|
|
end)
|
|
|
|
local big_icon = cairo.ImageSurface(cairo.Format.ARGB32, 256, 256)
|
|
local cr = cairo.Context(big_icon)
|
|
local small_icon = cairo.ImageSurface(cairo.Format.ARGB32, 32 , 32 )
|
|
local cr2 = cairo.Context(small_icon)
|
|
local wierd_ratio1 = cairo.ImageSurface(cairo.Format.ARGB32, 256, 128)
|
|
local cr3 = cairo.Context(wierd_ratio1)
|
|
local wierd_ratio2 = cairo.ImageSurface(cairo.Format.ARGB32, 128, 256)
|
|
local cr4 = cairo.Context(wierd_ratio2)
|
|
|
|
-- Checkboard shirt pattern icon!
|
|
for i=1, 5 do
|
|
for j=1, 5 do
|
|
cr:set_source_rgb(
|
|
i%2 == 1 and 1 or 0, j%2 == 1 and 1 or 0, i%2 == 0 and 0 or 1
|
|
)
|
|
cr:rectangle( (i-1)*48, (j-1)*48, 48, 48 )
|
|
cr:fill()
|
|
|
|
cr2:set_source_rgb(
|
|
i%2 == 1 and 1 or 0, j%2 == 1 and 1 or 0, i%2 == 0 and 0 or 1
|
|
)
|
|
cr2:rectangle( (i-1)*6, (j-1)*6, 6, 6 )
|
|
cr2:fill()
|
|
|
|
cr3:set_source_rgb(
|
|
i%2 == 1 and 1 or 0, j%2 == 1 and 1 or 0, i%2 == 0 and 0 or 1
|
|
)
|
|
cr3:rectangle( (i-1)*48, (j-1)*24, 48, 24 )
|
|
cr3:fill()
|
|
|
|
cr4:set_source_rgb(
|
|
i%2 == 1 and 1 or 0, j%2 == 1 and 1 or 0, i%2 == 0 and 0 or 1
|
|
)
|
|
cr4:rectangle( (i-1)*24, (j-1)*48, 24, 48 )
|
|
cr4:fill()
|
|
end
|
|
end
|
|
|
|
-- Test the icon size constraints.
|
|
table.insert(steps, function()
|
|
beautiful.notification_icon_size = 64
|
|
|
|
-- Icons that are too large (they should be downscaled)
|
|
local n1 = naughty.notification {
|
|
icon = big_icon,
|
|
title = "Has a nice icon!",
|
|
message = "big",
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(n1.id)
|
|
assert(n1.iconbox)
|
|
assert(n1.iconbox._private.image:get_width () == beautiful.notification_icon_size)
|
|
assert(n1.iconbox._private.image:get_height() == beautiful.notification_icon_size)
|
|
assert(n1.iconbox._private.image:get_width () == n1.size_info.icon_w)
|
|
assert(n1.iconbox._private.image:get_height() == n1.size_info.icon_h)
|
|
assert(n1.size_info.icon_scale_factor == 1/4)
|
|
|
|
-- Icons that are too small (they should not be upscaled)
|
|
local n2 = naughty.notification {
|
|
icon = small_icon,
|
|
title = "Has a nice icon!",
|
|
message = "small",
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(n2.id)
|
|
assert(n2.iconbox)
|
|
assert(n2.iconbox._private.image:get_width () == 32)
|
|
assert(n2.iconbox._private.image:get_height() == 32)
|
|
assert(n2.iconbox._private.image:get_width () == n2.size_info.icon_w)
|
|
assert(n2.iconbox._private.image:get_height() == n2.size_info.icon_h)
|
|
assert(not n2.size_info.icon_scale_factor)
|
|
|
|
-- Downscaled non square icons (aspect ratio should be kept).
|
|
local n3 = naughty.notification {
|
|
icon = wierd_ratio1,
|
|
title = "Has a nice icon!",
|
|
message = "big",
|
|
timeout = 25000,
|
|
}
|
|
|
|
local n4 = naughty.notification {
|
|
icon = wierd_ratio2,
|
|
title = "Has a nice icon!",
|
|
message = "big",
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(n3.iconbox)
|
|
assert(n3.iconbox._private.image:get_width () == beautiful.notification_icon_size)
|
|
assert(n3.iconbox._private.image:get_height() == beautiful.notification_icon_size/2)
|
|
assert(n3.iconbox._private.image:get_width () == n3.size_info.icon_w)
|
|
assert(n3.iconbox._private.image:get_height() == n3.size_info.icon_h)
|
|
assert(n3.size_info.icon_scale_factor == 1/4)
|
|
|
|
assert(n4.iconbox)
|
|
assert(n4.iconbox._private.image:get_width () == beautiful.notification_icon_size/2)
|
|
assert(n4.iconbox._private.image:get_height() == beautiful.notification_icon_size)
|
|
assert(n4.iconbox._private.image:get_width () == n4.size_info.icon_w)
|
|
assert(n4.iconbox._private.image:get_height() == n4.size_info.icon_h)
|
|
assert(n4.size_info.icon_scale_factor == 1/4)
|
|
|
|
-- The notification size should change proportionally to the icon size.
|
|
assert(n1.box.width == n2.box.width + 32)
|
|
assert(n1.box.height == n2.box.height + 32)
|
|
assert(n1.box.height == n3.box.height + 32)
|
|
assert(n1.box.width == n4.box.width + 32)
|
|
assert(n1.box.height == n4.box.height)
|
|
assert(n1.box.width == n3.box.width )
|
|
|
|
-- Make sure unconstrained icons work as expected.
|
|
beautiful.notification_icon_size = nil
|
|
|
|
local n5 = naughty.notification {
|
|
icon = big_icon,
|
|
title = "Has a nice icon!",
|
|
message = "big",
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(n5.iconbox)
|
|
assert(n5.iconbox._private.image:get_width () == 256)
|
|
assert(n5.iconbox._private.image:get_height() == 256)
|
|
assert(n5.iconbox._private.image:get_width () == n5.size_info.icon_w)
|
|
assert(n5.iconbox._private.image:get_height() == n5.size_info.icon_h)
|
|
assert(not n5.size_info.icon_scale_factor)
|
|
|
|
-- Make sure invalid icons don't prevent the message from being shown.
|
|
local n6 = naughty.notification {
|
|
icon = "this/is/an/invlid/path",
|
|
title = "Has a nice icon!",
|
|
message = "Very important life saving advice",
|
|
timeout = 25000,
|
|
}
|
|
|
|
n1:destroy()
|
|
n2:destroy()
|
|
n3:destroy()
|
|
n4:destroy()
|
|
n5:destroy()
|
|
n6:destroy()
|
|
assert(#active == 0)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test notifications with size constraints.
|
|
table.insert(steps, function()
|
|
local str = "foobar! "
|
|
assert(#active == 0)
|
|
|
|
-- 2^9 foobars is a lot of foobars.
|
|
for _=1, 10 do
|
|
str = str .. str
|
|
end
|
|
|
|
-- First, see what happen without any constraint and enormous messages.
|
|
-- This also test notifications larger than the workarea.
|
|
|
|
local n1 = naughty.notification {
|
|
title = str,
|
|
message = str,
|
|
timeout = 25000,
|
|
}
|
|
|
|
-- Same, but with an icon and larger borders.
|
|
local n2 = naughty.notification {
|
|
icon = big_icon,
|
|
title = str,
|
|
message = str,
|
|
timeout = 25000,
|
|
border_width = 40,
|
|
}
|
|
|
|
local wa = mouse.screen.workarea
|
|
assert(n1.box.width +2*n1.box.border_width <= wa.width )
|
|
assert(n1.box.height+2*n1.box.border_width <= wa.height)
|
|
assert(n2.box.width +2*n2.box.border_width <= wa.width )
|
|
assert(n2.box.height+2*n2.box.border_width <= wa.height)
|
|
|
|
-- Check with client icons.
|
|
assert(not n1.icon)
|
|
|
|
n1._private.clients = {{icon= big_icon, type = "normal"}}
|
|
assert(n1.icon == big_icon)
|
|
assert(n1.box.width +2*n1.box.border_width <= wa.width )
|
|
assert(n1.box.height+2*n1.box.border_width <= wa.height)
|
|
assert(n2.box.width +2*n2.box.border_width <= wa.width )
|
|
assert(n2.box.height+2*n2.box.border_width <= wa.height)
|
|
|
|
n1:destroy()
|
|
n2:destroy()
|
|
|
|
-- Now set a maximum size and try again.
|
|
beautiful.notification_max_width = 256
|
|
beautiful.notification_max_height = 96
|
|
|
|
local n3 = naughty.notification {
|
|
title = str,
|
|
message = str,
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(n3.box.width <= 256)
|
|
assert(n3.box.height <= 96 )
|
|
|
|
-- Now test when the icon is larger than the maximum.
|
|
local n4 = naughty.notification {
|
|
icon = big_icon,
|
|
title = str,
|
|
message = str,
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(n4.box.width <= 256)
|
|
assert(n4.box.height <= 96 )
|
|
assert(n4.iconbox._private.image:get_width () == n4.size_info.icon_w)
|
|
assert(n4.iconbox._private.image:get_height() == n4.size_info.icon_h)
|
|
assert(n4.size_info.icon_w <= 256)
|
|
assert(n4.size_info.icon_h <= 96 )
|
|
|
|
n3:destroy()
|
|
n4:destroy()
|
|
assert(#active == 0)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test more advanced features than what notify-send can provide.
|
|
|
|
-- Test the actions.
|
|
table.insert(steps, function()
|
|
|
|
assert(#active == 0)
|
|
|
|
send_notify("Awesome test", 0, "", "title", "message body",
|
|
{ "1", "one", "2", "two", "3", "three" }, {}, 25000)
|
|
|
|
return true
|
|
end)
|
|
|
|
table.insert(steps, function()
|
|
if #active == 0 then return end
|
|
|
|
assert(#active == 1)
|
|
local n = active[1]
|
|
|
|
assert(n.box)
|
|
assert(#n.actions == 3)
|
|
assert(n.actions[1].name == "one" )
|
|
assert(n.actions[2].name == "two" )
|
|
assert(n.actions[3].name == "three")
|
|
|
|
n:destroy()
|
|
|
|
return true
|
|
end)
|
|
|
|
--TODO Test too many actions.
|
|
|
|
--TODO Test action with long names.
|
|
|
|
local nid, name_u, message_u, actions_u = nil
|
|
|
|
-- Test updating a notification.
|
|
table.insert(steps, function()
|
|
send_notify("Awesome test", 0, "", "title", "message body",
|
|
{ "1", "one", "2", "two", "3", "three" }, {}, 25000, function(id)
|
|
nid = id
|
|
end)
|
|
|
|
return true
|
|
end)
|
|
|
|
table.insert(steps, function()
|
|
if #active == 0 or not nid then return end
|
|
|
|
local n = active[1]
|
|
|
|
n:connect_signal("property::title" , function(_, val) name_u = val end)
|
|
n:connect_signal("property::message", function(_, val) message_u = val end)
|
|
n:connect_signal("property::actions", function(_, val) actions_u = val end)
|
|
|
|
send_notify("Awesome test", nid, "", "updated title", "updated message body",
|
|
{ "1", "four", "2", "five", "3", "six" }, {}, 25000)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test if all properties have been updated.
|
|
table.insert(steps, function()
|
|
if not name_u then return end
|
|
if not message_u then return end
|
|
if not actions_u then return end
|
|
|
|
-- No new notification should have been created.
|
|
assert(#active == 1)
|
|
|
|
local n = active[1]
|
|
|
|
assert(n.title == "updated title" )
|
|
assert(n.message == "updated message body")
|
|
|
|
assert(#n.actions == 3)
|
|
assert(n.actions[1].name == "four" )
|
|
assert(n.actions[2].name == "five" )
|
|
assert(n.actions[3].name == "six" )
|
|
|
|
n:destroy()
|
|
assert(#active == 0)
|
|
|
|
assert(name_u == "updated title", name_u)
|
|
assert(message_u == "updated message body", name_u)
|
|
assert(#actions_u == 3)
|
|
|
|
return true
|
|
end)
|
|
|
|
local int_n = nil
|
|
|
|
-- Test replace_id with internally generated notifications.
|
|
-- Even if it makes little sense, it should work and is used in the wild.
|
|
table.insert(steps, function()
|
|
int_n = naughty.notification {
|
|
title = "foo",
|
|
message = "bar",
|
|
timeout = 25000,
|
|
}
|
|
|
|
local nid2 = int_n.id
|
|
assert(int_n.id)
|
|
local res, err = pcall(function()
|
|
int_n.id = 1337
|
|
end)
|
|
|
|
assert(err:match("Notification identifier can only be set once"))
|
|
assert(not res)
|
|
assert(int_n.id == nid2)
|
|
|
|
-- watch out, capi.dbus signals are not normal object signals
|
|
send_notify("Awesome test", nid2, "", "title2", "text2",
|
|
{ "the one action" }, {}, 123)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test that the values were replaced
|
|
table.insert(steps, function()
|
|
if int_n.title ~= "title2" then return end
|
|
|
|
assert(int_n.title == "title2")
|
|
assert(int_n.message == "text2" )
|
|
--assert(int_n.timeout == 123 ) --FIXME
|
|
int_n:destroy()
|
|
|
|
--TODO test what happens when updating a destroyed notification
|
|
-- There is currently no API to resurrect notifications.
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test adding actions, resident mode and action sharing.
|
|
table.insert(steps, function()
|
|
local n1 = naughty.notification {
|
|
title = "foo",
|
|
message = "bar",
|
|
timeout = 25000,
|
|
resident = true,
|
|
actions = { naughty.action { name = "a1" } }
|
|
}
|
|
|
|
local n2 = naughty.notification {
|
|
title = "foo",
|
|
message = "bar",
|
|
resident = true,
|
|
timeout = 25000,
|
|
actions = { naughty.action { name = "a2" } }
|
|
}
|
|
|
|
local is_called = {}
|
|
|
|
n1:connect_signal("invoked", function() is_called[1] = true end)
|
|
n2:connect_signal("invoked", function() is_called[2] = true end)
|
|
|
|
n1.actions[1]:invoke(n1)
|
|
n2.actions[1]:invoke(n2)
|
|
|
|
assert(is_called[1])
|
|
assert(is_called[2])
|
|
assert(not n1._private.is_destroyed)
|
|
assert(not n2._private.is_destroyed)
|
|
|
|
local shared_a = naughty.action { name = "a3" }
|
|
|
|
n1:append_actions {shared_a}
|
|
n2:append_actions {shared_a}
|
|
|
|
n1:connect_signal("invoked", function() is_called[3] = true end)
|
|
n2:connect_signal("invoked", function() is_called[4] = true end)
|
|
|
|
assert(n1.actions[2] == shared_a)
|
|
assert(n2.actions[2] == shared_a)
|
|
|
|
shared_a:invoke(n1)
|
|
|
|
assert(is_called[3])
|
|
assert(not is_called[4])
|
|
assert(not n1._private.is_destroyed)
|
|
assert(not n2._private.is_destroyed)
|
|
|
|
n1.resident = false
|
|
n2.resident = false
|
|
|
|
shared_a:invoke(n1)
|
|
|
|
assert(n1._private.is_destroyed)
|
|
assert(not n2._private.is_destroyed)
|
|
|
|
shared_a:invoke(n2)
|
|
assert(n2._private.is_destroyed)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test that exposing support for animations and persistence are exposed to DBus.
|
|
table.insert(steps, function()
|
|
assert(not naughty.persistence_enabled)
|
|
assert(not naughty.image_animations_enabled)
|
|
|
|
assert(gtable.hasitem(naughty.dbus._capabilities, "icon-static"))
|
|
assert(not gtable.hasitem(naughty.dbus._capabilities, "icon-multi"))
|
|
assert(not gtable.hasitem(naughty.dbus._capabilities, "persistence"))
|
|
|
|
naughty.persistence_enabled = true
|
|
naughty.image_animations_enabled = true
|
|
|
|
assert(naughty.persistence_enabled)
|
|
assert(naughty.image_animations_enabled)
|
|
|
|
assert(gtable.hasitem(naughty.dbus._capabilities, "icon-multi"))
|
|
assert(gtable.hasitem(naughty.dbus._capabilities, "persistence"))
|
|
assert(not gtable.hasitem(naughty.dbus._capabilities, "icon-static"))
|
|
|
|
naughty.persistence_enabled = false
|
|
naughty.image_animations_enabled = false
|
|
|
|
assert(not naughty.persistence_enabled)
|
|
assert(not naughty.image_animations_enabled)
|
|
|
|
assert( gtable.hasitem(naughty.dbus._capabilities, "icon-static"))
|
|
assert(not gtable.hasitem(naughty.dbus._capabilities, "icon-multi" ))
|
|
assert(not gtable.hasitem(naughty.dbus._capabilities, "persistence"))
|
|
|
|
return true
|
|
end)
|
|
|
|
local icon_requests = {}
|
|
|
|
-- Check if the action icon support is detected.
|
|
table.insert(steps, function()
|
|
assert(#active == 0)
|
|
|
|
naughty.connect_signal("request::action_icon", function(a, context, hints)
|
|
icon_requests[hints.id] = a
|
|
a.icon = hints.id == "list-add" and small_icon or big_icon
|
|
end)
|
|
|
|
naughty.connect_signal("request::icon", function(n, context, hints)
|
|
icon_requests[n] = true
|
|
end)
|
|
|
|
local hints = {
|
|
["action-icons"] = GLib.Variant("b", true),
|
|
}
|
|
|
|
send_notify("Awesome test", 0, "", "title", "message body",
|
|
{ "list-add", "add", "list-remove", "remove" }, hints, 25000)
|
|
|
|
return true
|
|
end)
|
|
|
|
table.insert(steps, function()
|
|
if #active ~= 1 then return end
|
|
|
|
local n = active[1]
|
|
|
|
assert(not icon_requests[n])
|
|
|
|
assert(n._private.freedesktop_hints)
|
|
assert(n._private.freedesktop_hints["action-icons"] == true)
|
|
|
|
assert(icon_requests["list-add" ] == n.actions[1])
|
|
assert(icon_requests["list-remove"] == n.actions[2])
|
|
|
|
assert(n.actions[1].icon == small_icon)
|
|
assert(n.actions[2].icon == big_icon )
|
|
|
|
assert(type(n.actions[1].position) == "number")
|
|
assert(type(n.actions[2].position) == "number")
|
|
assert(n.actions[1].position == 1)
|
|
assert(n.actions[2].position == 2)
|
|
|
|
n:destroy()
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Test the error popup.
|
|
table.insert(steps, function()
|
|
local got = nil
|
|
|
|
naughty.connect_signal("request::display_error", function(err)
|
|
got = err
|
|
end)
|
|
|
|
awesome.emit_signal("debug::error", "foo")
|
|
|
|
assert(got == "foo")
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Now check if the old deprecated (but still supported) APIs don't have errors.
|
|
table.insert(steps, function()
|
|
-- Tests are (by default) not allowed to call deprecated APIs
|
|
gdebug.deprecate = function() end
|
|
|
|
local n = naughty.notification {
|
|
title = "foo",
|
|
message = "bar",
|
|
timeout = 25000,
|
|
app_icon = "baz"
|
|
}
|
|
|
|
-- Make sure the suspension don't cause errors
|
|
assert(not naughty.is_suspended())
|
|
assert(not naughty.suspended)
|
|
naughty.suspend()
|
|
assert(naughty.is_suspended())
|
|
assert(naughty.suspended)
|
|
naughty.resume()
|
|
assert(not naughty.is_suspended())
|
|
assert(not naughty.suspended)
|
|
naughty.toggle()
|
|
assert(naughty.is_suspended())
|
|
assert(naughty.suspended)
|
|
naughty.toggle()
|
|
assert(not naughty.is_suspended())
|
|
assert(not naughty.suspended)
|
|
naughty.suspended = not naughty.suspended
|
|
assert(naughty.is_suspended())
|
|
assert(naughty.suspended)
|
|
naughty.suspended = not naughty.suspended
|
|
assert(not naughty.is_suspended())
|
|
assert(not naughty.suspended)
|
|
|
|
-- Replace the text
|
|
assert(icon_requests[n])
|
|
assert(n.title == "foo")
|
|
assert(n.message == "bar")
|
|
assert(n.text == "bar")
|
|
local width, height = n.width, n.height
|
|
assert(width)
|
|
assert(height)
|
|
naughty.replace_text(n, "foobar", "baz")
|
|
assert(n.title == "foobar")
|
|
assert(n.message == "baz")
|
|
assert(n.text == "baz")
|
|
assert(n.width > width)
|
|
assert(n.height == height)
|
|
width, height = n.width, n.height
|
|
naughty.replace_text(n, "foo", "bar\nbar")
|
|
assert(n.title == "foo")
|
|
assert(n.message == "bar\nbar")
|
|
assert(n.text == "bar\nbar")
|
|
assert(n.width < width)
|
|
assert(n.height > height)
|
|
width, height = n.width, n.height
|
|
naughty.replace_text(n, "foo", "bar")
|
|
assert(n.title == "foo")
|
|
assert(n.message == "bar")
|
|
assert(n.text == "bar")
|
|
assert(n.width == width)
|
|
assert(n.height < height)
|
|
|
|
-- Test the ID system
|
|
local id = n.id
|
|
assert(naughty.getById(id) == n)
|
|
assert(naughty.get_by_id(id) == n)
|
|
assert(naughty.getById(42) ~= n)
|
|
assert(naughty.get_by_id(42) ~= n)
|
|
|
|
-- The timeout
|
|
naughty.reset_timeout(n, 1337)
|
|
|
|
-- Destroy using the old API
|
|
local old_count = #destroyed
|
|
naughty.destroy(n)
|
|
assert(old_count == #destroyed - 1)
|
|
|
|
-- Destroy using the old API, while suspended
|
|
local n2 = naughty.notification {
|
|
title = "foo",
|
|
message = "bar",
|
|
timeout = 25000,
|
|
}
|
|
naughty.suspended = true
|
|
naughty.destroy(n2)
|
|
assert(old_count == #destroyed - 2)
|
|
naughty.suspended = false
|
|
|
|
-- The old notify function and "text" instead of "message"
|
|
n = naughty.notify { text = "foo" }
|
|
assert(n.message == "foo")
|
|
assert(n.text == "foo")
|
|
|
|
-- Calling `naughty.notify` with replace_id.
|
|
n2 = naughty.notify {
|
|
replaces_id = n.id,
|
|
message = "bar",
|
|
title = "foo",
|
|
}
|
|
|
|
assert(n == n2 )
|
|
assert(n.message == "bar")
|
|
assert(n.text == "bar")
|
|
assert(n.title == "foo")
|
|
|
|
-- Finish by testing disconnect_signal
|
|
naughty.disconnect_signal("destroyed", destroyed_callback)
|
|
naughty.disconnect_signal("added", added_callback)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Add a "new API" handler.
|
|
local current_template, had_error, handler_called = nil
|
|
|
|
table.insert(steps, function()
|
|
assert(naughty.has_display_handler == false)
|
|
|
|
naughty.connect_signal("request::display", function(n)
|
|
handler_called = true
|
|
|
|
naughty.layout.box {
|
|
notification = n,
|
|
widget_template = current_template
|
|
}
|
|
end)
|
|
|
|
return true
|
|
end)
|
|
|
|
-- Make sure the legacy popup is used when the new APIs fails.
|
|
table.insert(steps, function()
|
|
assert(naughty.has_display_handler == true)
|
|
|
|
function gpcall._error_handler()
|
|
had_error = true
|
|
end
|
|
|
|
local n = naughty.notification {
|
|
title = nil,
|
|
message = nil,
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(handler_called)
|
|
assert(not had_error)
|
|
assert(not n._private.widget_template_failed)
|
|
assert(not n.box)
|
|
|
|
-- Check adding messages later
|
|
n.title = "foo"
|
|
n.message = "bar"
|
|
n.icon = big_icon
|
|
|
|
n:destroy()
|
|
handler_called = false
|
|
|
|
-- Try with a broken template.
|
|
current_template = {widget = function() assert(false) end}
|
|
|
|
n = naughty.notification {
|
|
title = "foo",
|
|
message = "bar",
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(handler_called)
|
|
assert(had_error)
|
|
assert(not n.box)
|
|
|
|
-- Check changing existing content.
|
|
n.title = "bar"
|
|
n.message = "foo"
|
|
n.icon = big_icon
|
|
n.image = small_icon
|
|
|
|
handler_called = false
|
|
had_error = false
|
|
|
|
-- Break the default template
|
|
assert(dwidget.widget)
|
|
dwidget.widget = nil
|
|
dwidget.layout = function() assert(false) end
|
|
table.remove(dwidget, 1)
|
|
|
|
n = naughty.notification {
|
|
title = "foo",
|
|
message = "bar",
|
|
timeout = 25000,
|
|
}
|
|
|
|
assert(handler_called)
|
|
assert(n._private.widget_template_failed)
|
|
assert(had_error)
|
|
assert(n.box)
|
|
|
|
return true
|
|
end)
|
|
|
|
require("_runner").run_steps(steps)
|