Files
saphid--frame-control/tests/fakeframe/steamos-devkit-service/hooks/devkit-1-identify
T
saphidandClaude Opus 5.5 0181071b83 Tests against a fake Frame, and a headset smoke test
tests/fakeframe: a container that stands in for the Frame (Arch Linux, or
Arch Linux ARM on arm64) with sshd, rsync, Valve's steamos-devkit-service
and hooks (vendored unmodified), a fake Steam client for the devkit pipe and
the DevTools port (the app's JavaScript runs in Node against stand-in
SteamClient/appStore objects), stubs for steam, wpctl, flatpak, podman,
Lepton and friends, battery and thermal files under /sys, and fault
switches (fakeframe-ctl): pairing mode, approve/deny/timeout, Steam not
running, headset asleep, sshd off, disk full, runtimes missing. A second
container is the computer running Frame Control.

tests/e2e: 29 tests driving the real ui/server.py, frame_connect.py and
frame_titles.py against it; skipped unless FRAME_E2E=1. scripts/e2e.sh
builds, runs and tears down; CI runs it on ubuntu-24.04-arm.

tests/smoke + scripts/frame-smoke.sh: the core cases against a real Frame,
recorded with its BUILD_ID, cleaning up after itself; --pair to pair a
throwaway key. docs/testing.md describes the layers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 17:40:09 +10:00

152 lines
5.1 KiB
Python
Executable File

#!/usr/bin/env python3
# encoding: utf-8
# This file is part of steamos-devkit
# SPDX-License-Identifier: LGPL-2.1+
#
# Copyright 2017-2018 Collabora Ltd
#
# This package is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This package 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this package. If not, see
# <http://www.gnu.org/licenses/>.
# Sample script run to identify the machine we are running on. This
# is used by the devkit daemon to choose an identity for the machine,
# and can also be run directly.
#
# A script or binary (written in any language) named devkit-1-identify
# can be placed in the same directory as this sample, and it will be
# used preferentially.
#
# Execution environment:
# - Runs as root, or as an ordinary user who can run games
# - Part of a non-interactive secure shell session
# Input:
# - None
# Output:
# - Exit 0 on success or nonzero on error
# - On success, must print JSON to stdout containing one object with
# the following keys and string values, all of which are optional:
# - "machine_id": The hexadecimal systemd/D-Bus machine-id(5)
# (in the case of a conflict between systemd's /etc/machine-id and
# D-Bus' traditional /var/lib/dbus/machine-id, the systemd version
# should be preferred)
# - "hostname": The machine-readable hostname as set by sethostname(2)
# - "pretty_hostname": A human-friendly version of the hostname as
# found in systemd's machine-info(5)
# - "product_family", "product_name", "product_serial", "product_uuid",
# "sys_vendor": as for /sys/devices/virtual/dmi/id
# - "product": The best human-friendly description of the machine
# hardware we can devise, for example "Alienware ASM100"
# - "serial": The best hardware serial number we can devise
# - "machine_name": The best unique name for the machine that we can
# devise
# - Any diagnostic messages must be printed to stderr only
import json
import os
import re
import socket
import subprocess
import sys
_MACHINE_ID_RE = re.compile(r'^[0-9A-Fa-f]{32,32}$')
_USELESS_HOSTNAMES = frozenset((
'debian',
'host',
'localhost',
'steamos',
'ubuntu',
))
# The case combination sometimes varies, so this is lowercased and we
# use lower() to compare.
_USELESS_DMI_NAMES = frozenset((
'to be filled by o.e.m.',
))
if __name__ == '__main__':
info = {}
steam_serialnumber = None
for k in ('product_family', 'product_name', 'product_serial',
'product_uuid', 'sys_vendor'):
try:
with open('/sys/devices/virtual/dmi/id/{}'.format(k)) as reader:
v = reader.read().strip()
if v and v.lower() not in _USELESS_DMI_NAMES:
info[k] = v
except (IOError, OSError, UnicodeError):
pass
try:
for f in ('/etc/machine-id', '/var/lib/dbus/machine-id'):
with open(f) as reader:
v = reader.read().strip()
if _MACHINE_ID_RE.match(v):
info['machine_id'] = v
except (IOError, OSError, UnicodeError):
pass
if 'product_family' in info and 'sys_vendor' in info:
info['product'] = '{sys_vendor} {product_family}'.format(**info)
elif 'product_name' in info and 'sys_vendor' in info:
info['product'] = '{sys_vendor} {product_name}'.format(**info)
elif 'product_family' in info:
info['product'] = info['product_family']
elif 'product_name' in info:
info['product'] = info['product_name']
if os.access('/usr/bin/hostnamectl', os.X_OK):
try:
v = subprocess.check_output([
'hostnamectl', '--pretty',
]).decode('utf-8').strip()
except (subprocess.CalledProcessError, UnicodeError):
pass
else:
if v:
info['pretty_hostname'] = v
v = subprocess.check_output([
'hostnamectl', '--static',
]).decode('utf-8').strip()
if v:
info['hostname'] = v
if 'hostname' not in info:
try:
info['hostname'] = socket.gethostname()
except (IOError, OSError, UnicodeError):
pass
if steam_serialnumber:
info['serial'] = steam_serialnumber
elif 'product_serial' in info:
info['serial'] = info['product_serial']
if 'product' in info:
info['machine_name'] = info['product']
if 'hostname' in info and info['hostname'] not in _USELESS_HOSTNAMES:
info['machine_name'] = info['hostname']
if 'pretty_hostname' in info:
info['machine_name'] = info['pretty_hostname']
json.dump(info, sys.stdout, indent=4, sort_keys=True)
print()