CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
582 lines
23 KiB
C++
582 lines
23 KiB
C++
//===- UnsafeBufferReachableAnalysisTest.cpp ------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "../TestFixture.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowAnalysis.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageAnalysis.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/LUSummary.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityLinkage.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisDriver.h"
|
|
#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "gtest/gtest.h"
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
|
|
using namespace clang;
|
|
using namespace ssaf;
|
|
|
|
namespace clang::ssaf {
|
|
extern PointerFlowEntitySummary buildPointerFlowEntitySummary(EdgeSet Edges);
|
|
extern UnsafeBufferUsageEntitySummary
|
|
buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet);
|
|
} // namespace clang::ssaf
|
|
|
|
namespace {
|
|
|
|
class UnsafeBufferReachableAnalysisTest : public TestFixture {
|
|
protected:
|
|
using EPLEdge = std::pair<EntityPointerLevel, EntityPointerLevel>;
|
|
|
|
static constexpr EntityLinkage ExternalLinkage =
|
|
EntityLinkage(EntityLinkageType::External);
|
|
|
|
std::unique_ptr<LUSummary> makeLUSummary() {
|
|
NestedBuildNamespace NS(
|
|
{BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
|
|
return std::make_unique<LUSummary>(std::move(NS));
|
|
}
|
|
|
|
EntityId addEntity(LUSummary &LU, llvm::StringRef USR) {
|
|
NestedBuildNamespace NS(
|
|
{BuildNamespace(BuildNamespaceKind::LinkUnit, "TestLU")});
|
|
EntityName Name(USR.str(), "", NS);
|
|
EntityId Id = getIdTable(LU).getId(Name);
|
|
getLinkageTable(LU).insert({Id, ExternalLinkage});
|
|
return Id;
|
|
}
|
|
|
|
/// Insert a PointerFlowEntitySummary for an entity.
|
|
void insertPointerFlowSummary(LUSummary &LU, EntityId Id, EdgeSet Edges) {
|
|
getData(LU)[PointerFlowEntitySummary::summaryName()][Id] =
|
|
std::make_unique<PointerFlowEntitySummary>(
|
|
buildPointerFlowEntitySummary(std::move(Edges)));
|
|
}
|
|
|
|
/// Insert an UnsafeBufferUsageEntitySummary for an entity.
|
|
void insertUnsafeBufferUsageSummary(LUSummary &LU, EntityId Id,
|
|
EntityPointerLevelSet UnsafeBuffers) {
|
|
getData(LU)[UnsafeBufferUsageEntitySummary::summaryName()][Id] =
|
|
std::make_unique<UnsafeBufferUsageEntitySummary>(
|
|
buildUnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
|
|
}
|
|
|
|
class LetterEntityBiMap {
|
|
std::map<char, EntityId> Forward;
|
|
std::map<EntityId, char> Reverse;
|
|
|
|
public:
|
|
void insert(char C, EntityId Id) {
|
|
Forward.try_emplace(C, Id);
|
|
Reverse[Id] = C;
|
|
}
|
|
|
|
EntityId operator[](char C) const { return Forward.at(C); }
|
|
char operator[](EntityId Id) const { return Reverse.at(Id); }
|
|
size_t size() const { return Forward.size(); }
|
|
};
|
|
|
|
/// Create entities for the entity domain \p EntDom in \p LU. For simplicity,
|
|
/// entities are given by letters in \p EntDom. Return a "bi-directional map"
|
|
/// between letters and EntityIds.
|
|
LetterEntityBiMap createEntities(LUSummary &LU, llvm::ArrayRef<char> EntDom) {
|
|
LetterEntityBiMap Result;
|
|
for (char Name : EntDom)
|
|
Result.insert(Name, addEntity(LU, ("E" + llvm::Twine(Name)).str()));
|
|
return Result;
|
|
}
|
|
|
|
/// Insert both PointerFlow and UnsafeBufferUsage summaries for an entity
|
|
/// from a list of edges and a list of starter EPLs.
|
|
void insertSummaries(LUSummary &LU, EntityId Id,
|
|
llvm::ArrayRef<EPLEdge> EdgeList,
|
|
llvm::ArrayRef<EntityPointerLevel> StarterList) {
|
|
EdgeSet Edges;
|
|
for (const auto &[From, To] : EdgeList)
|
|
Edges[From].insert(To);
|
|
insertPointerFlowSummary(LU, Id, std::move(Edges));
|
|
|
|
EntityPointerLevelSet Starters;
|
|
for (const auto &EPL : StarterList)
|
|
Starters.insert(EPL);
|
|
insertUnsafeBufferUsageSummary(LU, Id, std::move(Starters));
|
|
}
|
|
|
|
/// Run the driver and return the flattened reachable EPL set.
|
|
std::optional<EntityPointerLevelSet>
|
|
computeReachables(std::unique_ptr<LUSummary> LU, unsigned Line) {
|
|
AnalysisDriver Driver(std::move(LU));
|
|
auto WPAOrErr =
|
|
Driver.run<PointerFlowAnalysisResult, UnsafeBufferUsageAnalysisResult,
|
|
UnsafeBufferReachableAnalysisResult>();
|
|
if (!WPAOrErr) {
|
|
ADD_FAILURE_AT(__FILE__, Line) << llvm::toString(WPAOrErr.takeError());
|
|
return std::nullopt;
|
|
}
|
|
auto ROrErr = WPAOrErr->get<UnsafeBufferReachableAnalysisResult>();
|
|
if (!ROrErr) {
|
|
ADD_FAILURE_AT(__FILE__, Line) << llvm::toString(ROrErr.takeError());
|
|
return std::nullopt;
|
|
}
|
|
EntityPointerLevelSet Result;
|
|
for (const auto &[Id, EPLs] : ROrErr->Reachables)
|
|
Result.insert(EPLs.begin(), EPLs.end());
|
|
return Result;
|
|
}
|
|
|
|
using Node = std::pair<char, unsigned>;
|
|
using Edge = std::pair<Node, Node>;
|
|
|
|
// FIXME: When we use more advanced search algorithms, it may involve
|
|
// a divide-and-conquer approach on sub-graphs organized by contributors.
|
|
// In that case, we may want to enumerate all possible partitions of
|
|
// how edges are distributed among contributors. For now we use
|
|
// `singlePartition`.
|
|
|
|
/// Compute reachables from \p StarterLayout in the graph defined by \p
|
|
/// EdgeLayout. Edges and starters are all belong to one contributor.
|
|
std::set<Node> singlePartition(llvm::ArrayRef<char> EntityDomain,
|
|
llvm::ArrayRef<Edge> EdgeLayout,
|
|
llvm::ArrayRef<Node> StarterLayout,
|
|
unsigned Line) {
|
|
auto LU = makeLUSummary();
|
|
auto Entities = createEntities(*LU, EntityDomain);
|
|
auto GetEPL = [&Entities](const Node &N) -> EntityPointerLevel {
|
|
return buildEntityPointerLevel(Entities[N.first], N.second);
|
|
};
|
|
auto GetNode = [&Entities](const EntityPointerLevel &N) -> Node {
|
|
return {Entities[N.getEntity()], N.getPointerLevel()};
|
|
};
|
|
|
|
std::vector<EPLEdge> Edges;
|
|
for (const auto &[F, T] : EdgeLayout)
|
|
Edges.push_back({GetEPL(F), GetEPL(T)});
|
|
|
|
std::vector<EntityPointerLevel> Starters;
|
|
for (const Node &N : StarterLayout)
|
|
Starters.push_back(GetEPL(N));
|
|
|
|
insertSummaries(*LU, Entities[EntityDomain[0]], Edges, Starters);
|
|
for (size_t Idx = 1; Idx < EntityDomain.size(); ++Idx)
|
|
insertSummaries(*LU, Entities[EntityDomain[Idx]], {}, {});
|
|
|
|
auto Reachables = computeReachables(std::move(LU), Line);
|
|
if (!Reachables)
|
|
return {};
|
|
|
|
std::set<Node> Result;
|
|
for (auto &EPL : *Reachables)
|
|
Result.insert(GetNode(EPL));
|
|
|
|
return Result;
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Tests below focus on pointer flow graph topology and the starter set.
|
|
// Letters represent distinct entities; numbers represent pointer levels.
|
|
//
|
|
// For example, `LinearChain` tests a graph forming a linear chain with 3
|
|
// edges: (a,1) -> (b,1) -> (c,1) -> (d,1) with starter {(a,1)}. Thus, {(a,1),
|
|
// (b,1), (c,1), (d,1)} is the expected reachable set.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Linear chain: (a,1) -> (b,1) -> (c,1) -> (d,1).
|
|
// Start from {(a,1)} => {(a,1), (b,1), (c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, LinearChain) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}}, {{'b', 1}, {'c', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 4u);
|
|
}
|
|
|
|
// Linear chain: (a,1) -> (b,2), (b,1) -> (c,2), (c,1) -> (d,2).
|
|
// Start from {(a,2)} => {(a,2), (b,3), (c,4), (d,5)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, LinearChain2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 2}}, {{'b', 1}, {'c', 2}}, {{'c', 1}, {'d', 2}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 4u);
|
|
EXPECT_EQ(Reachables,
|
|
(std::set<Node>{{'a', 2}, {'b', 3}, {'c', 4}, {'d', 5}}));
|
|
}
|
|
|
|
// Linear chain: (a,1) -> (b,2), (b,4) -> (c,1) -> (d,1).
|
|
// Start from {(a,2)} => {(a,2), (b,3)} (halted at (b,3) — no key (b,j<=3))
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, LinearChain3) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 2}}, {{'b', 4}, {'c', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 2u);
|
|
EXPECT_EQ(Reachables, (std::set<Node>{{'a', 2}, {'b', 3}}));
|
|
}
|
|
|
|
// Linear chain: (a,1) -> (b,1) -> (c,1) -> (d,1).
|
|
// Start from mid-chain {(c,1)} => {(c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, LinearChainFromMiddle) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}}, {{'b', 1}, {'c', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'c', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 2u);
|
|
EXPECT_TRUE(Reachables.count({'c', 1}));
|
|
EXPECT_TRUE(Reachables.count({'d', 1}));
|
|
}
|
|
|
|
// Diamond: (a,1) -> (b,1), (a,1) -> (c,1), (b,1) -> (d,1), (c,1) -> (d,1).
|
|
// Start from {(a,1)} => {(a,1), (b,1), (c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, Diamond) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'a', 1}, {'c', 1}},
|
|
{{'b', 1}, {'d', 1}},
|
|
{{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 4u);
|
|
}
|
|
|
|
// Diamond: (a,1) -> (b,2), (a,1) -> (c,2), (b,1) -> (d,2), (c,1) -> (d,2).
|
|
// Start from {(a,2)} => {(a,2), (b,3), (c,3), (d,4)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, Diamond2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 2}},
|
|
{{'a', 1}, {'c', 2}},
|
|
{{'b', 1}, {'d', 2}},
|
|
{{'c', 1}, {'d', 2}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables,
|
|
(std::set<Node>{{'a', 2}, {'b', 3}, {'c', 3}, {'d', 4}}));
|
|
}
|
|
|
|
// DisconnectedDiamond: (a,1) -> (b,2), (a,1) -> (c,2), (b,5) -> (d,1), (c,5) ->
|
|
// (d,1). Start from {(a,2)} => {(a,2), (b,3), (c,3)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, DisconnectedDiamond) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 2}},
|
|
{{'a', 1}, {'c', 2}},
|
|
{{'b', 5}, {'d', 1}},
|
|
{{'c', 5}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables, (std::set<Node>{{'a', 2}, {'b', 3}, {'c', 3}}));
|
|
}
|
|
|
|
// Diamond: (a,1) -> (b,1), (a,1) -> (c,1), (b,1) -> (d,1), (c,1) -> (d,1).
|
|
// Start from one branch {(b,1)} => {(b,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, DiamondFromBranch) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'a', 1}, {'c', 1}},
|
|
{{'b', 1}, {'d', 1}},
|
|
{{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'b', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 2u);
|
|
EXPECT_TRUE(Reachables.count({'b', 1}));
|
|
EXPECT_TRUE(Reachables.count({'d', 1}));
|
|
}
|
|
|
|
// Disconnected subgraphs: (a,1) -> (b,1), (c,1) -> (d,1).
|
|
// Start from {(a,1)} => {(a,1), (b,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, DisconnectedSubgraphs) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */ {{{'a', 1}, {'b', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 2u);
|
|
EXPECT_TRUE(Reachables.count({'a', 1}));
|
|
EXPECT_TRUE(Reachables.count({'b', 1}));
|
|
}
|
|
|
|
// Disconnected subgraphs: (a,1) -> (b,1), (c,1) -> (d,1).
|
|
// Start from tail {(b,1)} => {(b,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, DisconnectedSubgraphs2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */ {{{'a', 1}, {'b', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'b', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 1u);
|
|
EXPECT_TRUE(Reachables.count({'b', 1}));
|
|
}
|
|
|
|
// Cycle: (a,1) -> (b,1) -> (c,1) -> (d,1) -> (a,1).
|
|
// Start from {(c,1)} => {(a,1), (b,1), (c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, Cycle) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'b', 1}, {'c', 1}},
|
|
{{'c', 1}, {'d', 1}},
|
|
{{'d', 1}, {'a', 1}}},
|
|
/* StarterLayout */ {{'c', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 4u);
|
|
EXPECT_TRUE(Reachables.count({'a', 1}));
|
|
EXPECT_TRUE(Reachables.count({'b', 1}));
|
|
EXPECT_TRUE(Reachables.count({'c', 1}));
|
|
EXPECT_TRUE(Reachables.count({'d', 1}));
|
|
}
|
|
|
|
// Cycle: (a,1) -> (b,1) -> (c,1) -> (d,1) -> (a,1).
|
|
// Start from {(c,2)} => {(a,2), (b,2), (c,2), (d,2)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, Cycle2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'b', 1}, {'c', 1}},
|
|
{{'c', 1}, {'d', 1}},
|
|
{{'d', 1}, {'a', 1}}},
|
|
/* StarterLayout */ {{'c', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables,
|
|
(std::set<Node>{{'a', 2}, {'b', 2}, {'c', 2}, {'d', 2}}));
|
|
}
|
|
|
|
// Cycle: (a,1) -> (b,2) -> (c,3) -> (d,4) -> (a,1).
|
|
// Start from {(a,2)} => {(a,2), (b,3), (c,4), (d,5)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, Cycle3) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 2}},
|
|
{{'b', 2}, {'c', 3}},
|
|
{{'c', 3}, {'d', 4}},
|
|
{{'d', 4}, {'a', 1}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables,
|
|
(std::set<Node>{{'a', 2}, {'b', 3}, {'c', 4}, {'d', 5}}));
|
|
}
|
|
|
|
// Empty graph: no edges, start from {(a,1)} => {(a,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, EmptyGraph) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a'},
|
|
/* EdgeLayout */ {},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 1u);
|
|
EXPECT_TRUE(Reachables.count({'a', 1}));
|
|
}
|
|
|
|
// Star: (a,1) -> (b,1), (a,1) -> (c,1), (a,1) -> (d,1).
|
|
// Start from {(a,1)} => {(a,1), (b,1), (c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, StarFromHub) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}}, {{'a', 1}, {'c', 1}}, {{'a', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 4u);
|
|
}
|
|
|
|
// Star: (a,1) -> (b,2), (a,1) -> (c,2), (a,1) -> (d,2).
|
|
// Start from {(a,2)} => {(a,2), (b,3), (c,3), (d,3)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, StarFromHub2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 2}}, {{'a', 1}, {'c', 2}}, {{'a', 1}, {'d', 2}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables,
|
|
(std::set<Node>{{'a', 2}, {'b', 3}, {'c', 3}, {'d', 3}}));
|
|
}
|
|
|
|
// Star: (a,2) -> (b,1), (a,2) -> (c,1), (a,2) -> (d,1).
|
|
// Start from {(a,1)} => {(a,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, StarFromHub3) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 2}, {'b', 1}}, {{'a', 2}, {'c', 1}}, {{'a', 2}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables, (std::set<Node>{{'a', 1}}));
|
|
}
|
|
|
|
// Star: (a,1) -> (b,1), (a,1) -> (c,1), (a,1) -> (d,1).
|
|
// Start from leaf {(c,1)} => {(c,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, StarFromLeaf) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}}, {{'a', 1}, {'c', 1}}, {{'a', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'c', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 1u);
|
|
EXPECT_TRUE(Reachables.count({'c', 1}));
|
|
}
|
|
|
|
// Reverse star: (a,1) -> (d,1), (b,1) -> (d,1), (c,1) -> (d,1).
|
|
// Start from {(a,1)} => {(a,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSource) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'d', 1}}, {{'b', 1}, {'d', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 2u);
|
|
EXPECT_TRUE(Reachables.count({'a', 1}));
|
|
EXPECT_TRUE(Reachables.count({'d', 1}));
|
|
}
|
|
|
|
// Reverse star: (a,1) -> (d,2), (b,1) -> (d,2), (c,1) -> (d,2).
|
|
// Start from {(a,2)} => {(a,2), (d,3)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSource2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'d', 2}}, {{'b', 1}, {'d', 2}}, {{'c', 1}, {'d', 2}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables, (std::set<Node>{{'a', 2}, {'d', 3}}));
|
|
}
|
|
|
|
// Reverse star: (a,1) -> (d,1), (b,1) -> (d,1), (c,1) -> (d,1).
|
|
// Start from sink {(d,1)} => {(d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSink) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'d', 1}}, {{'b', 1}, {'d', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'d', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 1u);
|
|
EXPECT_TRUE(Reachables.count({'d', 1}));
|
|
}
|
|
|
|
// Reverse star: (a,1) -> (d,1), (b,1) -> (d,1), (c,1) -> (d,1).
|
|
// Start from sink {(d,2)} => {(d,2)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, ReverseStarFromSink2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'d', 1}}, {{'b', 1}, {'d', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'d', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables, (std::set<Node>{{'d', 2}}));
|
|
}
|
|
|
|
// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,1) -> (d,1).
|
|
// Start from {(a,1)} => {(a,1), (b,1), (c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromRoot) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'b', 1}, {'b', 1}},
|
|
{{'b', 1}, {'c', 1}},
|
|
{{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 4u);
|
|
}
|
|
|
|
// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,2) -> (d,2).
|
|
// Start from {(a,2)} => {(a,2), (b,2), (c,3), (d,4)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromRoot2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'b', 1}, {'b', 1}},
|
|
{{'b', 1}, {'c', 2}},
|
|
{{'c', 1}, {'d', 2}}},
|
|
/* StarterLayout */ {{'a', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables,
|
|
(std::set<Node>{{'a', 2}, {'b', 2}, {'c', 3}, {'d', 4}}));
|
|
}
|
|
|
|
// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,1) -> (d,1).
|
|
// Start from {(b,1)} => {(b,1), (c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromLoopNode) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'b', 1}, {'b', 1}},
|
|
{{'b', 1}, {'c', 1}},
|
|
{{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'b', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 3u);
|
|
EXPECT_TRUE(Reachables.count({'b', 1}));
|
|
EXPECT_TRUE(Reachables.count({'c', 1}));
|
|
EXPECT_TRUE(Reachables.count({'d', 1}));
|
|
}
|
|
|
|
// Self-loop: (a,1) -> (b,1) -> (b,1) -> (c,2) -> (d,2).
|
|
// Start from {(b,2)} => {(b,2), (c,3), (d,4)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, SelfLoopFromLoopNode2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */
|
|
{{{'a', 1}, {'b', 1}},
|
|
{{'b', 1}, {'b', 1}},
|
|
{{'b', 1}, {'c', 2}},
|
|
{{'c', 1}, {'d', 2}}},
|
|
/* StarterLayout */ {{'b', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables, (std::set<Node>{{'b', 2}, {'c', 3}, {'d', 4}}));
|
|
}
|
|
|
|
// Multiple starters: (a,1) -> (b,1), (c,1) -> (d,1) (disconnected).
|
|
// Start from {(a,1), (c,1)} => {(a,1), (b,1), (c,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersBothChains) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */ {{{'a', 1}, {'b', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'a', 1}, {'c', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 4u);
|
|
}
|
|
|
|
// Multiple starters: (a,1) -> (b,2), (c,1) -> (d,2).
|
|
// Start from {(a,2), (c,2)} => {(a,2), (b,3), (c,2), (d,3)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersBothChains2) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */ {{{'a', 1}, {'b', 2}}, {{'c', 1}, {'d', 2}}},
|
|
/* StarterLayout */ {{'a', 2}, {'c', 2}}, __LINE__);
|
|
EXPECT_EQ(Reachables,
|
|
(std::set<Node>{{'a', 2}, {'b', 3}, {'c', 2}, {'d', 3}}));
|
|
}
|
|
|
|
// Multiple starters: (a,1) -> (b,1), (c,1) -> (d,1) (disconnected).
|
|
// Start from leaves {(b,1), (d,1)} => {(b,1), (d,1)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, MultipleStartersLeaves) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c', 'd'},
|
|
/* EdgeLayout */ {{{'a', 1}, {'b', 1}}, {{'c', 1}, {'d', 1}}},
|
|
/* StarterLayout */ {{'b', 1}, {'d', 1}}, __LINE__);
|
|
EXPECT_EQ(Reachables.size(), 2u);
|
|
EXPECT_TRUE(Reachables.count({'b', 1}));
|
|
EXPECT_TRUE(Reachables.count({'d', 1}));
|
|
}
|
|
|
|
// Multi-key, same source entity: (a,1) -> (b,1), (a,2) -> (c,1).
|
|
// Start from {(a,3)} => {(a,3), (b,3), (c,2)}
|
|
TEST_F(UnsafeBufferReachableAnalysisTest, MultipleKeysSameEntity) {
|
|
auto Reachables = singlePartition(
|
|
/* EntityDomain */ {'a', 'b', 'c'},
|
|
/* EdgeLayout */ {{{'a', 1}, {'b', 1}}, {{'a', 2}, {'c', 1}}},
|
|
/* StarterLayout */ {{'a', 3}}, __LINE__);
|
|
EXPECT_EQ(Reachables, (std::set<Node>{{'a', 3}, {'b', 3}, {'c', 2}}));
|
|
}
|
|
|
|
} // namespace
|