1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-14 22:42:41 +01:00

Merge remote-tracking branch 'upstream/master' into upstream-RossComputerGuy/feat/expose-computefsclosure

This commit is contained in:
Robert Hensing 2025-10-15 15:30:43 +02:00
commit a9d9b50b72
467 changed files with 9259 additions and 5039 deletions

View file

@ -0,0 +1,10 @@
{
"drvPath": {
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"output": "bar"
},
"outputs": [
"baz",
"quux"
]
}

View file

@ -0,0 +1,9 @@
{
"drvPath": {
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"output": "bar"
},
"outputs": [
"*"
]
}

View file

@ -0,0 +1 @@
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"

View file

@ -0,0 +1,7 @@
{
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"outputs": [
"bar",
"baz"
]
}

View file

@ -0,0 +1,4 @@
{
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"output": "bar"
}

View file

@ -0,0 +1,7 @@
{
"drvPath": {
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"output": "bar"
},
"output": "baz"
}

View file

@ -0,0 +1 @@
"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"

View file

@ -0,0 +1,3 @@
[
"*"
]

View file

@ -0,0 +1,3 @@
[
"*"
]

View file

@ -0,0 +1 @@
null

View file

@ -0,0 +1,3 @@
[
"a"
]

View file

@ -0,0 +1,4 @@
[
"a",
"b"
]

View file

@ -0,0 +1,3 @@
[
"a"
]

View file

@ -0,0 +1,4 @@
[
"a",
"b"
]

View file

@ -0,0 +1,6 @@
{
"dependentRealisations": {},
"id": "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"signatures": []
}

View file

@ -0,0 +1,8 @@
{
"dependentRealisations": {
"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"
},
"id": "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"signatures": []
}

View file

@ -0,0 +1,8 @@
{
"dependentRealisations": {},
"id": "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo",
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
"signatures": [
"asdfasdfasdf"
]
}

View file

