# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import annotations
import ast
import functools
import inspect
import json
import os
from pathlib import Path
from extra_platforms import (
AARCH64,
ALL_TRAITS,
GITHUB_CI,
MACOS,
UBUNTU,
WINDOWS,
WSL2,
X86_64,
current_architecture,
current_ci,
current_os,
current_platform,
current_traits,
is_aarch64,
is_aix,
is_all_ci,
is_altlinux,
is_amzn,
is_android,
is_arch,
is_arm,
is_armv6l,
is_armv7l,
is_armv8l,
is_azure_pipelines,
is_bamboo,
is_buildkite,
is_buildroot,
is_cachyos,
is_centos,
is_circle_ci,
is_cirrus_ci,
is_cloudlinux,
is_codebuild,
is_cygwin,
is_debian,
is_exherbo,
is_fedora,
is_freebsd,
is_gentoo,
is_github_ci,
is_gitlab_ci,
is_guix,
is_heroku_ci,
is_hurd,
is_i386,
is_i586,
is_i686,
is_ibm_powerkvm,
is_kvmibm,
is_linuxmint,
is_loongarch64,
is_macos,
is_mageia,
is_mandriva,
is_midnightbsd,
is_mips,
is_mips64,
is_mips64el,
is_mipsel,
is_netbsd,
is_nobara,
is_openbsd,
is_opensuse,
is_oracle,
is_parallels,
is_pidora,
is_ppc,
is_ppc64,
is_ppc64le,
is_raspbian,
is_rhel,
is_riscv32,
is_riscv64,
is_rocky,
is_s390x,
is_scientific,
is_slackware,
is_sles,
is_solaris,
is_sparc,
is_sparc64,
is_sunos,
is_teamcity,
is_travis_ci,
is_tumbleweed,
is_tuxedo,
is_ubuntu,
is_ultramarine,
is_unknown_architecture,
is_unknown_ci,
is_unknown_linux,
is_wasm32,
is_wasm64,
is_windows,
is_wsl1,
is_wsl2,
is_x86_64,
is_xenserver,
)
from extra_platforms import detection as detection_module
from extra_platforms.pytest import unless_github_ci
[docs]
def test_detection_functions():
for platform in ALL_TRAITS:
check_func_id = f"is_{platform.id}"
assert check_func_id in globals()
check_func = globals()[check_func_id]
assert isinstance(check_func, functools._lru_cache_wrapper)
assert isinstance(check_func(), bool)
assert check_func() == platform.current
[docs]
def test_detection_heuristics_sorting():
"""Detection heuristics must be sorted within each section."""
detection_path = Path(inspect.getfile(detection_module))
tree = ast.parse(detection_path.read_bytes())
source_lines = detection_path.read_text().splitlines()
# Find section boundaries by looking for comment markers.
arch_section_start = None
platform_section_start = None
ci_section_start = None
for i, line in enumerate(source_lines, start=1):
if "Architecture detection heuristics" in line:
arch_section_start = i
elif "Platform detection heuristics" in line:
platform_section_start = i
elif "CI/CD detection heuristics" in line:
ci_section_start = i
assert arch_section_start is not None, "Architecture section not found"
assert platform_section_start is not None, "Platform section not found"
assert ci_section_start is not None, "CI/CD section not found"
assert arch_section_start < platform_section_start
assert platform_section_start < ci_section_start
# Collect heuristic functions by section.
all_heuristic_ids = []
arch_heuristics = []
platform_heuristics = []
ci_heuristics = []
for node in tree.body:
if isinstance(node, ast.FunctionDef) and node.name.startswith("is_"):
func_id = node.name
assert func_id.islower()
all_heuristic_ids.append(func_id)
line_no = node.lineno
if line_no >= arch_section_start and line_no < platform_section_start:
arch_heuristics.append(func_id)
elif line_no >= platform_section_start and line_no < ci_section_start:
platform_heuristics.append(func_id)
elif line_no >= ci_section_start:
ci_heuristics.append(func_id)
# Check there is no extra "is_" function.
assert {f"is_{p.id}" for p in ALL_TRAITS} == set(all_heuristic_ids)
# We only allow one generic "is_unknown*()" detection heuristics per category.
for heuristics in [arch_heuristics, platform_heuristics, ci_heuristics]:
non_generic_func_ids = [
func_id for func_id in heuristics if func_id.startswith("is_unknown")
]
assert len(non_generic_func_ids) <= 1, (
f"More than 1 is_unknown*() detection heuristics defined in {heuristics!r}"
)
if len(non_generic_func_ids):
assert non_generic_func_ids[-1].startswith("is_unknown")
# Verify each category is sorted alphabetically within itself.
assert non_generic_func_ids == sorted(non_generic_func_ids), (
f"Heuristics are not sorted: {non_generic_func_ids!r}"
)
[docs]
@functools.cache
def github_runner_os() -> str | None:
"""Returns the OS name as defined in the GitHub Actions matrix context.
.. caution::
This only works when running inside a GitHub Actions job that uses a ``matrix``
strategy with an ``os`` variant. Which is the case for the ``extra-platforms``
workflows.
"""
matrix_context_str = os.environ.get("EXTRA_PLATFORMS_TEST_MATRIX", "{}")
matrix_context = json.loads(matrix_context_str)
os_value = matrix_context.get("os")
if isinstance(os_value, str):
return os_value
return None
[docs]
@unless_github_ci
def test_github_runner_detection():
"""Test GitHub runner OS.
List of available GitHub runner images:
https://github.com/actions/runner-images#available-images
"""
assert is_all_ci()
assert is_github_ci()
assert current_ci() is GITHUB_CI
assert GITHUB_CI in current_traits()
assert current_architecture() in current_traits()
assert current_platform() in current_traits()
assert current_os() in current_traits()
assert current_ci() in current_traits()
assert github_runner_os() is not None, (
"The EXTRA_PLATFORMS_TEST_MATRIX environment variable is not set. "
"This test must be run inside a GitHub Actions job using a matrix strategy."
)
if github_runner_os() in {
"ubuntu-slim",
"ubuntu-24.04",
"ubuntu-22.04",
"macos-15-intel",
"windows-2025",
"windows-2022",
}:
assert is_x86_64()
assert current_architecture() is X86_64
assert X86_64 in current_traits()
else:
assert is_aarch64()
assert current_architecture() is AARCH64
assert AARCH64 in current_traits()
if github_runner_os() in {
"ubuntu-latest",
"ubuntu-slim",
"ubuntu-24.04",
"ubuntu-24.04-arm",
"ubuntu-22.04",
"ubuntu-22.04-arm",
}:
assert is_ubuntu()
assert current_platform() is UBUNTU
assert current_os() is UBUNTU
if github_runner_os() == "ubuntu-slim":
assert is_wsl2()
assert current_traits() == {GITHUB_CI, UBUNTU, WSL2, current_architecture()}
else:
assert not is_wsl2()
assert current_traits() == {GITHUB_CI, UBUNTU, current_architecture()}
if github_runner_os() in {
"macos-latest",
"macos-latest-large",
"macos-26",
"macos-26-xlarge",
"macos-15",
"macos-15-intel",
"macos-15-large",
"macos-15-xlarge",
"macos-14",
"macos-14-large",
"macos-14-xlarge",
}:
assert is_macos()
assert current_platform() is MACOS
assert current_os() is MACOS
assert current_traits() == {GITHUB_CI, MACOS, current_architecture()}
if github_runner_os() in {
"windows-latest",
"windows-11-arm",
"windows-2025",
"windows-2022",
}:
assert is_windows()
assert current_platform() is WINDOWS
assert current_os() is WINDOWS
assert current_traits() == {GITHUB_CI, WINDOWS, current_architecture()}
[docs]
def test_mutual_exclusion():
"""Only directly tests OSes on which the test suite is running via GitHub
actions."""
if is_ubuntu():
assert not is_aix()
assert not is_altlinux()
assert not is_amzn()
assert not is_android()
assert not is_arch()
assert not is_buildroot()
assert not is_cachyos()
assert not is_centos()
assert not is_cloudlinux()
assert not is_cygwin()
assert not is_debian()
assert not is_exherbo()
assert not is_fedora()
assert not is_freebsd()
assert not is_gentoo()
assert not is_guix()
assert not is_hurd()
assert not is_ibm_powerkvm()
assert not is_kvmibm()
assert not is_linuxmint()
assert not is_macos()
assert not is_mageia()
assert not is_mandriva()
assert not is_midnightbsd()
assert not is_netbsd()
assert not is_nobara()
assert not is_openbsd()
assert not is_opensuse()
assert not is_oracle()
assert not is_parallels()
assert not is_pidora()
assert not is_raspbian()
assert not is_rhel()
assert not is_rocky()
assert not is_scientific()
assert not is_slackware()
assert not is_sles()
assert not is_solaris()
assert not is_sunos()
assert not is_tumbleweed()
assert not is_tuxedo()
assert is_ubuntu()
assert not is_ultramarine()
assert not is_unknown_linux()
assert not is_windows()
assert not is_wsl1()
# ubuntu-slim is a GitHub actions image running on WSL2.
if github_runner_os() == "ubuntu-slim":
assert is_wsl2()
else:
assert not is_wsl2()
assert not is_xenserver()
if is_macos():
assert not is_aix()
assert not is_altlinux()
assert not is_amzn()
assert not is_android()
assert not is_arch()
assert not is_buildroot()
assert not is_cachyos()
assert not is_centos()
assert not is_cloudlinux()
assert not is_cygwin()
assert not is_debian()
assert not is_exherbo()
assert not is_fedora()
assert not is_freebsd()
assert not is_gentoo()
assert not is_guix()
assert not is_hurd()
assert not is_ibm_powerkvm()
assert not is_kvmibm()
assert not is_linuxmint()
assert is_macos()
assert not is_mageia()
assert not is_mandriva()
assert not is_midnightbsd()
assert not is_netbsd()
assert not is_nobara()
assert not is_openbsd()
assert not is_opensuse()
assert not is_oracle()
assert not is_parallels()
assert not is_pidora()
assert not is_raspbian()
assert not is_rhel()
assert not is_rocky()
assert not is_scientific()
assert not is_slackware()
assert not is_sles()
assert not is_solaris()
assert not is_sunos()
assert not is_tumbleweed()
assert not is_tuxedo()
assert not is_ubuntu()
assert not is_ultramarine()
assert not is_unknown_linux()
assert not is_windows()
assert not is_wsl1()
assert not is_wsl2()
assert not is_xenserver()
if is_windows():
assert not is_aix()
assert not is_altlinux()
assert not is_amzn()
assert not is_android()
assert not is_arch()
assert not is_buildroot()
assert not is_cachyos()
assert not is_centos()
assert not is_cloudlinux()
assert not is_cygwin()
assert not is_debian()
assert not is_exherbo()
assert not is_fedora()
assert not is_freebsd()
assert not is_gentoo()
assert not is_guix()
assert not is_hurd()
assert not is_ibm_powerkvm()
assert not is_kvmibm()
assert not is_linuxmint()
assert not is_macos()
assert not is_mageia()
assert not is_mandriva()
assert not is_midnightbsd()
assert not is_netbsd()
assert not is_nobara()
assert not is_openbsd()
assert not is_opensuse()
assert not is_oracle()
assert not is_parallels()
assert not is_pidora()
assert not is_raspbian()
assert not is_rhel()
assert not is_rocky()
assert not is_scientific()
assert not is_slackware()
assert not is_sles()
assert not is_solaris()
assert not is_sunos()
assert not is_tumbleweed()
assert not is_tuxedo()
assert not is_ubuntu()
assert not is_ultramarine()
assert not is_unknown_linux()
assert is_windows()
assert not is_wsl1()
assert not is_wsl2()
assert not is_xenserver()
if is_github_ci():
assert not is_azure_pipelines()
assert not is_bamboo()
assert not is_buildkite()
assert not is_circle_ci()
assert not is_cirrus_ci()
assert not is_codebuild()
assert is_github_ci()
assert not is_gitlab_ci()
assert not is_heroku_ci()
assert not is_teamcity()
assert not is_travis_ci()
assert not is_unknown_ci()
if is_x86_64():
assert not is_i386()
assert not is_i586()
assert not is_i686()
assert is_x86_64()
assert not is_arm()
assert not is_armv6l()
assert not is_armv7l()
assert not is_armv8l()
assert not is_aarch64()
assert not is_mips()
assert not is_mipsel()
assert not is_mips64()
assert not is_mips64el()
assert not is_ppc()
assert not is_ppc64()
assert not is_ppc64le()
assert not is_riscv32()
assert not is_riscv64()
assert not is_sparc()
assert not is_sparc64()
assert not is_s390x()
assert not is_loongarch64()
assert not is_unknown_architecture()
assert not is_wasm32()
assert not is_wasm64()
if is_aarch64():
assert not is_i386()
assert not is_i586()
assert not is_i686()
assert not is_x86_64()
assert not is_arm()
assert not is_armv6l()
assert not is_armv7l()
assert not is_armv8l()
assert is_aarch64()
assert not is_mips()
assert not is_mipsel()
assert not is_mips64()
assert not is_mips64el()
assert not is_ppc()
assert not is_ppc64()
assert not is_ppc64le()
assert not is_riscv32()
assert not is_riscv64()
assert not is_sparc()
assert not is_sparc64()
assert not is_s390x()
assert not is_loongarch64()
assert not is_unknown_architecture()
assert not is_wasm32()
assert not is_wasm64()