19 #include <unordered_set> 23 #include "gtest/gtest.h" 28 namespace synchronization_internal {
38 using Edges = std::vector<Edge>;
42 typedef std::map<int, GraphId>
IdMap;
44 auto iter =
id.find(num);
50 std::unordered_set<int> *seen) {
52 if (from == to)
return true;
53 for (
const auto &edge : *edges) {
54 if (edge.from == from) {
57 }
else if (seen->find(edge.to) == seen->end() &&
68 for (
const auto &edge : *edges) {
78 for (
int a : *nodes) {
79 for (
int b : *nodes) {
90 for (
int a : *nodes) {
91 for (
int b : *nodes) {
92 std::unordered_set<int> seen;
104 for (
int a : *nodes) {
105 for (
int b : *nodes) {
116 std::unordered_set<int> seen;
117 for (
const auto &
a : *nodes) {
118 for (
const auto &
b : *nodes) {
122 if (gc_reachable != reachable) {
127 ABSL_RAW_LOG(FATAL,
"gc_reachable %s reachable %s a %d b %d",
128 gc_reachable ?
"true" :
"false",
129 reachable ?
"true" :
"false",
a,
b);
138 for (
const auto &edge : *edges) {
147 for (
const auto &
a : *nodes) {
148 for (
const auto &
b : *nodes) {
154 if (count != edges->size()) {
157 ABSL_RAW_LOG(FATAL,
"edges->size() %zu count %d", edges->size(), count);
169 std::uniform_int_distribution<int> uniform(0, nodes->size()-1);
170 return uniform(*rng);
176 std::uniform_int_distribution<int> uniform(0, edges->size()-1);
177 return uniform(*rng);
183 while (i != edges->size() &&
184 ((*edges)[
i].from != from || (*edges)[
i].to !=
to)) {
187 return i == edges->size()? -1 :
i;
196 static const int kMaxNodes = 7;
197 static const int kDataOffset = 17;
200 RandomEngine rng(testing::UnitTest::GetInstance()->random_seed());
201 std::uniform_int_distribution<int> uniform(0, 5);
203 auto ptr = [](intptr_t
i) {
204 return reinterpret_cast<void*
>(
i + kDataOffset);
207 for (
int iter = 0; iter !=
n; iter++) {
208 for (
const auto &node : nodes) {
209 ASSERT_EQ(graph_cycles.
Ptr(
Get(
id, node)),
ptr(node)) <<
" node " << node;
211 CheckEdges(&nodes, &edges,
id, &graph_cycles);
216 if (nodes.size() < kMaxNodes) {
217 int new_node = next_node++;
220 id[new_node] = new_gnode;
221 ASSERT_EQ(
ptr(new_node), graph_cycles.
Ptr(new_gnode));
222 nodes.push_back(new_node);
227 if (nodes.size() > 0) {
229 int node = nodes[node_index];
230 nodes[node_index] = nodes.back();
233 ASSERT_EQ(graph_cycles.
Ptr(
Get(
id, node)),
nullptr);
236 while (i != edges.size()) {
237 if (edges[i].
from == node || edges[i].
to == node) {
238 edges[
i] = edges.back();
248 if (nodes.size() > 0) {
251 if (
EdgeIndex(&edges, nodes[from], nodes[to]) == -1) {
252 if (graph_cycles.
InsertEdge(
id[nodes[from]],
id[nodes[to]])) {
255 new_edge.
to = nodes[
to];
256 edges.push_back(new_edge);
258 std::unordered_set<int> seen;
259 ASSERT_TRUE(
IsReachable(&edges, nodes[to], nodes[from], &seen))
260 <<
"Edge " << nodes[
to] <<
"->" << nodes[
from];
267 if (edges.size() > 0) {
269 int from = edges[
i].from;
270 int to = edges[
i].to;
271 ASSERT_EQ(i,
EdgeIndex(&edges, from, to));
272 edges[
i] = edges.back();
274 ASSERT_EQ(-1,
EdgeIndex(&edges, from, to));
280 if (nodes.size() > 0) {
284 int path_len = graph_cycles.
FindPath(
id[nodes[from]],
id[nodes[to]],
286 std::unordered_set<int> seen;
287 bool reachable =
IsReachable(&edges, nodes[from], nodes[to], &seen);
290 ASSERT_EQ(path_len != 0, reachable);
291 ASSERT_EQ(path_len != 0, gc_reachable);
295 ASSERT_LE(path_len, kMaxNodes + 1);
297 ASSERT_EQ(
id[nodes[from]], path[0]);
298 ASSERT_EQ(
id[nodes[to]], path[path_len-1]);
299 for (
int i = 1;
i < path_len;
i++) {
300 ASSERT_TRUE(graph_cycles.
HasEdge(path[
i-1], path[
i]));
315 std::bernoulli_distribution one_in_1024(1.0 / 1024);
316 if (one_in_1024(rng)) {
317 CheckEdges(&nodes, &edges,
id, &graph_cycles);
319 for (
int i = 0;
i != 256;
i++) {
320 int new_node = next_node++;
323 id[new_node] = new_gnode;
324 ASSERT_EQ(
ptr(new_node), graph_cycles.
Ptr(new_gnode));
325 for (
const auto &node : nodes) {
326 ASSERT_NE(node, new_node);
328 nodes.push_back(new_node);
330 for (
int i = 0;
i != 256;
i++) {
331 ASSERT_GT(nodes.size(), 0);
333 int node = nodes[node_index];
334 nodes[node_index] = nodes.back();
339 while (j != edges.size()) {
340 if (edges[j].
from == node || edges[j].
to == node) {
341 edges[j] = edges.back();
359 return reinterpret_cast<void*
>(
static_cast<uintptr_t
>(
i));
363 return static_cast<int>(
reinterpret_cast<uintptr_t
>(
ptr));
368 for (
int i = 0;
i < 100;
i++) {
380 for (
int x = 1; x < 25; x++) {
381 EXPECT_TRUE(AddEdge(x, 2*x)) << x;
382 EXPECT_TRUE(AddEdge(x, 3*x)) << x;
387 std::string
Path(
int x,
int y) {
391 for (
int i = 0;
i < np;
i++) {
396 if (!result.empty()) result.push_back(
' ');
398 snprintf(buf,
sizeof(buf),
"%d", Num(g_.
Ptr(path[
i])));
412 EXPECT_FALSE(AddEdge(8, 4));
413 EXPECT_EQ(
"4 8", Path(4, 8));
419 EXPECT_TRUE(AddEdge(16, 9));
421 EXPECT_FALSE(AddEdge(9, 2));
422 EXPECT_EQ(
"2 4 8 16 9", Path(2, 9));
427 ASSERT_TRUE(AddEdge(2, 4));
428 ASSERT_TRUE(AddEdge(4, 6));
429 ASSERT_TRUE(AddEdge(6, 8));
430 ASSERT_TRUE(AddEdge(8, 10));
431 ASSERT_TRUE(AddEdge(10, 12));
432 ASSERT_FALSE(AddEdge(12, 2));
433 EXPECT_EQ(
"2 4 6 8 10 ...", Path(2, 12));
438 ASSERT_TRUE(AddEdge(1, 2));
439 ASSERT_TRUE(AddEdge(2, 3));
440 ASSERT_TRUE(AddEdge(3, 4));
441 ASSERT_TRUE(AddEdge(4, 5));
442 g_.RemoveNode(g_.Ptr(
id_[3]));
444 ASSERT_TRUE(AddEdge(5, 1));
449 for (
int i = 0;
i < N;
i++) {
450 for (
int j = 1; j < N; j++) {
451 ASSERT_TRUE(AddEdge(
i,
i+j));
455 ASSERT_TRUE(AddEdge(2*N-1, 0));
457 ASSERT_FALSE(AddEdge(10, 9));
std::vector< Edge > Edges
static GraphId Get(const IdMap &id, int num)
TEST(GraphCycles, RandomizedTest)
int FindPath(GraphId source, GraphId dest, int max_path_len, GraphId path[]) const
#define ABSL_RAW_LOG(severity,...)
static int EdgeIndex(Edges *edges, int from, int to)
static void PrintGCTransitiveClosure(Nodes *nodes, const IdMap &id, GraphCycles *gc)
bool AddEdge(int x, int y)
std::mt19937_64 RandomEngine
#define ABSL_PREDICT_FALSE(x)
bool IsReachable(GraphId source_node, GraphId dest_node) const
bool InsertEdge(GraphId source_node, GraphId dest_node)
void RemoveEdge(GraphId source_node, GraphId dest_node)
GraphId path[kMaxDeadlockPathLen]
static int Num(void *ptr)
TEST_F(GraphCyclesTest, NoCycle)
static void CheckTransitiveClosure(Nodes *nodes, Edges *edges, const IdMap &id, GraphCycles *gc)
static int RandomNode(RandomEngine *rng, Nodes *nodes)
bool HasEdge(GraphId source_node, GraphId dest_node) const
static void PrintGCEdges(Nodes *nodes, const IdMap &id, GraphCycles *gc)
static void PrintEdges(Edges *edges)
#define ABSL_ARRAYSIZE(array)
bool CheckInvariants() const
void RemoveNode(void *ptr)
static void PrintTransitiveClosure(Nodes *nodes, Edges *edges)
static void CheckEdges(Nodes *nodes, Edges *edges, const IdMap &id, GraphCycles *gc)
std::string Path(int x, int y)
static void CheckInvariants(const GraphCycles &gc)
static bool IsReachable(Edges *edges, int from, int to, std::unordered_set< int > *seen)
static int RandomEdge(RandomEngine *rng, Edges *edges)
std::map< int, GraphId > IdMap