#include #include #include "nix/util/experimental-features.hh" #include "nix/store/derivations.hh" #include "nix/store/tests/libstore.hh" #include "nix/util/tests/json-characterization.hh" namespace nix { using nlohmann::json; class DerivationTest : public virtual 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 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); \ } struct DerivationOutputJsonTest : DerivationTest, JsonCharacterizationTest, ::testing::WithParamInterface> {}; MAKE_OUTPUT_JSON_TEST_P(DerivationOutputJsonTest) 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, ::testing::WithParamInterface> {}; 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, ::testing::WithParamInterface> {}; MAKE_OUTPUT_JSON_TEST_P(CaDerivationOutputJsonTest); INSTANTIATE_TEST_SUITE_P( CaDerivationOutputJSON, CaDerivationOutputJsonTest, ::testing::Values( std::pair{ "caFloating", DerivationOutput{DerivationOutput::CAFloating{ .method = ContentAddressMethod::Raw::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }}, })); struct ImpureDerivationOutputJsonTest : ImpureDerivationTest, JsonCharacterizationTest, ::testing::WithParamInterface> {}; MAKE_OUTPUT_JSON_TEST_P(ImpureDerivationOutputJsonTest); INSTANTIATE_TEST_SUITE_P( ImpureDerivationOutputJSON, ImpureDerivationOutputJsonTest, ::testing::Values( std::pair{ "impure", DerivationOutput{DerivationOutput::Impure{ .method = ContentAddressMethod::Raw::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }}, })); #undef MAKE_OUTPUT_JSON_TEST_P #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); }); \ } struct DerivationJsonAtermTest : DerivationTest, JsonCharacterizationTest, ::testing::WithParamInterface {}; MAKE_TEST_P(DerivationJsonAtermTest); Derivation makeSimpleDrv() { Derivation drv; drv.name = "simple-derivation"; drv.inputSrcs = { StorePath("c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"), }; drv.inputDrvs = { .map = { { StorePath("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; } INSTANTIATE_TEST_SUITE_P(DerivationJSONATerm, DerivationJsonAtermTest, ::testing::Values(makeSimpleDrv())); struct DynDerivationJsonAtermTest : DynDerivationTest, JsonCharacterizationTest, ::testing::WithParamInterface {}; MAKE_TEST_P(DynDerivationJsonAtermTest); Derivation makeDynDepDerivation() { Derivation drv; drv.name = "dyn-dep-derivation"; drv.inputSrcs = { StorePath{"c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"}, }; drv.inputDrvs = { .map = { { StorePath{"c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"}, DerivedPathMap::ChildNode{ .value = { "cat", "dog", }, .childMap = { { "cat", DerivedPathMap::ChildNode{ .value = { "kitten", }, }, }, { "goose", DerivedPathMap::ChildNode{ .value = { "gosling", }, }, }, }, }, }, }, }; drv.platform = "wasm-sel4"; drv.builder = "foo"; drv.args = { "bar", "baz", }; drv.env = StringPairs{ { "BIG_BAD", "WOLF", }, }; return drv; } INSTANTIATE_TEST_SUITE_P(DynDerivationJSONATerm, DynDerivationJsonAtermTest, ::testing::Values(makeDynDepDerivation())); #undef MAKE_TEST_P } // namespace nix