"""Tests for locale_country and set_wireless_regdom in the archsetup installer. The regulatory domain was derived by fixed offset: "${current_lang:3:2}", with a comment reading "positions 3-4". That is right only for a two-letter language code. validate_config accepts ^[a-z]{2,3}(_[A-Z]{2})?, glibc ships 75 three-letter languages, and ber_DZ.UTF-8 yields "_D" while C yields "". A garbage region matches no line in /etc/conf.d/wireless-regdom, sed exits 0, and the || error_warn never fires, so the domain is silently never set and WiFi falls back to the conservative "00" domain. The fix derives the country from the _CC group by pattern and verifies the substitution actually landed. These tests exercise the REAL function bodies, extracted from the `archsetup` script at run time, against a fixture conf file. Run from repo root: python3 -m unittest tests.installer-steps.test_wireless_regdom """ import os import shutil import subprocess import tempfile import unittest REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) ARCHSETUP = os.path.join(REPO_ROOT, "archsetup") # A realistic slice of the wireless-regdb-owned file: every entry commented. CONF = "".join(f'#WIRELESS_REGDOM="{cc}"\n' for cc in ("AT", "AU", "BR", "CA", "DE", "DZ", "ES", "FR", "GB", "JP", "PE", "US")) class LocaleCountry(unittest.TestCase): """The pure derivation.""" def setUp(self): self.tmp = tempfile.mkdtemp(prefix="regdom-") self.addCleanup(shutil.rmtree, self.tmp, True) self.wrapper = os.path.join(self.tmp, "run.sh") with open(self.wrapper, "w") as f: f.write( "#!/bin/bash\n" 'ARCHSETUP="$1"; shift\n' "source <(sed -n '/^locale_country() {/,/^}/p' \"$ARCHSETUP\")\n" 'locale_country "$1"\n' ) os.chmod(self.wrapper, 0o755) def country(self, locale): r = subprocess.run(["bash", self.wrapper, ARCHSETUP, locale], capture_output=True, text=True) return r.stdout.strip() # ---------------------------------------------------------- normal ---- def test_two_letter_language(self): self.assertEqual(self.country("en_US.UTF-8"), "US") self.assertEqual(self.country("de_DE.UTF-8"), "DE") def test_three_letter_language(self): # The bug: offset 3-4 of "ber_DZ.UTF-8" is "_D". self.assertEqual(self.country("ber_DZ.UTF-8"), "DZ") self.assertEqual(self.country("ayc_PE.UTF-8"), "PE") # -------------------------------------------------------- boundary ---- def test_locale_without_an_encoding(self): self.assertEqual(self.country("fr_CA"), "CA") def test_locale_with_a_modifier(self): self.assertEqual(self.country("ca_ES@valencia"), "ES") def test_no_country_in_the_locale(self): self.assertEqual(self.country("C"), "") self.assertEqual(self.country("POSIX"), "") self.assertEqual(self.country("eo"), "") def test_empty_locale(self): self.assertEqual(self.country(""), "") # ----------------------------------------------------------- error ---- def test_a_lowercase_country_is_not_accepted(self): # ISO 3166-1 alpha-2 is uppercase; the regdom file keys on that. self.assertEqual(self.country("en_us.UTF-8"), "") def test_a_three_letter_country_is_not_accepted(self): self.assertEqual(self.country("en_USA.UTF-8"), "") class SetWirelessRegdom(unittest.TestCase): """The step: uncomment the matching line, and say so when it can't.""" def setUp(self): self.tmp = tempfile.mkdtemp(prefix="regdom-step-") self.addCleanup(shutil.rmtree, self.tmp, True) self.conf = os.path.join(self.tmp, "wireless-regdom") with open(self.conf, "w") as f: f.write(CONF) self.wrapper = os.path.join(self.tmp, "run.sh") with open(self.wrapper, "w") as f: f.write( "#!/bin/bash\n" 'ARCHSETUP="$1"; shift\n' "source <(sed -n " "'/^locale_country() {/,/^}/p;" "/^set_wireless_regdom() {/,/^}/p' \"$ARCHSETUP\")\n" 'display() { :; }\n' 'backup_system_file() { echo "BACKUP:$1"; }\n' 'error_warn() { echo "WARN:$1"; return 1; }\n' 'set_wireless_regdom "$1" "$2"\n' 'echo "RETURNED=$?"\n' ) os.chmod(self.wrapper, 0o755) def run_step(self, locale): env = dict(os.environ) env["logfile"] = os.path.join(self.tmp, "log") return subprocess.run( ["bash", self.wrapper, ARCHSETUP, locale, self.conf], capture_output=True, text=True, env=env) def active(self): with open(self.conf) as f: return [l.strip() for l in f if l.startswith("WIRELESS_REGDOM=")] # ---------------------------------------------------------- normal ---- def test_a_two_letter_locale_sets_its_domain(self): self.run_step("en_US.UTF-8") self.assertEqual(self.active(), ['WIRELESS_REGDOM="US"']) def test_a_three_letter_locale_sets_its_domain(self): self.run_step("ber_DZ.UTF-8") self.assertEqual(self.active(), ['WIRELESS_REGDOM="DZ"']) def test_only_the_matching_line_is_uncommented(self): self.run_step("de_DE.UTF-8") self.assertEqual(len(self.active()), 1) # -------------------------------------------------------- boundary ---- def test_a_locale_with_no_country_warns_and_names_the_locale(self): # The trailing verify catches this case too, so a bare "did it warn?" # assertion passes with the early guard deleted and proves nothing. What # the guard buys is a message that names the locale, instead of one # reporting that country "" is missing from the file. r = self.run_step("C") self.assertIn("WARN:", r.stdout) self.assertIn("'C'", r.stdout) self.assertEqual(self.active(), []) def test_a_locale_with_no_country_does_not_back_up_the_conf(self): # Nothing is going to be written, so nothing should be backed up. r = self.run_step("POSIX") self.assertNotIn("BACKUP", r.stdout) def test_a_country_absent_from_the_file_warns(self): # The sibling gap: sed exits 0 when it matches nothing, so without an # explicit check a distro reshuffling this file fails silently. r = self.run_step("en_ZZ.UTF-8") self.assertIn("WARN:", r.stdout) self.assertEqual(self.active(), []) def test_an_already_uncommented_domain_is_left_alone(self): with open(self.conf, "w") as f: f.write('WIRELESS_REGDOM="US"\n' + CONF) r = self.run_step("en_US.UTF-8") self.assertNotIn("WARN:", r.stdout) self.assertEqual(self.active(), ['WIRELESS_REGDOM="US"']) # ----------------------------------------------------------- error ---- def test_a_missing_conf_file_warns(self): r = self.run_step("en_US.UTF-8") os.remove(self.conf) r = self.run_step("en_US.UTF-8") self.assertIn("WARN:", r.stdout) if __name__ == "__main__": unittest.main()