summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-21 00:18:02 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2019-10-21 00:18:02 -0500
commit25750e7495e178fc22a07cd1d9dcfd34c7141036 (patch)
tree9c8765a825ec4a40b7caed0d3e3ddf15b631d183
parent64b7153ce0079f62331a2e66f45c4bbd0c703028 (diff)
downloadhorizon-25750e7495e178fc22a07cd1d9dcfd34c7141036.tar.gz
horizon-25750e7495e178fc22a07cd1d9dcfd34c7141036.tar.bz2
horizon-25750e7495e178fc22a07cd1d9dcfd34c7141036.tar.xz
horizon-25750e7495e178fc22a07cd1d9dcfd34c7141036.zip
hscript: Implement Language, add tests
-rw-r--r--hscript/meta.cc72
-rw-r--r--hscript/meta.hh9
-rw-r--r--hscript/script.cc63
-rw-r--r--tests/fixtures/0115-language-simple.installfile6
-rw-r--r--tests/fixtures/0116-language-country.installfile6
-rw-r--r--tests/fixtures/0117-language-codeset.installfile6
-rw-r--r--tests/fixtures/0118-language-c-exception.installfile6
-rw-r--r--tests/fixtures/0119-language-invalid.installfile6
-rw-r--r--tests/fixtures/0120-language-iso.installfile6
-rw-r--r--tests/fixtures/0121-language-duplicate.installfile7
-rw-r--r--tests/spec/validator_spec.rb43
11 files changed, 209 insertions, 21 deletions
diff --git a/hscript/meta.cc b/hscript/meta.cc
index 7e8cfe8..e5e0ba4 100644
--- a/hscript/meta.cc
+++ b/hscript/meta.cc
@@ -13,6 +13,7 @@
#include <assert.h>
#include <fstream>
#include <regex>
+#include <set>
#include <sstream>
#ifdef HAS_INSTALL_ENV
# include <unistd.h>
@@ -179,6 +180,77 @@ Key *PkgInstall::parseFromData(const std::string &data, int lineno, int *errors,
}
+/* All ISO 639-1 language codes.
+ * Source: https://www.loc.gov/standards/iso639-2/ISO-639-2_utf-8.txt
+ * Python to construct table:
+ * >>> f = open('ISO-639-2_utf-8.txt')
+ * >>> x = csv.reader(f, delimiter='|')
+ * >>> langs = [lang[2] for lang in iter(x) if lang != '']
+ * >>> print('"' + '", "'.join(langs) + '", "C."')
+ */
+const std::set<std::string> valid_langs = {
+ "aa", "ab", "af", "ak", "sq", "am", "ar", "an", "hy", "as", "av", "ae",
+ "ay", "az", "ba", "bm", "eu", "be", "bn", "bh", "bi", "bs", "br", "bg",
+ "my", "ca", "ch", "ce", "zh", "cu", "cv", "kw", "co", "cr", "cs", "da",
+ "dv", "nl", "dz", "en", "eo", "et", "ee", "fo", "fj", "fi", "fr", "fy",
+ "ff", "ka", "de", "gd", "ga", "gl", "gv", "el", "gn", "gu", "ht", "ha",
+ "he", "hz", "hi", "ho", "hr", "hu", "ig", "is", "io", "ii", "iu", "ie",
+ "ia", "id", "ik", "it", "jv", "ja", "kl", "kn", "ks", "kr", "kk", "km",
+ "ki", "rw", "ky", "kv", "kg", "ko", "kj", "ku", "lo", "la", "lv", "li",
+ "ln", "lt", "lb", "lu", "lg", "mk", "mh", "ml", "mi", "mr", "ms", "mg",
+ "mt", "mn", "na", "nv", "nr", "nd", "ng", "ne", "nn", "nb", "no", "ny",
+ "oc", "oj", "or", "om", "os", "pa", "fa", "pi", "pl", "pt", "ps", "qu",
+ "rm", "ro", "rn", "ru", "sg", "sa", "si", "sk", "sl", "se", "sm", "sn",
+ "sd", "so", "st", "es", "sc", "sr", "ss", "su", "sw", "sv", "ty", "ta",
+ "tt", "te", "tg", "tl", "th", "bo", "ti", "to", "tn", "ts", "tk", "tr",
+ "tw", "ug", "uk", "ur", "uz", "ve", "vi", "vo", "cy", "wa", "wo", "xh",
+ "yi", "yo", "za", "zu", "C."
+};
+
+
+Key *Language::parseFromData(const std::string &data, int lineno, int *errors,
+ int *warnings) {
+ if(data.length() < 2 ||
+ valid_langs.find(data.substr(0, 2)) == valid_langs.end()) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "language: invalid language specified",
+ "language must be a valid ISO 639-1 language code");
+ return nullptr;
+ }
+
+ /* We know a valid language appears, but is it real? */
+ if(data.length() > 2) {
+ /* data[1] is . if language is C.UTF-8 */
+ if(data[2] != '_' && data[1] != '.') {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "language: invalid language specified",
+ "language must be a valid ISO 639-1 language code, "
+ "optionally followed by '_' and a country code");
+ return nullptr;
+ }
+ /* we don't really care about the country code, but we do care about
+ * codeset - we (via musl) *only* support UTF-8. */
+ std::string::size_type dot = data.find_first_of('.');
+ if(dot != std::string::npos && data.substr(dot+1, 5) != "UTF-8") {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "language: invalid language specified",
+ "you cannot specify a non-UTF-8 codeset");
+ return nullptr;
+ }
+ }
+
+ return new Language(lineno, data);
+}
+
+
+bool Language::execute(ScriptOptions) const {
+ return false;
+}
+
+
Key *Firmware::parseFromData(const std::string &data, int lineno, int *errors,
int *warnings) {
bool value;
diff --git a/hscript/meta.hh b/hscript/meta.hh
index 8a50576..7dc3f68 100644
--- a/hscript/meta.hh
+++ b/hscript/meta.hh
@@ -45,6 +45,13 @@ public:
};
class Language : public StringKey {
+private:
+ Language(int _line, const std::string &my_lang) :
+ StringKey(_line, my_lang) {}
+public:
+ static Key *parseFromData(const std::string &data, int lineno, int *errors,
+ int *warnings);
+ bool execute(ScriptOptions) const override;
};
class Keymap : public StringKey {
@@ -63,7 +70,7 @@ class Timezone : public StringKey {
class Repository : public StringKey {
private:
- Repository(int _line, const std::string my_url) :
+ Repository(int _line, const std::string &my_url) :
StringKey(_line, my_url) {}
public:
static Key *parseFromData(const std::string &data, int lineno, int *errors,
diff --git a/hscript/script.cc b/hscript/script.cc
index 33003bf..69a46cc 100644
--- a/hscript/script.cc
+++ b/hscript/script.cc
@@ -88,8 +88,8 @@ struct Script::ScriptPrivate {
std::set<std::string> packages;
/*! The root shadow line. */
std::unique_ptr<RootPassphrase> rootpw;
- /*! Target system's mountpoints. */
- std::vector< std::unique_ptr<Mount> > mounts;
+ /*! The system language. */
+ std::unique_ptr<Language> lang;
/*! Network addressing configuration */
std::vector< std::unique_ptr<NetAddress> > addresses;
@@ -103,6 +103,8 @@ struct Script::ScriptPrivate {
/*! Disk identification keys */
std::vector< std::unique_ptr<DiskId> > diskids;
+ /*! Target system's mountpoints. */
+ std::vector< std::unique_ptr<Mount> > mounts;
#ifdef NON_LIBRE_FIRMWARE
std::unique_ptr<Firmware> firmware;
@@ -119,18 +121,6 @@ struct Script::ScriptPrivate {
int *errors, int *warnings, const ScriptOptions &opts) {
if(key_name == "network") {
return store_network(obj, lineno, errors, warnings, opts);
- } else if(key_name == "hostname") {
- return store_hostname(obj, lineno, errors, warnings, opts);
- } else if(key_name == "pkginstall") {
- return store_pkginstall(obj, lineno, errors, warnings, opts);
- } else if(key_name == "rootpw") {
- return store_rootpw(obj, lineno, errors, warnings, opts);
- } else if(key_name == "firmware") {
- return store_firmware(obj, lineno, errors, warnings, opts);
- } else if(key_name == "mount") {
- std::unique_ptr<Mount> mount(dynamic_cast<Mount *>(obj));
- this->mounts.push_back(std::move(mount));
- return true;
} else if(key_name == "netaddress") {
std::unique_ptr<NetAddress> addr(dynamic_cast<NetAddress *>(obj));
this->addresses.push_back(std::move(addr));
@@ -139,14 +129,20 @@ struct Script::ScriptPrivate {
std::unique_ptr<NetSSID> ssid(dynamic_cast<NetSSID *>(obj));
this->ssids.push_back(std::move(ssid));
return true;
+ } else if(key_name == "hostname") {
+ return store_hostname(obj, lineno, errors, warnings, opts);
+ } else if(key_name == "pkginstall") {
+ return store_pkginstall(obj, lineno, errors, warnings, opts);
+ } else if(key_name == "rootpw") {
+ return store_rootpw(obj, lineno, errors, warnings, opts);
+ } else if(key_name == "language") {
+ return store_lang(obj, lineno, errors, warnings, opts);
+ } else if(key_name == "firmware") {
+ return store_firmware(obj, lineno, errors, warnings, opts);
} else if(key_name == "repository") {
std::unique_ptr<Repository> repo(dynamic_cast<Repository *>(obj));
this->repos.push_back(std::move(repo));
return true;
- } else if(key_name == "diskid") {
- std::unique_ptr<DiskId> diskid(dynamic_cast<DiskId *>(obj));
- this->diskids.push_back(std::move(diskid));
- return true;
} else if(key_name == "username") {
return store_username(obj, lineno, errors, warnings, opts);
} else if(key_name == "useralias") {
@@ -157,6 +153,14 @@ struct Script::ScriptPrivate {
return store_usericon(obj, lineno, errors, warnings, opts);
} else if(key_name == "usergroups") {
return store_usergroups(obj, lineno, errors, warnings, opts);
+ } else if(key_name == "diskid") {
+ std::unique_ptr<DiskId> diskid(dynamic_cast<DiskId *>(obj));
+ this->diskids.push_back(std::move(diskid));
+ return true;
+ } else if(key_name == "mount") {
+ std::unique_ptr<Mount> mount(dynamic_cast<Mount *>(obj));
+ this->mounts.push_back(std::move(mount));
+ return true;
} else {
return false;
}
@@ -241,6 +245,18 @@ struct Script::ScriptPrivate {
#endif
}
+ bool store_lang(Keys::Key *obj, int lineno, int *errors, int *,
+ ScriptOptions) {
+ if(this->lang) {
+ DUPLICATE_ERROR(this->lang, std::string("language"),
+ this->lang->value())
+ return false;
+ }
+ std::unique_ptr<Language> l(dynamic_cast<Language *>(obj));
+ this->lang = std::move(l);
+ return true;
+ }
+
bool store_username(Keys::Key *obj, int lineno, int *errors, int *,
ScriptOptions) {
if(accounts.size() >= 255) {
@@ -527,9 +543,16 @@ bool Script::validate() const {
/* REQ: Runner.Validate.rootpw */
if(!this->internal->rootpw->validate(this->opts)) failures++;
- /* language */
+ /* REQ: Runner.Validate.language */
+ if(internal->lang && !internal->lang->validate(this->opts)) failures++;
+
/* keymap */
- /* firmware */
+
+#ifdef NON_LIBRE_FIRMWARE
+ /* REQ: Runner.Validate.firmware */
+ if(!this->internal->firmware->validate(this->opts)) failures++;
+#endif
+
/* timezone */
/* REQ: Script.repository */
diff --git a/tests/fixtures/0115-language-simple.installfile b/tests/fixtures/0115-language-simple.installfile
new file mode 100644
index 0000000..ced3f0c
--- /dev/null
+++ b/tests/fixtures/0115-language-simple.installfile
@@ -0,0 +1,6 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+language cy
diff --git a/tests/fixtures/0116-language-country.installfile b/tests/fixtures/0116-language-country.installfile
new file mode 100644
index 0000000..a6812a9
--- /dev/null
+++ b/tests/fixtures/0116-language-country.installfile
@@ -0,0 +1,6 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+language ie_IE
diff --git a/tests/fixtures/0117-language-codeset.installfile b/tests/fixtures/0117-language-codeset.installfile
new file mode 100644
index 0000000..ae18d4a
--- /dev/null
+++ b/tests/fixtures/0117-language-codeset.installfile
@@ -0,0 +1,6 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+language zh_TW.UTF-8
diff --git a/tests/fixtures/0118-language-c-exception.installfile b/tests/fixtures/0118-language-c-exception.installfile
new file mode 100644
index 0000000..4e2f53d
--- /dev/null
+++ b/tests/fixtures/0118-language-c-exception.installfile
@@ -0,0 +1,6 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+language C.UTF-8
diff --git a/tests/fixtures/0119-language-invalid.installfile b/tests/fixtures/0119-language-invalid.installfile
new file mode 100644
index 0000000..209eacf
--- /dev/null
+++ b/tests/fixtures/0119-language-invalid.installfile
@@ -0,0 +1,6 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+language klingon
diff --git a/tests/fixtures/0120-language-iso.installfile b/tests/fixtures/0120-language-iso.installfile
new file mode 100644
index 0000000..63f92e5
--- /dev/null
+++ b/tests/fixtures/0120-language-iso.installfile
@@ -0,0 +1,6 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+language en_US.ISO-8859-1
diff --git a/tests/fixtures/0121-language-duplicate.installfile b/tests/fixtures/0121-language-duplicate.installfile
new file mode 100644
index 0000000..83f7314
--- /dev/null
+++ b/tests/fixtures/0121-language-duplicate.installfile
@@ -0,0 +1,7 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+language en
+language en
diff --git a/tests/spec/validator_spec.rb b/tests/spec/validator_spec.rb
index 08f5886..3ed43ea 100644
--- a/tests/spec/validator_spec.rb
+++ b/tests/spec/validator_spec.rb
@@ -186,6 +186,44 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do
expect(last_command_started).to have_output(/error: .*firmware.*value/)
end
end
+ context "for 'language' key" do
+ # Runner.Validate.language
+ # Runner.Validate.language.Format
+ it "supports a simple language" do
+ use_fixture '0115-language-simple.installfile'
+ run_validate
+ expect(last_command_started).to have_output(PARSER_SUCCESS)
+ expect(last_command_started).to have_output(VALIDATOR_SUCCESS)
+ end
+ it "supports a language_territory value" do
+ use_fixture '0116-language-country.installfile'
+ run_validate
+ expect(last_command_started).to have_output(PARSER_SUCCESS)
+ expect(last_command_started).to have_output(VALIDATOR_SUCCESS)
+ end
+ it "supports the UTF-8 codeset" do
+ use_fixture '0117-language-codeset.installfile'
+ run_validate
+ expect(last_command_started).to have_output(PARSER_SUCCESS)
+ expect(last_command_started).to have_output(VALIDATOR_SUCCESS)
+ end
+ it "supports the special case C.UTF-8" do
+ use_fixture '0118-language-c-exception.installfile'
+ run_validate
+ expect(last_command_started).to have_output(PARSER_SUCCESS)
+ expect(last_command_started).to have_output(VALIDATOR_SUCCESS)
+ end
+ it "requires a valid language" do
+ use_fixture '0119-language-invalid.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*language.*invalid/)
+ end
+ it "requires the UTF-8 codeset" do
+ use_fixture '0120-language-iso.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*language.*codeset/)
+ end
+ end
context "for 'mount' key" do
# Runner.Validate.mount.
it "fails with an invalid value" do
@@ -537,6 +575,11 @@ RSpec.describe 'HorizonScript validation', :type => :aruba do
skip "This build does not support firmware" if last_command_started.stdout !~ /supports non-free/
expect(last_command_started).to have_output(/error: .*duplicate.*firmware/)
end
+ it "fails with a duplicate 'language' key" do
+ use_fixture '0121-language-duplicate.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*duplicate.*language/)
+ end
end
context "user account keys:" do
context "'username'" do