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

176 lines
7.0 KiB
Python

# SPDX-License-Identifier: MIT
"""Tests for utils.locale: host TZ detection + IANA->Windows translation."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from winpodx.utils.locale import (
_tz_from_etc_timezone,
_tz_from_localtime_symlink,
_tz_from_timedatectl,
detect_timezone,
iana_to_windows,
resolve_timezone_for_oem,
)
class TestDetectTimezone:
def test_falls_back_to_utc_when_all_helpers_return_none(self, monkeypatch):
monkeypatch.setattr("winpodx.utils.locale._tz_from_timedatectl", lambda: None)
monkeypatch.setattr("winpodx.utils.locale._tz_from_localtime_symlink", lambda: None)
monkeypatch.setattr("winpodx.utils.locale._tz_from_etc_timezone", lambda: None)
assert detect_timezone() == "UTC"
def test_timedatectl_wins_over_other_helpers(self, monkeypatch):
monkeypatch.setattr("winpodx.utils.locale._tz_from_timedatectl", lambda: "Asia/Seoul")
monkeypatch.setattr(
"winpodx.utils.locale._tz_from_localtime_symlink", lambda: "America/New_York"
)
monkeypatch.setattr("winpodx.utils.locale._tz_from_etc_timezone", lambda: "Europe/Paris")
assert detect_timezone() == "Asia/Seoul"
def test_localtime_symlink_used_when_timedatectl_unavailable(self, monkeypatch):
monkeypatch.setattr("winpodx.utils.locale._tz_from_timedatectl", lambda: None)
monkeypatch.setattr(
"winpodx.utils.locale._tz_from_localtime_symlink", lambda: "Europe/Berlin"
)
monkeypatch.setattr("winpodx.utils.locale._tz_from_etc_timezone", lambda: "Europe/Paris")
assert detect_timezone() == "Europe/Berlin"
def test_etc_timezone_used_when_both_others_unavailable(self, monkeypatch):
monkeypatch.setattr("winpodx.utils.locale._tz_from_timedatectl", lambda: None)
monkeypatch.setattr("winpodx.utils.locale._tz_from_localtime_symlink", lambda: None)
monkeypatch.setattr(
"winpodx.utils.locale._tz_from_etc_timezone", lambda: "Australia/Sydney"
)
assert detect_timezone() == "Australia/Sydney"
def test_helper_exception_does_not_break_chain(self, monkeypatch):
"""Defensive: a helper raising must not abort the detection chain."""
def boom():
raise RuntimeError("nope")
monkeypatch.setattr("winpodx.utils.locale._tz_from_timedatectl", boom)
monkeypatch.setattr("winpodx.utils.locale._tz_from_localtime_symlink", lambda: "Asia/Tokyo")
monkeypatch.setattr("winpodx.utils.locale._tz_from_etc_timezone", lambda: None)
assert detect_timezone() == "Asia/Tokyo"
class TestTimedatectlHelper:
def test_returns_value_on_success(self):
with patch("subprocess.run") as mock_run:
mock_run.return_value.returncode = 0
mock_run.return_value.stdout = "Asia/Seoul\n"
assert _tz_from_timedatectl() == "Asia/Seoul"
def test_returns_none_when_command_missing(self):
with patch("subprocess.run", side_effect=FileNotFoundError):
assert _tz_from_timedatectl() is None
def test_returns_none_when_command_fails(self):
with patch("subprocess.run") as mock_run:
mock_run.return_value.returncode = 1
mock_run.return_value.stdout = ""
assert _tz_from_timedatectl() is None
def test_returns_none_on_empty_output(self):
with patch("subprocess.run") as mock_run:
mock_run.return_value.returncode = 0
mock_run.return_value.stdout = "\n"
assert _tz_from_timedatectl() is None
class TestLocaltimeSymlinkHelper:
def test_parses_zoneinfo_suffix(self, monkeypatch):
monkeypatch.setattr(
"winpodx.utils.locale.os.readlink",
lambda p: "/usr/share/zoneinfo/Asia/Seoul",
)
assert _tz_from_localtime_symlink() == "Asia/Seoul"
def test_parses_relative_zoneinfo_suffix(self, monkeypatch):
monkeypatch.setattr(
"winpodx.utils.locale.os.readlink",
lambda p: "../usr/share/zoneinfo/Europe/London",
)
assert _tz_from_localtime_symlink() == "Europe/London"
def test_returns_none_when_target_lacks_zoneinfo(self, monkeypatch):
monkeypatch.setattr(
"winpodx.utils.locale.os.readlink",
lambda p: "/some/other/path",
)
assert _tz_from_localtime_symlink() is None
def test_returns_none_when_localtime_not_a_symlink(self, monkeypatch):
def boom(p):
raise OSError("not a symlink")
monkeypatch.setattr("winpodx.utils.locale.os.readlink", boom)
assert _tz_from_localtime_symlink() is None
class TestEtcTimezoneHelper:
def test_returns_first_nonblank_line(self, tmp_path, monkeypatch):
f = tmp_path / "timezone"
f.write_text("# comment\n\nAmerica/Chicago\nignored\n")
monkeypatch.setattr("winpodx.utils.locale.Path", lambda p: f if p == "/etc/timezone" else p)
assert _tz_from_etc_timezone() == "America/Chicago"
def test_returns_none_when_file_missing(self, tmp_path, monkeypatch):
missing = tmp_path / "absent"
monkeypatch.setattr(
"winpodx.utils.locale.Path", lambda p: missing if p == "/etc/timezone" else p
)
assert _tz_from_etc_timezone() is None
class TestIanaToWindows:
@pytest.mark.parametrize(
"iana,expected",
[
("Asia/Seoul", "Korea Standard Time"),
("America/New_York", "Eastern Standard Time"),
("Europe/Berlin", "W. Europe Standard Time"),
("Australia/Sydney", "AUS Eastern Standard Time"),
("Etc/UTC", "UTC"),
("UTC", "UTC"),
],
)
def test_known_mappings(self, iana, expected):
assert iana_to_windows(iana) == expected
def test_unknown_iana_falls_back_to_utc(self):
assert iana_to_windows("Made/Up/Zone") == "UTC"
def test_empty_input_falls_back_to_utc(self):
assert iana_to_windows("") == "UTC"
class TestResolveTimezoneForOem:
def test_empty_triggers_host_detection(self, monkeypatch):
monkeypatch.setattr("winpodx.utils.locale.detect_timezone", lambda: "Asia/Tokyo")
assert resolve_timezone_for_oem("") == "Tokyo Standard Time"
def test_iana_name_gets_translated(self):
assert resolve_timezone_for_oem("Asia/Seoul") == "Korea Standard Time"
def test_windows_id_passes_through_verbatim(self):
# Niche territory variant not in the CLDR 001 wildcard table --
# user knows the Windows-side string and we shouldn't second-guess.
assert resolve_timezone_for_oem("Russia Time Zone 11") == "Russia Time Zone 11"
def test_utc_case_insensitive(self):
assert resolve_timezone_for_oem("UTC") == "UTC"
assert resolve_timezone_for_oem("utc") == "UTC"
def test_whitespace_stripped(self):
assert resolve_timezone_for_oem(" Asia/Seoul ") == "Korea Standard Time"
def test_unknown_iana_falls_back_to_utc(self):
assert resolve_timezone_for_oem("Made/Up/Zone") == "UTC"