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
4 changes: 2 additions & 2 deletions providers/base/units/audio/jobs.pxu
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ estimated_duration: 30.0
imports: from com.canonical.plainbox import manifest
requires:
manifest.has_line_out == 'True'
dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving','All In One','All-In-One','AIO']
dmi.sane_product == "non-portable"
_purpose:
Check that external line out connection works correctly
_steps:
Expand All @@ -585,7 +585,7 @@ estimated_duration: 120.0
imports: from com.canonical.plainbox import manifest
requires:
manifest.has_line_in == 'True'
dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving','All In One','All-In-One','AIO']
dmi.sane_product == "non-portable"
manifest.has_audio_playback == 'True'
package.name == 'alsa-base'
package.name == 'gstreamer1.0-plugins-good' or package.name == 'gstreamer0.10-plugins-good'
Expand Down
18 changes: 9 additions & 9 deletions providers/base/units/graphics/jobs.pxu
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ id: graphics/{index}_auto_glxgears_{product_slug}
flags: also-after-suspend
requires:
executable.name == 'glxgears'
dmi.product in ['Notebook','Laptop','Portable','All In One','All-In-One','AIO','Convertible', 'Tablet', 'Detachable']
dmi.sane_product == "portable"
user: root
command:
# shellcheck disable=SC1091
Expand All @@ -327,7 +327,7 @@ id: graphics/{index}_valid_glxgears_{product_slug}
flags: also-after-suspend
requires:
executable.name == 'glxgears'
dmi.product in ['Notebook','Laptop','Portable','All In One','All-In-One','AIO','Convertible', 'Tablet', 'Detachable']
dmi.sane_product == "portable"
user: root
command:
# shellcheck disable=SC1091
Expand Down Expand Up @@ -356,7 +356,7 @@ id: graphics/{index}_auto_glxgears_fullscreen_{product_slug}
flags: also-after-suspend
requires:
executable.name == 'glxgears'
dmi.product in ['Notebook','Laptop','Portable','All In One','All-In-One','AIO','Convertible','Tablet', 'Detachable']
dmi.sane_product == "portable"
user: root
command:
# shellcheck disable=SC1091
Expand All @@ -377,7 +377,7 @@ id: graphics/{index}_valid_glxgears_fullscreen_{product_slug}
flags: also-after-suspend
requires:
executable.name == 'glxgears'
dmi.product in ['Notebook','Laptop','Portable','All In One','All-In-One','AIO','Convertible','Tablet', 'Detachable']
dmi.sane_product == "portable"
user: root
command:
# shellcheck disable=SC1091
Expand Down Expand Up @@ -405,7 +405,7 @@ flags: also-after-suspend
user: root
requires:
executable.name == 'glxgears'
dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving', 'Mini PC']
dmi.sane_product == "non-portable"
command:
prime_offload_tester.py -c glxgears -t 30
summary:
Expand All @@ -420,7 +420,7 @@ flags: also-after-suspend
user: root
requires:
executable.name == 'glxgears'
dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving', 'Mini PC']
dmi.sane_product == "non-portable"
command:
prime_offload_tester.py -c "glxgears -fullscreen" -t 30
summary:
Expand All @@ -435,7 +435,7 @@ flags: also-after-suspend
user: root
requires:
executable.name == 'glxgears'
dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving', 'Mini PC']
dmi.sane_product == "non-portable"
command:
prime_offload_tester.py -c glxgears -t 30
summary:
Expand All @@ -456,7 +456,7 @@ flags: also-after-suspend
user: root
requires:
executable.name == 'glxgears'
dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower','Space-saving', 'Mini PC']
dmi.sane_product == "non-portable"
command:
prime_offload_tester.py -c "glxgears -fullscreen" -t 30
summary:
Expand Down Expand Up @@ -673,7 +673,7 @@ plugin: shell
category_id: com.canonical.plainbox::graphics
id: graphics/nvlink-status-check
requires:
dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower', 'Space-saving']
dmi.sane_product == "non-portable"
graphics_card.driver == 'nvidia'
command: nvidia_nvlink_check.sh
_summary: Check NVIDIA NVLINK status
Expand Down
6 changes: 3 additions & 3 deletions providers/base/units/monitor/jobs.pxu
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ template-resource: graphics_card
template-filter: graphics_card.prime_gpu_offload == 'Off'
id: monitor/{index}_multi-head_{product_slug}
template-id: monitor/index_multi-head_product_slug
requires: dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower', 'Space-saving', 'Mini PC']
requires: dmi.sane_product == "non-portable"
flags: also-after-suspend
plugin: manual
category_id: com.canonical.plainbox::monitor
Expand All @@ -146,7 +146,7 @@ _summary:
Verify multi-monitor output functionality on desktop systems.

