feat(cpp-object-oriented-modular-refactoring): step 11 - source-target-resolver
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#include "fesa/core/ascii.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
TEST(Ascii, LowercasesOnlyAsciiUppercaseBytes) {
|
||||
EXPECT_EQ(fesa::AsciiLower('A'), 'a');
|
||||
EXPECT_EQ(fesa::AsciiLower('Z'), 'z');
|
||||
EXPECT_EQ(fesa::AsciiLower('a'), 'a');
|
||||
EXPECT_EQ(fesa::AsciiLower('0'), '0');
|
||||
|
||||
const char non_ascii = static_cast<char>(0xc0U);
|
||||
EXPECT_EQ(fesa::AsciiLower(non_ascii), non_ascii);
|
||||
}
|
||||
|
||||
TEST(Ascii, ComparesNamesWithoutLocaleOrUnicodeFolding) {
|
||||
EXPECT_TRUE(fesa::AsciiCaseInsensitiveEquals("Beam-1", "bEaM-1"));
|
||||
EXPECT_FALSE(fesa::AsciiCaseInsensitiveEquals("Beam-1", "Beam-10"));
|
||||
|
||||
const std::string uppercase_byte{static_cast<char>(0xc0U)};
|
||||
const std::string lowercase_byte{static_cast<char>(0xe0U)};
|
||||
EXPECT_FALSE(
|
||||
fesa::AsciiCaseInsensitiveEquals(uppercase_byte, lowercase_byte));
|
||||
}
|
||||
|
||||
TEST(Ascii, ParsesOnlyCompletePositiveBaseTenSourceLabels) {
|
||||
auto one = fesa::ParsePositiveSourceLabel("1");
|
||||
auto leading_zeroes = fesa::ParsePositiveSourceLabel("00042");
|
||||
auto maximum = fesa::ParsePositiveSourceLabel(
|
||||
std::to_string((std::numeric_limits<std::int64_t>::max)()));
|
||||
|
||||
ASSERT_TRUE(one.HasValue());
|
||||
EXPECT_EQ(one.Value(), 1);
|
||||
ASSERT_TRUE(leading_zeroes.HasValue());
|
||||
EXPECT_EQ(leading_zeroes.Value(), 42);
|
||||
ASSERT_TRUE(maximum.HasValue());
|
||||
EXPECT_EQ(maximum.Value(), (std::numeric_limits<std::int64_t>::max)());
|
||||
|
||||
const std::string overflow =
|
||||
std::to_string((std::numeric_limits<std::int64_t>::max)()) + "0";
|
||||
const std::vector<std::string> invalid_values{"", "0", "-1", "+", "+1",
|
||||
" 1", "1 ", "1x", overflow};
|
||||
for (const std::string& invalid : invalid_values) {
|
||||
SCOPED_TRACE(invalid);
|
||||
auto result = fesa::ParsePositiveSourceLabel(invalid);
|
||||
ASSERT_FALSE(result.HasValue());
|
||||
EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kInput);
|
||||
ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U);
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0U].code,
|
||||
"invalid-source-label");
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0U].entity_identity, invalid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "fesa/model/source_target_resolver.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
fesa::SourceTargetIndexEntry Entry(const fesa::SourceEntityKind entity_kind,
|
||||
std::string instance_name,
|
||||
std::string target_name,
|
||||
const std::int64_t source_label,
|
||||
std::string source_label_text,
|
||||
const fesa::EntityIndex entity_index,
|
||||
const std::size_t declaration_order) {
|
||||
fesa::SourceEntityId source_id{instance_name, source_label,
|
||||
std::move(source_label_text)};
|
||||
return {entity_kind,
|
||||
std::move(instance_name),
|
||||
std::move(target_name),
|
||||
std::move(source_id),
|
||||
entity_index,
|
||||
declaration_order};
|
||||
}
|
||||
|
||||
std::vector<fesa::EntityIndex> Indices(
|
||||
const std::vector<fesa::ResolvedSourceTarget>& targets) {
|
||||
std::vector<fesa::EntityIndex> indices;
|
||||
indices.reserve(targets.size());
|
||||
for (const auto& target : targets) {
|
||||
indices.push_back(target.entity_index);
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
void ExpectFailure(const fesa::SourceTargetIndex& index,
|
||||
fesa::SourceTargetQuery query, const std::string& code) {
|
||||
const auto result = fesa::SourceTargetResolver{index}.Resolve(query);
|
||||
ASSERT_FALSE(result.HasValue());
|
||||
EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kInput);
|
||||
ASSERT_FALSE(result.GetStatus().Diagnostics().empty());
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0U].code, code);
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0U].entity_identity,
|
||||
query.target_name_or_label);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(SourceTargetResolver,
|
||||
SeparatesNamespacesAndInstancesAndExpandsInDeclarationOrder) {
|
||||
using fesa::SourceEntityKind;
|
||||
fesa::SourceTargetIndex index{{
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "Ends", 20, "0020", 1U, 30U),
|
||||
Entry(SourceEntityKind::kElement, "Beam-1", "Ends", 700, "0700", 4U, 10U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "Ends", 10, "0010", 0U, 20U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-2", "Ends", 10, "0010", 2U, 40U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "", 10, "0010", 0U, 0U),
|
||||
}};
|
||||
const fesa::SourceTargetResolver resolver{index};
|
||||
|
||||
auto nodes = resolver.Resolve({SourceEntityKind::kNode, "bEaM-1", "eNdS"});
|
||||
ASSERT_TRUE(nodes.HasValue());
|
||||
EXPECT_EQ(Indices(nodes.Value()), (std::vector<fesa::EntityIndex>{0U, 1U}));
|
||||
EXPECT_EQ(nodes.Value()[0U].source_id.instance_name, "Beam-1");
|
||||
EXPECT_EQ(nodes.Value()[0U].source_id.source_label_text, "0010");
|
||||
|
||||
auto elements =
|
||||
resolver.Resolve({SourceEntityKind::kElement, "BEAM-1", "ends"});
|
||||
ASSERT_TRUE(elements.HasValue());
|
||||
EXPECT_EQ(Indices(elements.Value()), (std::vector<fesa::EntityIndex>{4U}));
|
||||
|
||||
auto second_instance =
|
||||
resolver.Resolve({SourceEntityKind::kNode, "beam-2", "ENDS"});
|
||||
ASSERT_TRUE(second_instance.HasValue());
|
||||
EXPECT_EQ(Indices(second_instance.Value()),
|
||||
(std::vector<fesa::EntityIndex>{2U}));
|
||||
|
||||
auto direct = resolver.Resolve({SourceEntityKind::kNode, "Beam-1", "10"});
|
||||
ASSERT_TRUE(direct.HasValue());
|
||||
EXPECT_EQ(Indices(direct.Value()), (std::vector<fesa::EntityIndex>{0U}));
|
||||
}
|
||||
|
||||
TEST(SourceTargetResolver, RejectsMissingAmbiguousAndNonpositiveTargets) {
|
||||
using fesa::SourceEntityKind;
|
||||
const fesa::SourceTargetIndex index{{
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "", 10, "10", 0U, 0U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-2", "", 10, "10", 1U, 1U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "10", 20, "20", 2U, 2U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "Shared", 10, "10", 0U, 3U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-2", "Shared", 10, "10", 1U, 4U),
|
||||
}};
|
||||
|
||||
ExpectFailure(index, {SourceEntityKind::kNode, "", "Missing"},
|
||||
"unresolved-source-target");
|
||||
ExpectFailure(index, {SourceEntityKind::kNode, "", "0"},
|
||||
"invalid-source-target");
|
||||
ExpectFailure(index, {SourceEntityKind::kNode, "", "-7"},
|
||||
"invalid-source-target");
|
||||
ExpectFailure(index, {SourceEntityKind::kNode, "", "10"},
|
||||
"ambiguous-source-target");
|
||||
ExpectFailure(index, {SourceEntityKind::kNode, "", "Shared"},
|
||||
"ambiguous-source-target");
|
||||
ExpectFailure(index, {SourceEntityKind::kNode, "Beam-1", "10"},
|
||||
"ambiguous-source-target");
|
||||
}
|
||||
|
||||
TEST(SourceTargetResolver,
|
||||
ReportsDuplicateMembershipsInDeterministicDeclarationOrder) {
|
||||
using fesa::SourceEntityKind;
|
||||
const fesa::SourceTargetIndex index{{
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "Duplicate", 10, "10", 0U, 1U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "Duplicate", 10, "10", 0U, 9U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "Duplicate", 20, "20", 1U, 2U),
|
||||
Entry(SourceEntityKind::kNode, "Beam-1", "Duplicate", 20, "20", 1U, 7U),
|
||||
}};
|
||||
|
||||
const auto result = fesa::SourceTargetResolver{index}.Resolve(
|
||||
{SourceEntityKind::kNode, "Beam-1", "duplicate"});
|
||||
|
||||
ASSERT_FALSE(result.HasValue());
|
||||
EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kInput);
|
||||
ASSERT_EQ(result.GetStatus().Diagnostics().size(), 2U);
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0U].code,
|
||||
"duplicate-source-target");
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0U].message,
|
||||
"Duplicate resolved source target 20.");
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[1U].code,
|
||||
"duplicate-source-target");
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[1U].message,
|
||||
"Duplicate resolved source target 10.");
|
||||
}
|
||||
Reference in New Issue
Block a user