Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 65 additions & 13 deletions providers/base/bin/resolution_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,79 @@
import sys

from argparse import ArgumentParser
from checkbox_support.helpers.release_info import get_release_info

gi.require_version("Gdk", "3.0")
from gi.repository import Gdk # noqa: E402
# Define the Ubuntu release number where GTK4 needs to be used
# to handle resolution scaling correctly (starting from Ubuntu 25.04)
GTK4_UBUNTU_RELEASE = 25

# Import GTK/GDK based on the release version
release = int(get_release_info()["release"].split(".")[0])
if release >= GTK4_UBUNTU_RELEASE:
gi.require_version("Gtk", "4.0")
gi.require_version("Gdk", "4.0")
from gi.repository import Gtk, Gdk

Gtk.init()
else:
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk


def get_gobject_geometry(gobject, index=None):
"""Get the width and height of a given GDK object.

:param gobject: Gdk.Screen or Gdk.Monitor object
:param index: Index of the monitor
:returns: Tuple with monitor geometry
"""
if release >= GTK4_UBUNTU_RELEASE:
geom = gobject.get_geometry()
scale = gobject.get_scale()
print(
"Resolution is considering the following scale: %s" % (scale),
)
return geom.width * scale, geom.height * scale
else:
geom = gobject.get_monitor_geometry(index)
return geom.width, geom.height


def check_resolution():
screen = Gdk.Screen.get_default()
n = screen.get_n_monitors()
for i in range(n):
geom = screen.get_monitor_geometry(i)
print("Monitor %d:" % (i + 1))
print(" %d x %d" % (geom.width, geom.height))
"""Check the resolution of all connected monitors."""
if release >= GTK4_UBUNTU_RELEASE:
display = Gdk.Display.get_default()
monitors = display.get_monitors()
monitor_count = monitors.get_n_items()
for i in range(monitor_count):
monitor = monitors.get_item(i)
width, height = get_gobject_geometry(monitor)
print("Monitor %d:" % (i + 1))
print(" %d x %d" % (width, height))
else:
screen = Gdk.Screen.get_default()
for i in range(screen.get_n_monitors()):
width, height = get_gobject_geometry(screen, i)
print("Monitor %d:" % (i + 1))
print(" %d x %d" % (width, height))


def compare_resolution(min_h, min_v):
# Evaluate just the primary display
screen = Gdk.Screen.get_default()
geom = screen.get_monitor_geometry(screen.get_primary_monitor())
"""Compare the resolution of the primary display against minimums."""
if release >= GTK4_UBUNTU_RELEASE:
display = Gdk.Display.get_default()
monitors = display.get_monitors()
primary_monitor = monitors.get_item(0)
width, height = get_gobject_geometry(primary_monitor)
else:
screen = Gdk.Screen.get_default()
width, height = get_gobject_geometry(
screen, screen.get_primary_monitor()
)

print("Minimum acceptable display resolution: %d x %d" % (min_h, min_v))
print("Detected display resolution: %d x %d" % (geom.width, geom.height))
return geom.width >= min_h and geom.height >= min_v
print("Detected display resolution: %d x %d" % (width, height))
return width >= min_h and height >= min_v


def main():
Expand Down
184 changes: 184 additions & 0 deletions providers/base/tests/test_monitor_resolution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/usr/bin/env python3
# Copyright 2025 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import unittest
import sys
from unittest.mock import patch, MagicMock

# Mock gi module
sys.modules["gi"] = MagicMock()
sys.modules["gi.repository"] = MagicMock()

from resolution_test import ( # noqa: E402
get_gobject_geometry,
compare_resolution,
check_resolution,
)


class TestMonitorResolution(unittest.TestCase):
"""Test cases for monitor resolution detection."""

def test_get_gobject_geometry_fractional_scaling(self):
"""Test get_gobject_geometry with GTK4 behavior with fractional scaling."""
# Gtk4 is used for releases 25 and above
with patch("resolution_test.release", 25):
# Mock the display geometry
mock_geom = MagicMock()
mock_geom.width = 1280
mock_geom.height = 720

# Mock retrieving geometry from Gdk.Monitor including fractional scale factor
mock_obj = MagicMock()
mock_obj.get_geometry.return_value = mock_geom
mock_obj.get_scale.return_value = 1.5

# Get width and height from the mocked object
width, height = get_gobject_geometry(mock_obj)

# Validate the returned width and height are as expected
self.assertEqual((width, height), (1920, 1080))

