aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorJohn Ericson <John.Ericson@Obsidian.Systems>2020-06-04 21:04:35 +0000
committerJohn Ericson <John.Ericson@Obsidian.Systems>2020-06-04 21:04:35 +0000
commite5cc1ebc5db1ef837da82f5ce7824bb29cbcc44b (patch)
tree42d659874572b812526596f6534ffe884e77080f /src/libutil
parenta7b82fd006606dad591f8da88907e79b4c87b1f7 (diff)
parent0f44b60e6dc999697bf9f2a4b3652a0551016598 (diff)
Merge remote-tracking branch 'upstream/master' into no-stringly-typed-derivation-output
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/args.cc11
-rw-r--r--src/libutil/args.hh2
-rw-r--r--src/libutil/lru-cache.hh1
-rw-r--r--src/libutil/tests/lru-cache.cc130
-rw-r--r--src/libutil/url.cc1
-rw-r--r--src/libutil/url.hh6
-rw-r--r--src/libutil/util.hh3
7 files changed, 149 insertions, 5 deletions
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index f829415d1..afeaf4cea 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -217,10 +217,15 @@ MultiCommand::MultiCommand(const Commands & commands)
{
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
assert(!command);
- auto i = commands.find(ss[0]);
+ auto cmd = ss[0];
+ if (auto alias = get(deprecatedAliases, cmd)) {
+ warn("'%s' is a deprecated alias for '%s'", cmd, *alias);
+ cmd = *alias;
+ }
+ auto i = commands.find(cmd);
if (i == commands.end())
- throw UsageError("'%s' is not a recognised command", ss[0]);
- command = {ss[0], i->second()};
+ throw UsageError("'%s' is not a recognised command", cmd);
+ command = {cmd, i->second()};
}});
categories[Command::catDefault] = "Available commands";
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
index 1932e6a8a..154d1e6aa 100644
--- a/src/libutil/args.hh
+++ b/src/libutil/args.hh
@@ -234,6 +234,8 @@ public:
std::map<Command::Category, std::string> categories;
+ std::map<std::string, std::string> deprecatedAliases;
+
// Selected command, if any.
std::optional<std::pair<std::string, ref<Command>>> command;
diff --git a/src/libutil/lru-cache.hh b/src/libutil/lru-cache.hh
index 8b83f842c..6ef4a3e06 100644
--- a/src/libutil/lru-cache.hh
+++ b/src/libutil/lru-cache.hh
@@ -1,5 +1,6 @@
#pragma once
+#include <cassert>
#include <map>
#include <list>
#include <optional>
diff --git a/src/libutil/tests/lru-cache.cc b/src/libutil/tests/lru-cache.cc
new file mode 100644
index 000000000..091d3d5ed
--- /dev/null
+++ b/src/libutil/tests/lru-cache.cc
@@ -0,0 +1,130 @@
+#include "lru-cache.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * size
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, sizeOfEmptyCacheIsZero) {
+ LRUCache<std::string, std::string> c(10);
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ TEST(LRUCache, sizeOfSingleElementCacheIsOne) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("foo", "bar");
+ ASSERT_EQ(c.size(), 1);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * upsert / get
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, getFromEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ auto val = c.get("x");
+ ASSERT_EQ(val.has_value(), false);
+ }
+
+ TEST(LRUCache, getExistingValue) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("foo", "bar");
+ auto val = c.get("foo");
+ ASSERT_EQ(val, "bar");
+ }
+
+ TEST(LRUCache, getNonExistingValueFromNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("foo", "bar");
+ auto val = c.get("another");
+ ASSERT_EQ(val.has_value(), false);
+ }
+
+ TEST(LRUCache, upsertOnZeroCapacityCache) {
+ LRUCache<std::string, std::string> c(0);
+ c.upsert("foo", "bar");
+ auto val = c.get("foo");
+ ASSERT_EQ(val.has_value(), false);
+ }
+
+ TEST(LRUCache, updateExistingValue) {
+ LRUCache<std::string, std::string> c(1);
+ c.upsert("foo", "bar");
+
+ auto val = c.get("foo");
+ ASSERT_EQ(val.value_or("error"), "bar");
+ ASSERT_EQ(c.size(), 1);
+
+ c.upsert("foo", "changed");
+ val = c.get("foo");
+ ASSERT_EQ(val.value_or("error"), "changed");
+ ASSERT_EQ(c.size(), 1);
+ }
+
+ TEST(LRUCache, overwriteOldestWhenCapacityIsReached) {
+ LRUCache<std::string, std::string> c(3);
+ c.upsert("one", "eins");
+ c.upsert("two", "zwei");
+ c.upsert("three", "drei");
+
+ ASSERT_EQ(c.size(), 3);
+ ASSERT_EQ(c.get("one").value_or("error"), "eins");
+
+ // exceed capacity
+ c.upsert("another", "whatever");
+
+ ASSERT_EQ(c.size(), 3);
+ // Retrieving "one" makes it the most recent element thus
+ // two will be the oldest one and thus replaced.
+ ASSERT_EQ(c.get("two").has_value(), false);
+ ASSERT_EQ(c.get("another").value(), "whatever");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * clear
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, clearEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.clear();
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ TEST(LRUCache, clearNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("one", "eins");
+ c.upsert("two", "zwei");
+ c.upsert("three", "drei");
+ ASSERT_EQ(c.size(), 3);
+ c.clear();
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * erase
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, eraseFromEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ ASSERT_EQ(c.erase("foo"), false);
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ TEST(LRUCache, eraseMissingFromNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("one", "eins");
+ ASSERT_EQ(c.erase("foo"), false);
+ ASSERT_EQ(c.size(), 1);
+ ASSERT_EQ(c.get("one").value_or("error"), "eins");
+ }
+
+ TEST(LRUCache, eraseFromNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("one", "eins");
+ ASSERT_EQ(c.erase("one"), true);
+ ASSERT_EQ(c.size(), 0);
+ ASSERT_EQ(c.get("one").value_or("empty"), "empty");
+ }
+}
diff --git a/src/libutil/url.cc b/src/libutil/url.cc
index 5d5328e5d..88c09eef9 100644
--- a/src/libutil/url.cc
+++ b/src/libutil/url.cc
@@ -4,6 +4,7 @@
namespace nix {
std::regex refRegex(refRegexS, std::regex::ECMAScript);
+std::regex badGitRefRegex(badGitRefRegexS, std::regex::ECMAScript);
std::regex revRegex(revRegexS, std::regex::ECMAScript);
std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript);
diff --git a/src/libutil/url.hh b/src/libutil/url.hh
index 1503023a2..4a0d4071b 100644
--- a/src/libutil/url.hh
+++ b/src/libutil/url.hh
@@ -49,6 +49,12 @@ const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRege
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check
extern std::regex refRegex;
+// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
+// This is because of the definition of a ref in refs.c in https://github.com/git/git
+// See tests/fetchGitRefs.sh for the full definition
+const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
+extern std::regex badGitRefRegex;
+
// A Git revision (a SHA-1 commit hash).
const static std::string revRegexS = "[0-9a-fA-F]{40}";
extern std::regex revRegex;
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index a63ee05b3..4b117f9bc 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -459,8 +459,7 @@ string base64Encode(const string & s);
string base64Decode(const string & s);
-/* Get a value for the specified key from an associate container, or a
- default value if the key doesn't exist. */
+/* Get a value for the specified key from an associate container. */
template <class T>
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
{