Improve tests for OutputsSpec

This commit is contained in:
John Ericson 2023-01-11 17:31:32 -05:00
parent 5ba6e5d0d9
commit 0faf5326bd
3 changed files with 97 additions and 56 deletions

View file

@ -40,22 +40,33 @@ std::optional<OutputsSpec> OutputsSpec::parseOpt(std::string_view s)
OutputsSpec OutputsSpec::parse(std::string_view s) OutputsSpec OutputsSpec::parse(std::string_view s)
{ {
std::optional spec = OutputsSpec::parseOpt(s); std::optional spec = parseOpt(s);
if (!spec) if (!spec)
throw Error("Invalid outputs specifier: '%s'", s); throw Error("Invalid outputs specifier: '%s'", s);
return *spec; return *spec;
} }
std::pair<std::string_view, ExtendedOutputsSpec> ExtendedOutputsSpec::parse(std::string_view s) std::optional<std::pair<std::string_view, ExtendedOutputsSpec>> ExtendedOutputsSpec::parseOpt(std::string_view s)
{ {
auto found = s.rfind('^'); auto found = s.rfind('^');
if (found == std::string::npos) if (found == std::string::npos)
return { s, ExtendedOutputsSpec::Default {} }; return std::pair { s, ExtendedOutputsSpec::Default {} };
auto spec = OutputsSpec::parse(s.substr(found + 1)); auto specOpt = OutputsSpec::parseOpt(s.substr(found + 1));
return { s.substr(0, found), ExtendedOutputsSpec::Explicit { spec } }; if (!specOpt)
return std::nullopt;
return std::pair { s.substr(0, found), ExtendedOutputsSpec::Explicit { *std::move(specOpt) } };
}
std::pair<std::string_view, ExtendedOutputsSpec> ExtendedOutputsSpec::parse(std::string_view s)
{
std::optional spec = parseOpt(s);
if (!spec)
throw Error("Invalid extended outputs specifier: '%s'", s);
return *spec;
} }

View file

@ -25,9 +25,7 @@ struct OutputNames : std::set<std::string> {
OutputNames() = delete; OutputNames() = delete;
}; };
struct AllOutputs { struct AllOutputs : std::monostate { };
bool operator < (const AllOutputs & _) const { return false; }
};
typedef std::variant<AllOutputs, OutputNames> _OutputsSpecRaw; typedef std::variant<AllOutputs, OutputNames> _OutputsSpecRaw;
@ -64,9 +62,7 @@ struct OutputsSpec : _OutputsSpecRaw {
std::string to_string() const; std::string to_string() const;
}; };
struct DefaultOutputs { struct DefaultOutputs : std::monostate { };
bool operator < (const DefaultOutputs & _) const { return false; }
};
typedef std::variant<DefaultOutputs, OutputsSpec> _ExtendedOutputsSpecRaw; typedef std::variant<DefaultOutputs, OutputsSpec> _ExtendedOutputsSpecRaw;
@ -84,6 +80,7 @@ struct ExtendedOutputsSpec : _ExtendedOutputsSpecRaw {
/* Parse a string of the form 'prefix^output1,...outputN' or /* Parse a string of the form 'prefix^output1,...outputN' or
'prefix^*', returning the prefix and the extended outputs spec. */ 'prefix^*', returning the prefix and the extended outputs spec. */
static std::pair<std::string_view, ExtendedOutputsSpec> parse(std::string_view s); static std::pair<std::string_view, ExtendedOutputsSpec> parse(std::string_view s);
static std::optional<std::pair<std::string_view, ExtendedOutputsSpec>> parseOpt(std::string_view s);
std::string to_string() const; std::string to_string() const;
}; };

View file