id: monitor/multi-head
requires: dmi.product in ['Desktop','Low Profile Desktop','Tower','Mini Tower', 'Space-saving', 'Mini PC']
requires: dmi.sane_product == "non-portable"
flags: also-after-suspend
plugin: manual
category_id: com.canonical.plainbox::monitor
Expand Down Expand Up @@ -187,7 +187,7 @@ template-resource: graphics_card
template-filter: graphics_card.prime_gpu_offload == 'Off'
id: monitor/{index}_dim_brightness_{product_slug}
template-id: monitor/index_dim_brightness_product_slug
requires: dmi.product in ['Notebook','Laptop','Portable','All In One','All-In-One','AIO','Convertible', 'Tablet', 'Detachable']
requires: dmi.sane_product == "portable"
plugin: user-interact-verify
category_id: com.canonical.plainbox::monitor
user: root
Expand Down
2 changes: 1 addition & 1 deletion providers/base/units/power-management/jobs.pxu
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ category_id: com.canonical.plainbox::power-management
id: power-management/light_sensor
estimated_duration: 10.0
requires:
dmi.product in ['Notebook','Laptop','Portable','Convertible', 'Tablet', 'Detachable']
dmi.sane_product == "portable"
executable.name == 'monitor-sensor'
flags: also-after-suspend
command: light_sensor_test.sh
Expand Down
2 changes: 1 addition & 1 deletion providers/base/units/stress/jobs.pxu
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ plugin: manual
category_id: com.canonical.plainbox::stress
id: stress/wireless_hotkey
estimated_duration: 60.0
requires: dmi.product in ['Notebook','Laptop','Portable']
requires: dmi.sane_product == "portable"
_purpose:
To make sure that stressing the wifi hotkey does not cause applets to disappear from the panel or the system to lock up
_steps:
Expand Down
2 changes: 1 addition & 1 deletion providers/base/units/suspend/suspend.pxu
Original file line number Diff line number Diff line change
Expand Up @@ -1531,7 +1531,7 @@ category_id: com.canonical.plainbox::suspend
id: touchpad/touchpad_after_suspend
depends: suspend/suspend_advanced_auto
requires:
dmi.product in ['Notebook','Laptop','Portable']
dmi.sane_product == "portable"
xinput.device_class == 'XITouchClass' and xinput.touch_mode != 'dependent'
command: true
estimated_duration: 1.2
Expand Down
39 changes: 36 additions & 3 deletions providers/resource/bin/dmi_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
#
# This file is part of Checkbox.
#
# Copyright 2011 Canonical Ltd.
# Copyright 2011-2025 Canonical Ltd.
#
# Checkbox 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.

#
# Checkbox is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Expand All @@ -27,6 +26,38 @@
COMMAND = "dmidecode"


def sane_product(og_product: str) -> str:
"""
Transform the product key into a usable form

The product key is basically free-form text. In order to make it more
usable in resource expressions, which usually want to know if a device is
portable (a laptop/tablet) or not, this cleans up the key to a "canonical"
answer, either `non-portable` or `portable`
"""
cleaned = og_product.lower().replace(" ", "-")
if cleaned in [
"desktop",
"low-profile-desktop",
"tower",
"mini-tower",
"space-saving",
"all-in-one",
"aio",
]:
return "not-portable"
elif cleaned in [
"notebook",
"laptop",
"portable",
"convertible",
"tablet",
"detachable",
]:
return "portable"
return "unknown"


class DmiResult:

attributes = (
Expand All @@ -45,7 +76,9 @@ def addDmiDevice(self, device):
for attribute in self.attributes:
value = getattr(device, attribute, None)
if value is not None:
print("%s: %s" % (attribute, value))
print("{}: {}".format(attribute, value))
if attribute == "product" and value:
print("{}: {}".format("sane_product", sane_product(value)))

print()

Expand Down
56 changes: 56 additions & 0 deletions providers/resource/tests/test_dmi_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
# This file is part of Checkbox.
#
# Copyright 2025 Canonical Ltd.
# Written by:
# Massimiliano Girardi <[email protected]>
#
# Checkbox 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.
#
# Checkbox 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 Checkbox. If not, see <http://www.gnu.org/licenses/>.

import unittest
from unittest import TestCase
from unittest.mock import MagicMock

import dmi_resource


class TestDmiResource(TestCase):
def test_sane_product_portable(self):
products = [
"Notebook",
"Laptop",
"Portable",
"Convertible",
"Tablet",
"Detachable",
]
category = set(map(dmi_resource.sane_product, products))
self.assertEqual(category, {"portable"})

def test_sane_product_non_portable(self):
products = [
"Desktop",
"Low Profile Desktop",
"Tower",
"Mini-Tower",
"Space Saving",
"All-in-One",
"aio",
]
category = set(map(dmi_resource.sane_product, products))
self.assertEqual(category, {"not-portable"})

def test_sane_product_unknown(self):
products = ["strange-iot-product"]
category = set(map(dmi_resource.sane_product, products))
self.assertEqual(category, {"unknown"})
Loading