mirror of
https://github.com/NixOS/nix.git
synced 2025-11-15 06:52:43 +01:00
Instead of parsing a structured attrs at some later point, we parsed it right away when parsing the A-Term format, and likewise serialize it to `__json = <JSON dump>` when serializing a derivation to A-Term. The JSON format can directly contain the JSON structured attrs without so encoding it, so we just do that.
303 lines
12 KiB
C++
303 lines
12 KiB
C++
#include <nlohmann/json.hpp>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "nix/util/experimental-features.hh"
|
|
#include "nix/store/derivations.hh"
|
|
|
|
#include "nix/store/tests/libstore.hh"
|
|
#include "nix/util/tests/characterization.hh"
|
|
|
|
namespace nix {
|
|
|
|
using nlohmann::json;
|
|
|
|
class DerivationTest : public CharacterizationTest, public LibStoreTest
|
|
{
|
|
std::filesystem::path unitTestData = getUnitTestData() / "derivation";
|
|
|
|
public:
|
|
std::filesystem::path goldenMaster(std::string_view testStem) const override
|
|
{
|
|
return unitTestData / testStem;
|
|
}
|
|
|
|
/**
|
|
* We set these in tests rather than the regular globals so we don't have
|
|
* to worry about race conditions if the tests run concurrently.
|
|
*/
|
|
ExperimentalFeatureSettings mockXpSettings;
|
|
};
|
|
|
|
class CaDerivationTest : public DerivationTest
|
|
{
|
|
void SetUp() override
|
|
{
|
|
mockXpSettings.set("experimental-features", "ca-derivations");
|
|
}
|
|
};
|
|
|
|
class DynDerivationTest : public DerivationTest
|
|
{
|
|
void SetUp() override
|
|
{
|
|
mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
|
|
}
|
|
};
|
|
|
|
class ImpureDerivationTest : public DerivationTest
|
|
{
|
|
void SetUp() override
|
|
{
|
|
mockXpSettings.set("experimental-features", "impure-derivations");
|
|
}
|
|
};
|
|
|
|
TEST_F(DerivationTest, BadATerm_version)
|
|
{
|
|
ASSERT_THROW(
|
|
parseDerivation(*store, readFile(goldenMaster("bad-version.drv")), "whatever", mockXpSettings), FormatError);
|
|
}
|
|
|
|
TEST_F(DynDerivationTest, BadATerm_oldVersionDynDeps)
|
|
{
|
|
ASSERT_THROW(
|
|
parseDerivation(
|
|
*store, readFile(goldenMaster("bad-old-version-dyn-deps.drv")), "dyn-dep-derivation", mockXpSettings),
|
|
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(*store, 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(*store, (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"); }); \
|
|
}
|
|
|
|
TEST_JSON(
|
|
DerivationTest,
|
|
inputAddressed,
|
|
(DerivationOutput::InputAddressed{
|
|
.path = store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"),
|
|
}),
|
|
"drv-name",
|
|
"output-name")
|
|
|
|
TEST_JSON(
|
|
DerivationTest,
|
|
caFixedFlat,
|
|
(DerivationOutput::CAFixed{
|
|
.ca =
|
|
{
|
|
.method = ContentAddressMethod::Raw::Flat,
|
|
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
|
|
},
|
|
}),
|
|
"drv-name",
|
|
"output-name")
|
|
|
|
TEST_JSON(
|
|
DerivationTest,
|
|
caFixedNAR,
|
|
(DerivationOutput::CAFixed{
|
|
.ca =
|
|
{
|
|
.method = ContentAddressMethod::Raw::NixArchive,
|
|
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
|
|
},
|
|
}),
|
|
"drv-name",
|
|
"output-name")
|
|
|
|
TEST_JSON(
|
|
DynDerivationTest,
|
|
caFixedText,
|
|
(DerivationOutput::CAFixed{
|
|
.ca =
|
|
{
|
|
.method = ContentAddressMethod::Raw::Text,
|
|
.hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
|
|
},
|
|
}),
|
|
"drv-name",
|
|
"output-name")
|
|
|
|
TEST_JSON(
|
|
CaDerivationTest,
|
|
caFloating,
|
|
(DerivationOutput::CAFloating{
|
|
.method = ContentAddressMethod::Raw::NixArchive,
|
|
.hashAlgo = HashAlgorithm::SHA256,
|
|
}),
|
|
"drv-name",
|
|
"output-name")
|
|
|
|
TEST_JSON(DerivationTest, deferred, DerivationOutput::Deferred{}, "drv-name", "output-name")
|
|
|
|
TEST_JSON(
|
|
ImpureDerivationTest,
|
|
impure,
|
|
(DerivationOutput::Impure{
|
|
.method = ContentAddressMethod::Raw::NixArchive,
|
|
.hashAlgo = HashAlgorithm::SHA256,
|
|
}),
|
|
"drv-name",
|
|
"output-name")
|
|
|
|
#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(*store, encoded, mockXpSettings); \
|
|
ASSERT_EQ(got, expected); \
|
|
}); \
|
|
} \
|
|
\
|
|
TEST_F(FIXTURE, Derivation_##NAME##_to_json) \
|
|
{ \
|
|
writeTest( \
|
|
#NAME ".json", \
|
|
[&]() -> json { return Derivation{VAL}.toJSON(*store); }, \
|
|
[](const auto & file) { return json::parse(readFile(file)); }, \
|
|
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
|
|
}
|
|
|
|
#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(*store), expected.toJSON(*store)); \
|
|
ASSERT_EQ(got, expected); \
|
|
}); \
|
|
} \
|
|
\
|
|
TEST_F(FIXTURE, Derivation_##NAME##_to_aterm) \
|
|
{ \
|
|
writeTest(#NAME ".drv", [&]() -> std::string { return (VAL).unparse(*store, false); }); \
|
|
}
|
|
|
|
Derivation makeSimpleDrv(const Store & store)
|
|
{
|
|
Derivation drv;
|
|
drv.name = "simple-derivation";
|
|
drv.inputSrcs = {
|
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
|
|
};
|
|
drv.inputDrvs = {
|
|
.map =
|
|
{
|
|
{
|
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
|
|
{
|
|
.value =
|
|
{
|
|
"cat",
|
|
"dog",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
drv.platform = "wasm-sel4";
|
|
drv.builder = "foo";
|
|
drv.args = {
|
|
"bar",
|
|
"baz",
|
|
};
|
|
drv.env = StringPairs{
|
|
{
|
|
"BIG_BAD",
|
|
"WOLF",
|
|
},
|
|
};
|
|
return drv;
|
|
}
|
|
|
|
TEST_JSON(DerivationTest, simple, makeSimpleDrv(*store))
|
|
|
|
TEST_ATERM(DerivationTest, simple, makeSimpleDrv(*store), "simple-derivation")
|
|
|
|
Derivation makeDynDepDerivation(const Store & store)
|
|
{
|
|
Derivation drv;
|
|
drv.name = "dyn-dep-derivation";
|
|
drv.inputSrcs = {
|
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
|
|
};
|
|
drv.inputDrvs = {
|
|
.map =
|
|
{
|
|
{
|
|
store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
|
|
DerivedPathMap<StringSet>::ChildNode{
|
|
.value =
|
|
{
|
|
"cat",
|
|
"dog",
|
|
},
|
|
.childMap =
|
|
{
|
|
{
|
|
"cat",
|
|
DerivedPathMap<StringSet>::ChildNode{
|
|
.value =
|
|
{
|
|
"kitten",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"goose",
|
|
DerivedPathMap<StringSet>::ChildNode{
|
|
.value =
|
|
{
|
|
"gosling",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
drv.platform = "wasm-sel4";
|
|
drv.builder = "foo";
|
|
drv.args = {
|
|
"bar",
|
|
"baz",
|
|
};
|
|
drv.env = StringPairs{
|
|
{
|
|
"BIG_BAD",
|
|
"WOLF",
|
|
},
|
|
};
|
|
return drv;
|
|
}
|
|
|
|
TEST_JSON(DynDerivationTest, dynDerivationDeps, makeDynDepDerivation(*store))
|
|
|
|
TEST_ATERM(DynDerivationTest, dynDerivationDeps, makeDynDepDerivation(*store), "dyn-dep-derivation")
|
|
|
|
#undef TEST_JSON
|
|
#undef TEST_ATERM
|
|
|
|
} // namespace nix
|