Skip to content

Commit

Permalink
Make status line parsing testable. (#1528)
Browse files Browse the repository at this point in the history
Removes Want::UNKNOWN as this was only ever referenced in to_string, and never otherwise used in the product.
  • Loading branch information
BillyONeal authored Nov 4, 2024
1 parent 360e5c6 commit 84e2dcf
Show file tree
Hide file tree
Showing 16 changed files with 225 additions and 102 deletions.
1 change: 0 additions & 1 deletion include/vcpkg/base/contractual-constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,5 +581,4 @@ namespace vcpkg
inline constexpr StringLiteral StatusInstalled = "installed";
inline constexpr StringLiteral StatusNotInstalled = "not-installed";
inline constexpr StringLiteral StatusPurge = "purge";
inline constexpr StringLiteral StatusUnknown = "unknown";
}
12 changes: 12 additions & 0 deletions include/vcpkg/base/message-data.inc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,10 @@ DECLARE_MESSAGE(ExpectedFailOrSkip, (), "", "expected 'fail', 'skip', or 'pass'
DECLARE_MESSAGE(ExpectedFeatureListTerminal, (), "", "expected ',' or ']' in feature list")
DECLARE_MESSAGE(ExpectedFeatureName, (), "", "expected feature name (must be lowercase, digits, '-')")
DECLARE_MESSAGE(ExpectedExplicitTriplet, (), "", "expected an explicit triplet")
DECLARE_MESSAGE(ExpectedInstallStateField,
(),
"The values in ''s are locale-invariant",
"expected one of 'not-installed', 'half-installed', or 'installed'")
DECLARE_MESSAGE(ExpectedOneSetOfTags,
(msg::count, msg::old_value, msg::new_value, msg::value),
"{old_value} is a left tag and {new_value} is the right tag. {value} is the input.",
Expand All @@ -1181,7 +1185,15 @@ DECLARE_MESSAGE(ExpectedPathToExist, (msg::path), "", "Expected {path} to exist
DECLARE_MESSAGE(ExpectedPortName, (), "", "expected a port name here (must be lowercase, digits, '-')")
DECLARE_MESSAGE(ExpectedReadWriteReadWrite, (), "", "unexpected argument: expected 'read', readwrite', or 'write'")
DECLARE_MESSAGE(ExpectedStatusField, (), "", "Expected 'status' field in status paragraph")
DECLARE_MESSAGE(ExpectedTextHere,
(msg::expected),
"{expected} is a locale-invariant string a parser was searching for",
"expected '{expected}' here")
DECLARE_MESSAGE(ExpectedTripletName, (), "", "expected a triplet name here (must be lowercase, digits, '-')")
DECLARE_MESSAGE(ExpectedWantField,
(),
"The values in ''s are locale-invariant",
"expected one of 'install', 'hold', 'deinstall', or 'purge' here")
DECLARE_MESSAGE(ExportArchitectureReq,
(),
"",
Expand Down
1 change: 1 addition & 0 deletions include/vcpkg/base/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ namespace vcpkg
}

bool require_character(char ch);
bool require_text(StringLiteral keyword);

bool try_match_keyword(StringView keyword_content);

Expand Down
2 changes: 1 addition & 1 deletion include/vcpkg/fwd/statusparagraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ namespace vcpkg
enum class Want
{
ERROR_STATE,
UNKNOWN,
INSTALL,
HOLD,
DEINSTALL,
PURGE
};

struct StatusLine;
struct StatusParagraph;
struct InstalledPackageView;
}
3 changes: 3 additions & 0 deletions include/vcpkg/paragraphs.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <vcpkg/sourceparagraph.h>

#include <string>
#include <utility>
#include <vector>

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

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

void append_paragraph_field(StringView name, StringView field, std::string& out_str);

bool is_port_directory(const ReadOnlyFilesystem& fs, const Path& maybe_directory);

struct PortLoadResult
Expand Down
28 changes: 24 additions & 4 deletions include/vcpkg/statusparagraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <vcpkg/fwd/installedpaths.h>
#include <vcpkg/fwd/statusparagraph.h>

#include <vcpkg/base/fmt.h>

#include <vcpkg/binaryparagraph.h>

#include <map>
Expand All @@ -13,18 +15,35 @@

namespace vcpkg
{
struct StatusLine
{
Want want = Want::ERROR_STATE;
InstallState state = InstallState::ERROR_STATE;

bool is_installed() const noexcept { return want == Want::INSTALL && state == InstallState::INSTALLED; }
void to_string(std::string& out) const;
std::string to_string() const;

friend bool operator==(const StatusLine& lhs, const StatusLine& rhs)
{
return lhs.want == rhs.want && lhs.state == rhs.state;
}

friend bool operator!=(const StatusLine& lhs, const StatusLine& rhs) { return !(lhs == rhs); }
};

ExpectedL<StatusLine> parse_status_line(StringView text, Optional<StringView> origin, TextRowCol init_rowcol);

// metadata for a package's representation in the 'installed' tree
struct StatusParagraph
{
StatusParagraph() noexcept;
StatusParagraph() = default;
StatusParagraph(StringView origin, Paragraph&& fields);

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

BinaryParagraph package;
Want want;
InstallState state;
StatusLine status;
};

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

VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::InstallState);
VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::Want);
VCPKG_FORMAT_WITH_TO_STRING(vcpkg::StatusLine);
6 changes: 6 additions & 0 deletions locales/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,8 @@
"ExpectedFailOrSkip": "expected 'fail', 'skip', or 'pass' here",
"ExpectedFeatureListTerminal": "expected ',' or ']' in feature list",
"ExpectedFeatureName": "expected feature name (must be lowercase, digits, '-')",
"ExpectedInstallStateField": "expected one of 'not-installed', 'half-installed', or 'installed'",
"_ExpectedInstallStateField.comment": "The values in ''s are locale-invariant",
"ExpectedOneSetOfTags": "Found {count} sets of {old_value}.*{new_value} but expected exactly 1, in block:\n{value}",
"_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.",
"ExpectedOneVersioningField": "expected only one versioning field",
Expand All @@ -699,7 +701,11 @@
"ExpectedPortName": "expected a port name here (must be lowercase, digits, '-')",
"ExpectedReadWriteReadWrite": "unexpected argument: expected 'read', readwrite', or 'write'",
"ExpectedStatusField": "Expected 'status' field in status paragraph",
"ExpectedTextHere": "expected '{expected}' here",
"_ExpectedTextHere.comment": "{expected} is a locale-invariant string a parser was searching for",
"ExpectedTripletName": "expected a triplet name here (must be lowercase, digits, '-')",
"ExpectedWantField": "expected one of 'install', 'hold', 'deinstall', or 'purge' here",
"_ExpectedWantField.comment": "The values in ''s are locale-invariant",
"ExportArchitectureReq": "Export prefab requires targeting at least one of the following architectures arm64-v8a, armeabi-v7a, x86_64, x86 to be present.",
"ExportPrefabRequiresAndroidTriplet": "export prefab requires an Android triplet.",
"Exported7zipArchive": "7zip archive exported at: {path}",
Expand Down
52 changes: 40 additions & 12 deletions src/vcpkg-test/statusparagraphs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,38 @@ using namespace vcpkg;
using namespace vcpkg::Paragraphs;
using namespace vcpkg::Test;

static constexpr StringLiteral test_origin = "test";
static constexpr TextRowCol test_textrowcol = {42, 34};

TEST_CASE ("parse status lines", "[statusparagraphs]")
{
REQUIRE(parse_status_line("install ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::INSTALL, InstallState::INSTALLED});
REQUIRE(parse_status_line("hold ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::HOLD, InstallState::INSTALLED});
REQUIRE(parse_status_line("deinstall ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::DEINSTALL, InstallState::INSTALLED});
REQUIRE(parse_status_line("purge ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::PURGE, InstallState::INSTALLED});

REQUIRE(
parse_status_line("install ok not-installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::INSTALL, InstallState::NOT_INSTALLED});
REQUIRE(
parse_status_line("install ok half-installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::INSTALL, InstallState::HALF_INSTALLED});

REQUIRE(parse_status_line("meow ok installed", test_origin, test_textrowcol).error() ==
LocalizedString::from_raw("test:42:34: error: expected one of 'install', 'hold', 'deinstall', or 'purge' "
"here\n on expression: meow ok installed\n ^"));
REQUIRE(parse_status_line("install ko half-installed", test_origin, test_textrowcol).error() ==
LocalizedString::from_raw("test:42:41: error: expected ' ok ' here\n on expression: install ko "
"half-installed\n ^"));
REQUIRE(parse_status_line("install ok meow", test_origin, test_textrowcol).error() ==
LocalizedString::from_raw("test:42:45: error: expected one of 'not-installed', 'half-installed', or "
"'installed'\n on expression: install ok meow\n ^"));
}

TEST_CASE ("find installed", "[statusparagraphs]")
{
auto pghs = parse_paragraphs(R"(
Expand All @@ -23,9 +55,8 @@ Status: install ok installed

REQUIRE(pghs);

StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
}));
StatusParagraphs status_db(Util::fmap(
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));

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

REQUIRE(pghs);

StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
}));
StatusParagraphs status_db(Util::fmap(
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));

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

REQUIRE(pghs);

StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
}));
StatusParagraphs status_db(Util::fmap(
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));

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

StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) {
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh));
}));
StatusParagraphs status_db(Util::fmap(
*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));

// Feature "openssl" is installed and should therefore be found
auto it = status_db.find_installed({{"ffmpeg", Test::X64_WINDOWS}, "openssl"});
Expand Down
3 changes: 1 addition & 2 deletions src/vcpkg-test/update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ TEST_CASE ("find outdated packages features 2", "[update]")

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

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

Expand Down
21 changes: 21 additions & 0 deletions src/vcpkg/base/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,27 @@ namespace vcpkg
return true;
}