def test_compare_resolution_gtk3(self):
"""Test compare_resolution with GTK3 behavior."""
# Gtk3 is used for releases below 25
with patch("resolution_test.release", 24):
with patch("resolution_test.Gdk") as mock_gdk:
# Mock the display geometry
mock_geom = MagicMock()
mock_geom.width = 1024
mock_geom.height = 768

# Mock retrieving geometry from Gdk.Screen primary monitor
mock_screen = MagicMock()
mock_screen.get_monitor_geometry.return_value = mock_geom
mock_screen.get_primary_monitor.return_value = 0
mock_gdk.Screen.get_default.return_value = mock_screen

# Get the resolution comparison result
result = compare_resolution(800, 600)

# Validate that the resolution meets the minimum requirements
self.assertTrue(result)

def test_compare_resolution_gtk4_with_scaling(self):
"""Test compare_resolution with GTK4 behavior and scale factor."""
# Gtk4 is used for releases 25 and above
with patch("resolution_test.release", 25):
with patch("resolution_test.Gdk") as mock_gdk:
# Mock the display geometry
mock_geom = MagicMock()
mock_geom.width = 960
mock_geom.height = 540

# Mock retrieving geometry from Gdk.Monitor
mock_monitor = MagicMock()
mock_monitor.get_geometry.return_value = mock_geom
mock_monitor.get_scale.return_value = 2

# Mock retrieving geometry from mocked monitor
mock_monitors = MagicMock()
mock_monitors.get_item.return_value = mock_monitor

# Mock retrieving Gdk.Display and its monitors
mock_display = MagicMock()
mock_display.get_monitors.return_value = mock_monitors
mock_gdk.Display.get_default.return_value = mock_display

# Get the resolution comparison result
result = compare_resolution(800, 600)

# Validate that the resolution meets the minimum requirements when properly scaled
# Without scaling, the resolution is 960x540 which is below 800x600
self.assertTrue(result)

def test_check_resolution_gtk3(self):
"""Test check_resolution with GTK3 behavior."""
# Gtk3 is used for releases below 25
with patch("resolution_test.release", 24):
with patch("resolution_test.Gdk") as mock_gdk:
with patch("builtins.print") as mock_print:
# Mock the display geometry for multiple monitors
mock_geom1 = MagicMock()
mock_geom1.width = 1920
mock_geom1.height = 1080

mock_geom2 = MagicMock()
mock_geom2.width = 1024
mock_geom2.height = 768

# Mock retrieving geometry from Gdk.Screen
mock_screen = MagicMock()
mock_screen.get_n_monitors.return_value = 2
mock_screen.get_monitor_geometry.side_effect = [
mock_geom1,
mock_geom2,
]
mock_gdk.Screen.get_default.return_value = mock_screen

# Call check_resolution
check_resolution()

# Validate that print was called with expected output
expected_calls = [
unittest.mock.call("Monitor 1:"),
unittest.mock.call(" 1920 x 1080"),
unittest.mock.call("Monitor 2:"),
unittest.mock.call(" 1024 x 768"),
]
mock_print.assert_has_calls(expected_calls)

def test_check_resolution_gtk4(self):
"""Test check_resolution with GTK4 behavior."""
# Gtk4 is used for releases 25 and above
with patch("resolution_test.release", 25):
with patch("resolution_test.Gdk") as mock_gdk:
with patch("builtins.print") as mock_print:
# Mock the display geometry with scaling
mock_geom = MagicMock()
mock_geom.width = 960
mock_geom.height = 540

# Mock monitor with scale factor
mock_monitor = MagicMock()
mock_monitor.get_geometry.return_value = mock_geom
mock_monitor.get_scale.return_value = 2

# Mock monitors collection
mock_monitors = MagicMock()
mock_monitors.get_n_items.return_value = 1
mock_monitors.get_item.return_value = mock_monitor

# Mock display
mock_display = MagicMock()
mock_display.get_monitors.return_value = mock_monitors
mock_gdk.Display.get_default.return_value = mock_display

# Call check_resolution
check_resolution()

# Validate that print was called with expected output
expected_calls = [
unittest.mock.call(
"Resolution is considering the following scale: 2"
),
unittest.mock.call("Monitor 1:"),
unittest.mock.call(" 1920 x 1080"),
]
mock_print.assert_has_calls(expected_calls)


if __name__ == "__main__":
unittest.main()
Loading