aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil/tests')
-rw-r--r--src/libutil/tests/config.cc264
-rw-r--r--src/libutil/tests/hash.cc80
-rw-r--r--src/libutil/tests/json.cc193
-rw-r--r--src/libutil/tests/xml-writer.cc105
4 files changed, 642 insertions, 0 deletions
diff --git a/src/libutil/tests/config.cc b/src/libutil/tests/config.cc
new file mode 100644
index 000000000..74c59fd31
--- /dev/null
+++ b/src/libutil/tests/config.cc
@@ -0,0 +1,264 @@
+#include "json.hh"
+#include "config.hh"
+#include "args.hh"
+
+#include <sstream>
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * Config
+ * --------------------------------------------------------------------------*/
+
+ TEST(Config, setUndefinedSetting) {
+ Config config;
+ ASSERT_EQ(config.set("undefined-key", "value"), false);
+ }
+
+ TEST(Config, setDefinedSetting) {
+ Config config;
+ std::string value;
+ Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
+ ASSERT_EQ(config.set("name-of-the-setting", "value"), true);
+ }
+
+ TEST(Config, getDefinedSetting) {
+ Config config;
+ std::string value;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
+
+ config.getSettings(settings, /* overridenOnly = */ false);
+ const auto iter = settings.find("name-of-the-setting");
+ ASSERT_NE(iter, settings.end());
+ ASSERT_EQ(iter->second.value, "");
+ ASSERT_EQ(iter->second.description, "description");
+ }
+
+ TEST(Config, getDefinedOverridenSettingNotSet) {
+ Config config;
+ std::string value;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
+
+ config.getSettings(settings, /* overridenOnly = */ true);
+ const auto e = settings.find("name-of-the-setting");
+ ASSERT_EQ(e, settings.end());
+ }
+
+ TEST(Config, getDefinedSettingSet1) {
+ Config config;
+ std::string value;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, value, "name-of-the-setting", "description"};
+
+ setting.assign("value");
+
+ config.getSettings(settings, /* overridenOnly = */ false);
+ const auto iter = settings.find("name-of-the-setting");
+ ASSERT_NE(iter, settings.end());
+ ASSERT_EQ(iter->second.value, "value");
+ ASSERT_EQ(iter->second.description, "description");
+ }
+
+ TEST(Config, getDefinedSettingSet2) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+
+ ASSERT_TRUE(config.set("name-of-the-setting", "value"));
+
+ config.getSettings(settings, /* overridenOnly = */ false);
+ const auto e = settings.find("name-of-the-setting");
+ ASSERT_NE(e, settings.end());
+ ASSERT_EQ(e->second.value, "value");
+ ASSERT_EQ(e->second.description, "description");
+ }
+
+ TEST(Config, addSetting) {
+ class TestSetting : public AbstractSetting {
+ public:
+ TestSetting() : AbstractSetting("test", "test", {}) {}
+ void set(const std::string & value) {}
+ std::string to_string() const { return {}; }
+ };
+
+ Config config;
+ TestSetting setting;
+
+ ASSERT_FALSE(config.set("test", "value"));
+ config.addSetting(&setting);
+ ASSERT_TRUE(config.set("test", "value"));
+ }
+
+ TEST(Config, withInitialValue) {
+ const StringMap initials = {
+ { "key", "value" },
+ };
+ Config config(initials);
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+ config.getSettings(settings, /* overridenOnly = */ false);
+ ASSERT_EQ(settings.find("key"), settings.end());
+ }
+
+ Setting<std::string> setting{&config, "default-value", "key", "description"};
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+ config.getSettings(settings, /* overridenOnly = */ false);
+ ASSERT_EQ(settings["key"].value, "value");
+ }
+ }
+
+ TEST(Config, resetOverriden) {
+ Config config;
+ config.resetOverriden();
+ }
+
+ TEST(Config, resetOverridenWithSetting) {
+ Config config;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+
+ setting.set("foo");
+ ASSERT_EQ(setting.get(), "foo");
+ config.getSettings(settings, /* overridenOnly = */ true);
+ ASSERT_TRUE(settings.empty());
+ }
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+
+ setting.override("bar");
+ ASSERT_TRUE(setting.overriden);
+ ASSERT_EQ(setting.get(), "bar");
+ config.getSettings(settings, /* overridenOnly = */ true);
+ ASSERT_FALSE(settings.empty());
+ }
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+
+ config.resetOverriden();
+ ASSERT_FALSE(setting.overriden);
+ config.getSettings(settings, /* overridenOnly = */ true);
+ ASSERT_TRUE(settings.empty());
+ }
+ }
+
+ TEST(Config, toJSONOnEmptyConfig) {
+ std::stringstream out;
+ { // Scoped to force the destructor of JSONObject to write the final `}`
+ JSONObject obj(out);
+ Config config;
+ config.toJSON(obj);
+ }
+
+ ASSERT_EQ(out.str(), "{}");
+ }
+
+ TEST(Config, toJSONOnNonEmptyConfig) {
+ std::stringstream out;
+ { // Scoped to force the destructor of JSONObject to write the final `}`
+ JSONObject obj(out);
+
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+ setting.assign("value");
+
+ config.toJSON(obj);
+ }
+ ASSERT_EQ(out.str(), R"#({"name-of-the-setting":{"description":"description","value":"value"}})#");
+ }
+
+ TEST(Config, setSettingAlias) {
+ Config config;
+ Setting<std::string> setting{&config, "", "some-int", "best number", { "another-int" }};
+ ASSERT_TRUE(config.set("some-int", "1"));
+ ASSERT_EQ(setting.get(), "1");
+ ASSERT_TRUE(config.set("another-int", "2"));
+ ASSERT_EQ(setting.get(), "2");
+ ASSERT_TRUE(config.set("some-int", "3"));
+ ASSERT_EQ(setting.get(), "3");
+ }
+
+ /* FIXME: The reapplyUnknownSettings method doesn't seem to do anything
+ * useful (these days). Whenever we add a new setting to Config the
+ * unknown settings are always considered. In which case is this function
+ * actually useful? Is there some way to register a Setting without calling
+ * addSetting? */
+ TEST(Config, DISABLED_reapplyUnknownSettings) {
+ Config config;
+ ASSERT_FALSE(config.set("name-of-the-setting", "unknownvalue"));
+ Setting<std::string> setting{&config, "default", "name-of-the-setting", "description"};
+ ASSERT_EQ(setting.get(), "default");
+ config.reapplyUnknownSettings();
+ ASSERT_EQ(setting.get(), "unknownvalue");
+ }
+
+ TEST(Config, applyConfigEmpty) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ config.applyConfig("");
+ config.getSettings(settings);
+ ASSERT_TRUE(settings.empty());
+ }
+
+ TEST(Config, applyConfigEmptyWithComment) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ config.applyConfig("# just a comment");
+ config.getSettings(settings);
+ ASSERT_TRUE(settings.empty());
+ }
+
+ TEST(Config, applyConfigAssignment) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+ config.applyConfig(
+ "name-of-the-setting = value-from-file #useful comment\n"
+ "# name-of-the-setting = foo\n"
+ );
+ config.getSettings(settings);
+ ASSERT_FALSE(settings.empty());
+ ASSERT_EQ(settings["name-of-the-setting"].value, "value-from-file");
+ }
+
+ TEST(Config, applyConfigWithReassignedSetting) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+ config.applyConfig(
+ "name-of-the-setting = first-value\n"
+ "name-of-the-setting = second-value\n"
+ );
+ config.getSettings(settings);
+ ASSERT_FALSE(settings.empty());
+ ASSERT_EQ(settings["name-of-the-setting"].value, "second-value");
+ }
+
+ TEST(Config, applyConfigFailsOnMissingIncludes) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+
+ ASSERT_THROW(config.applyConfig(
+ "name-of-the-setting = value-from-file\n"
+ "# name-of-the-setting = foo\n"
+ "include /nix/store/does/not/exist.nix"
+ ), Error);
+ }
+
+ TEST(Config, applyConfigInvalidThrows) {
+ Config config;
+ ASSERT_THROW(config.applyConfig("value == key"), UsageError);
+ ASSERT_THROW(config.applyConfig("value "), UsageError);
+ }
+}
diff --git a/src/libutil/tests/hash.cc b/src/libutil/tests/hash.cc
new file mode 100644
index 000000000..7cb439817
--- /dev/null
+++ b/src/libutil/tests/hash.cc
@@ -0,0 +1,80 @@
+#include "hash.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * hashString
+ * --------------------------------------------------------------------------*/
+
+ TEST(hashString, testKnownMD5Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc1321
+ auto s1 = "";
+ auto hash = hashString(HashType::htMD5, s1);
+ ASSERT_EQ(hash.to_string(Base::Base16), "md5:d41d8cd98f00b204e9800998ecf8427e");
+ }
+
+ TEST(hashString, testKnownMD5Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc1321
+ auto s2 = "abc";
+ auto hash = hashString(HashType::htMD5, s2);
+ ASSERT_EQ(hash.to_string(Base::Base16), "md5:900150983cd24fb0d6963f7d28e17f72");
+ }
+
+ TEST(hashString, testKnownSHA1Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc3174
+ auto s = "abc";
+ auto hash = hashString(HashType::htSHA1, s);
+ ASSERT_EQ(hash.to_string(Base::Base16),"sha1:a9993e364706816aba3e25717850c26c9cd0d89d");
+ }
+
+ TEST(hashString, testKnownSHA1Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc3174
+ auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ auto hash = hashString(HashType::htSHA1, s);
+ ASSERT_EQ(hash.to_string(Base::Base16),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+ }
+
+ TEST(hashString, testKnownSHA256Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abc";
+
+ auto hash = hashString(HashType::htSHA256, s);
+ ASSERT_EQ(hash.to_string(Base::Base16),
+ "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+ }
+
+ TEST(hashString, testKnownSHA256Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ auto hash = hashString(HashType::htSHA256, s);
+ ASSERT_EQ(hash.to_string(Base::Base16),
+ "sha256:248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
+ }
+
+ TEST(hashString, testKnownSHA512Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abc";
+ auto hash = hashString(HashType::htSHA512, s);
+ ASSERT_EQ(hash.to_string(Base::Base16),
+ "sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9"
+ "7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd"
+ "454d4423643ce80e2a9ac94fa54ca49f");
+ }
+
+ TEST(hashString, testKnownSHA512Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
+
+ auto hash = hashString(HashType::htSHA512, s);
+ ASSERT_EQ(hash.to_string(Base::Base16),
+ "sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1"
+ "7299aeadb6889018501d289e4900f7e4331b99dec4b5433a"
+ "c7d329eeb6dd26545e96e55b874be909");
+ }
+
+ TEST(hashString, hashingWithUnknownAlgoExits) {
+ auto s = "unknown";
+ ASSERT_DEATH(hashString(HashType::htUnknown, s), "");
+ }
+}
diff --git a/src/libutil/tests/json.cc b/src/libutil/tests/json.cc
new file mode 100644
index 000000000..dea73f53a
--- /dev/null
+++ b/src/libutil/tests/json.cc
@@ -0,0 +1,193 @@
+#include "json.hh"
+#include <gtest/gtest.h>
+#include <sstream>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * toJSON
+ * --------------------------------------------------------------------------*/
+
+ TEST(toJSON, quotesCharPtr) {
+ const char* input = "test";
+ std::stringstream out;
+ toJSON(out, input);
+
+ ASSERT_EQ(out.str(), "\"test\"");
+ }
+
+ TEST(toJSON, quotesStdString) {
+ std::string input = "test";
+ std::stringstream out;
+ toJSON(out, input);
+
+ ASSERT_EQ(out.str(), "\"test\"");
+ }
+
+ TEST(toJSON, convertsNullptrtoNull) {
+ auto input = nullptr;
+ std::stringstream out;
+ toJSON(out, input);
+
+ ASSERT_EQ(out.str(), "null");
+ }
+
+ TEST(toJSON, convertsNullToNull) {
+ const char* input = 0;
+ std::stringstream out;
+ toJSON(out, input);
+
+ ASSERT_EQ(out.str(), "null");
+ }
+
+
+ TEST(toJSON, convertsFloat) {
+ auto input = 1.024f;
+ std::stringstream out;
+ toJSON(out, input);
+
+ ASSERT_EQ(out.str(), "1.024");
+ }
+
+ TEST(toJSON, convertsDouble) {
+ const double input = 1.024;
+ std::stringstream out;
+ toJSON(out, input);
+
+ ASSERT_EQ(out.str(), "1.024");
+ }
+
+ TEST(toJSON, convertsBool) {
+ auto input = false;
+ std::stringstream out;
+ toJSON(out, input);
+
+ ASSERT_EQ(out.str(), "false");
+ }
+
+ TEST(toJSON, quotesTab) {
+ std::stringstream out;
+ toJSON(out, "\t");
+
+ ASSERT_EQ(out.str(), "\"\\t\"");
+ }
+
+ TEST(toJSON, quotesNewline) {
+ std::stringstream out;
+ toJSON(out, "\n");
+
+ ASSERT_EQ(out.str(), "\"\\n\"");
+ }
+
+ TEST(toJSON, quotesCreturn) {
+ std::stringstream out;
+ toJSON(out, "\r");
+
+ ASSERT_EQ(out.str(), "\"\\r\"");
+ }
+
+ TEST(toJSON, quotesCreturnNewLine) {
+ std::stringstream out;
+ toJSON(out, "\r\n");
+
+ ASSERT_EQ(out.str(), "\"\\r\\n\"");
+ }
+
+ TEST(toJSON, quotesDoublequotes) {
+ std::stringstream out;
+ toJSON(out, "\"");
+
+ ASSERT_EQ(out.str(), "\"\\\"\"");
+ }
+
+ TEST(toJSON, substringEscape) {
+ std::stringstream out;
+ const char *s = "foo\t";
+ toJSON(out, s+3, s + strlen(s));
+
+ ASSERT_EQ(out.str(), "\"\\t\"");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * JSONObject
+ * --------------------------------------------------------------------------*/
+
+ TEST(JSONObject, emptyObject) {
+ std::stringstream out;
+ {
+ JSONObject t(out);
+ }
+ ASSERT_EQ(out.str(), "{}");
+ }
+
+ TEST(JSONObject, objectWithList) {
+ std::stringstream out;
+ {
+ JSONObject t(out);
+ auto l = t.list("list");
+ l.elem("element");
+ }
+ ASSERT_EQ(out.str(), R"#({"list":["element"]})#");
+ }
+
+ TEST(JSONObject, objectWithListIndent) {
+ std::stringstream out;
+ {
+ JSONObject t(out, true);
+ auto l = t.list("list");
+ l.elem("element");
+ }
+ ASSERT_EQ(out.str(),
+R"#({
+ "list": [
+ "element"
+ ]
+})#");
+ }
+
+ TEST(JSONObject, objectWithPlaceholderAndList) {
+ std::stringstream out;
+ {
+ JSONObject t(out);
+ auto l = t.placeholder("list");
+ l.list().elem("element");
+ }
+
+ ASSERT_EQ(out.str(), R"#({"list":["element"]})#");
+ }
+
+ TEST(JSONObject, objectWithPlaceholderAndObject) {
+ std::stringstream out;
+ {
+ JSONObject t(out);
+ auto l = t.placeholder("object");
+ l.object().attr("key", "value");
+ }
+
+ ASSERT_EQ(out.str(), R"#({"object":{"key":"value"}})#");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * JSONList
+ * --------------------------------------------------------------------------*/
+
+ TEST(JSONList, empty) {
+ std::stringstream out;
+ {
+ JSONList l(out);
+ }
+ ASSERT_EQ(out.str(), R"#([])#");
+ }
+
+ TEST(JSONList, withElements) {
+ std::stringstream out;
+ {
+ JSONList l(out);
+ l.elem("one");
+ l.object();
+ l.placeholder().write("three");
+ }
+ ASSERT_EQ(out.str(), R"#(["one",{},"three"])#");
+ }
+}
+
diff --git a/src/libutil/tests/xml-writer.cc b/src/libutil/tests/xml-writer.cc
new file mode 100644
index 000000000..adcde25c9
--- /dev/null
+++ b/src/libutil/tests/xml-writer.cc
@@ -0,0 +1,105 @@
+#include "xml-writer.hh"
+#include <gtest/gtest.h>
+#include <sstream>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * XMLWriter
+ * --------------------------------------------------------------------------*/
+
+ TEST(XMLWriter, emptyObject) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n");
+ }
+
+ TEST(XMLWriter, objectWithEmptyElement) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ t.openElement("foobar");
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithAttrs) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {
+ { "foo", "bar" }
+ };
+ t.openElement("foobar", attrs);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\"></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithEmptyAttrs) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {};
+ t.openElement("foobar", attrs);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithAttrsEscaping) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {
+ { "<key>", "<value>" }
+ };
+ t.openElement("foobar", attrs);
+ }
+
+ // XXX: While "<value>" is escaped, "<key>" isn't which I think is a bug.
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar <key>=\"&lt;value&gt;\"></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithAttrsIndented) {
+ std::stringstream out;
+ {
+ XMLWriter t(true, out);
+ XMLAttrs attrs = {
+ { "foo", "bar" }
+ };
+ t.openElement("foobar", attrs);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\">\n</foobar>\n");
+ }
+
+ TEST(XMLWriter, writeEmptyElement) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ t.writeEmptyElement("foobar");
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar />");
+ }
+
+ TEST(XMLWriter, writeEmptyElementWithAttributes) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {
+ { "foo", "bar" }
+ };
+ t.writeEmptyElement("foobar", attrs);
+
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\" />");
+ }
+
+}