@ -4,63 +4,96 @@
namespace nix { namespace nix {
TEST(OutputsSpec_parse, basic) #define TEST_DONT_PARSE(NAME, STR) \
{ TEST(OutputsSpec, bad_ ## NAME) { \
{ std::optional OutputsSpecOpt = \
auto outputsSpec = OutputsSpec::parse("*"); OutputsSpec::parseOpt(STR); \
ASSERT_TRUE(std::get_if<OutputsSpec::All>(&outputsSpec)); ASSERT_FALSE(OutputsSpecOpt); \
} }
{ TEST_DONT_PARSE(empty, "")
auto outputsSpec = OutputsSpec::parse("out"); TEST_DONT_PARSE(garbage, "&*()")
ASSERT_TRUE(std::get<OutputsSpec::Names>(outputsSpec) == OutputsSpec::Names({"out"})); TEST_DONT_PARSE(double_star, "**")
} TEST_DONT_PARSE(star_first, "*,foo")
TEST_DONT_PARSE(star_second, "foo,*")
{ #undef TEST_DONT_PARSE
auto outputsSpec = OutputsSpec::parse("out,bin");
ASSERT_TRUE(std::get<OutputsSpec::Names>(outputsSpec) == OutputsSpec::Names({"out", "bin"}));
}
{ TEST(OutputsSpec, all) {
std::optional outputsSpecOpt = OutputsSpec::parseOpt("&*()"); std::string_view str = "*";
ASSERT_FALSE(outputsSpecOpt); OutputsSpec expected = OutputsSpec::All { };
} ASSERT_EQ(OutputsSpec::parse(str), expected);
ASSERT_EQ(expected.to_string(), str);
}
TEST(OutputsSpec, names_out) {
std::string_view str = "out";
OutputsSpec expected = OutputsSpec::Names { "out" };
ASSERT_EQ(OutputsSpec::parse(str), expected);
ASSERT_EQ(expected.to_string(), str);
}
TEST(OutputsSpec, names_out_bin) {
OutputsSpec expected = OutputsSpec::Names { "out", "bin" };
ASSERT_EQ(OutputsSpec::parse("out,bin"), expected);
// N.B. This normalization is OK.
ASSERT_EQ(expected.to_string(), "bin,out");
} }
TEST(ExtendedOutputsSpec_parse, basic) #define TEST_DONT_PARSE(NAME, STR) \
{ TEST(ExtendedOutputsSpec, bad_ ## NAME) { \
{ std::optional extendedOutputsSpecOpt = \
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo"); ExtendedOutputsSpec::parseOpt(STR); \
ASSERT_EQ(prefix, "foo"); ASSERT_FALSE(extendedOutputsSpecOpt); \
ASSERT_TRUE(std::get_if<ExtendedOutputsSpec::Default>(&extendedOutputsSpec));
} }
{ TEST_DONT_PARSE(carot_empty, "^")
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^*"); TEST_DONT_PARSE(prefix_carot_empty, "foo^")
ASSERT_EQ(prefix, "foo");
auto * explicit_p = std::get_if<ExtendedOutputsSpec::Explicit>(&extendedOutputsSpec);
ASSERT_TRUE(explicit_p);
ASSERT_TRUE(std::get_if<OutputsSpec::All>(explicit_p));
}
{ #undef TEST_DONT_PARSE
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^out");
ASSERT_EQ(prefix, "foo");
ASSERT_TRUE(std::get<OutputsSpec::Names>(std::get<ExtendedOutputsSpec::Explicit>(extendedOutputsSpec)) == OutputsSpec::Names({"out"}));
}
{ TEST(ExtendedOutputsSpec, defeault) {
std::string_view str = "foo";
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
ASSERT_EQ(prefix, "foo");
ExtendedOutputsSpec expected = ExtendedOutputsSpec::Default { };
ASSERT_EQ(extendedOutputsSpec, expected);
ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
}
TEST(ExtendedOutputsSpec, all) {
std::string_view str = "foo^*";
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
ASSERT_EQ(prefix, "foo");
ExtendedOutputsSpec expected = OutputsSpec::All { };
ASSERT_EQ(extendedOutputsSpec, expected);
ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
}
TEST(ExtendedOutputsSpec, out) {
std::string_view str = "foo^out";
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
ASSERT_EQ(prefix, "foo");
ExtendedOutputsSpec expected = OutputsSpec::Names { "out" };
ASSERT_EQ(extendedOutputsSpec, expected);
ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
}
TEST(ExtendedOutputsSpec, out_bin) {
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^out,bin"); auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^out,bin");
ASSERT_EQ(prefix, "foo"); ASSERT_EQ(prefix, "foo");
ASSERT_TRUE(std::get<OutputsSpec::Names>(std::get<ExtendedOutputsSpec::Explicit>(extendedOutputsSpec)) == OutputsSpec::Names({"out", "bin"})); ExtendedOutputsSpec expected = OutputsSpec::Names { "out", "bin" };
} ASSERT_EQ(extendedOutputsSpec, expected);
ASSERT_EQ(std::string { prefix } + expected.to_string(), "foo^bin,out");
}
{ TEST(ExtendedOutputsSpec, many_carrot) {
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^bar^out,bin"); auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^bar^out,bin");
ASSERT_EQ(prefix, "foo^bar"); ASSERT_EQ(prefix, "foo^bar");
ASSERT_TRUE(std::get<OutputsSpec::Names>(std::get<ExtendedOutputsSpec::Explicit>(extendedOutputsSpec)) == OutputsSpec::Names({"out", "bin"})); ExtendedOutputsSpec expected = OutputsSpec::Names { "out", "bin" };
} ASSERT_EQ(extendedOutputsSpec, expected);
ASSERT_EQ(std::string { prefix } + expected.to_string(), "foo^bar^bin,out");
} }
} }