Skip to content

Commit 84e2dcf

Browse files
authored
Make status line parsing testable. (#1528)
Removes Want::UNKNOWN as this was only ever referenced in to_string, and never otherwise used in the product.
1 parent 360e5c6 commit 84e2dcf

16 files changed

+225
-102
lines changed

include/vcpkg/base/contractual-constants.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,5 +581,4 @@ namespace vcpkg
581581
inline constexpr StringLiteral StatusInstalled = "installed";
582582
inline constexpr StringLiteral StatusNotInstalled = "not-installed";
583583
inline constexpr StringLiteral StatusPurge = "purge";
584-
inline constexpr StringLiteral StatusUnknown = "unknown";
585584
}

include/vcpkg/base/message-data.inc.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,10 @@ DECLARE_MESSAGE(ExpectedFailOrSkip, (), "", "expected 'fail', 'skip', or 'pass'
11721172
DECLARE_MESSAGE(ExpectedFeatureListTerminal, (), "", "expected ',' or ']' in feature list")
11731173
DECLARE_MESSAGE(ExpectedFeatureName, (), "", "expected feature name (must be lowercase, digits, '-')")
11741174
DECLARE_MESSAGE(ExpectedExplicitTriplet, (), "", "expected an explicit triplet")
1175+
DECLARE_MESSAGE(ExpectedInstallStateField,
1176+
(),
1177+
"The values in ''s are locale-invariant",
1178+
"expected one of 'not-installed', 'half-installed', or 'installed'")
11751179
DECLARE_MESSAGE(ExpectedOneSetOfTags,
11761180
(msg::count, msg::old_value, msg::new_value, msg::value),
11771181
"{old_value} is a left tag and {new_value} is the right tag. {value} is the input.",
@@ -1181,7 +1185,15 @@ DECLARE_MESSAGE(ExpectedPathToExist, (msg::path), "", "Expected {path} to exist
11811185
DECLARE_MESSAGE(ExpectedPortName, (), "", "expected a port name here (must be lowercase, digits, '-')")
11821186
DECLARE_MESSAGE(ExpectedReadWriteReadWrite, (), "", "unexpected argument: expected 'read', readwrite', or 'write'")
11831187
DECLARE_MESSAGE(ExpectedStatusField, (), "", "Expected 'status' field in status paragraph")
1188+
DECLARE_MESSAGE(ExpectedTextHere,
1189+
(msg::expected),
1190+
"{expected} is a locale-invariant string a parser was searching for",
1191+
"expected '{expected}' here")
11841192
DECLARE_MESSAGE(ExpectedTripletName, (), "", "expected a triplet name here (must be lowercase, digits, '-')")
1193+
DECLARE_MESSAGE(ExpectedWantField,
1194+
(),
1195+
"The values in ''s are locale-invariant",
1196+
"expected one of 'install', 'hold', 'deinstall', or 'purge' here")
11851197
DECLARE_MESSAGE(ExportArchitectureReq,
11861198
(),
11871199
"",

include/vcpkg/base/parse.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ namespace vcpkg
9090
}
9191

9292
bool require_character(char ch);
93+
bool require_text(StringLiteral keyword);
9394

9495
bool try_match_keyword(StringView keyword_content);
9596

include/vcpkg/fwd/statusparagraph.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ namespace vcpkg
1313
enum class Want
1414
{
1515
ERROR_STATE,
16-
UNKNOWN,
1716
INSTALL,
1817
HOLD,
1918
DEINSTALL,
2019
PURGE
2120
};
2221

22+
struct StatusLine;
2323
struct StatusParagraph;
2424
struct InstalledPackageView;
2525
}

include/vcpkg/paragraphs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <vcpkg/sourceparagraph.h>
1111

12+
#include <string>
1213
#include <utility>
1314
#include <vector>
1415

@@ -24,6 +25,8 @@ namespace vcpkg::Paragraphs
2425

2526
ExpectedL<std::vector<Paragraph>> parse_paragraphs(StringView str, StringView origin);
2627

28+
void append_paragraph_field(StringView name, StringView field, std::string& out_str);
29+
2730
bool is_port_directory(const ReadOnlyFilesystem& fs, const Path& maybe_directory);
2831

2932
struct PortLoadResult

include/vcpkg/statusparagraph.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <vcpkg/fwd/installedpaths.h>
66
#include <vcpkg/fwd/statusparagraph.h>
77

8+
#include <vcpkg/base/fmt.h>
9+
810
#include <vcpkg/binaryparagraph.h>
911

1012
#include <map>
@@ -13,18 +15,35 @@
1315

1416
namespace vcpkg
1517
{
18+
struct StatusLine
19+
{
20+
Want want = Want::ERROR_STATE;
21+
InstallState state = InstallState::ERROR_STATE;
22+
23+
bool is_installed() const noexcept { return want == Want::INSTALL && state == InstallState::INSTALLED; }
24+
void to_string(std::string& out) const;
25+
std::string to_string() const;
26+
27+
friend bool operator==(const StatusLine& lhs, const StatusLine& rhs)
28+
{
29+
return lhs.want == rhs.want && lhs.state == rhs.state;
30+
}
31+
32+
friend bool operator!=(const StatusLine& lhs, const StatusLine& rhs) { return !(lhs == rhs); }
33+
};
34+
35+
ExpectedL<StatusLine> parse_status_line(StringView text, Optional<StringView> origin, TextRowCol init_rowcol);
1636

1737
// metadata for a package's representation in the 'installed' tree
1838
struct StatusParagraph
1939
{
20-
StatusParagraph() noexcept;
40+
StatusParagraph() = default;
2141
StatusParagraph(StringView origin, Paragraph&& fields);
2242

23-
bool is_installed() const { return want == Want::INSTALL && state == InstallState::INSTALLED; }
43+
bool is_installed() const noexcept { return status.is_installed(); }
2444

2545
BinaryParagraph package;
26-
Want want;
27-
InstallState state;
46+
StatusLine status;
2847
};
2948

3049
void serialize(const StatusParagraph& pgh, std::string& out_str);
@@ -59,3 +78,4 @@ namespace vcpkg
5978

6079
VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::InstallState);
6180
VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::Want);
81+
VCPKG_FORMAT_WITH_TO_STRING(vcpkg::StatusLine);

locales/messages.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,8 @@
691691
"ExpectedFailOrSkip": "expected 'fail', 'skip', or 'pass' here",
692692
"ExpectedFeatureListTerminal": "expected ',' or ']' in feature list",
693693
"ExpectedFeatureName": "expected feature name (must be lowercase, digits, '-')",
694+
"ExpectedInstallStateField": "expected one of 'not-installed', 'half-installed', or 'installed'",
695+
"_ExpectedInstallStateField.comment": "The values in ''s are locale-invariant",
694696
"ExpectedOneSetOfTags": "Found {count} sets of {old_value}.*{new_value} but expected exactly 1, in block:\n{value}",
695697
"_ExpectedOneSetOfTags.comment": "{old_value} is a left tag and {new_value} is the right tag. {value} is the input. An example of {count} is 42.",
696698
"ExpectedOneVersioningField": "expected only one versioning field",
@@ -699,7 +701,11 @@
699701
"ExpectedPortName": "expected a port name here (must be lowercase, digits, '-')",
700702
"ExpectedReadWriteReadWrite": "unexpected argument: expected 'read', readwrite', or 'write'",
701703
"ExpectedStatusField": "Expected 'status' field in status paragraph",
704+
"ExpectedTextHere": "expected '{expected}' here",
705+
"_ExpectedTextHere.comment": "{expected} is a locale-invariant string a parser was searching for",
702706
"ExpectedTripletName": "expected a triplet name here (must be lowercase, digits, '-')",
707+
"ExpectedWantField": "expected one of 'install', 'hold', 'deinstall', or 'purge' here",
708+
"_ExpectedWantField.comment": "The values in ''s are locale-invariant",
703709
"ExportArchitectureReq": "Export prefab requires targeting at least one of the following architectures arm64-v8a, armeabi-v7a, x86_64, x86 to be present.",
704710
"ExportPrefabRequiresAndroidTriplet": "export prefab requires an Android triplet.",
705711
"Exported7zipArchive": "7zip archive exported at: {path}",

src/vcpkg-test/statusparagraphs.cpp

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,38 @@ using namespace vcpkg;
99
using namespace vcpkg::Paragraphs;
1010
using namespace vcpkg::Test;
1111

12+
static constexpr StringLiteral test_origin = "test";
13+
static constexpr TextRowCol test_textrowcol = {42, 34};
14+
15+
TEST_CASE ("parse status lines", "[statusparagraphs]")
16+
{
17+
REQUIRE(parse_status_line("install ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
18+
StatusLine{Want::INSTALL, InstallState::INSTALLED});
19+
REQUIRE(parse_status_line("hold ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
20+
StatusLine{Want::HOLD, InstallState::INSTALLED});
21+
REQUIRE(parse_status_line("deinstall ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
22+
StatusLine{Want::DEINSTALL, InstallState::INSTALLED});
23+
REQUIRE(parse_status_line("purge ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
24+
StatusLine{Want::PURGE, InstallState::INSTALLED});
25+
26+
REQUIRE(
27+
parse_status_line("install ok not-installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
28+
StatusLine{Want::INSTALL, InstallState::NOT_INSTALLED});
29+
REQUIRE(
30+
parse_status_line("install ok half-installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
31+
StatusLine{Want::INSTALL, InstallState::HALF_INSTALLED});
32+
33+
REQUIRE(parse_status_line("meow ok installed", test_origin, test_textrowcol).error() ==
34+
LocalizedString::from_raw("test:42:34: error: expected one of 'install', 'hold', 'deinstall', or 'purge' "
35+
"here\n on expression: meow ok installed\n ^"));
36+
REQUIRE(parse_status_line("install ko half-installed", test_origin, test_textrowcol).error() ==
37+
LocalizedString::from_raw("test:42:41: error: expected ' ok ' here\n on expression: install ko "
38+
"half-installed\n ^"));
39+
REQUIRE(parse_status_line("install ok meow", test_origin, test_textrowcol).error() ==
40+
LocalizedString::from_raw("test:42:45: error: expected one of 'not-installed', 'half-installed', or "
41+
"'installed'\n on expression: install ok meow\n ^"));
42+
}
43+
1244
TEST_CASE ("find installed", "[statusparagraphs]")
1345
{
1446
auto pghs = parse_paragraphs(R"(
@@ -23,9 +55,8 @@ Status: install ok installed
2355

2456
REQUIRE(pghs);
2557

26-
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
27-
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
28-
}));
58+
StatusParagraphs status_db(Util::fmap(
59+
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
2960

3061
auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS});
3162
REQUIRE(it != status_db.end());
@@ -45,9 +76,8 @@ Status: purge ok not-installed
4576

4677
REQUIRE(pghs);
4778

48-
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
49-
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
50-
}));
79+
StatusParagraphs status_db(Util::fmap(
80+
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
5181

5282
auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS});
5383
REQUIRE(it == status_db.end());
@@ -75,9 +105,8 @@ Status: purge ok not-installed
75105

76106
REQUIRE(pghs);
77107

78-
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
79-
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
80-
}));
108+
StatusParagraphs status_db(Util::fmap(
109+
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
81110

82111
auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS});
83112
REQUIRE(it != status_db.end());
@@ -108,9 +137,8 @@ Status: install ok installed
108137
"test-origin");
109138
REQUIRE(pghs);
110139

