1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
"""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()
|