aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/derivations.hh1
-rw-r--r--src/libstore/misc.cc2
-rw-r--r--src/libstore/outputs-spec.cc22
-rw-r--r--src/libstore/path-regex.hh7
-rw-r--r--src/libstore/path.cc6
-rw-r--r--src/libstore/path.hh3
-rw-r--r--src/libstore/realisation.hh2
-rw-r--r--src/libstore/tests/libstoretests.hh23
-rw-r--r--src/libstore/tests/local.mk2
-rw-r--r--src/libstore/tests/outputs-spec.cc14
-rw-r--r--src/libstore/tests/path.cc144
11 files changed, 213 insertions, 13 deletions
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index 7ee3ded6a..f42c13cdc 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -13,6 +13,7 @@
namespace nix {
+class Store;
/* Abstract syntax of derivations. */
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index da96dcebc..1c187535d 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -319,7 +319,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
[&](const OutputsSpec::Names & names) {
return static_cast<std::set<std::string>>(names);
},
- }, bfd.outputs);
+ }, bfd.outputs.raw());
for (auto & output : outputNames) {
auto outputHash = get(outputHashes, output);
if (!outputHash)
diff --git a/src/libstore/outputs-spec.cc b/src/libstore/outputs-spec.cc
index d0f39a854..e26c38138 100644
--- a/src/libstore/outputs-spec.cc
+++ b/src/libstore/outputs-spec.cc
@@ -1,8 +1,10 @@
+#include <regex>
+#include <nlohmann/json.hpp>
+
#include "util.hh"
+#include "regex-combinators.hh"
#include "outputs-spec.hh"
-#include "nlohmann/json.hpp"
-
-#include <regex>
+#include "path-regex.hh"
namespace nix {
@@ -18,10 +20,14 @@ bool OutputsSpec::contains(const std::string & outputName) const
}, raw());
}
+static std::string outputSpecRegexStr =
+ regex::either(
+ regex::group(R"(\*)"),
+ regex::group(regex::list(nameRegexStr)));
std::optional<OutputsSpec> OutputsSpec::parseOpt(std::string_view s)
{
- static std::regex regex(R"((\*)|([a-z]+(,[a-z]+)*))");
+ static std::regex regex(std::string { outputSpecRegexStr });
std::smatch match;
std::string s2 { s }; // until some improves std::regex
@@ -42,7 +48,7 @@ OutputsSpec OutputsSpec::parse(std::string_view s)
{
std::optional spec = parseOpt(s);
if (!spec)
- throw Error("Invalid outputs specifier: '%s'", s);
+ throw Error("invalid outputs specifier '%s'", s);
return *spec;
}
@@ -65,7 +71,7 @@ std::pair<std::string_view, ExtendedOutputsSpec> ExtendedOutputsSpec::parse(std:
{
std::optional spec = parseOpt(s);
if (!spec)
- throw Error("Invalid extended outputs specifier: '%s'", s);
+ throw Error("invalid extended outputs specifier '%s'", s);
return *spec;
}
@@ -163,7 +169,7 @@ void adl_serializer<OutputsSpec>::to_json(json & json, OutputsSpec t) {
[&](const OutputsSpec::Names & names) {
json = names;
},
- }, t);
+ }, t.raw());
}
@@ -183,7 +189,7 @@ void adl_serializer<ExtendedOutputsSpec>::to_json(json & json, ExtendedOutputsSp
[&](const ExtendedOutputsSpec::Explicit & e) {
adl_serializer<OutputsSpec>::to_json(json, e);
},
- }, t);
+ }, t.raw());
}
}
diff --git a/src/libstore/path-regex.hh b/src/libstore/path-regex.hh
new file mode 100644
index 000000000..6893c3876
--- /dev/null
+++ b/src/libstore/path-regex.hh
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace nix {
+
+static constexpr std::string_view nameRegexStr = R"([0-9a-zA-Z\+\-\._\?=]+)";
+
+}
diff --git a/src/libstore/path.cc b/src/libstore/path.cc
index 392db225e..46be54281 100644
--- a/src/libstore/path.cc
+++ b/src/libstore/path.cc
@@ -8,8 +8,10 @@ static void checkName(std::string_view path, std::string_view name)
{
if (name.empty())
throw BadStorePath("store path '%s' has an empty name", path);
- if (name.size() > 211)
- throw BadStorePath("store path '%s' has a name longer than 211 characters", path);
+ if (name.size() > StorePath::MaxPathLen)
+ throw BadStorePath("store path '%s' has a name longer than '%d characters",
+ StorePath::MaxPathLen, path);
+ // See nameRegexStr for the definition
for (auto c : name)
if (!((c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'z')
diff --git a/src/libstore/path.hh b/src/libstore/path.hh
index 7f13c11e9..1e5579b90 100644
--- a/src/libstore/path.hh
+++ b/src/libstore/path.hh
@@ -6,7 +6,6 @@
namespace nix {
-class Store;
struct Hash;
class StorePath
@@ -18,6 +17,8 @@ public:
/* Size of the hash part of store paths, in base-32 characters. */
constexpr static size_t HashLen = 32; // i.e. 160 bits
+ constexpr static size_t MaxPathLen = 211;
+
StorePath() = delete;
StorePath(std::string_view baseName);
diff --git a/src/libstore/realisation.hh b/src/libstore/realisation.hh
index 9429c7004..48d0283de 100644
--- a/src/libstore/realisation.hh
+++ b/src/libstore/realisation.hh
@@ -10,6 +10,8 @@
namespace nix {
+class Store;
+
struct DrvOutput {
// The hash modulo of the derivation
Hash drvHash;
diff --git a/src/libstore/tests/libstoretests.hh b/src/libstore/tests/libstoretests.hh
new file mode 100644
index 000000000..05397659b
--- /dev/null
+++ b/src/libstore/tests/libstoretests.hh
@@ -0,0 +1,23 @@
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "store-api.hh"
+
+namespace nix {
+
+class LibStoreTest : public ::testing::Test {
+ public:
+ static void SetUpTestSuite() {
+ initLibStore();
+ }
+
+ protected:
+ LibStoreTest()
+ : store(openStore("dummy://"))
+ { }
+
+ ref<Store> store;
+};
+
+
+} /* namespace nix */
diff --git a/src/libstore/tests/local.mk b/src/libstore/tests/local.mk
index f74295d97..a2cf8a0cf 100644
--- a/src/libstore/tests/local.mk
+++ b/src/libstore/tests/local.mk
@@ -12,4 +12,4 @@ libstore-tests_CXXFLAGS += -I src/libstore -I src/libutil
libstore-tests_LIBS = libstore libutil
-libstore-tests_LDFLAGS := $(GTEST_LIBS)
+libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS)
diff --git a/src/libstore/tests/outputs-spec.cc b/src/libstore/tests/outputs-spec.cc
index c9c2cafd0..06e4cabbd 100644
--- a/src/libstore/tests/outputs-spec.cc
+++ b/src/libstore/tests/outputs-spec.cc
@@ -40,6 +40,20 @@ TEST(OutputsSpec, names_out) {
ASSERT_EQ(expected.to_string(), str);
}
+TEST(OutputsSpec, names_underscore) {
+ std::string_view str = "a_b";
+ OutputsSpec expected = OutputsSpec::Names { "a_b" };
+ ASSERT_EQ(OutputsSpec::parse(str), expected);
+ ASSERT_EQ(expected.to_string(), str);
+}
+
+TEST(OutputsSpec, names_numberic) {
+ std::string_view str = "01";
+ OutputsSpec expected = OutputsSpec::Names { "01" };
+ ASSERT_EQ(OutputsSpec::parse(str), expected);
+ ASSERT_EQ(expected.to_string(), str);
+}
+
TEST(OutputsSpec, names_out_bin) {
OutputsSpec expected = OutputsSpec::Names { "out", "bin" };
ASSERT_EQ(OutputsSpec::parse("out,bin"), expected);
diff --git a/src/libstore/tests/path.cc b/src/libstore/tests/path.cc
new file mode 100644
index 000000000..8ea252c92
--- /dev/null
+++ b/src/libstore/tests/path.cc
@@ -0,0 +1,144 @@
+#include <regex>
+
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+#include "path-regex.hh"
+#include "store-api.hh"
+
+#include "libstoretests.hh"
+
+namespace nix {
+
+#define STORE_DIR "/nix/store/"
+#define HASH_PART "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q"
+
+class StorePathTest : public LibStoreTest
+{
+};
+
+static std::regex nameRegex { std::string { nameRegexStr } };
+
+#define TEST_DONT_PARSE(NAME, STR) \
+ TEST_F(StorePathTest, bad_ ## NAME) { \
+ std::string_view str = \
+ STORE_DIR HASH_PART "-" STR; \
+ ASSERT_THROW( \
+ store->parseStorePath(str), \
+ BadStorePath); \
+ std::string name { STR }; \
+ EXPECT_FALSE(std::regex_match(name, nameRegex)); \
+ }
+
+TEST_DONT_PARSE(empty, "")
+TEST_DONT_PARSE(garbage, "&*()")
+TEST_DONT_PARSE(double_star, "**")
+TEST_DONT_PARSE(star_first, "*,foo")
+TEST_DONT_PARSE(star_second, "foo,*")
+TEST_DONT_PARSE(bang, "foo!o")
+
+#undef TEST_DONT_PARSE
+
+#define TEST_DO_PARSE(NAME, STR) \
+ TEST_F(StorePathTest, good_ ## NAME) { \
+ std::string_view str = \
+ STORE_DIR HASH_PART "-" STR; \
+ auto p = store->parseStorePath(str); \
+ std::string name { p.name() }; \
+ EXPECT_TRUE(std::regex_match(name, nameRegex)); \
+ }
+
+// 0-9 a-z A-Z + - . _ ? =
+
+TEST_DO_PARSE(numbers, "02345")
+TEST_DO_PARSE(lower_case, "foo")
+TEST_DO_PARSE(upper_case, "FOO")
+TEST_DO_PARSE(plus, "foo+bar")
+TEST_DO_PARSE(dash, "foo-dev")
+TEST_DO_PARSE(underscore, "foo_bar")
+TEST_DO_PARSE(period, "foo.txt")
+TEST_DO_PARSE(question_mark, "foo?why")
+TEST_DO_PARSE(equals_sign, "foo=foo")
+
+#undef TEST_DO_PARSE
+
+// For rapidcheck
+void showValue(const StorePath & p, std::ostream & os) {
+ os << p.to_string();
+}
+
+}
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<StorePath> {
+ static Gen<StorePath> arbitrary();
+};
+
+Gen<StorePath> Arbitrary<StorePath>::arbitrary()
+{
+ auto len = *gen::inRange<size_t>(1, StorePath::MaxPathLen);
+
+ std::string pre { HASH_PART "-" };
+ pre.reserve(pre.size() + len);
+
+ for (size_t c = 0; c < len; ++c) {
+ switch (auto i = *gen::inRange<uint8_t>(0, 10 + 2 * 26 + 6)) {
+ case 0 ... 9:
+ pre += '0' + i;
+ case 10 ... 35:
+ pre += 'A' + (i - 10);
+ break;
+ case 36 ... 61:
+ pre += 'a' + (i - 36);
+ break;
+ case 62:
+ pre += '+';
+ break;
+ case 63:
+ pre += '-';
+ break;
+ case 64:
+ pre += '.';
+ break;
+ case 65:
+ pre += '_';
+ break;
+ case 66:
+ pre += '?';
+ break;
+ case 67:
+ pre += '=';
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ return gen::just(StorePath { pre });
+}
+
+} // namespace rc
+
+namespace nix {
+
+RC_GTEST_FIXTURE_PROP(
+ StorePathTest,
+ prop_regex_accept,
+ (const StorePath & p))
+{
+ RC_ASSERT(std::regex_match(std::string { p.name() }, nameRegex));
+}
+
+RC_GTEST_FIXTURE_PROP(
+ StorePathTest,
+ prop_round_rip,
+ (const StorePath & p))
+{
+ RC_ASSERT(p == store->parseStorePath(store->printStorePath(p)));
+}
+
+}