From 3a12e6d274e5d5ffb413087556d6c981d7d84da5 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 24 Jun 2019 17:17:51 -0400 Subject: [PATCH] tests: Test Lua side screen management. --- tests/run.sh | 2 +- tests/test-dpi.lua | 548 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 tests/test-dpi.lua diff --git a/tests/run.sh b/tests/run.sh index 45add1a2c..75e305d67 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -255,7 +255,7 @@ for f in $tests; do error="$(echo "$error" | grep -vE ".{19} W: awesome: (Can't read color .* from GTK)" || true)" if [[ $fail_on_warning ]]; then # Filter out ignored warnings. - error="$(echo "$error" | grep -vE ".{19} W: awesome: (a_glib_poll|Cannot reliably detect EOF|beautiful: can't get colorscheme from xrdb|Can't read color .* from GTK+3 theme|A notification|Notification)" || true)" + error="$(echo "$error" | grep -vE ".{19} W: awesome: (Removing last screen through fake_remove|a_glib_poll|Cannot reliably detect EOF|beautiful: can't get colorscheme from xrdb|Can't read color .* from GTK+3 theme|A notification|Notification)" || true)" fi if [[ -n "$error" ]]; then color_red diff --git a/tests/test-dpi.lua b/tests/test-dpi.lua new file mode 100644 index 000000000..6b0c28d4b --- /dev/null +++ b/tests/test-dpi.lua @@ -0,0 +1,548 @@ +-- This testsuite intends to tests the delayed screen creation mode and it's +-- automatic fallback when no screens are created. + +local runner = require("_runner") +local ascreen = require("awful.screen") +local gtable = require("gears.table") +local grect = require("gears.geometry").rectangle + +local xft_dpi = 42 + +local fake_viewports, max_id, allow_zero = screen._viewports(), 1, false +local called = 0 + +local saved_dpi1, saved_dpi2, saved_dpi3 + +-- Make sure what is tested matches some criteria. +-- Prevents silly errors when adding more test case. +local function validate_viewports(viewports) + for _, a in ipairs(viewports) do + assert(type(a.id) == "number") + assert(a.id > max_id) + assert(type(a.outputs ) == "table") + assert(type(a.geometry) == "table") + + for _, prop in ipairs {"x", "y", "width", "height"} do + assert(type(a.geometry[prop]) == "number") + assert(a.geometry[prop] >= 0) + end + + max_id = a.id + end +end + +local function fake_replace(viewports) + fake_viewports = viewports + screen.emit_signal("property::_viewports", fake_viewports) +end + +-- Rollback the clock and replay the initialization. +local function fake_replay(viewports) + if viewports then + validate_viewports(viewports) + end + + viewports = viewports or fake_viewports + + ascreen._viewports, fake_viewports = {}, viewports + + while screen.count() > 0 do + screen[1]:fake_remove() + end + + assert(screen.count() == 0) + + screen.emit_signal("scanning") + screen.emit_signal("property::_viewports", fake_viewports) + screen.emit_signal("scanned") + assert(screen.count() == #viewports) + + assert(type(ascreen._get_xft_dpi()) ~= "boolean") + + -- Check of the viewports (+new metadata) is created. + for k, a in ipairs(viewports) do + local s = screen[k] + + -- Check if the extra metadata are computed. + assert(s.data.viewport.maximum_dpi) + assert(s.data.viewport.minimum_dpi) + assert(s.data.viewport.preferred_dpi) + assert(s.dpi) + assert(s.maximum_dpi == s.data.viewport.maximum_dpi ) + assert(s.minimum_dpi == s.data.viewport.minimum_dpi ) + assert(s.preferred_dpi == s.data.viewport.preferred_dpi) + + -- Make sure it enters either the main `if` or the fallback one. + assert(s.data.viewport.minimum_dpi ~= math.huge) + assert(s.data.viewport.preferred_dpi ~= math.huge) + + -- Check the range. + assert(s.data.viewport.maximum_dpi >= s.data.viewport.minimum_dpi) + assert(s.data.viewport.preferred_dpi >= s.data.viewport.minimum_dpi) + assert(s.data.viewport.preferred_dpi <= s.data.viewport.maximum_dpi) + + -- Check if the screen was created using the right viewport + -- (order *is* relevant). + assert(#s.data.viewport.outputs == #a.outputs) + assert(s.data.viewport and s.data.viewport.id == a.id) + end + + -- The original shall not be modified, the CAPI for this is read-only. + for _, a in ipairs(fake_viewports) do + assert(not a.minimum_dpi ) + assert(not a.maximum_dpi ) + assert(not a.preferred_dpi) + assert(not a.dpi ) + end +end + +local function remove_output(viewport, name) + for k, output in ipairs(viewport.outputs) do + if output.name == name then + table.remove(viewport.outputs, k) + screen.emit_signal("property::_viewports", fake_viewports) + return + end + end + assert(false) +end + +local function add_output(viewport, output) + table.insert(viewport.outputs, output) + screen.emit_signal("property::_viewports", fake_viewports) +end + +local function remove_viewport(id) + for k, a in ipairs(fake_viewports) do + if a.id == id then + table.remove(fake_viewports, k) + screen.emit_signal("property::_viewports", fake_viewports) + return + end + end + assert(false) +end + +local function add_viewport(viewport) + assert(viewport.id) + assert(viewport.geometry) + assert(viewport.outputs) + table.insert(fake_viewports, viewport) + screen.emit_signal("property::_viewports", fake_viewports) +end + +local steps = { + +-- Monkeypatch for the tests. +function() + -- This should be true for all tests for. + assert(not screen.automatic_factory) + + -- Avoids touching the real XRDB, the tests don't control it. + function ascreen._get_xft_dpi() + return xft_dpi + end + + -- This testsuite needs to manipulate this. + function ascreen._get_viewports() + local clone = gtable.clone(fake_viewports, true) + assert(#clone > 0 or allow_zero) + return clone + end + + return true +end, + +-- Remove all the screens, which is **not** supported, but necessary to play +-- with the initial screen creation code. +function() + -- Simple scenario, a single screen. + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 2, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "leet", mm_width = 353, mm_height = 11 }}, + }, + } + + -- It should honor the XFT DPI when set. + assert(xft_dpi == screen[1].dpi) + + -- Now that we know it works, unset it for the other steps. + xft_dpi = nil + + -- Check if the "normal" fallback works. + fake_replay() + assert(screen[1].dpi ~= 42) + + return true +end, + +-- Test with multiple outputs. +function() + -- Simple scenario, a single screen. + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 3, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = { + { name = "leet", mm_width = 353 , mm_height = 11 }, + { name = "leet", mm_width = 353/2, mm_height = 11/2 }, + { name = "leet", mm_width = 353/3, mm_height = 11/3 }, + }, + }, + } + + assert(screen[1].maximum_dpi == screen[1].minimum_dpi*3) + assert(screen[1].preferred_dpi == screen[1].minimum_dpi ) + + return true +end, + +-- Test with zero outputs. +function() + -- Simple scenario, a single screen. + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 4, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = {}, + }, + } + + assert(screen[1].maximum_dpi == screen[1].minimum_dpi) + assert(screen[1].preferred_dpi == screen[1].minimum_dpi) + + return true +end, + +-- Test with 2 screens. +function() + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 5, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "0x1ee7", mm_width = 353, mm_height = 11 }}, + }, + { + -- 1 has already been consumed, this makes the test more future proof. + id = 6, + geometry = { x = 1337, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "3leet", mm_width = 353, mm_height = 11 }}, + }, + } + + saved_dpi1 = screen[1].preferred_dpi + + return true +end, + +-- Test with 2 screens with twice the density. +function() + -- Simple scenario, a single screen. + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 7, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "0x1ee7", mm_width = 353/2, mm_height = 11/2 }}, + }, + { + -- 1 has already been consumed, this makes the test more future proof. + id = 8, + geometry = { x = 1337, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "3leet", mm_width = 353/3, mm_height = 11/3 }}, + }, + } + + -- Test if scaling works as expected. + assert(saved_dpi1*2 == screen[1].preferred_dpi) + assert(saved_dpi1*3 == screen[2].preferred_dpi) + + -- Revert to the previous settings if the DPI happens to accidentally match. + -- For the following tests, it has to be different from the fallback. + if screen[1].preferred_dpi == screen[1].dpi then + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 9, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "0x1ee7", mm_width = 353, mm_height = 11 }}, + } + } + end + + saved_dpi1 = screen[1].preferred_dpi + + -- The next test requires a different value. + assert(saved_dpi1 ~= screen[1].dpi) + + -- Enable autodpi. + ascreen.set_auto_dpi_enabled(true) + + return true +end, + +-- Test if autodpi does something. +function() + -- Replay with the same settings. + fake_replay() + + -- If this isn't true, then auto-dpi didn't do its job. + assert(screen[1].dpi == saved_dpi1) + + -- Revert to a single viewport for the next tests. + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 10, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "leet", mm_width = 353, mm_height = 11 }}, + }, + } + + saved_dpi1 = screen[1].dpi + saved_dpi2 = screen[1].maximum_dpi + saved_dpi3 = screen[1].minimum_dpi + + return true +end, + +-- Test adding an output. +function() + local viewport = screen[1].data.viewport + + add_output(fake_viewports[1], { + name = "foobar", + mm_width = 353/2, + mm_height = 11/2 + }) + add_output(fake_viewports[1], { + name = "foobar2", + mm_width = 353*2, + mm_height = 11*2 + }) + + -- It should have been kept. + assert(viewport == screen[1].data.viewport) + + -- If this isn't true, then auto-dpi didn't do its job. + assert(screen[1].dpi ~= saved_dpi1) + + -- Now that there is multiple DPIs for the same viewport, the number + -- should double. + assert(#screen[1].data.viewport.outputs == 3) + assert(screen[1].maximum_dpi == saved_dpi2*2) + assert(screen[1].minimum_dpi == saved_dpi3/2) + assert(screen[1].dpi == saved_dpi1/2) + + remove_output(fake_viewports[1], "leet") + assert(#screen[1].data.viewport.outputs == 2) + remove_output(fake_viewports[1], "foobar") + + -- At this point, only 1 DPI is left. + assert(#screen[1].data.viewport.outputs == 1) + assert(screen[1].maximum_dpi == saved_dpi1/2) + assert(screen[1].minimum_dpi == saved_dpi1/2) + assert(screen[1].dpi == saved_dpi1/2) + + return true +end, + +-- Test adding/removing viewports. +function() + assert(screen.count() == 1) + + add_viewport { + id = 11, + geometry = { x = 1337, y = 0, width = 1337, height = 42 }, + outputs = {{ + name = "foobar", + mm_width = 353/2, + mm_height = 11/2 + }} + } + + assert(screen.count() == 2) + assert(screen[1].data.viewport.id == 10) + assert(screen[2].data.viewport.id == 11) + assert(grect.are_equal(screen[1].data.viewport.geometry, screen[1].geometry)) + assert(grect.are_equal(screen[2].data.viewport.geometry, screen[2].geometry)) + assert(#ascreen._viewports == 2) + + remove_viewport(10) + + assert(#ascreen._viewports == 1) + assert(screen.count() == 1) + assert(screen[1].data.viewport.id == 11) + assert(grect.are_equal(screen[1].data.viewport.geometry, screen[1].geometry)) + + return true +end, + +-- Test resizing. +function() + local s, sa = screen[1], screen[1].data.viewport + assert(screen.count() == 1) + assert(#ascreen._viewports == 1) + + -- First, to the same size (replace internal screen with external screen). + -- It may or may not create a different viewport in practice, but it's possible + -- if the first screen is removed then the new one added in 2 steps. + fake_replace { + { + id = 12, + geometry = { x = 1337, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "foobar3", mm_width = 353/2, mm_height = 11/2}} + } + } + + assert(screen.count() == 1) + assert(s == screen[1]) + assert(s.data.viewport ~= sa) + assert(s.data.viewport.id == 12) + + -- Now 2 smaller (resolution) screens side by side to make sure it doesn't + -- go haywire with overlapping + fake_replace { + { + id = 13, + geometry = { x = 1337, y = 0, width = 680, height = 42 }, + outputs = {{ name = "foobar4", mm_width = 353/2, mm_height = 11/2}} + }, + { + id = 14, + geometry = { x = 1337+680, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "foobar5", mm_width = 353/2, mm_height = 11/2}} + } + } + + assert(screen.count() == 2) + assert(s == screen[1]) + assert(s.data.viewport.id == 13) + assert(s.geometry.x == 1337) + assert(s.geometry.width == 680) + + -- Disconnect the default handler to test the fallback. + screen.disconnect_signal("request::create", ascreen.create_screen_handler) + screen.disconnect_signal("request::remove", ascreen.remove_screen_handler) + screen.disconnect_signal("request::resize", ascreen.resize_screen_handler) + + return true +end, + +-- Test the fallback screen creation. +function() + fake_replay { + { + -- 1 has already been consumed, this makes the test more future proof. + id = 15, + geometry = { x = 0, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "0x1ee7", mm_width = 353, mm_height = 11 }}, + }, + { + -- 1 has already been consumed, this makes the test more future proof. + id = 16, + geometry = { x = 1337, y = 0, width = 1337, height = 42 }, + outputs = {{ name = "3leet", mm_width = 353, mm_height = 11 }}, + }, + } + + assert(screen.count() == 2) + assert(screen[1].data.viewport.id == 15) + assert(screen[2].data.viewport.id == 16) + + -- Connect custom handler and see if the internals accidently depend on + -- implementation details. + screen.connect_signal("request::create", function(viewport) + assert(viewport.minimum_dpi) + assert(viewport.maximum_dpi) + assert(viewport.preferred_dpi) + local geo = viewport.geometry + local s = screen.fake_add(geo.x, geo.y, geo.width, geo.height) + s.outputs = viewport.outputs + called = called + 1 + end) + screen.connect_signal("request::remove", function(viewport) + local geo = viewport.geometry + for s in screen do + if grect.are_equal(geo, s.geometry) then + s:fake_remove() + called = called + 1 + return + end + end + end) + + return true +end, + +-- Test if alternate implementations don't explode the internals. +function() + ascreen._viewports = {} + + while screen.count() > 0 do + screen[1]:fake_remove() + end + + assert(screen.count() == 0) + +-- screen.emit_signal("scanning") + screen.emit_signal("property::_viewports", fake_viewports) + screen.emit_signal("scanned") + assert(screen.count() == 2) + + assert(grect.are_equal( + screen[1].geometry, {x = 0, y = 0, width = 1337, height = 42} + )) + assert(grect.are_equal( + screen[2].geometry, {x = 1337, y = 0, width = 1337, height = 42} + )) + + -- Call the DPI properties just to check it doesn't cause an error) + assert(screen[1].dpi or true) + assert(screen[1].maximum_dpi or true) + assert(screen[1].minimum_dpi or true) + assert(screen[1].preferred_dpi or true) + assert(screen[2].dpi or true) + assert(screen[2].maximum_dpi or true) + assert(screen[2].minimum_dpi or true) + assert(screen[2].preferred_dpi or true) + assert(called == 2) + + -- Try adding an viewport. + table.insert(fake_viewports, { + -- 1 has already been consumed, this makes the test more future proof. + id = 17, + geometry = { x = 1337, y = 42, width = 1337, height = 42 }, + outputs = {{ name = "last", mm_width = 353, mm_height = 11 }}, + }) + screen.emit_signal("property::_viewports", fake_viewports) + assert(screen.count() == 3) + assert(called == 3) + assert(grect.are_equal( + screen[3].geometry, {x = 1337, y = 42, width = 1337, height = 42} + )) + + -- Remove the middle one. + table.remove(fake_viewports, 2) + screen.emit_signal("property::_viewports", fake_viewports) + + assert(screen.count() == 2) + assert(called == 4) + assert(grect.are_equal( + screen[1].geometry, {x = 0, y = 0, width = 1337, height = 42} + )) + assert(grect.are_equal( + screen[2].geometry, {x = 1337, y = 42, width = 1337, height = 42} + )) + + return true +end +} + +runner.run_steps(steps)