#include #include #include #include #include #include #include #if defined(_MSC_VER) #pragma warning(disable : 6237) #endif using namespace vcpkg; using namespace vcpkg::Paragraphs; using namespace vcpkg::Test; static Json::Object parse_json_object(StringView sv) { auto json = Json::parse(sv); // we're not testing json parsing here, so just fail on errors if (auto r = json.get()) { return std::move(r->first.object()); } else { Checks::exit_with_message(VCPKG_LINE_INFO, json.error()->format()); } } static Parse::ParseExpected test_parse_manifest(StringView sv, bool expect_fail = false) { auto object = parse_json_object(sv); auto res = SourceControlFile::parse_manifest_file(fs::u8path(""), object); if (!res.has_value() && !expect_fail) { print_error_message(res.error()); } return res; } TEST_CASE ("manifest construct minimum", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "zlib", "version-string": "1.2.8" })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->name == "zlib"); REQUIRE(pgh.core_paragraph->version == "1.2.8"); REQUIRE(pgh.core_paragraph->maintainers.empty()); REQUIRE(pgh.core_paragraph->description.empty()); REQUIRE(pgh.core_paragraph->dependencies.empty()); } TEST_CASE ("manifest construct maximum", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "s", "version-string": "v", "maintainers": ["m"], "description": "d", "dependencies": ["bd"], "default-features": ["df"], "features": [ { "name": "iroh", "description": "zuko's uncle", "dependencies": [ "firebending", { "name": "tea" }, { "name": "order.white-lotus", "features": [ "the-ancient-ways" ], "platform": "!(windows & arm)" } ] }, { "name": "zuko", "description": ["son of the fire lord", "firebending 師父"] } ] })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->name == "s"); REQUIRE(pgh.core_paragraph->version == "v"); REQUIRE(pgh.core_paragraph->maintainers.size() == 1); REQUIRE(pgh.core_paragraph->maintainers[0] == "m"); REQUIRE(pgh.core_paragraph->description.size() == 1); REQUIRE(pgh.core_paragraph->description[0] == "d"); REQUIRE(pgh.core_paragraph->dependencies.size() == 1); REQUIRE(pgh.core_paragraph->dependencies[0].name == "bd"); REQUIRE(pgh.core_paragraph->default_features.size() == 1); REQUIRE(pgh.core_paragraph->default_features[0] == "df"); REQUIRE(pgh.feature_paragraphs.size() == 2); REQUIRE(pgh.feature_paragraphs[0]->name == "iroh"); REQUIRE(pgh.feature_paragraphs[0]->description.size() == 1); REQUIRE(pgh.feature_paragraphs[0]->description[0] == "zuko's uncle"); REQUIRE(pgh.feature_paragraphs[0]->dependencies.size() == 3); REQUIRE(pgh.feature_paragraphs[0]->dependencies[0].name == "firebending"); REQUIRE(pgh.feature_paragraphs[0]->dependencies[1].name == "order.white-lotus"); REQUIRE(pgh.feature_paragraphs[0]->dependencies[1].features.size() == 1); REQUIRE(pgh.feature_paragraphs[0]->dependencies[1].features[0] == "the-ancient-ways"); REQUIRE_FALSE(pgh.feature_paragraphs[0]->dependencies[1].platform.evaluate( {{"VCPKG_CMAKE_SYSTEM_NAME", ""}, {"VCPKG_TARGET_ARCHITECTURE", "arm"}})); REQUIRE(pgh.feature_paragraphs[0]->dependencies[1].platform.evaluate( {{"VCPKG_CMAKE_SYSTEM_NAME", ""}, {"VCPKG_TARGET_ARCHITECTURE", "x86"}})); REQUIRE(pgh.feature_paragraphs[0]->dependencies[1].platform.evaluate( {{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}, {"VCPKG_TARGET_ARCHITECTURE", "x86"}})); REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].name == "tea"); REQUIRE(pgh.feature_paragraphs[1]->name == "zuko"); REQUIRE(pgh.feature_paragraphs[1]->description.size() == 2); REQUIRE(pgh.feature_paragraphs[1]->description[0] == "son of the fire lord"); REQUIRE(pgh.feature_paragraphs[1]->description[1] == "firebending 師父"); } TEST_CASE ("SourceParagraph manifest two dependencies", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "zlib", "version-string": "1.2.8", "dependencies": ["z", "openssl"] })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->dependencies.size() == 2); REQUIRE(pgh.core_paragraph->dependencies[0].name == "openssl"); REQUIRE(pgh.core_paragraph->dependencies[1].name == "z"); } TEST_CASE ("SourceParagraph manifest three dependencies", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "zlib", "version-string": "1.2.8", "dependencies": ["z", "openssl", "xyz"] })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->dependencies.size() == 3); // should be ordered REQUIRE(pgh.core_paragraph->dependencies[0].name == "openssl"); REQUIRE(pgh.core_paragraph->dependencies[1].name == "xyz"); REQUIRE(pgh.core_paragraph->dependencies[2].name == "z"); } TEST_CASE ("SourceParagraph manifest construct qualified dependencies", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "zlib", "version-string": "1.2.8", "dependencies": [ { "name": "liba", "platform": "windows" }, { "name": "libb", "platform": "uwp" } ] })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->name == "zlib"); REQUIRE(pgh.core_paragraph->version == "1.2.8"); REQUIRE(pgh.core_paragraph->maintainers.empty()); REQUIRE(pgh.core_paragraph->description.empty()); REQUIRE(pgh.core_paragraph->dependencies.size() == 2); REQUIRE(pgh.core_paragraph->dependencies[0].name == "liba"); REQUIRE(pgh.core_paragraph->dependencies[0].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); REQUIRE(pgh.core_paragraph->dependencies[1].name == "libb"); REQUIRE(pgh.core_paragraph->dependencies[1].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}})); } TEST_CASE ("SourceParagraph manifest default features", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "a", "version-string": "1.0", "default-features": ["a1"] })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->default_features.size() == 1); REQUIRE(pgh.core_paragraph->default_features[0] == "a1"); } TEST_CASE ("SourceParagraph manifest description paragraph", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "a", "version-string": "1.0", "description": ["line 1", "line 2", "line 3"] })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->description.size() == 3); REQUIRE(pgh.core_paragraph->description[0] == "line 1"); REQUIRE(pgh.core_paragraph->description[1] == "line 2"); REQUIRE(pgh.core_paragraph->description[2] == "line 3"); } TEST_CASE ("SourceParagraph manifest supports", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "a", "version-string": "1.0", "supports": "!(windows | osx)" })json"); REQUIRE(m_pgh.has_value()); auto& pgh = **m_pgh.get(); REQUIRE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}})); REQUIRE_FALSE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); REQUIRE_FALSE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}})); } TEST_CASE ("SourceParagraph manifest empty supports", "[manifests]") { auto m_pgh = test_parse_manifest(R"json({ "name": "a", "version-string": "1.0", "supports": "" })json", true); REQUIRE_FALSE(m_pgh.has_value()); } TEST_CASE ("Serialize all the ports", "[manifests]") { std::vector args_list = {"x-format-manifest"}; auto& fs = Files::get_real_filesystem(); auto args = VcpkgCmdArguments::create_from_arg_sequence(args_list.data(), args_list.data() + args_list.size()); VcpkgPaths paths{fs, args}; std::vector scfs; for (auto dir : fs::directory_iterator(paths.ports)) { const auto control = dir / fs::u8path("CONTROL"); const auto manifest = dir / fs::u8path("vcpkg.json"); if (fs.exists(control)) { auto contents = fs.read_contents(control, VCPKG_LINE_INFO); auto pghs = Paragraphs::parse_paragraphs(contents, fs::u8string(control)); REQUIRE(pghs); scfs.push_back(std::move( *SourceControlFile::parse_control_file(control, std::move(pghs).value_or_exit(VCPKG_LINE_INFO)) .value_or_exit(VCPKG_LINE_INFO))); } else if (fs.exists(manifest)) { std::error_code ec; auto contents = Json::parse_file(fs, manifest, ec); REQUIRE_FALSE(ec); REQUIRE(contents); auto scf = SourceControlFile::parse_manifest_file(manifest, contents.value_or_exit(VCPKG_LINE_INFO).first.object()); REQUIRE(scf); scfs.push_back(std::move(*scf.value_or_exit(VCPKG_LINE_INFO))); } } for (auto& scf : scfs) { auto serialized = serialize_manifest(scf); auto serialized_scf = SourceControlFile::parse_manifest_file({}, serialized).value_or_exit(VCPKG_LINE_INFO); REQUIRE(*serialized_scf == scf); } }