blob: 201dcdf73ce9c9f5eb0e04eef774a5f2c396b53d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
**/
#include "transaction/CycleDetector.hpp"
#include <cstdint>
#include <memory>
#include <stack>
#include <unordered_set>
#include <utility>
#include <vector>
#include "transaction/DirectedGraph.hpp"
#include "transaction/Transaction.hpp"
#include "gtest/gtest.h"
namespace quickstep {
namespace transaction {
class CycleDetectorTest : public testing::Test {
protected:
const std::uint64_t kNumberOfTransactions = 12;
CycleDetectorTest()
: wait_for_graph_(std::make_unique<DirectedGraph>()) {
}
virtual void SetUp() {
std::vector<transaction_id> transactions(kNumberOfTransactions);
for (std::uint64_t i = 0; i < kNumberOfTransactions; ++i) {
transactions.push_back(transaction_id(i));
}
std::vector<DirectedGraph::node_id> node_ids;
for (std::uint64_t i = 0; i < kNumberOfTransactions; ++i) {
node_ids.push_back(wait_for_graph_->addNodeUnchecked(transactions[i]));
}
}
void initializeCycleDetector() {
for (const auto &edge : edges_) {
wait_for_graph_->addEdgeUnchecked(edge.first, edge.second);
}
cycle_detector_.reset(new CycleDetector(wait_for_graph_.get()));
}
void checkVictims(
const std::unordered_set<DirectedGraph::node_id> &expected_victims) {
const std::vector<DirectedGraph::node_id> victims =
cycle_detector_->chooseVictimsToBreakCycle();
std::unordered_set<DirectedGraph::node_id> remaining_nodes;
for (DirectedGraph::node_id node = 0; node < wait_for_graph_->getNumNodes();
++node) {
if (std::find(victims.begin(), victims.end(), node) == victims.end()) {
// Node is not in victims, then insert it to remaining set.
remaining_nodes.insert(node);
}
}
for (const auto node : remaining_nodes) {
ASSERT_FALSE(isSelfReachableNode(node, remaining_nodes));
}
}
bool isSelfReachableNode(
const DirectedGraph::node_id start_node,
const std::unordered_set<DirectedGraph::node_id> &node_set) {
std::unordered_set<DirectedGraph::node_id> marked_nodes;
std::stack<DirectedGraph::node_id> to_be_visied_nodes;
const std::vector<DirectedGraph::node_id> neighbors_of_start_node =
wait_for_graph_->getAdjacentNodes(start_node);
for (const auto node : neighbors_of_start_node) {
marked_nodes.insert(node);
to_be_visied_nodes.push(node);
}
while (!to_be_visied_nodes.empty()) {
const DirectedGraph::node_id current_node = to_be_visied_nodes.top();
to_be_visied_nodes.pop();
if (current_node == start_node) {
return true;
}
if (node_set.count(current_node) == 1 &&
marked_nodes.count(current_node) == 0) {
// Means, we did not visited this node yet, and it is in the node set,
// so we should process it (mark it and push all of its neighbors
// into stack).
marked_nodes.insert(current_node);
const auto neighbors = wait_for_graph_->getAdjacentNodes(current_node);
for (const auto neighbor : neighbors) {
to_be_visied_nodes.push(neighbor);
}
}
}
return false;
}
std::vector<std::pair<DirectedGraph::node_id, DirectedGraph::node_id>> edges_;
std::unique_ptr<DirectedGraph> wait_for_graph_;
std::unique_ptr<CycleDetector> cycle_detector_;
};
TEST_F(CycleDetectorTest, Interleaving) {
edges_ = {{0, 1},
{1, 0}};
initializeCycleDetector();
std::unordered_set<DirectedGraph::node_id> expected_victims = {1};
checkVictims(expected_victims);
}
TEST_F(CycleDetectorTest, MultipleCycle) {
// This edge contains lots of cycles of degree 1, 2 and 3.
edges_ = {{0, 1},
{1, 2}, {1, 3}, {1, 4},
{2, 5},
{3, 4}, {3, 6},
{4, 1}, {4, 5}, {4, 6},
{5, 2}, {5, 7},
{6, 7}, {6, 9},
{7, 6},
{8, 6},
{9, 8}, {9, 10},
{10, 11},
{11, 9}};
initializeCycleDetector();
std::unordered_set<DirectedGraph::node_id> expected_victims
= {4, 5, 7, 8, 9, 10, 11};
checkVictims(expected_victims);
}
} // namespace transaction
} // namespace quickstep