mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2025-06-17 00:07:37 +02:00
@ -97,6 +97,7 @@ parsers = [
|
||||
'wc',
|
||||
'who',
|
||||
'xml',
|
||||
'xrandr',
|
||||
'yaml',
|
||||
'zipinfo'
|
||||
]
|
||||
|
320
jc/parsers/xrandr.py
Normal file
320
jc/parsers/xrandr.py
Normal file
@ -0,0 +1,320 @@
|
||||
"""jc - JSON CLI output utility `xrandr` command output parser
|
||||
|
||||
Options supported:
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc.parsers.xrandr
|
||||
result = jc.parsers.xrandr.parse(xrandr_command_output)
|
||||
|
||||
Schema:
|
||||
{
|
||||
"screens": [
|
||||
{
|
||||
"screen_number": 0,
|
||||
"minimum_width": 8,
|
||||
"minimum_height": 8,
|
||||
"current_width": 1920,
|
||||
"current_height": 1080,
|
||||
"maximum_width": 32767,
|
||||
"maximum_height": 32767,
|
||||
"associated_device": {
|
||||
"associated_modes": [
|
||||
{
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"is_high_resolution": false,
|
||||
"frequencies": [
|
||||
{
|
||||
"frequency": 60.03,
|
||||
"is_current": true,
|
||||
"is_preferred": true
|
||||
},
|
||||
{
|
||||
"frequency": 59.93,
|
||||
"is_current": false,
|
||||
"is_preferred": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"resolution_width": 1680,
|
||||
"resolution_height": 1050,
|
||||
"is_high_resolution": false,
|
||||
"frequencies": [
|
||||
{
|
||||
"frequency": 59.88,
|
||||
"is_current": false,
|
||||
"is_preferred": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"is_connected": true,
|
||||
"is_primary": true,
|
||||
"device_name": "eDP1",
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"offset_width": 0,
|
||||
"offset_height": 0,
|
||||
"dimension_width": 310,
|
||||
"dimension_height": 170
|
||||
}
|
||||
}
|
||||
],
|
||||
"unassociated_devices": []
|
||||
}
|
||||
Translated from:
|
||||
Screen 0: minimum 8 x 8, current 1920 x 1080, maximum 32767 x 32767
|
||||
eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm
|
||||
1920x1080 60.03*+ 59.93
|
||||
1680x1050 59.88
|
||||
Examples:
|
||||
|
||||
$ xrandr | jc --xrandr
|
||||
"""
|
||||
import re
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info:
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
|
||||
version = "1.9"
|
||||
description = "`xrandr` command parser"
|
||||
author = "Kevin Lyter"
|
||||
author_email = "lyter_git at sent.com"
|
||||
|
||||
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
|
||||
compatible = ["linux", "darwin", "cygwin", "aix", "freebsd"]
|
||||
magic_commands = ["xrandr"]
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
try:
|
||||
from typing import TypedDict
|
||||
|
||||
Frequency = TypedDict(
|
||||
"Frequency",
|
||||
{
|
||||
"frequency": float,
|
||||
"is_current": bool,
|
||||
"is_preferred": bool,
|
||||
},
|
||||
)
|
||||
Mode = TypedDict(
|
||||
"Mode",
|
||||
{
|
||||
"resolution_width": int,
|
||||
"resolution_height": int,
|
||||
"is_high_resolution": bool,
|
||||
"frequencies": List[Frequency],
|
||||
},
|
||||
)
|
||||
Device = TypedDict(
|
||||
"Device",
|
||||
{
|
||||
"device_name": str,
|
||||
"is_connected": bool,
|
||||
"is_primary": bool,
|
||||
"resolution_width": int,
|
||||
"resolution_height": int,
|
||||
"offset_width": int,
|
||||
"offset_height": int,
|
||||
"dimension_width": int,
|
||||
"dimension_height": int,
|
||||
"associated_modes": List[Mode],
|
||||
},
|
||||
)
|
||||
Screen = TypedDict(
|
||||
"Screen",
|
||||
{
|
||||
"screen_number": int,
|
||||
"minimum_width": int,
|
||||
"minimum_height": int,
|
||||
"current_width": int,
|
||||
"current_height": int,
|
||||
"maximum_width": int,
|
||||
"maximum_height": int,
|
||||
"associated_device": Device,
|
||||
},
|
||||
)
|
||||
Response = TypedDict(
|
||||
"Response",
|
||||
{
|
||||
"screens": List[Screen],
|
||||
"unassociated_devices": List[Device],
|
||||
},
|
||||
)
|
||||
except ImportError:
|
||||
Screen = Dict[str, Union[int, str]]
|
||||
Device = Dict[str, Union[str, int, bool]]
|
||||
Frequency = Dict[str, Union[float, bool]]
|
||||
Mode = Dict[str, Union[int, bool, List[Frequency]]]
|
||||
Response = Dict[str, Union[Device, Mode, Screen]]
|
||||
|
||||
|
||||
_screen_pattern = (
|
||||
r"Screen (?P<screen_number>\d+): "
|
||||
+ "minimum (?P<minimum_width>\d+) x (?P<minimum_height>\d+), "
|
||||
+ "current (?P<current_width>\d+) x (?P<current_height>\d+), "
|
||||
+ "maximum (?P<maximum_width>\d+) x (?P<maximum_height>\d+)"
|
||||
)
|
||||
|
||||
|
||||
def _parse_screen(next_lines: List[str]) -> Optional[Screen]:
|
||||
next_line = next_lines.pop()
|
||||
result = re.match(_screen_pattern, next_line)
|
||||
if not result:
|
||||
next_lines.append(next_line)
|
||||
return None
|
||||
|
||||
raw_matches = result.groupdict()
|
||||
screen: Screen = {}
|
||||
for k, v in raw_matches.items():
|
||||
screen[k] = int(v)
|
||||
|
||||
if next_lines:
|
||||
device: Optional[Device] = _parse_device(next_lines)
|
||||
if device:
|
||||
screen["associated_device"] = device
|
||||
|
||||
return screen
|
||||
|
||||
|
||||
# eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis)
|
||||
# 310mm x 170mm
|
||||
# regex101 demo link
|
||||
_device_pattern = (
|
||||
r"(?P<device_name>.+) "
|
||||
+ "(?P<is_connected>(connected|disconnected)) ?"
|
||||
+ "(?P<is_primary> primary)? ?"
|
||||
+ "((?P<resolution_width>\d+)x(?P<resolution_height>\d+)"
|
||||
+ "\+(?P<offset_width>\d+)\+(?P<offset_height>\d+))? "
|
||||
+ "\(normal left inverted right x axis y axis\)"
|
||||
+ "( ((?P<dimension_width>\d+)mm x (?P<dimension_height>\d+)mm)?)?"
|
||||
)
|
||||
|
||||
|
||||
def _parse_device(next_lines: List[str]) -> Optional[Device]:
|
||||
if not next_lines:
|
||||
return None
|
||||
|
||||
next_line = next_lines.pop()
|
||||
result = re.match(_device_pattern, next_line)
|
||||
if not result:
|
||||
next_lines.append(next_line)
|
||||
return None
|
||||
|
||||
matches = result.groupdict()
|
||||
|
||||
device: Device = {
|
||||
"associated_modes": [],
|
||||
"is_connected": matches["is_connected"] == "connected",
|
||||
"is_primary": matches["is_primary"] is not None
|
||||
and len(matches["is_primary"]) > 0,
|
||||
"device_name": matches["device_name"],
|
||||
}
|
||||
for k, v in matches.items():
|
||||
if k not in {"is_connected", "is_primary", "device_name"}:
|
||||
try:
|
||||
if v:
|
||||
device[k] = int(v)
|
||||
except ValueError:
|
||||
print(f"Error: {next_line} : {k} - {v} is not int-able")
|
||||
|
||||
while next_lines:
|
||||
next_line = next_lines.pop()
|
||||
next_mode: Optional[Mode] = _parse_mode(next_line)
|
||||
if next_mode:
|
||||
device["associated_modes"].append(next_mode)
|
||||
else:
|
||||
next_lines.append(next_line)
|
||||
break
|
||||
return device
|
||||
|
||||
|
||||
# 1920x1080i 60.03*+ 59.93
|
||||
# 1920x1080 60.00 + 50.00 59.94
|
||||
_mode_pattern = r"\s*(?P<resolution_width>\d+)x(?P<resolution_height>\d+)(?P<is_high_resolution>i)?\s+(?P<rest>.*)"
|
||||
_frequencies_pattern = r"(((?P<frequency>\d+\.\d+)(?P<star>\*| |)(?P<plus>\+?)?)+)"
|
||||
|
||||
|
||||
def _parse_mode(line: str) -> Optional[Mode]:
|
||||
result = re.match(_mode_pattern, line)
|
||||
frequencies: List[Frequency] = []
|
||||
if not result:
|
||||
return None
|
||||
|
||||
d = result.groupdict()
|
||||
resolution_width = int(d["resolution_width"])
|
||||
resolution_height = int(d["resolution_height"])
|
||||
is_high_resolution = d["is_high_resolution"] is not None
|
||||
|
||||
mode: Mode = {
|
||||
"resolution_width": resolution_width,
|
||||
"resolution_height": resolution_height,
|
||||
"is_high_resolution": is_high_resolution,
|
||||
"frequencies": frequencies,
|
||||
}
|
||||
|
||||
result = re.finditer(_frequencies_pattern, d["rest"])
|
||||
if not result:
|
||||
return mode
|
||||
|
||||
for match in result:
|
||||
d = match.groupdict()
|
||||
frequency = float(d["frequency"])
|
||||
is_current = len(d["star"]) > 0
|
||||
is_preferred = len(d["plus"]) > 0
|
||||
f: Frequency = {
|
||||
"frequency": frequency,
|
||||
"is_current": is_current,
|
||||
"is_preferred": is_preferred,
|
||||
}
|
||||
mode["frequencies"].append(f)
|
||||
return mode
|
||||
|
||||
|
||||
def parse(data: str, raw=False, quiet=False):
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string) text data to parse
|
||||
raw: (boolean) output preprocessed JSON if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
List of Dictionaries. Raw or processed structured data.
|
||||
"""
|
||||
if not quiet:
|
||||
jc.utils.compatibility(__name__, info.compatible)
|
||||
|
||||
warned = False
|
||||
parent = ""
|
||||
next_is_parent = False
|
||||
new_section = False
|
||||
|
||||
linedata = data.splitlines()
|
||||
linedata.reverse() # For popping
|
||||
|
||||
result: Response = {"screens": [], "unassociated_devices": []}
|
||||
if jc.utils.has_data(data):
|
||||
result: Response = {"screens": [], "unassociated_devices": []}
|
||||
while linedata:
|
||||
screen = _parse_screen(linedata)
|
||||
if screen:
|
||||
result["screens"].append(screen)
|
||||
else:
|
||||
device = _parse_device(linedata)
|
||||
if device:
|
||||
result["unassociated_devices"].append(device)
|
||||
if not result["unassociated_devices"] and not result["screens"]:
|
||||
return {}
|
||||
return result
|
38
tests/fixtures/generic/xrandr.out
vendored
Normal file
38
tests/fixtures/generic/xrandr.out
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
Screen 0: minimum 8 x 8, current 1920 x 1080, maximum 32767 x 32767
|
||||
eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm
|
||||
1920x1080 60.03*+ 59.93
|
||||
1680x1050 59.88
|
||||
1400x1050 59.98
|
||||
1600x900 60.00 59.95 59.82
|
||||
1280x1024 60.02
|
||||
1400x900 59.96 59.88
|
||||
1280x960 60.00
|
||||
1368x768 60.00 59.88 59.85
|
||||
1280x800 59.81 59.91
|
||||
1280x720 59.86 60.00 59.74
|
||||
1024x768 60.00
|
||||
1024x576 60.00 59.90 59.82
|
||||
960x540 60.00 59.63 59.82
|
||||
800x600 60.32 56.25
|
||||
864x486 60.00 59.92 59.57
|
||||
640x480 59.94
|
||||
720x405 59.51 60.00 58.99
|
||||
640x360 59.84 59.32 60.00
|
||||
DP1 disconnected (normal left inverted right x axis y axis)
|
||||
DP2 disconnected (normal left inverted right x axis y axis)
|
||||
HDMI1 connected (normal left inverted right x axis y axis)
|
||||
1920x1080 60.00 + 50.00 59.94
|
||||
1920x1080i 60.00 50.00 59.94
|
||||
1680x1050 59.88
|
||||
1280x1024 75.02 60.02
|
||||
1440x900 59.90
|
||||
1280x960 60.00
|
||||
1280x720 60.00 50.00 59.94
|
||||
1024x768 75.03 70.07 60.00
|
||||
832x624 74.55
|
||||
800x600 72.19 75.00 60.32 56.25
|
||||
720x576 50.00
|
||||
720x480 60.00 59.94
|
||||
640x480 75.00 72.81 66.67 60.00 59.94
|
||||
720x400 70.08
|
||||
VIRTUAL1 disconnected (normal left inverted right x axis y axis)
|
43
tests/fixtures/generic/xrandr_2.out
vendored
Normal file
43
tests/fixtures/generic/xrandr_2.out
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 16384 x 16384
|
||||
eDP-1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 309mm x 174mm
|
||||
1920x1080 60.03*+ 60.01 59.97 59.96 59.93
|
||||
1680x1050 59.95 59.88
|
||||
1400x1050 59.98
|
||||
1600x900 59.99 59.94 59.95 59.82
|
||||
1280x1024 60.02
|
||||
1400x900 59.96 59.88
|
||||
1280x960 60.00
|
||||
1440x810 60.00 59.97
|
||||
1368x768 59.88 59.85
|
||||
1280x800 59.99 59.97 59.81 59.91
|
||||
1280x720 60.00 59.99 59.86 59.74
|
||||
1024x768 60.04 60.00
|
||||
960x720 60.00
|
||||
928x696 60.05
|
||||
896x672 60.01
|
||||
1024x576 59.95 59.96 59.90 59.82
|
||||
960x600 59.93 60.00
|
||||
960x540 59.96 59.99 59.63 59.82
|
||||
800x600 60.00 60.32 56.25
|
||||
840x525 60.01 59.88
|
||||
864x486 59.92 59.57
|
||||
700x525 59.98
|
||||
800x450 59.95 59.82
|
||||
640x512 60.02
|
||||
700x450 59.96 59.88
|
||||
640x480 60.00 59.94
|
||||
720x405 59.51 58.99
|
||||
684x384 59.88 59.85
|
||||
640x400 59.88 59.98
|
||||
640x360 59.86 59.83 59.84 59.32
|
||||
512x384 60.00
|
||||
512x288 60.00 59.92
|
||||
480x270 59.63 59.82
|
||||
400x300 60.32 56.34
|
||||
432x243 59.92 59.57
|
||||
320x240 60.05
|
||||
360x202 59.51 59.13
|
||||
320x180 59.84 59.32
|
||||
DP-1 disconnected (normal left inverted right x axis y axis)
|
||||
HDMI-1 disconnected (normal left inverted right x axis y axis)
|
||||
DP-2 disconnected (normal left inverted right x axis y axis)
|
15
tests/fixtures/generic/xrandr_device.out
vendored
Normal file
15
tests/fixtures/generic/xrandr_device.out
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
HDMI1 connected (normal left inverted right x axis y axis)
|
||||
1920x1080 60.00 + 50.00 59.94
|
||||
1920x1080i 60.00 50.00 59.94
|
||||
1680x1050 59.88
|
||||
1280x1024 75.02 60.02
|
||||
1440x900 59.90
|
||||
1280x960 60.00
|
||||
1280x720 60.00 50.00 59.94
|
||||
1024x768 75.03 70.07 60.00
|
||||
832x624 74.55
|
||||
800x600 72.19 75.00 60.32 56.25
|
||||
720x576 50.00
|
||||
720x480 60.00 59.94
|
||||
640x480 75.00 72.81 66.67 60.00 59.94
|
||||
720x400 70.08
|
56
tests/fixtures/generic/xrandr_simple.json
vendored
Normal file
56
tests/fixtures/generic/xrandr_simple.json
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"screens": [
|
||||
{
|
||||
"screen_number": 0,
|
||||
"minimum_width": 8,
|
||||
"minimum_height": 8,
|
||||
"current_width": 1920,
|
||||
"current_height": 1080,
|
||||
"maximum_width": 32767,
|
||||
"maximum_height": 32767,
|
||||
"associated_device": {
|
||||
"associated_modes": [
|
||||
{
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"is_high_resolution": false,
|
||||
"frequencies": [
|
||||
{
|
||||
"frequency": 60.03,
|
||||
"is_current": true,
|
||||
"is_preferred": true
|
||||
},
|
||||
{
|
||||
"frequency": 59.93,
|
||||
"is_current": false,
|
||||
"is_preferred": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"resolution_width": 1680,
|
||||
"resolution_height": 1050,
|
||||
"is_high_resolution": false,
|
||||
"frequencies": [
|
||||
{
|
||||
"frequency": 59.88,
|
||||
"is_current": false,
|
||||
"is_preferred": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"is_connected": true,
|
||||
"is_primary": true,
|
||||
"device_name": "eDP1",
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"offset_width": 0,
|
||||
"offset_height": 0,
|
||||
"dimension_width": 310,
|
||||
"dimension_height": 170
|
||||
}
|
||||
}
|
||||
],
|
||||
"unassociated_devices": []
|
||||
}
|
4
tests/fixtures/generic/xrandr_simple.out
vendored
Normal file
4
tests/fixtures/generic/xrandr_simple.out
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
Screen 0: minimum 8 x 8, current 1920 x 1080, maximum 32767 x 32767
|
||||
eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm
|
||||
1920x1080 60.03*+ 59.93
|
||||
1680x1050 59.88
|
172
tests/test_xrandr.py
Normal file
172
tests/test_xrandr.py
Normal file
@ -0,0 +1,172 @@
|
||||
import json
|
||||
import re
|
||||
import unittest
|
||||
from typing import Optional
|
||||
|
||||
from jc.parsers.xrandr import (
|
||||
_parse_screen,
|
||||
_parse_device,
|
||||
_parse_mode,
|
||||
_device_pattern,
|
||||
_screen_pattern,
|
||||
_mode_pattern,
|
||||
_frequencies_pattern,
|
||||
parse,
|
||||
Mode,
|
||||
Device,
|
||||
Screen,
|
||||
)
|
||||
|
||||
|
||||
class XrandrTests(unittest.TestCase):
|
||||
def test_regexes(self):
|
||||
devices = [
|
||||
"HDMI1 connected (normal left inverted right x axis y axis)",
|
||||
"VIRTUAL1 disconnected (normal left inverted right x axis y axis)",
|
||||
"eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm",
|
||||
"eDP-1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 309mm x 174mm"
|
||||
]
|
||||
for device in devices:
|
||||
self.assertIsNotNone(re.match(_device_pattern, device))
|
||||
|
||||
screens = [
|
||||
"Screen 0: minimum 8 x 8, current 1920 x 1080, maximum 32767 x 32767",
|
||||
"Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 16384 x 16384"
|
||||
]
|
||||
for screen in screens:
|
||||
self.assertIsNotNone(re.match(_screen_pattern, screen))
|
||||
|
||||
modes = [
|
||||
"1920x1080 60.03*+ 59.93",
|
||||
"1680x1050 59.88",
|
||||
"1400x1050 59.98",
|
||||
"1600x900 60.00 59.95 59.82",
|
||||
"1280x1024 60.02",
|
||||
"1400x900 59.96 59.88",
|
||||
]
|
||||
for mode in modes:
|
||||
match = re.match(_mode_pattern, mode)
|
||||
self.assertIsNotNone(match)
|
||||
if match:
|
||||
rest = match.groupdict()["rest"]
|
||||
self.assertIsNotNone(re.match(_frequencies_pattern, rest))
|
||||
|
||||
def test_screens(self):
|
||||
sample = "Screen 0: minimum 8 x 8, current 1920 x 1080, maximum 32767 x 32767"
|
||||
|
||||
actual: Optional[Screen] = _parse_screen([sample])
|
||||
self.assertIsNotNone(actual)
|
||||
|
||||
expected = {
|
||||
"screen_number": 0,
|
||||
"minimum_width": 8,
|
||||
"minimum_height": 8,
|
||||
"current_width": 1920,
|
||||
"current_height": 1080,
|
||||
"maximum_width": 32767,
|
||||
"maximum_height": 32767,
|
||||
}
|
||||
if actual:
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, actual[k], f"screens regex failed on {k}")
|
||||
|
||||
sample = "Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 16384 x 16384"
|
||||
actual = _parse_screen([sample])
|
||||
if actual:
|
||||
self.assertEqual(320, actual["minimum_width"])
|
||||
else:
|
||||
raise AssertionError("Screen should not be None")
|
||||
|
||||
def test_device(self):
|
||||
# regex101 sample link for tests/edits https://regex101.com/r/3cHMv3/1
|
||||
sample = "eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm"
|
||||
actual: Optional[Device] = _parse_device([sample])
|
||||
|
||||
expected = {
|
||||
"device_name": "eDP1",
|
||||
"is_connected": True,
|
||||
"is_primary": True,
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"offset_width": 0,
|
||||
"offset_height": 0,
|
||||
"dimension_width": 310,
|
||||
"dimension_height": 170,
|
||||
}
|
||||
|
||||
self.assertIsNotNone(actual)
|
||||
|
||||
if actual:
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, actual[k], f"Devices regex failed on {k}")
|
||||
|
||||
with open("tests/fixtures/generic/xrandr_device.out", "r") as f:
|
||||
extended_sample = f.read().splitlines()
|
||||
extended_sample.reverse()
|
||||
|
||||
device = _parse_device(extended_sample)
|
||||
if device:
|
||||
self.assertEqual(
|
||||
59.94, device["associated_modes"][12]["frequencies"][4]["frequency"]
|
||||
)
|
||||
|
||||
def test_mode(self):
|
||||
sample_1 = "1920x1080 60.03*+ 59.93"
|
||||
expected = {
|
||||
"frequencies": [
|
||||
{"frequency": 60.03, "is_current": True, "is_preferred": True},
|
||||
{"frequency": 59.93, "is_current": False, "is_preferred": False},
|
||||
],
|
||||
"resolution_width": 1920,
|
||||
"resolution_height": 1080,
|
||||
"is_high_resolution": False,
|
||||
}
|
||||
actual: Optional[Mode] = _parse_mode(sample_1)
|
||||
|
||||
self.assertIsNotNone(actual)
|
||||
|
||||
if actual:
|
||||
for k, v in expected.items():
|
||||
self.assertEqual(v, actual[k], f"mode regex failed on {k}")
|
||||
|
||||
sample_2 = " 1920x1080i 60.00 50.00 59.94"
|
||||
actual: Optional[Mode] = _parse_mode(sample_2)
|
||||
self.assertIsNotNone(actual)
|
||||
if actual:
|
||||
self.assertEqual(True, actual["is_high_resolution"])
|
||||
self.assertEqual(50.0, actual["frequencies"][1]["frequency"])
|
||||
|
||||
def test_complete(self):
|
||||
self.maxDiff = None
|
||||
with open("tests/fixtures/generic/xrandr.out", "r") as f:
|
||||
txt = f.read()
|
||||
actual = parse(txt)
|
||||
|
||||
self.assertEqual(1, len(actual["screens"]))
|
||||
self.assertEqual(4, len(actual["unassociated_devices"]))
|
||||
self.assertEqual(
|
||||
18, len(actual["screens"][0]["associated_device"]["associated_modes"])
|
||||
)
|
||||
|
||||
with open("tests/fixtures/generic/xrandr_2.out", "r") as f:
|
||||
txt = f.read()
|
||||
actual = parse(txt)
|
||||
|
||||
self.assertEqual(1, len(actual["screens"]))
|
||||
self.assertEqual(3, len(actual["unassociated_devices"]))
|
||||
self.assertEqual(
|
||||
38, len(actual["screens"][0]["associated_device"]["associated_modes"])
|
||||
)
|
||||
|
||||
with open("tests/fixtures/generic/simple_xrandr.out", "r") as f:
|
||||
txt = f.read()
|
||||
actual = parse(txt)
|
||||
|
||||
with open("tests/fixtures/generic/simple_xrandr.json", "w") as f:
|
||||
json.dump(actual, f, indent=True)
|
||||
|
||||
self.assertEqual(1, len(actual["screens"]))
|
||||
self.assertEqual(0, len(actual["unassociated_devices"]))
|
||||
self.assertEqual(
|
||||
2, len(actual["screens"][0]["associated_device"]["associated_modes"])
|
||||
)
|
Reference in New Issue
Block a user