@ -5,13 +5,13 @@
#include "nix/store/derivations.hh"
#include "nix/store/tests/libstore.hh"
#include "nix/util/tests/characterization.hh"
#include "nix/util/tests/json-characterization.hh"
namespace nix {
using nlohmann::json;
class DerivationTest : public CharacterizationTest, public LibStoreTest
class DerivationTest : public virtual CharacterizationTest, public LibStoreTest
{
std::filesystem::path unitTestData = getUnitTestData() / "derivation";
@ -66,146 +66,183 @@ TEST_F(DynDerivationTest, BadATerm_oldVersionDynDeps)
FormatError);
}
#define TEST_JSON(FIXTURE, NAME, VAL, DRV_NAME, OUTPUT_NAME) \
TEST_F(FIXTURE, DerivationOutput_##NAME##_from_json) \
{ \
readTest("output-" #NAME ".json", [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \
DerivationOutput got = DerivationOutput::fromJSON(DRV_NAME, OUTPUT_NAME, encoded, mockXpSettings); \
DerivationOutput expected{VAL}; \
ASSERT_EQ(got, expected); \
}); \
} \
\
TEST_F(FIXTURE, DerivationOutput_##NAME##_to_json) \
{ \
writeTest( \
"output-" #NAME ".json", \
[&]() -> json { return DerivationOutput{(VAL)}.toJSON((DRV_NAME), (OUTPUT_NAME)); }, \
[](const auto & file) { return json::parse(readFile(file)); }, \
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
#define MAKE_OUTPUT_JSON_TEST_P(FIXTURE) \
TEST_P(FIXTURE, from_json) \
{ \
const auto & [name, expected] = GetParam(); \
/* Don't use readJsonTest because we want to check experimental \
features. */ \
readTest(Path{"output-"} + name + ".json", [&](const auto & encoded_) { \
json j = json::parse(encoded_); \
DerivationOutput got = DerivationOutput::fromJSON(j, mockXpSettings); \
ASSERT_EQ(got, expected); \
}); \
} \
\
TEST_P(FIXTURE, to_json) \
{ \
const auto & [name, value] = GetParam(); \
writeJsonTest("output-" + name, value); \
}
TEST_JSON(
DerivationTest,
inputAddressed,
(DerivationOutput::InputAddressed{
.path = store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"),
}),
"drv-name",
"output-name")
struct DerivationOutputJsonTest : DerivationTest,
JsonCharacterizationTest<DerivationOutput>,
::testing::WithParamInterface<std::pair<std::string_view, DerivationOutput>>
{};
TEST_JSON(
DerivationTest,
caFixedFlat,
(DerivationOutput::CAFixed{
.ca =
{
.method = ContentAddressMethod::Raw::Flat,
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
},
}),
"drv-name",
"output-name")
MAKE_OUTPUT_JSON_TEST_P(DerivationOutputJsonTest)
TEST_JSON(
DerivationTest,
caFixedNAR,
(DerivationOutput::CAFixed{
.ca =
{
INSTANTIATE_TEST_SUITE_P(
DerivationOutputJSON,
DerivationOutputJsonTest,
::testing::Values(
std::pair{
"inputAddressed",
DerivationOutput{DerivationOutput::InputAddressed{
.path = StorePath{"c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"},
}},
},
std::pair{
"caFixedFlat",
DerivationOutput{DerivationOutput::CAFixed{
.ca =
{
.method = ContentAddressMethod::Raw::Flat,
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
},
}},
},
std::pair{
"caFixedNAR",
DerivationOutput{DerivationOutput::CAFixed{
.ca =
{
.method = ContentAddressMethod::Raw::NixArchive,
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
},
}},
},
std::pair{
"deferred",
DerivationOutput{DerivationOutput::Deferred{}},
}));
struct DynDerivationOutputJsonTest : DynDerivationTest,
JsonCharacterizationTest<DerivationOutput>,
::testing::WithParamInterface<std::pair<std::string_view, DerivationOutput>>
{};
MAKE_OUTPUT_JSON_TEST_P(DynDerivationOutputJsonTest);
INSTANTIATE_TEST_SUITE_P(
DynDerivationOutputJSON,
DynDerivationOutputJsonTest,
::testing::Values(
std::pair{
"caFixedText",
DerivationOutput{DerivationOutput::CAFixed{
.ca =
{
.method = ContentAddressMethod::Raw::Text,
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
},
}},
}));
struct CaDerivationOutputJsonTest : CaDerivationTest,
JsonCharacterizationTest<DerivationOutput>,
::testing::WithParamInterface<std::pair<std::string_view, DerivationOutput>>
{};
MAKE_OUTPUT_JSON_TEST_P(CaDerivationOutputJsonTest);
INSTANTIATE_TEST_SUITE_P(
CaDerivationOutputJSON,
CaDerivationOutputJsonTest,
::testing::Values(
std::pair{
"caFloating",
DerivationOutput{DerivationOutput::CAFloating{
.method = ContentAddressMethod::Raw::NixArchive,
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
},
}),
"drv-name",
"output-name")
.hashAlgo = HashAlgorithm::SHA256,
}},
}));
TEST_JSON(
DynDerivationTest,
caFixedText,
(DerivationOutput::CAFixed{
.ca =
{
.method = ContentAddressMethod::Raw::Text,
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
},
}),
"drv-name",
"output-name")
struct ImpureDerivationOutputJsonTest : ImpureDerivationTest,
JsonCharacterizationTest<DerivationOutput>,
::testing::WithParamInterface<std::pair<std::string_view, DerivationOutput>>
{};
TEST_JSON(
CaDerivationTest,
caFloating,
(DerivationOutput::CAFloating{
.method = ContentAddressMethod::Raw::NixArchive,
.hashAlgo = HashAlgorithm::SHA256,
}),
"drv-name",
"output-name")
MAKE_OUTPUT_JSON_TEST_P(ImpureDerivationOutputJsonTest);
TEST_JSON(DerivationTest, deferred, DerivationOutput::Deferred{}, "drv-name", "output-name")
INSTANTIATE_TEST_SUITE_P(
ImpureDerivationOutputJSON,
ImpureDerivationOutputJsonTest,
::testing::Values(
std::pair{
"impure",
DerivationOutput{DerivationOutput::Impure{
.method = ContentAddressMethod::Raw::NixArchive,
.hashAlgo = HashAlgorithm::SHA256,
}},
}));
TEST_JSON(
ImpureDerivationTest,
impure,
(DerivationOutput::Impure{
.method = ContentAddressMethod::Raw::NixArchive,
.hashAlgo = HashAlgorithm::SHA256,
}),
"drv-name",
"output-name")
#undef MAKE_OUTPUT_JSON_TEST_P
#undef TEST_JSON
#define TEST_JSON(FIXTURE, NAME, VAL) \
TEST_F(FIXTURE, Derivation_##NAME##_from_json) \
{ \
readTest(#NAME ".json", [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \
Derivation expected{VAL}; \
Derivation got = Derivation::fromJSON(encoded, mockXpSettings); \
ASSERT_EQ(got, expected); \
}); \
} \
\
TEST_F(FIXTURE, Derivation_##NAME##_to_json) \
{ \
writeTest( \
#NAME ".json", \
[&]() -> json { return Derivation{VAL}.toJSON(); }, \
[](const auto & file) { return json::parse(readFile(file)); }, \
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
#define MAKE_TEST_P(FIXTURE) \
TEST_P(FIXTURE, from_json) \
{ \
const auto & drv = GetParam(); \
/* Don't use readJsonTest because we want to check experimental \
features. */ \
readTest(drv.name + ".json", [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \
Derivation got = Derivation::fromJSON(encoded, mockXpSettings); \
ASSERT_EQ(got, drv); \
}); \
} \
\
TEST_P(FIXTURE, to_json) \
{ \
const auto & drv = GetParam(); \
writeJsonTest(drv.name, drv); \
} \
\
TEST_P(FIXTURE, from_aterm) \
{ \
const auto & drv = GetParam(); \
readTest(drv.name + ".drv", [&](auto encoded) { \
auto got = parseDerivation(*store, std::move(encoded), drv.name, mockXpSettings); \
ASSERT_EQ(got.toJSON(), drv.toJSON()); \
ASSERT_EQ(got, drv); \
}); \
} \
\
TEST_P(FIXTURE, to_aterm) \
{ \
const auto & drv = GetParam(); \
writeTest(drv.name + ".drv", [&]() -> std::string { return drv.unparse(*store, false); }); \
}
#define TEST_ATERM(FIXTURE, NAME, VAL, DRV_NAME) \
TEST_F(FIXTURE, Derivation_##NAME##_from_aterm) \
{ \
readTest(#NAME ".drv", [&](auto encoded) { \
Derivation expected{VAL}; \
auto got = parseDerivation(*store, std::move(encoded), DRV_NAME, mockXpSettings); \
ASSERT_EQ(got.toJSON(), expected.toJSON()); \
ASSERT_EQ(got, expected); \
}); \
} \
\
TEST_F(FIXTURE, Derivation_##NAME##_to_aterm) \
{ \
writeTest(#NAME ".drv", [&]() -> std::string { return (VAL).unparse(*store, false); }); \
}
struct DerivationJsonAtermTest : DerivationTest,
JsonCharacterizationTest<Derivation>,
::testing::WithParamInterface<Derivation>
{};
Derivation makeSimpleDrv(const Store & store)
MAKE_TEST_P(DerivationJsonAtermTest);
Derivation makeSimpleDrv()
{
Derivation drv;
drv.name = "simple-derivation";
drv.inputSrcs = {
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
StorePath("c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
};
drv.inputDrvs = {
.map =
{
{
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
StorePath("c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
{
.value =
{
@ -231,22 +268,27 @@ Derivation makeSimpleDrv(const Store & store)
return drv;
}
TEST_JSON(DerivationTest, simple, makeSimpleDrv(*store))
INSTANTIATE_TEST_SUITE_P(DerivationJSONATerm, DerivationJsonAtermTest, ::testing::Values(makeSimpleDrv()));
TEST_ATERM(DerivationTest, simple, makeSimpleDrv(*store), "simple-derivation")
struct DynDerivationJsonAtermTest : DynDerivationTest,
JsonCharacterizationTest<Derivation>,
::testing::WithParamInterface<Derivation>
{};
Derivation makeDynDepDerivation(const Store & store)
MAKE_TEST_P(DynDerivationJsonAtermTest);
Derivation makeDynDepDerivation()
{
Derivation drv;
drv.name = "dyn-dep-derivation";
drv.inputSrcs = {
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
StorePath{"c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"},
};
drv.inputDrvs = {
.map =
{
{
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
StorePath{"c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"},
DerivedPathMap<StringSet>::ChildNode{
.value =
{
@ -293,11 +335,8 @@ Derivation makeDynDepDerivation(const Store & store)
return drv;
}
TEST_JSON(DynDerivationTest, dynDerivationDeps, makeDynDepDerivation(*store))
INSTANTIATE_TEST_SUITE_P(DynDerivationJSONATerm, DynDerivationJsonAtermTest, ::testing::Values(makeDynDepDerivation()));
TEST_ATERM(DynDerivationTest, dynDerivationDeps, makeDynDepDerivation(*store), "dyn-dep-derivation")
#undef TEST_JSON
#undef TEST_ATERM
#undef MAKE_TEST_P
} // namespace nix

View file

@ -3,13 +3,23 @@
#include <gtest/gtest.h>
#include <rapidcheck/gtest.h>
#include "nix/util/tests/characterization.hh"
#include "nix/store/tests/derived-path.hh"
#include "nix/store/tests/libstore.hh"
namespace nix {
class DerivedPathTest : public LibStoreTest
{};
class DerivedPathTest : public CharacterizationTest, public LibStoreTest
{
std::filesystem::path unitTestData = getUnitTestData() / "derived-path";
public:
std::filesystem::path goldenMaster(std::string_view testStem) const override
{
return unitTestData / testStem;
}
};
/**
* Round trip (string <-> data structure) test for
@ -107,4 +117,90 @@ RC_GTEST_FIXTURE_PROP(DerivedPathTest, prop_round_rip, (const DerivedPath & o))
#endif
/* ----------------------------------------------------------------------------
* JSON
* --------------------------------------------------------------------------*/
using nlohmann::json;
#define TEST_JSON(TYPE, NAME, VAL) \
static const TYPE NAME = VAL; \
\
TEST_F(DerivedPathTest, NAME##_from_json) \
{ \
readTest(#NAME ".json", [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \
TYPE got = static_cast<TYPE>(encoded); \
ASSERT_EQ(got, NAME); \
}); \
} \
\
TEST_F(DerivedPathTest, NAME##_to_json) \
{ \
writeTest( \
#NAME ".json", \
[&]() -> json { return static_cast<json>(NAME); }, \
[](const auto & file) { return json::parse(readFile(file)); }, \
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
}
TEST_JSON(
SingleDerivedPath, single_opaque, SingleDerivedPath::Opaque{StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"}});
TEST_JSON(
SingleDerivedPath,
single_built,
(SingleDerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"}}),
.output = "bar",
}));
TEST_JSON(
SingleDerivedPath,
single_built_built,
(SingleDerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"}}),
.output = "bar",
}),
.output = "baz",
}));
TEST_JSON(DerivedPath, multi_opaque, DerivedPath::Opaque{StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"}});
TEST_JSON(
DerivedPath,
mutli_built,
(DerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"}}),
.outputs = OutputsSpec::Names{"bar", "baz"},
}));
TEST_JSON(
DerivedPath,
multi_built_built,
(DerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"}}),
.output = "bar",
}),
.outputs = OutputsSpec::Names{"baz", "quux"},
}));
TEST_JSON(
DerivedPath,
multi_built_built_wildcard,
(DerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Built{
.drvPath = make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"}}),
.output = "bar",
}),
.outputs = OutputsSpec::All{},
}));
} // namespace nix

View file

@ -0,0 +1,27 @@
#include <gtest/gtest.h>
#include "nix/store/dummy-store.hh"
#include "nix/store/globals.hh"
#include "nix/store/realisation.hh"
namespace nix {
TEST(DummyStore, realisation_read)
{
initLibStore(/*loadConfig=*/false);
auto store = [] {
auto cfg = make_ref<DummyStoreConfig>(StoreReference::Params{});
cfg->readOnly = false;
return cfg->openStore();
}();
auto drvHash = Hash::parseExplicitFormatUnprefixed(
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", HashAlgorithm::SHA256, HashFormat::Base16);
auto outputName = "foo";
EXPECT_EQ(store->queryRealisation({drvHash, outputName}), nullptr);
}
} // namespace nix

View file

@ -18,4 +18,20 @@ TEST(HttpBinaryCacheStore, constructConfigNoTrailingSlash)
EXPECT_EQ(config.cacheUri.to_string(), "https://foo.bar.baz/a/b");
}
TEST(HttpBinaryCacheStore, constructConfigWithParams)
{
StoreConfig::Params params{{"compression", "xz"}};
HttpBinaryCacheStoreConfig config{"https", "foo.bar.baz/a/b/", params};
EXPECT_EQ(config.cacheUri.to_string(), "https://foo.bar.baz/a/b");
EXPECT_EQ(config.getReference().params, params);
}
TEST(HttpBinaryCacheStore, constructConfigWithParamsAndUrlWithParams)
{
StoreConfig::Params params{{"compression", "xz"}};
HttpBinaryCacheStoreConfig config{"https", "foo.bar.baz/a/b?some-param=some-value", params};
EXPECT_EQ(config.cacheUri.to_string(), "https://foo.bar.baz/a/b?some-param=some-value");
EXPECT_EQ(config.getReference().params, params);
}
} // namespace nix

View file

@ -52,7 +52,6 @@ gtest = dependency('gmock')
deps_private += gtest
subdir('nix-meson-build-support/common')
subdir('nix-meson-build-support/asan-options')
sources = files(
'common-protocol.cc',
@ -61,6 +60,7 @@ sources = files(
'derivation.cc',
'derived-path.cc',
'downstream-placeholder.cc',
'dummy-store.cc',
'http-binary-cache-store.cc',
'legacy-ssh-store.cc',
'local-binary-cache-store.cc',
@ -74,14 +74,16 @@ sources = files(
'outputs-spec.cc',
'path-info.cc',
'path.cc',
'realisation.cc',
'references.cc',
's3-binary-cache-store.cc',
's3.cc',
's3-url.cc',
'serve-protocol.cc',
'ssh-store.cc',
'store-reference.cc',
'uds-remote-store.cc',
'worker-protocol.cc',
'write-derivation.cc',
)
include_dirs = [ include_directories('.') ]
@ -103,7 +105,7 @@ this_exe = executable(
test(
meson.project_name(),
this_exe,
env : asan_test_options_env + {
env : {
'_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data',
'HOME' : meson.current_build_dir() / 'test-home',
'NIX_REMOTE' : meson.current_build_dir() / 'test-home' / 'store',
@ -137,7 +139,7 @@ if get_option('benchmarks')
benchmark(
'nix-store-benchmarks',
benchmark_exe,
env : asan_test_options_env + {
env : {
'_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data',
},
)

View file

@ -1,18 +1,43 @@
#include "nix/store/tests/outputs-spec.hh"
#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
#include <rapidcheck/gtest.h>
#include "nix/store/tests/outputs-spec.hh"
#include "nix/util/tests/json-characterization.hh"
namespace nix {
TEST(OutputsSpec, no_empty_names)
class OutputsSpecTest : public virtual CharacterizationTest
{
std::filesystem::path unitTestData = getUnitTestData() / "outputs-spec";
public:
std::filesystem::path goldenMaster(std::string_view testStem) const override
{
return unitTestData / testStem;
}
};
class ExtendedOutputsSpecTest : public virtual CharacterizationTest
{
std::filesystem::path unitTestData = getUnitTestData() / "outputs-spec" / "extended";
public:
std::filesystem::path goldenMaster(std::string_view testStem) const override
{
return unitTestData / testStem;
}
};
TEST_F(OutputsSpecTest, no_empty_names)
{
ASSERT_DEATH(OutputsSpec::Names{StringSet{}}, "");
}
#define TEST_DONT_PARSE(NAME, STR) \
TEST(OutputsSpec, bad_##NAME) \
TEST_F(OutputsSpecTest, bad_##NAME) \
{ \
std::optional OutputsSpecOpt = OutputsSpec::parseOpt(STR); \
ASSERT_FALSE(OutputsSpecOpt); \
@ -26,7 +51,7 @@ TEST_DONT_PARSE(star_second, "foo,*")
#undef TEST_DONT_PARSE
TEST(OutputsSpec, all)
TEST_F(OutputsSpecTest, all)
{
std::string_view str = "*";
OutputsSpec expected = OutputsSpec::All{};
@ -34,7 +59,7 @@ TEST(OutputsSpec, all)
ASSERT_EQ(expected.to_string(), str);
}
TEST(OutputsSpec, names_out)
TEST_F(OutputsSpecTest, names_out)
{
std::string_view str = "out";
OutputsSpec expected = OutputsSpec::Names{"out"};
@ -42,7 +67,7 @@ TEST(OutputsSpec, names_out)
ASSERT_EQ(expected.to_string(), str);
}
TEST(OutputsSpec, names_underscore)
TEST_F(OutputsSpecTest, names_underscore)
{
std::string_view str = "a_b";
OutputsSpec expected = OutputsSpec::Names{"a_b"};
@ -50,7 +75,7 @@ TEST(OutputsSpec, names_underscore)
ASSERT_EQ(expected.to_string(), str);
}
TEST(OutputsSpec, names_numeric)
TEST_F(OutputsSpecTest, names_numeric)
{
std::string_view str = "01";
OutputsSpec expected = OutputsSpec::Names{"01"};
@ -58,7 +83,7 @@ TEST(OutputsSpec, names_numeric)
ASSERT_EQ(expected.to_string(), str);
}
TEST(OutputsSpec, names_out_bin)
TEST_F(OutputsSpecTest, names_out_bin)
{
OutputsSpec expected = OutputsSpec::Names{"out", "bin"};
ASSERT_EQ(OutputsSpec::parse("out,bin"), expected);
@ -68,32 +93,32 @@ TEST(OutputsSpec, names_out_bin)
#define TEST_SUBSET(X, THIS, THAT) X((OutputsSpec{THIS}).isSubsetOf(THAT));
TEST(OutputsSpec, subsets_all_all)
TEST_F(OutputsSpecTest, subsets_all_all)
{
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::All{}, OutputsSpec::All{});
}
TEST(OutputsSpec, subsets_names_all)
TEST_F(OutputsSpecTest, subsets_names_all)
{
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names{"a"}, OutputsSpec::All{});
}
TEST(OutputsSpec, subsets_names_names_eq)
TEST_F(OutputsSpecTest, subsets_names_names_eq)
{
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names{"a"}, OutputsSpec::Names{"a"});
}
TEST(OutputsSpec, subsets_names_names_noneq)
TEST_F(OutputsSpecTest, subsets_names_names_noneq)
{
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names{"a"}, (OutputsSpec::Names{"a", "b"}));
}
TEST(OutputsSpec, not_subsets_all_names)
TEST_F(OutputsSpecTest, not_subsets_all_names)
{
TEST_SUBSET(ASSERT_FALSE, OutputsSpec::All{}, OutputsSpec::Names{"a"});
}
TEST(OutputsSpec, not_subsets_names_names)
TEST_F(OutputsSpecTest, not_subsets_names_names)
{
TEST_SUBSET(ASSERT_FALSE, (OutputsSpec::Names{"a", "b"}), (OutputsSpec::Names{"a"}));
}
@ -102,22 +127,22 @@ TEST(OutputsSpec, not_subsets_names_names)
#define TEST_UNION(RES, THIS, THAT) ASSERT_EQ(OutputsSpec{RES}, (OutputsSpec{THIS}).union_(THAT));
TEST(OutputsSpec, union_all_all)
TEST_F(OutputsSpecTest, union_all_all)
{
TEST_UNION(OutputsSpec::All{}, OutputsSpec::All{}, OutputsSpec::All{});
}
TEST(OutputsSpec, union_all_names)
TEST_F(OutputsSpecTest, union_all_names)
{
TEST_UNION(OutputsSpec::All{}, OutputsSpec::All{}, OutputsSpec::Names{"a"});
}
TEST(OutputsSpec, union_names_all)
TEST_F(OutputsSpecTest, union_names_all)
{
TEST_UNION(OutputsSpec::All{}, OutputsSpec::Names{"a"}, OutputsSpec::All{});
}
TEST(OutputsSpec, union_names_names)
TEST_F(OutputsSpecTest, union_names_names)
{
TEST_UNION((OutputsSpec::Names{"a", "b"}), OutputsSpec::Names{"a"}, OutputsSpec::Names{"b"});
}
@ -125,7 +150,7 @@ TEST(OutputsSpec, union_names_names)
#undef TEST_UNION
#define TEST_DONT_PARSE(NAME, STR) \
TEST(ExtendedOutputsSpec, bad_##NAME) \
TEST_F(ExtendedOutputsSpecTest, bad_##NAME) \
{ \
std::optional extendedOutputsSpecOpt = ExtendedOutputsSpec::parseOpt(STR); \
ASSERT_FALSE(extendedOutputsSpecOpt); \
@ -140,7 +165,7 @@ TEST_DONT_PARSE(star_second, "^foo,*")
#undef TEST_DONT_PARSE
TEST(ExtendedOutputsSpec, default)
TEST_F(ExtendedOutputsSpecTest, default)
{
std::string_view str = "foo";
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
@ -150,7 +175,7 @@ TEST(ExtendedOutputsSpec, default)
ASSERT_EQ(std::string{prefix} + expected.to_string(), str);
}
TEST(ExtendedOutputsSpec, all)
TEST_F(ExtendedOutputsSpecTest, all)
{
std::string_view str = "foo^*";
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
@ -160,7 +185,7 @@ TEST(ExtendedOutputsSpec, all)
ASSERT_EQ(std::string{prefix} + expected.to_string(), str);
}
TEST(ExtendedOutputsSpec, out)
TEST_F(ExtendedOutputsSpecTest, out)
{
std::string_view str = "foo^out";
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
@ -170,7 +195,7 @@ TEST(ExtendedOutputsSpec, out)
ASSERT_EQ(std::string{prefix} + expected.to_string(), str);
}
TEST(ExtendedOutputsSpec, out_bin)
TEST_F(ExtendedOutputsSpecTest, out_bin)
{
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^out,bin");
ASSERT_EQ(prefix, "foo");
@ -179,7 +204,7 @@ TEST(ExtendedOutputsSpec, out_bin)
ASSERT_EQ(std::string{prefix} + expected.to_string(), "foo^bin,out");
}
TEST(ExtendedOutputsSpec, many_carrot)
TEST_F(ExtendedOutputsSpecTest, many_carrot)
{
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^bar^out,bin");
ASSERT_EQ(prefix, "foo^bar");
@ -188,28 +213,49 @@ TEST(ExtendedOutputsSpec, many_carrot)
ASSERT_EQ(std::string{prefix} + expected.to_string(), "foo^bar^bin,out");
}
#define TEST_JSON(TYPE, NAME, STR, VAL) \
\
TEST(TYPE, NAME##_to_json) \
{ \
using nlohmann::literals::operator"" _json; \
ASSERT_EQ(STR##_json, ((nlohmann::json) TYPE{VAL})); \
} \
\
TEST(TYPE, NAME##_from_json) \
{ \
using nlohmann::literals::operator"" _json; \
ASSERT_EQ(TYPE{VAL}, (STR##_json).get<TYPE>()); \
#define MAKE_TEST_P(FIXTURE, TYPE) \
TEST_P(FIXTURE, from_json) \
{ \
const auto & [name, value] = GetParam(); \
readJsonTest(name, value); \
} \
\
TEST_P(FIXTURE, to_json) \
{ \
const auto & [name, value] = GetParam(); \
writeJsonTest(name, value); \
}
TEST_JSON(OutputsSpec, all, R"(["*"])", OutputsSpec::All{})
TEST_JSON(OutputsSpec, name, R"(["a"])", OutputsSpec::Names{"a"})
TEST_JSON(OutputsSpec, names, R"(["a","b"])", (OutputsSpec::Names{"a", "b"}))
struct OutputsSpecJsonTest : OutputsSpecTest,
JsonCharacterizationTest<OutputsSpec>,
::testing::WithParamInterface<std::pair<std::string_view, OutputsSpec>>
{};
TEST_JSON(ExtendedOutputsSpec, def, R"(null)", ExtendedOutputsSpec::Default{})
TEST_JSON(ExtendedOutputsSpec, all, R"(["*"])", ExtendedOutputsSpec::Explicit{OutputsSpec::All{}})
TEST_JSON(ExtendedOutputsSpec, name, R"(["a"])", ExtendedOutputsSpec::Explicit{OutputsSpec::Names{"a"}})
TEST_JSON(ExtendedOutputsSpec, names, R"(["a","b"])", (ExtendedOutputsSpec::Explicit{OutputsSpec::Names{"a", "b"}}))
MAKE_TEST_P(OutputsSpecJsonTest, OutputsSpec);
INSTANTIATE_TEST_SUITE_P(
OutputsSpecJSON,
OutputsSpecJsonTest,
::testing::Values(
std::pair{"all", OutputsSpec{OutputsSpec::All{}}},
std::pair{"name", OutputsSpec{OutputsSpec::Names{"a"}}},
std::pair{"names", OutputsSpec{OutputsSpec::Names{"a", "b"}}}));
struct ExtendedOutputsSpecJsonTest : ExtendedOutputsSpecTest,
JsonCharacterizationTest<ExtendedOutputsSpec>,
::testing::WithParamInterface<std::pair<std::string_view, ExtendedOutputsSpec>>
{};
MAKE_TEST_P(ExtendedOutputsSpecJsonTest, ExtendedOutputsSpec);
INSTANTIATE_TEST_SUITE_P(
ExtendedOutputsSpecJSON,
ExtendedOutputsSpecJsonTest,
::testing::Values(
std::pair{"def", ExtendedOutputsSpec{ExtendedOutputsSpec::Default{}}},
std::pair{"all", ExtendedOutputsSpec{ExtendedOutputsSpec::Explicit{OutputsSpec::All{}}}},
std::pair{"name", ExtendedOutputsSpec{ExtendedOutputsSpec::Explicit{OutputsSpec::Names{"a"}}}},
std::pair{"names", ExtendedOutputsSpec{ExtendedOutputsSpec::Explicit{OutputsSpec::Names{"a", "b"}}}}));
#undef TEST_JSON

View file

@ -83,7 +83,6 @@ mkMesonExecutable (finalAttrs: {
}
(
''
export ASAN_OPTIONS=abort_on_error=1:print_summary=1:detect_leaks=0
export _NIX_TEST_UNIT_DATA=${data + "/src/libstore-tests/data"}
export NIX_REMOTE=$HOME/store
${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage}

View file

@ -7,7 +7,7 @@
#include "nix/store/path-regex.hh"
#include "nix/store/store-api.hh"
#include "nix/util/tests/characterization.hh"
#include "nix/util/tests/json-characterization.hh"
#include "nix/store/tests/libstore.hh"
#include "nix/store/tests/path.hh"
@ -16,7 +16,7 @@ namespace nix {
#define STORE_DIR "/nix/store/"
#define HASH_PART "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q"
class StorePathTest : public CharacterizationTest, public LibStoreTest
class StorePathTest : public virtual CharacterizationTest, public LibStoreTest
{
std::filesystem::path unitTestData = getUnitTestData() / "store-path";
@ -149,27 +149,30 @@ RC_GTEST_FIXTURE_PROP(StorePathTest, prop_check_regex_eq_parse, ())
using nlohmann::json;
#define TEST_JSON(FIXTURE, NAME, VAL) \
static const StorePath NAME = VAL; \
\
TEST_F(FIXTURE, NAME##_from_json) \
{ \
readTest(#NAME ".json", [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \
StorePath got = static_cast<StorePath>(encoded); \
ASSERT_EQ(got, NAME); \
}); \
} \
\
TEST_F(FIXTURE, NAME##_to_json) \
{ \
writeTest( \
#NAME ".json", \
[&]() -> json { return static_cast<json>(NAME); }, \
[](const auto & file) { return json::parse(readFile(file)); }, \
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
}
struct StorePathJsonTest : StorePathTest,
JsonCharacterizationTest<StorePath>,
::testing::WithParamInterface<std::pair<std::string_view, StorePath>>
{};
TEST_JSON(StorePathTest, simple, StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"});
TEST_P(StorePathJsonTest, from_json)
{
auto & [name, expected] = GetParam();
readJsonTest(name, expected);
}
TEST_P(StorePathJsonTest, to_json)
{
auto & [name, value] = GetParam();
writeJsonTest(name, value);
}
INSTANTIATE_TEST_SUITE_P(
StorePathJSON,
StorePathJsonTest,
::testing::Values(
std::pair{
"simple",
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
}));
} // namespace nix

View file

@ -0,0 +1,97 @@
#include <regex>
#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
#include <rapidcheck/gtest.h>
#include "nix/store/store-api.hh"
#include "nix/util/tests/json-characterization.hh"
#include "nix/store/tests/libstore.hh"
namespace nix {
class RealisationTest : public JsonCharacterizationTest<Realisation>, public LibStoreTest
{
std::filesystem::path unitTestData = getUnitTestData() / "realisation";
public:
std::filesystem::path goldenMaster(std::string_view testStem) const override
{
return unitTestData / testStem;
}
};
/* ----------------------------------------------------------------------------
* JSON
* --------------------------------------------------------------------------*/
using nlohmann::json;
struct RealisationJsonTest : RealisationTest, ::testing::WithParamInterface<std::pair<std::string_view, Realisation>>
{};
TEST_P(RealisationJsonTest, from_json)
{
const auto & [name, expected] = GetParam();
readJsonTest(name, expected);
}
TEST_P(RealisationJsonTest, to_json)
{
const auto & [name, value] = GetParam();
writeJsonTest(name, value);
}
INSTANTIATE_TEST_SUITE_P(
RealisationJSON,
RealisationJsonTest,
([] {
Realisation simple{
.id =
{
.drvHash = Hash::parseExplicitFormatUnprefixed(
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
HashAlgorithm::SHA256,
HashFormat::Base16),
.outputName = "foo",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
};
return ::testing::Values(
std::pair{
"simple",
simple,
},
std::pair{
"with-signature",
[&] {
auto r = simple;
// FIXME actually sign properly
r.signatures = {"asdfasdfasdf"};
return r;
}()},
std::pair{
"with-dependent-realisations",
[&] {
auto r = simple;
r.dependentRealisations = {{
{
.drvHash = Hash::parseExplicitFormatUnprefixed(
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
HashAlgorithm::SHA256,
HashFormat::Base16),
.outputName = "foo",
},
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
}};
return r;
}(),
});
}
()));
} // namespace nix

View file

@ -15,4 +15,131 @@ TEST(S3BinaryCacheStore, constructConfig)
} // namespace nix
#elif NIX_WITH_CURL_S3
# include "nix/store/http-binary-cache-store.hh"
# include "nix/store/filetransfer.hh"
# include "nix/store/s3-url.hh"
# include <gtest/gtest.h>
namespace nix {
TEST(S3BinaryCacheStore, constructConfig)
{
S3BinaryCacheStoreConfig config{"s3", "foobar", {}};
// The bucket name is stored as the host part of the authority in cacheUri
EXPECT_EQ(
config.cacheUri,
(ParsedURL{
.scheme = "s3",
.authority = ParsedURL::Authority{.host = "foobar"},
}));
}
TEST(S3BinaryCacheStore, constructConfigWithRegion)
{
Store::Config::Params params{{"region", "eu-west-1"}};
S3BinaryCacheStoreConfig config{"s3", "my-bucket", params};
EXPECT_EQ(
config.cacheUri,
(ParsedURL{
.scheme = "s3",
.authority = ParsedURL::Authority{.host = "my-bucket"},
.query = (StringMap) {{"region", "eu-west-1"}},
}));
EXPECT_EQ(config.region.get(), "eu-west-1");
}
TEST(S3BinaryCacheStore, defaultSettings)
{
S3BinaryCacheStoreConfig config{"s3", "test-bucket", {}};
EXPECT_EQ(
config.cacheUri,
(ParsedURL{
.scheme = "s3",
.authority = ParsedURL::Authority{.host = "test-bucket"},
}));
// Check default values
EXPECT_EQ(config.region.get(), "us-east-1");
EXPECT_EQ(config.profile.get(), "default");
EXPECT_EQ(config.scheme.get(), "https");
EXPECT_EQ(config.endpoint.get(), "");
}
/**
* Test that S3BinaryCacheStore properly preserves S3-specific parameters
*/
TEST(S3BinaryCacheStore, s3StoreConfigPreservesParameters)
{
StringMap params;
params["region"] = "eu-west-1";
params["endpoint"] = "custom.s3.com";
S3BinaryCacheStoreConfig config("s3", "test-bucket", params);
// The config should preserve S3-specific parameters
EXPECT_EQ(
config.cacheUri,
(ParsedURL{
.scheme = "s3",
.authority = ParsedURL::Authority{.host = "test-bucket"},
.query = (StringMap) {{"region", "eu-west-1"}, {"endpoint", "custom.s3.com"}},
}));
}
/**
* Test that S3 store scheme is properly registered
*/
TEST(S3BinaryCacheStore, s3SchemeRegistration)
{
auto schemes = S3BinaryCacheStoreConfig::uriSchemes();
EXPECT_TRUE(schemes.count("s3") > 0) << "S3 scheme should be supported";
// Verify HttpBinaryCacheStoreConfig doesn't directly list S3
auto httpSchemes = HttpBinaryCacheStoreConfig::uriSchemes();
EXPECT_FALSE(httpSchemes.count("s3") > 0) << "HTTP store shouldn't directly list S3 scheme";
}
/**
* Test that only S3-specific parameters are preserved in cacheUri,
* while non-S3 store parameters are not propagated to the URL
*/
TEST(S3BinaryCacheStore, parameterFiltering)
{
StringMap params;
params["region"] = "eu-west-1";
params["endpoint"] = "minio.local";
params["want-mass-query"] = "true"; // Non-S3 store parameter
params["priority"] = "10"; // Non-S3 store parameter
S3BinaryCacheStoreConfig config("s3", "test-bucket", params);
// Only S3-specific params should be in cacheUri.query
EXPECT_EQ(
config.cacheUri,
(ParsedURL{
.scheme = "s3",
.authority = ParsedURL::Authority{.host = "test-bucket"},
.query = (StringMap) {{"region", "eu-west-1"}, {"endpoint", "minio.local"}},
}));
// But the non-S3 params should still be set on the config
EXPECT_EQ(config.wantMassQuery.get(), true);
EXPECT_EQ(config.priority.get(), 10);
// And all params (S3 and non-S3) should be returned by getReference()
auto ref = config.getReference();
EXPECT_EQ(ref.params["region"], "eu-west-1");
EXPECT_EQ(ref.params["endpoint"], "minio.local");
EXPECT_EQ(ref.params["want-mass-query"], "true");
EXPECT_EQ(ref.params["priority"], "10");
}
} // namespace nix
#endif

View file

@ -1,13 +1,17 @@
#include "nix/store/s3.hh"
#include "nix/store/s3-url.hh"
#include "nix/util/tests/gmock-matchers.hh"
#if NIX_WITH_S3_SUPPORT
#if NIX_WITH_S3_SUPPORT || NIX_WITH_CURL_S3
# include <gtest/gtest.h>
# include <gmock/gmock.h>
namespace nix {
// =============================================================================
// ParsedS3URL Tests
// =============================================================================
struct ParsedS3URLTestCase
{
std::string url;
@ -86,18 +90,41 @@ INSTANTIATE_TEST_SUITE_P(
}),
[](const ::testing::TestParamInfo<ParsedS3URLTestCase> & info) { return info.param.description; });
TEST(InvalidParsedS3URLTest, parseS3URLErrors)
// Parameterized test for invalid S3 URLs
struct InvalidS3URLTestCase
{
auto invalidBucketMatcher = ::testing::ThrowsMessage<BadURL>(
testing::HasSubstrIgnoreANSIMatcher("error: URI has a missing or invalid bucket name"));
std::string url;
std::string expectedErrorSubstring;
std::string description;
};
/* Empty bucket (authority) */
ASSERT_THAT([]() { ParsedS3URL::parse(parseURL("s3:///key")); }, invalidBucketMatcher);
/* Invalid bucket name */
ASSERT_THAT([]() { ParsedS3URL::parse(parseURL("s3://127.0.0.1")); }, invalidBucketMatcher);
class InvalidParsedS3URLTest : public ::testing::WithParamInterface<InvalidS3URLTestCase>, public ::testing::Test
{};
TEST_P(InvalidParsedS3URLTest, parseS3URLErrors)
{
const auto & testCase = GetParam();
ASSERT_THAT(
[&testCase]() { ParsedS3URL::parse(parseURL(testCase.url)); },
::testing::ThrowsMessage<BadURL>(testing::HasSubstrIgnoreANSIMatcher(testCase.expectedErrorSubstring)));
}
// Parameterized test for s3ToHttpsUrl conversion
INSTANTIATE_TEST_SUITE_P(
InvalidUrls,
InvalidParsedS3URLTest,
::testing::Values(
InvalidS3URLTestCase{"s3:///key", "error: URI has a missing or invalid bucket name", "empty_bucket"},
InvalidS3URLTestCase{"s3://127.0.0.1", "error: URI has a missing or invalid bucket name", "ip_address_bucket"},
InvalidS3URLTestCase{"s3://bucket with spaces/key", "is not a valid URL", "bucket_with_spaces"},
InvalidS3URLTestCase{"s3://", "error: URI has a missing or invalid bucket name", "completely_empty"},
InvalidS3URLTestCase{"s3://bucket", "error: URI has a missing or invalid key", "missing_key"}),
[](const ::testing::TestParamInfo<InvalidS3URLTestCase> & info) { return info.param.description; });
// =============================================================================
// S3 URL to HTTPS Conversion Tests
// =============================================================================
struct S3ToHttpsConversionTestCase
{
ParsedS3URL input;

View file

@ -127,17 +127,17 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST(ServeProtoTest, buildResult_2_2, "build-result-2.2", 2 << 8 | 2, ({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t{
BuildResult{
.status = BuildResult::OutputRejected,
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
BuildResult{
.status = BuildResult::NotDeterministic,
}}},
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
},
BuildResult{
.status = BuildResult::Built,
},
}}},
BuildResult{.inner{BuildResult::Success{
.status = BuildResult::Success::Built,
}}},
};
t;
}))
@ -145,20 +145,24 @@ VERSIONED_CHARACTERIZATION_TEST(ServeProtoTest, buildResult_2_2, "build-result-2
VERSIONED_CHARACTERIZATION_TEST(ServeProtoTest, buildResult_2_3, "build-result-2.3", 2 << 8 | 3, ({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t{
BuildResult{
.status = BuildResult::OutputRejected,
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
}}},
BuildResult{
.status = BuildResult::NotDeterministic,
.errorMsg = "no idea why",
.inner{BuildResult::Failure{
.status = BuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
.isNonDeterministic = true,
}},
.timesBuilt = 3,
.isNonDeterministic = true,
.startTime = 30,
.stopTime = 50,
},
BuildResult{
.status = BuildResult::Built,
.inner{BuildResult::Success{
.status = BuildResult::Success::Built,
}},
.startTime = 30,
.stopTime = 50,
},
@ -170,48 +174,52 @@ VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest, buildResult_2_6, "build-result-2.6", 2 << 8 | 6, ({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t{
BuildResult{
.status = BuildResult::OutputRejected,
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
}}},
BuildResult{
.status = BuildResult::NotDeterministic,
.errorMsg = "no idea why",
.inner{BuildResult::Failure{
.status = BuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
.isNonDeterministic = true,
}},
.timesBuilt = 3,
.isNonDeterministic = true,
.startTime = 30,
.stopTime = 50,
},
BuildResult{
.status = BuildResult::Built,
.inner{BuildResult::Success{
.status = BuildResult::Success::Built,
.builtOutputs =
{
{
"foo",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
},
},
{
"bar",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
},
},
},
}},
.timesBuilt = 1,
.builtOutputs =
{
{
"foo",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
},
},
{
"bar",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
},
},
},
.startTime = 30,
.stopTime = 50,
#if 0

View file

@ -22,4 +22,24 @@ TEST(UDSRemoteStore, constructConfig_to_string)
EXPECT_EQ(config.getReference().to_string(), "daemon");
}
TEST(UDSRemoteStore, constructConfigWithParams)
{
StoreConfig::Params params{{"max-connections", "1"}};
UDSRemoteStoreConfig config{"unix", "/tmp/socket", params};
auto storeReference = config.getReference();
EXPECT_EQ(storeReference.to_string(), "unix:///tmp/socket?max-connections=1");
EXPECT_EQ(storeReference.render(/*withParams=*/false), "unix:///tmp/socket");
EXPECT_EQ(storeReference.params, params);
}
TEST(UDSRemoteStore, constructConfigWithParamsNoPath)
{
StoreConfig::Params params{{"max-connections", "1"}};
UDSRemoteStoreConfig config{"unix", "", params};
auto storeReference = config.getReference();
EXPECT_EQ(storeReference.to_string(), "daemon?max-connections=1");
EXPECT_EQ(storeReference.render(/*withParams=*/false), "daemon");
EXPECT_EQ(storeReference.params, params);
}
} // namespace nix

View file

@ -180,17 +180,17 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST(WorkerProtoTest, buildResult_1_27, "build-result-1.27", 1 << 8 | 27, ({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t{
BuildResult{
.status = BuildResult::OutputRejected,
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
BuildResult{
.status = BuildResult::NotDeterministic,
}}},
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
},
BuildResult{
.status = BuildResult::Built,
},
}}},
BuildResult{.inner{BuildResult::Success{
.status = BuildResult::Success::Built,
}}},
};
t;
}))
@ -199,16 +199,16 @@ VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest, buildResult_1_28, "build-result-1.28", 1 << 8 | 28, ({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t{
BuildResult{
.status = BuildResult::OutputRejected,
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
BuildResult{
.status = BuildResult::NotDeterministic,
}}},
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
},
BuildResult{
.status = BuildResult::Built,
}}},
BuildResult{.inner{BuildResult::Success{
.status = BuildResult::Success::Built,
.builtOutputs =
{
{
@ -236,7 +236,7 @@ VERSIONED_CHARACTERIZATION_TEST(
},
},
},
},
}}},
};
t;
}))
@ -245,48 +245,52 @@ VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest, buildResult_1_29, "build-result-1.29", 1 << 8 | 29, ({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t{
BuildResult{
.status = BuildResult::OutputRejected,
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
}}},
BuildResult{
.status = BuildResult::NotDeterministic,
.errorMsg = "no idea why",
.inner{BuildResult::Failure{
.status = BuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
.isNonDeterministic = true,
}},
.timesBuilt = 3,
.isNonDeterministic = true,
.startTime = 30,
.stopTime = 50,
},
BuildResult{
.status = BuildResult::Built,
.inner{BuildResult::Success{
.status = BuildResult::Success::Built,
.builtOutputs =
{
{
"foo",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
},
},
{
"bar",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
},
},
},
}},
.timesBuilt = 1,
.builtOutputs =
{
{
"foo",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
},
},
{
"bar",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
},
},
},
.startTime = 30,
.stopTime = 50,
},
@ -298,48 +302,52 @@ VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest, buildResult_1_37, "build-result-1.37", 1 << 8 | 37, ({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t{
BuildResult{
.status = BuildResult::OutputRejected,
BuildResult{.inner{BuildResult::Failure{
.status = BuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
}}},
BuildResult{
.status = BuildResult::NotDeterministic,
.errorMsg = "no idea why",
.inner{BuildResult::Failure{
.status = BuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
.isNonDeterministic = true,
}},
.timesBuilt = 3,
.isNonDeterministic = true,
.startTime = 30,
.stopTime = 50,
},
BuildResult{
.status = BuildResult::Built,
.inner{BuildResult::Success{
.status = BuildResult::Success::Built,
.builtOutputs =
{
{
"foo",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
},
},
{
"bar",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
},
},
},
}},
.timesBuilt = 1,
.builtOutputs =
{
{
"foo",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
},
},
{
"bar",
{
.id =
DrvOutput{
.drvHash =
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
},
},
},
.startTime = 30,
.stopTime = 50,
.cpuUser = std::chrono::microseconds(500s),
@ -353,10 +361,10 @@ VERSIONED_CHARACTERIZATION_TEST(WorkerProtoTest, keyedBuildResult_1_29, "keyed-b
using namespace std::literals::chrono_literals;
std::tuple<KeyedBuildResult, KeyedBuildResult /*, KeyedBuildResult*/> t{
KeyedBuildResult{
{
.status = KeyedBuildResult::OutputRejected,
{.inner{BuildResult::Failure{
.status = KeyedBuildResult::Failure::OutputRejected,
.errorMsg = "no idea why",
},
}}},
/* .path = */
DerivedPath::Opaque{
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-xxx"},
@ -364,10 +372,12 @@ VERSIONED_CHARACTERIZATION_TEST(WorkerProtoTest, keyedBuildResult_1_29, "keyed-b
},
KeyedBuildResult{
{
.status = KeyedBuildResult::NotDeterministic,
.errorMsg = "no idea why",
.inner{BuildResult::Failure{
.status = KeyedBuildResult::Failure::NotDeterministic,
.errorMsg = "no idea why",
.isNonDeterministic = true,
}},
.timesBuilt = 3,
.isNonDeterministic = true,
.startTime = 30,
.stopTime = 50,
},

View file

@ -0,0 +1,57 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "nix/util/tests/gmock-matchers.hh"
#include "nix/store/derivations.hh"
#include "nix/store/dummy-store-impl.hh"
#include "nix/store/tests/libstore.hh"
namespace nix {
namespace {
class WriteDerivationTest : public LibStoreTest
{
protected:
WriteDerivationTest(ref<DummyStoreConfig> config_)
: LibStoreTest(config_->openDummyStore())
, config(std::move(config_))
{
config->readOnly = false;
}
WriteDerivationTest()
: WriteDerivationTest(make_ref<DummyStoreConfig>(DummyStoreConfig::Params{}))
{
}
ref<DummyStoreConfig> config;
};
static Derivation makeSimpleDrv()
{
Derivation drv;
drv.name = "simple-derivation";
drv.platform = "system";
drv.builder = "foo";
drv.args = {"bar", "baz"};
drv.env = StringPairs{{"BIG_BAD", "WOLF"}};
return drv;
}
} // namespace
TEST_F(WriteDerivationTest, addToStoreFromDumpCalledOnce)
{
auto drv = makeSimpleDrv();
auto path1 = writeDerivation(*store, drv, NoRepair);
config->readOnly = true;
auto path2 = writeDerivation(*store, drv, NoRepair);
EXPECT_EQ(path1, path2);
EXPECT_THAT(
[&] { writeDerivation(*store, drv, Repair); },
::testing::ThrowsMessage<Error>(testing::HasSubstrIgnoreANSIMatcher(
"operation 'addToStoreFromDump' is not supported by store 'dummy://'")));
}
} // namespace nix