111-
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
112-
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
113-
}));
140+
StatusParagraphs status_db(Util::fmap(
141+
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
114142

115143
// Feature "openssl" is installed and should therefore be found
116144
auto it = status_db.find_installed({{"ffmpeg", Test::X64_WINDOWS}, "openssl"});

src/vcpkg-test/update.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ TEST_CASE ("find outdated packages features 2", "[update]")
6464

6565
status_paragraphs.push_back(make_status_feature_pgh("a", "b"));
6666
status_paragraphs.back()->package.version = Version{"0", 0};
67-
status_paragraphs.back()->state = InstallState::NOT_INSTALLED;
68-
status_paragraphs.back()->want = Want::PURGE;
67+
status_paragraphs.back()->status = {Want::PURGE, InstallState::NOT_INSTALLED};
6968

7069
StatusParagraphs status_db(std::move(status_paragraphs));
7170

src/vcpkg/base/parse.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,27 @@ namespace vcpkg
146146
return true;
147147
}
148148

149+
bool ParserBase::require_text(StringLiteral text)
150+
{
151+
auto encoded = m_it;
152+
// check that the encoded stream matches the keyword:
153+
for (const char ch : text)
154+
{
155+
if (encoded.is_eof() || *encoded != static_cast<char32_t>(ch))
156+
{
157+
add_error(msg::format(msgExpectedTextHere, msg::expected = text));
158+
return false;
159+
}
160+
161+
++encoded;
162+
}
163+
164+
// success
165+
m_it = encoded;
166+
m_column += static_cast<int>(text.size());
167+
return true;
168+
}
169+
149170
bool ParserBase::try_match_keyword(StringView keyword_content)
150171
{
151172
auto encoded = m_it;

0 commit comments

Comments
 (0)