aboutsummaryrefslogtreecommitdiff
path: root/tests/installer-steps/test_select_locale.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/installer-steps/test_select_locale.py')
-rw-r--r--tests/installer-steps/test_select_locale.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/tests/installer-steps/test_select_locale.py b/tests/installer-steps/test_select_locale.py
new file mode 100644
index 0000000..b9b9ce7
--- /dev/null
+++ b/tests/installer-steps/test_select_locale.py
@@ -0,0 +1,160 @@
+"""Tests for select_locale in the archsetup installer.
+
+select_locale resolves the system locale during pre-flight. Three inputs, in
+precedence order: an existing LANG= in locale.conf wins outright, then LOCALE
+from the config file, then a numbered menu.
+
+That menu is why these tests exist. It carries its own "[1]" default, LOCALE is
+optional in archsetup.conf.example, and archsetup does not require an archangel
+install -- so an unattended run (--config-file) can reach the menu's bare read
+with nobody there to answer it. configure_build_environment's own "no LANG= in
+locale.conf" branch shows archsetup expects that state to occur. Unattended
+therefore takes the menu's default, the same ruling nvidia_preflight and
+configure_autologin make for their prompts.
+
+These tests exercise the REAL function body, extracted from the `archsetup`
+script at run time (not a copy), against a fixture locale.conf.
+
+Run from repo root:
+ python3 -m unittest tests.installer-steps.test_select_locale
+"""
+
+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")
+
+
+class SelectLocaleHarness(unittest.TestCase):
+ """Source select_locale out of the real archsetup script."""
+
+ def setUp(self):
+ self.tmp = tempfile.mkdtemp(prefix="select-locale-test-")
+ self.conf = os.path.join(self.tmp, "locale.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 "
+ "'/^select_locale() {/,/^}/p' \"$ARCHSETUP\")\n"
+ 'select_locale "$LOCALE_CONF"\n'
+ 'echo "RESOLVED=$locale"\n'
+ # Whatever the prompt did not consume is still on stdin.
+ 'if IFS= read -r leftover; then echo "LEFTOVER:$leftover"; '
+ 'else echo "LEFTOVER:"; fi\n'
+ )
+ os.chmod(self.wrapper, 0o755)
+
+ def tearDown(self):
+ shutil.rmtree(self.tmp, ignore_errors=True)
+
+ def write_conf(self, body):
+ with open(self.conf, "w") as f:
+ f.write(body)
+
+ def run_select(self, locale="", config_file="", stdin=""):
+ env = dict(os.environ)
+ env["locale"] = locale
+ env["config_file"] = config_file
+ env["LOCALE_CONF"] = self.conf
+ return subprocess.run(
+ ["bash", self.wrapper, ARCHSETUP],
+ input=stdin, capture_output=True, text=True, env=env,
+ )
+
+ def resolved(self, r):
+ for line in r.stdout.splitlines():
+ if line.startswith("RESOLVED="):
+ return line.split("=", 1)[1]
+ self.fail("no RESOLVED line; stdout=%r stderr=%r" % (r.stdout, r.stderr))
+
+ # ---------------------------------------------------------- normal ----
+ def test_existing_locale_conf_wins_and_does_not_prompt(self):
+ self.write_conf("LANG=de_DE.UTF-8\n")
+ r = self.run_select(stdin="sentinel\n")
+ self.assertIn("[OK] Locale: de_DE.UTF-8", r.stdout)
+ # locale.conf is authoritative, so the global is deliberately left
+ # unset -- configure_build_environment reads LANG out of the file.
+ self.assertEqual(self.resolved(r), "")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_config_file_locale_used_when_conf_has_no_lang(self):
+ self.write_conf("")
+ r = self.run_select(locale="fr_FR.UTF-8", stdin="sentinel\n")
+ self.assertIn("(from config)", r.stdout)
+ self.assertEqual(self.resolved(r), "fr_FR.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_menu_choice_selects_that_locale(self):
+ self.write_conf("")
+ r = self.run_select(stdin="3\nsentinel\n")
+ self.assertEqual(self.resolved(r), "de_DE.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ # -------------------------------------------------------- boundary ----
+ def test_missing_locale_conf_falls_through_to_the_menu(self):
+ # No file at all, not just no LANG= line.
+ r = self.run_select(stdin="2\nsentinel\n")
+ self.assertEqual(self.resolved(r), "en_GB.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_bare_enter_takes_choice_one(self):
+ self.write_conf("")
+ r = self.run_select(stdin="\nsentinel\n")
+ self.assertEqual(self.resolved(r), "en_US.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_unattended_config_file_takes_the_default_without_reading(self):
+ # THE BUG. --config-file is the documented unattended mode and LOCALE
+ # is optional, so this is an ordinary unattended install on a machine
+ # whose locale.conf carries no LANG=.
+ self.write_conf("")
+ r = self.run_select(config_file="/etc/archsetup.conf",
+ stdin="sentinel\n")
+ self.assertEqual(self.resolved(r), "en_US.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_unattended_still_prefers_an_existing_locale_conf(self):
+ self.write_conf("LANG=ja_JP.UTF-8\n")
+ r = self.run_select(config_file="/etc/archsetup.conf",
+ stdin="sentinel\n")
+ self.assertIn("[OK] Locale: ja_JP.UTF-8", r.stdout)
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_unattended_still_prefers_the_configured_locale(self):
+ # Unattended does not mean "always en_US" -- LOCALE is an answer, and
+ # the menu default must not override it.
+ self.write_conf("")
+ r = self.run_select(locale="pt_BR.UTF-8",
+ config_file="/etc/archsetup.conf", stdin="sentinel\n")
+ self.assertEqual(self.resolved(r), "pt_BR.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ # ----------------------------------------------------------- error ----
+ def test_unrecognised_choice_falls_back_to_en_us(self):
+ self.write_conf("")
+ r = self.run_select(stdin="42\nsentinel\n")
+ self.assertEqual(self.resolved(r), "en_US.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_manual_entry_reads_a_second_line(self):
+ self.write_conf("")
+ r = self.run_select(stdin="9\nnl_NL.UTF-8\nsentinel\n")
+ self.assertEqual(self.resolved(r), "nl_NL.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+ def test_manual_entry_left_empty_falls_back_to_en_us(self):
+ self.write_conf("")
+ r = self.run_select(stdin="9\n\nsentinel\n")
+ self.assertEqual(self.resolved(r), "en_US.UTF-8")
+ self.assertIn("LEFTOVER:sentinel", r.stdout)
+
+
+if __name__ == "__main__":
+ unittest.main()