Files
wehub-resource-sync 3e779be6f3
CI / lint (push) Failing after 13m4s
CI / test (3.11, ubuntu-latest) (push) Failing after 2m4s
CI / test (3.13, ubuntu-latest) (push) Successful in 13m30s
CI / test (3.14, ubuntu-latest) (push) Successful in 17m21s
CI / test (3.12, ubuntu-latest) (push) Successful in 17m55s
CI / discover-apps-ps (push) Successful in 1m56s
CI / test (3.9, ubuntu-latest) (push) Successful in 13m17s
CI / test (3.10, ubuntu-latest) (push) Successful in 26m21s
CI / audit (push) Successful in 13m38s
Deploy site / deploy (push) Has been cancelled
CI / test (3.14, ubuntu-24.04-arm) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:37 +08:00

311 lines
12 KiB
Python

# SPDX-License-Identifier: MIT
"""Tests for utils.specs — host CPU/RAM detection + tier presets (v0.2.1)."""
from __future__ import annotations
from unittest.mock import patch
from winpodx.utils.specs import (
HostSpecs,
TuningCapability,
all_tiers,
detect_host_specs,
format_tuning_summary,
recommend_tier,
recommend_tuning_profile,
)
class TestRecommendTier:
def test_high_when_both_axes_clear(self):
t = recommend_tier(HostSpecs(cpu_threads=16, ram_gb=64))
assert t.name == "high"
assert t.cpu_cores == 8
assert t.ram_gb == 12
def test_mid_when_both_axes_clear_mid(self):
t = recommend_tier(HostSpecs(cpu_threads=8, ram_gb=20))
assert t.name == "mid"
assert t.cpu_cores == 4
assert t.ram_gb == 6
def test_mid_bumps_to_8gb_at_24gb_host(self):
# #630: a >=24 GB host (still mid CPU tier) gets an 8 GB VM instead of
# 6 — Windows 11 runs better with 8 GB and the host can spare it.
for ram in (24, 28, 31):
t = recommend_tier(HostSpecs(cpu_threads=8, ram_gb=ram))
assert t.name == "mid", ram
assert t.cpu_cores == 4, ram
assert t.ram_gb == 8, ram
# Just under 24 GB stays at 6.
assert recommend_tier(HostSpecs(cpu_threads=8, ram_gb=23)).ram_gb == 6
def test_low_for_small_machines(self):
t = recommend_tier(HostSpecs(cpu_threads=2, ram_gb=8))
assert t.name == "low"
assert t.cpu_cores == 2
assert t.ram_gb == 4
def test_ram_rich_but_cpu_poor_falls_to_low(self):
# Picking the higher tier requires BOTH axes to clear — a host
# with 64 GB but only 4 cores still gets "low" (CPU is the
# bottleneck for the VM workload).
t = recommend_tier(HostSpecs(cpu_threads=4, ram_gb=64))
assert t.name == "low"
def test_cpu_rich_but_ram_poor_falls_to_low(self):
# Symmetric case — lots of CPU but only 8 GB RAM.
t = recommend_tier(HostSpecs(cpu_threads=24, ram_gb=8))
assert t.name == "low"
def test_exactly_at_mid_threshold(self):
t = recommend_tier(HostSpecs(cpu_threads=6, ram_gb=16))
assert t.name == "mid"
def test_just_below_mid_threshold(self):
t = recommend_tier(HostSpecs(cpu_threads=5, ram_gb=16))
assert t.name == "low" # cpu under 6 -> low
t = recommend_tier(HostSpecs(cpu_threads=6, ram_gb=15))
assert t.name == "low" # ram under 16 -> low
def test_exactly_at_high_threshold(self):
t = recommend_tier(HostSpecs(cpu_threads=12, ram_gb=32))
assert t.name == "high"
class TestDetectHostSpecs:
def test_returns_usable_object(self):
# Just verify the function runs and returns sane values on the
# test host — exact numbers vary by CI runner.
s = detect_host_specs()
assert s.cpu_threads >= 1
assert s.ram_gb >= 1
class TestAllTiers:
def test_returns_three_in_order(self):
tiers = all_tiers()
assert [t.name for t in tiers] == ["low", "mid", "high"]
# CPU and RAM must be monotonically non-decreasing.
cpus = [t.cpu_cores for t in tiers]
rams = [t.ram_gb for t in tiers]
assert cpus == sorted(cpus)
assert rams == sorted(rams)
def _cap(**overrides) -> TuningCapability:
"""Build a TuningCapability with sensible defaults; override per test."""
base = {
"invtsc": True,
"io_uring": True,
"hugepages_enabled": False,
"dedicated_host": True,
"kernel_version": (6, 18),
"cpu_vendor": "intel",
"nested_kvm": False,
}
base.update(overrides)
return TuningCapability(**base)
class TestRecommendTuningProfile:
"""Profile-resolution policy for #215. The capability is a snapshot of
host state; the user_pref dial decides how aggressive to be."""
def test_off_disables_everything(self):
p = recommend_tuning_profile(_cap(nested_kvm=True), user_pref="off")
assert p.name == "off"
assert not p.apply_invtsc
assert not p.apply_platform_tick
assert not p.apply_io_uring
assert not p.apply_cpu_pinning
# #245: new flags also gated off.
assert not p.apply_hv_enlightenments
assert not p.apply_virtio_rng
assert not p.apply_evmcs
assert not p.apply_nested_virt
def test_safe_applies_only_t1_tunings(self):
p = recommend_tuning_profile(_cap(), user_pref="safe")
assert p.name == "safe"
assert p.apply_invtsc
assert p.apply_platform_tick
# Host-setup tunings are off in safe mode regardless of capability.
assert not p.apply_io_uring
assert not p.apply_hugepages
assert not p.apply_cpu_pinning
assert not p.apply_no_balloon
# #245: hv-* + virtio-rng safe -> on. evmcs + nested-virt need
# explicit host-side opt-in -> off under "safe".
assert p.apply_hv_enlightenments
assert p.apply_virtio_rng
assert not p.apply_evmcs
assert not p.apply_nested_virt
def test_auto_applies_everything_supported(self):
p = recommend_tuning_profile(
_cap(hugepages_enabled=True, nested_kvm=True), user_pref="auto"
)
assert p.name == "auto"
assert p.apply_invtsc
assert p.apply_io_uring
assert p.apply_hugepages
assert p.apply_cpu_pinning # dedicated host
assert p.apply_platform_tick
assert p.apply_no_balloon
# #245: hv-* + virtio-rng always on under auto for x86 hosts.
assert p.apply_hv_enlightenments
assert p.apply_virtio_rng
# nested-virt + evmcs gated on nested_kvm; here it's True.
assert p.apply_evmcs # intel
assert p.apply_nested_virt
def test_auto_skips_unsupported_capabilities(self):
cap = _cap(invtsc=False, io_uring=False, dedicated_host=False, nested_kvm=False)
p = recommend_tuning_profile(cap, user_pref="auto")
assert not p.apply_invtsc
assert not p.apply_io_uring
assert not p.apply_cpu_pinning
assert not p.apply_no_balloon
# platform_tick is guest-side + always safe.
assert p.apply_platform_tick
# #245: hv-* + virtio-rng still on (x86 + always-safe). evmcs +
# nested-virt gated on nested_kvm -> off here.
assert p.apply_hv_enlightenments
assert p.apply_virtio_rng
assert not p.apply_evmcs
assert not p.apply_nested_virt
def test_invtsc_requires_x86_vendor(self):
# invtsc is x86-only; ARM TSC story is different. The recommender
# must not flip it on even when /proc/cpuinfo happens to expose
# the named flags (unlikely but defensive).
p = recommend_tuning_profile(_cap(cpu_vendor="arm"), user_pref="auto")
assert not p.apply_invtsc
# #245: hv-* + nested-virt are also x86-only.
assert not p.apply_hv_enlightenments
assert not p.apply_nested_virt
assert not p.apply_evmcs
# virtio-rng is generic; on for any vendor.
assert p.apply_virtio_rng
def test_auto_nested_virt_intel_uses_vmx(self):
# Intel + nested_kvm => +vmx + hv-evmcs.
p = recommend_tuning_profile(_cap(cpu_vendor="intel", nested_kvm=True), user_pref="auto")
assert p.apply_nested_virt
assert p.apply_evmcs
def test_auto_nested_virt_amd_uses_svm_no_evmcs(self):
# AMD + nested_kvm => +svm. evmcs is Intel-only -> stays off.
p = recommend_tuning_profile(_cap(cpu_vendor="amd", nested_kvm=True), user_pref="auto")
assert p.apply_nested_virt
assert not p.apply_evmcs
def test_auto_evmcs_requires_nested_kvm(self):
# Intel without nested_kvm exposed -> evmcs off (no-op without nesting).
p = recommend_tuning_profile(_cap(cpu_vendor="intel", nested_kvm=False), user_pref="auto")
assert not p.apply_evmcs
assert not p.apply_nested_virt
def test_performance_forces_pinning_and_no_balloon(self):
# `performance` bypasses cap.dedicated_host so soft-gated knobs
# (CPU pinning + no-balloon) come on even on a shared host.
cap = _cap(dedicated_host=False)
p = recommend_tuning_profile(cap, user_pref="performance")
assert p.name == "performance"
assert p.apply_cpu_pinning
assert p.apply_no_balloon
# Hard-gated knobs still respect capability detection.
assert p.apply_invtsc # invtsc=True in fixture
assert p.apply_io_uring # io_uring=True in fixture
# hv-* + virtio-rng same as auto.
assert p.apply_hv_enlightenments
assert p.apply_virtio_rng
def test_performance_still_skips_unsupported_hard_gates(self):
# invtsc + io_uring are HARD-gated -- performance can't force a
# CPU flag QEMU would reject or a kernel feature that crashes.
cap = _cap(invtsc=False, io_uring=False, dedicated_host=False)
p = recommend_tuning_profile(cap, user_pref="performance")
assert not p.apply_invtsc
assert not p.apply_io_uring
# Soft-gated knobs forced on regardless.
assert p.apply_cpu_pinning
assert p.apply_no_balloon
def test_performance_matches_auto_when_host_already_dedicated(self):
# On a dedicated host, performance and auto resolve to the same
# set of apply_* flags (only the .name differs).
cap = _cap(dedicated_host=True, nested_kvm=True)
perf = recommend_tuning_profile(cap, user_pref="performance")
auto = recommend_tuning_profile(cap, user_pref="auto")
for field in (
"apply_invtsc",
"apply_io_uring",
"apply_hugepages",
"apply_cpu_pinning",
"apply_platform_tick",
"apply_no_balloon",
"apply_hv_enlightenments",
"apply_virtio_rng",
"apply_evmcs",
"apply_nested_virt",
):
assert getattr(perf, field) == getattr(auto, field), (
f"{field} differs between performance and auto on dedicated host"
)
def test_unknown_pref_falls_back_to_auto(self):
# Defensive: a hand-edited TOML slipping past Config validation
# with a typo like "automatic" should be treated as auto, not
# silently kill all tunings.
p = recommend_tuning_profile(_cap(), user_pref="automatic-typo")
assert p.name == "auto"
class TestFormatTuningSummary:
def test_renders_yes_no_for_every_capability(self):
cap = _cap()
profile = recommend_tuning_profile(cap, user_pref="auto")
out = format_tuning_summary(cap, profile)
assert "invtsc" in out
assert "io_uring" in out
assert "hugepages" in out
assert "Profile: auto" in out
assert "+invtsc:" in out
# #245: new rows.
assert "nested_kvm" in out
assert "hv-* + no-hpet" in out
assert "virtio-rng" in out
assert "nested virt" in out
assert "hv-evmcs" in out
class TestDetectTuningCapabilityIntegration:
"""`detect_tuning_capability` reads /proc + os.uname; smoke-test that it
runs to completion on the actual test host without raising."""
def test_runs_without_error(self):
from winpodx.utils.specs import detect_tuning_capability
cap = detect_tuning_capability(vm_cpu_cores=4, vm_ram_gb=6)
assert isinstance(cap.invtsc, bool)
assert isinstance(cap.io_uring, bool)
assert isinstance(cap.hugepages_enabled, bool)
assert isinstance(cap.dedicated_host, bool)
assert cap.cpu_vendor in ("intel", "amd", "arm", "unknown")
def test_kernel_version_parses(self):
from winpodx.utils.specs import _read_kernel_version
with patch("os.uname") as mock_uname:
mock_uname.return_value.release = "6.18.29-1-longterm"
assert _read_kernel_version() == (6, 18)
def test_kernel_version_unparseable_returns_none(self):
from winpodx.utils.specs import _read_kernel_version
with patch("os.uname") as mock_uname:
mock_uname.return_value.release = "weird-no-numbers"
assert _read_kernel_version() is None