bool ParserBase::require_text(StringLiteral text)
{
auto encoded = m_it;
// check that the encoded stream matches the keyword:
for (const char ch : text)
{
if (encoded.is_eof() || *encoded != static_cast<char32_t>(ch))
{
add_error(msg::format(msgExpectedTextHere, msg::expected = text));
return false;
}

++encoded;
}

// success
m_it = encoded;
m_column += static_cast<int>(text.size());
return true;
}

bool ParserBase::try_match_keyword(StringView keyword_content)
{
auto encoded = m_it;
Expand Down
32 changes: 11 additions & 21 deletions src/vcpkg/binaryparagraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
#include <vcpkg/paragraphparser.h>
#include <vcpkg/paragraphs.h>

using namespace vcpkg::Paragraphs;

namespace vcpkg
{
BinaryParagraph::BinaryParagraph(StringView origin, Paragraph&& fields)
: spec(), version(), description(), maintainers(), feature(), default_features(), dependencies(), abi()
{
ParagraphParser parser(origin, std::move(fields));
this->spec = PackageSpec(parser.required_field(ParagraphIdPackage),
Expand Down Expand Up @@ -177,15 +180,6 @@ namespace vcpkg

bool operator!=(const BinaryParagraph& lhs, const BinaryParagraph& rhs) { return !(lhs == rhs); }

static void serialize_string(StringView name, const std::string& field, std::string& out_str)
{
if (field.empty())
{
return;
}

out_str.append(name.data(), name.size()).append(": ").append(field).push_back('\n');
}
static void serialize_array(StringView name,
const std::vector<std::string>& array,
std::string& out_str,
Expand Down Expand Up @@ -223,33 +217,29 @@ namespace vcpkg
{
const size_t initial_end = out_str.size();

serialize_string(ParagraphIdPackage, pgh.spec.name(), out_str);

serialize_string(ParagraphIdVersion, pgh.version.text, out_str);
append_paragraph_field(ParagraphIdPackage, pgh.spec.name(), out_str);
append_paragraph_field(ParagraphIdVersion, pgh.version.text, out_str);
if (pgh.version.port_version != 0)
{
fmt::format_to(std::back_inserter(out_str), "{}: {}\n", ParagraphIdPortVersion, pgh.version.port_version);
}

if (pgh.is_feature())
{
serialize_string(ParagraphIdFeature, pgh.feature, out_str);
append_paragraph_field(ParagraphIdFeature, pgh.feature, out_str);
}

if (!pgh.dependencies.empty())
{
serialize_string(ParagraphIdDepends, serialize_deps_list(pgh.dependencies, pgh.spec.triplet()), out_str);
append_paragraph_field(
ParagraphIdDepends, serialize_deps_list(pgh.dependencies, pgh.spec.triplet()), out_str);
}

serialize_string(ParagraphIdArchitecture, pgh.spec.triplet().to_string(), out_str);
serialize_string(ParagraphIdMultiArch, "same", out_str);

append_paragraph_field(ParagraphIdArchitecture, pgh.spec.triplet().to_string(), out_str);
append_paragraph_field(ParagraphIdMultiArch, "same", out_str);
serialize_paragraph(ParagraphIdMaintainer, pgh.maintainers, out_str);

serialize_string(ParagraphIdAbi, pgh.abi, out_str);

append_paragraph_field(ParagraphIdAbi, pgh.abi, out_str);
serialize_paragraph(ParagraphIdDescription, pgh.description, out_str);

serialize_array(ParagraphIdDefaultFeatures, pgh.default_features, out_str);

// sanity check the serialized data
Expand Down
10 changes: 4 additions & 6 deletions src/vcpkg/commands.install.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,7 @@ namespace vcpkg

StatusParagraph source_paragraph;
source_paragraph.package = bcf.core_paragraph;
source_paragraph.want = Want::INSTALL;
source_paragraph.state = InstallState::HALF_INSTALLED;
source_paragraph.status = StatusLine{Want::INSTALL, InstallState::HALF_INSTALLED};

write_update(fs, installed, source_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
Expand All @@ -291,8 +290,7 @@ namespace vcpkg
{
StatusParagraph& feature_paragraph = features_spghs.emplace_back();
feature_paragraph.package = feature;
feature_paragraph.want = Want::INSTALL;
feature_paragraph.state = InstallState::HALF_INSTALLED;
feature_paragraph.status = StatusLine{Want::INSTALL, InstallState::HALF_INSTALLED};

write_update(fs, installed, feature_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
Expand All @@ -303,13 +301,13 @@ namespace vcpkg

install_package_and_write_listfile(fs, package_dir, install_dir);

source_paragraph.state = InstallState::INSTALLED;
source_paragraph.status.state = InstallState::INSTALLED;
write_update(fs, installed, source_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));

for (auto&& feature_paragraph : features_spghs)
{
feature_paragraph.state = InstallState::INSTALLED;
feature_paragraph.status.state = InstallState::INSTALLED;
write_update(fs, installed, feature_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
}
Expand Down
Loading

0 comments on commit 84e2dcf

Please sign in to comment.