Skip to content

Commit beb25cd

Browse files
committed
test: 覆盖 Form B 描述符的两处解析缺口
#355 需要 xpkg (.lua) 解析器读两个它此前**根本没读**的键,而两处都是静默的 ——没读的键就只是「这个特性不存在」: - `targets.<x>.required_features`:描述符因此无法表达让可选 host 工具变得 可负担的成本门(compat.protobuf 的 protoc 会带进 libprotoc 的 ~157 个 TU, 只要运行时的消费者绝不能编译它)。没有这个门,target 要么总是构建、 要么根本拿不到。 - `deps` 的值此前只能是版本**字符串**,于是 Form B 描述符压根没有语法去向 自己的依赖请求工具。 之前的 e2e 只走了 Form A(path 依赖)路径,这两处没有任何覆盖。第三个用例钉住 「未知 dep 键要被记录而不是吞掉」——半配置的依赖且毫无信号,正是既有的 per-feature 键记录机制要防的那种失败。
1 parent f3c7214 commit beb25cd

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#include <gtest/gtest.h>
2+
3+
import std;
4+
import mcpp.manifest;
5+
import mcpp.manifest.xpkg;
6+
import mcpp.platform;
7+
import mcpp.platform.axis;
8+
9+
// #355 needed two things from the Form B (xpkg .lua) descriptor parser that it
10+
// simply did not read, and neither failed loudly:
11+
//
12+
// * `targets.<x>.required_features` was dropped, so a descriptor could not
13+
// express the cost gate that makes an optional host tool affordable —
14+
// compat.protobuf's `protoc` pulls in libprotoc's ~157 extra TUs, which the
15+
// consumers who only want the runtime must never compile. Without the gate
16+
// the target is either always built or never available.
17+
// * `deps` values could only be a version STRING, so a Form B descriptor had
18+
// no syntax at all for requesting a tool from one of its own dependencies.
19+
//
20+
// Both were silent: an unread key is just an absent feature. These tests are
21+
// what makes them loud.
22+
23+
namespace {
24+
25+
mcpp::manifest::Manifest parse_or_fail(std::string_view lua) {
26+
auto m = mcpp::manifest::synthesize_from_xpkg_lua(
27+
lua, "compat.demo", "1.0.0", mcpp::platform::HostPlatform::current());
28+
EXPECT_TRUE(m.has_value()) << (m ? "" : m.error().message);
29+
return m.value_or(mcpp::manifest::Manifest{});
30+
}
31+
32+
const mcpp::manifest::Target* find_target(const mcpp::manifest::Manifest& m,
33+
std::string_view name) {
34+
for (auto const& t : m.targets)
35+
if (t.name == name) return &t;
36+
return nullptr;
37+
}
38+
39+
} // namespace
40+
41+
TEST(XpkgHostTools, TargetsCarryRequiredFeatures) {
42+
auto m = parse_or_fail(R"LUA(
43+
package = {
44+
spec = "1", name = "demo", namespace = "compat", type = "package",
45+
mcpp = {
46+
sources = { "*/src/**.cc" },
47+
targets = {
48+
["demo"] = { kind = "lib" },
49+
["protoc"] = { kind = "bin", main = "src/compiler/main.cc",
50+
required_features = { "protoc", "upb" } },
51+
},
52+
},
53+
}
54+
)LUA");
55+
56+
auto const* lib = find_target(m, "demo");
57+
ASSERT_NE(lib, nullptr);
58+
EXPECT_EQ(lib->kind, mcpp::manifest::Target::Library);
59+
EXPECT_TRUE(lib->requiredFeatures.empty());
60+
61+
auto const* tool = find_target(m, "protoc");
62+
ASSERT_NE(tool, nullptr);
63+
EXPECT_EQ(tool->kind, mcpp::manifest::Target::Binary);
64+
EXPECT_EQ(tool->main, "src/compiler/main.cc");
65+
EXPECT_EQ(tool->requiredFeatures,
66+
(std::vector<std::string>{"protoc", "upb"}));
67+
}
68+
69+
TEST(XpkgHostTools, DepsAcceptBothAStringAndATable) {
70+
// The string form is the long-standing one and must keep working
71+
// unchanged; the table form is what lets a descriptor request a tool.
72+
auto m = parse_or_fail(R"LUA(
73+
package = {
74+
spec = "1", name = "demo", namespace = "compat", type = "package",
75+
mcpp = {
76+
sources = { "*/src/**.cc" },
77+
deps = {
78+
["compat.zlib"] = "1.3.2",
79+
["compat.protobuf"] = { version = "35.1", tools = { "protoc" } },
80+
},
81+
},
82+
}
83+
)LUA");
84+
85+
const mcpp::manifest::DependencySpec* zlib = nullptr;
86+
const mcpp::manifest::DependencySpec* pb = nullptr;
87+
for (auto const& [k, spec] : m.dependencies) {
88+
if (k.find("zlib") != std::string::npos) zlib = &spec;
89+
if (k.find("protobuf") != std::string::npos) pb = &spec;
90+
}
91+
92+
ASSERT_NE(zlib, nullptr);
93+
EXPECT_EQ(zlib->version, "1.3.2");
94+
EXPECT_TRUE(zlib->tools.empty());
95+
96+
ASSERT_NE(pb, nullptr);
97+
EXPECT_EQ(pb->version, "35.1");
98+
EXPECT_EQ(pb->tools, (std::vector<std::string>{"protoc"}));
99+
}
100+
101+
TEST(XpkgHostTools, UnknownDepKeyIsRecordedRatherThanSwallowed) {
102+
// A descriptor author writing an unsupported key must be told. Silently
103+
// ignoring it leaves the dependency half-configured with no signal — the
104+
// exact failure the per-feature key recording already exists to prevent.
105+
auto m = parse_or_fail(R"LUA(
106+
package = {
107+
spec = "1", name = "demo", namespace = "compat", type = "package",
108+
mcpp = {
109+
sources = { "*/src/**.cc" },
110+
deps = {
111+
["compat.protobuf"] = { version = "35.1", no_such_key = "x" },
112+
},
113+
},
114+
}
115+
)LUA");
116+
117+
bool recorded = false;
118+
for (auto const& k : m.xpkgUnknownKeys)
119+
if (k.find("no_such_key") != std::string::npos) recorded = true;
120+
EXPECT_TRUE(recorded) << "unknown dep key was swallowed";
121+
122+
// ...and the keys it DOES understand still take effect.
123+
for (auto const& [k, spec] : m.dependencies)
124+
if (k.find("protobuf") != std::string::npos)
125+
EXPECT_EQ(spec.version, "35.1");
126+
}

0 commit comments

Comments
 (0)