1
0
Fork 0
arangodb/tests/Graph/KShortestPathsFinder.cpp

264 lines
9.1 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2019-2019 ArangoDB GmbH, Cologne, Germany
///
/// Licensed 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Markus Pfeiffer
////////////////////////////////////////////////////////////////////////////////
#include "gtest/gtest.h"
#include "fakeit.hpp"
#include "Aql/AqlFunctionFeature.h"
#include "Aql/Ast.h"
#include "Aql/ExecutionPlan.h"
#include "Aql/OptimizerRulesFeature.h"
#include "Aql/Query.h"
#include "ClusterEngine/ClusterEngine.h"
#include "Graph/KShortestPathsFinder.h"
#include "Graph/ShortestPathOptions.h"
#include "Graph/ShortestPathResult.h"
#include "Random/RandomGenerator.h"
#include "RestServer/AqlFeature.h"
#include "RestServer/DatabaseFeature.h"
#include "RestServer/DatabasePathFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "RestServer/TraverserEngineRegistryFeature.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "Transaction/Methods.h"
#include "Transaction/StandaloneContext.h"
#include "Utils/SingleCollectionTransaction.h"
#include "VocBase/LogicalCollection.h"
#include <velocypack/Builder.h>
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h>
// test setup
#include "../Mocks/Servers.h"
#include "../Mocks/StorageEngineMock.h"
#include "GraphTestTools.h"
#include "IResearch/common.h"
using namespace arangodb;
using namespace arangodb::aql;
using namespace arangodb::graph;
using namespace arangodb::velocypack;
namespace arangodb {
namespace tests {
namespace graph {
class KShortestPathsFinderTest : public ::testing::Test {
protected:
GraphTestSetup s;
MockGraphDatabase gdb;
arangodb::aql::Query* query;
arangodb::graph::ShortestPathOptions* spo;
KShortestPathsFinder* finder;
KShortestPathsFinderTest() : gdb("testVocbase") {
gdb.addVertexCollection("v", 100);
gdb.addEdgeCollection(
"e", "v",
{{1, 2}, {2, 3}, {3, 4}, {5, 4}, {6, 5}, {7, 6}, {8, 7},
{1, 10}, {10, 11}, {11, 12}, {12, 4}, {12, 5}, {21, 22}, {22, 23},
{23, 24}, {24, 25}, {21, 26}, {26, 27}, {27, 28}, {28, 25}, {30, 31},
{31, 32}, {32, 33}, {33, 34}, {34, 35}, {32, 30}, {33, 35}, {40, 41},
{41, 42}, {41, 43}, {42, 44}, {43, 44}, {44, 45}, {45, 46}, {46, 47},
{48, 47}, {49, 47}, {50, 47}, {48, 46}, {50, 46}, {50, 47}, {48, 46},
{50, 46}, {40, 60}, {60, 61}, {61, 62}, {62, 63}, {63, 64}, {64, 47},
{70, 71}, {70, 71}, {70, 71}});
query = gdb.getQuery("RETURN 1");
spo = gdb.getShortestPathOptions(query);
finder = new KShortestPathsFinder(*spo);
}
~KShortestPathsFinderTest() { delete finder; }
};
TEST_F(KShortestPathsFinderTest, path_from_vertex_to_itself) {
auto start = velocypack::Parser::fromJson("\"v/0\"");
auto end = velocypack::Parser::fromJson("\"v/0\"");
ShortestPathResult result;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
ASSERT_TRUE(false == finder->getNextPathShortestPathResult(result));
}
TEST_F(KShortestPathsFinderTest, no_path_exists) {
auto start = velocypack::Parser::fromJson("\"v/0\"");
auto end = velocypack::Parser::fromJson("\"v/1\"");
ShortestPathResult result;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(false == finder->getNextPathShortestPathResult(result));
// Repeat to see that we keep returning false and don't crash
ASSERT_TRUE(false == finder->getNextPathShortestPathResult(result));
}
TEST_F(KShortestPathsFinderTest, path_of_length_1) {
auto start = velocypack::Parser::fromJson("\"v/1\"");
auto end = velocypack::Parser::fromJson("\"v/2\"");
ShortestPathResult result;
std::string msgs;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
auto cpr = checkPath(spo, result, {"1", "2"}, {{}, {"v/1", "v/2"}}, msgs);
ASSERT_TRUE(cpr) << msgs;
}
TEST_F(KShortestPathsFinderTest, path_of_length_4) {
auto start = velocypack::Parser::fromJson("\"v/1\"");
auto end = velocypack::Parser::fromJson("\"v/4\"");
ShortestPathResult result;
std::string msgs;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
auto cpr = checkPath(spo, result, {"1", "2", "3", "4"},
{{}, {"v/1", "v/2"}, {"v/2", "v/3"}, {"v/3", "v/4"}}, msgs);
ASSERT_TRUE(cpr) << msgs;
}
TEST_F(KShortestPathsFinderTest, path_of_length_5_with_loops_to_start_end) {
auto start = velocypack::Parser::fromJson("\"v/30\"");
auto end = velocypack::Parser::fromJson("\"v/35\"");
ShortestPathResult result;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
ASSERT_TRUE(result.length() == 5);
}
TEST_F(KShortestPathsFinderTest, two_paths_of_length_5) {
auto start = velocypack::Parser::fromJson("\"v/21\"");
auto end = velocypack::Parser::fromJson("\"v/25\"");
ShortestPathResult result;
std::string msgs;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
auto cpr = checkPath(spo, result, {"21", "22", "23", "24", "25"},
{{},
{"v/21", "v/22"},
{"v/22", "v/23"},
{"v/23", "v/24"},
{"v/24", "v/25"}},
msgs) ||
checkPath(spo, result, {"21", "26", "27", "28", "25"},
{{},
{"v/21", "v/26"},
{"v/26", "v/27"},
{"v/27", "v/28"},
{"v/28", "v/25"}},
msgs);
ASSERT_TRUE(cpr) << msgs;
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
cpr = checkPath(spo, result, {"21", "22", "23", "24", "25"},
{{},
{"v/21", "v/22"},
{"v/22", "v/23"},
{"v/23", "v/24"},
{"v/24", "v/25"}},
msgs) ||
checkPath(spo, result, {"21", "26", "27", "28", "25"},
{{},
{"v/21", "v/26"},
{"v/26", "v/27"},
{"v/27", "v/28"},
{"v/28", "v/25"}},
msgs);
ASSERT_TRUE(cpr) << msgs;
}
TEST_F(KShortestPathsFinderTest, many_edges_between_two_nodes) {
auto start = velocypack::Parser::fromJson("\"v/70\"");
auto end = velocypack::Parser::fromJson("\"v/71\"");
ShortestPathResult result;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
ASSERT_TRUE(true == finder->getNextPathShortestPathResult(result));
ASSERT_TRUE(false == finder->getNextPathShortestPathResult(result));
}
class KShortestPathsFinderTestWeights : public ::testing::Test {
protected:
GraphTestSetup s;
MockGraphDatabase gdb;
arangodb::aql::Query* query;
arangodb::graph::ShortestPathOptions* spo;
KShortestPathsFinder* finder;
KShortestPathsFinderTestWeights() : gdb("testVocbase") {
gdb.addVertexCollection("v", 10);
gdb.addEdgeCollection("e", "v",
{{1, 2, 10},
{1, 3, 10},
{1, 10, 100},
{2, 4, 10},
{3, 4, 20},
{7, 3, 10},
{8, 3, 10},
{9, 3, 10}});
query = gdb.getQuery("RETURN 1");
spo = gdb.getShortestPathOptions(query);
spo->weightAttribute = "cost";
finder = new KShortestPathsFinder(*spo);
}
~KShortestPathsFinderTestWeights() { delete finder; }
};
TEST_F(KShortestPathsFinderTestWeights, diamond_path) {
auto start = velocypack::Parser::fromJson("\"v/1\"");
auto end = velocypack::Parser::fromJson("\"v/4\"");
ShortestPathResult result;
std::string msgs;
finder->startKShortestPathsTraversal(start->slice(), end->slice());
ASSERT_TRUE(finder->getNextPathShortestPathResult(result));
auto cpr = checkPath(spo, result, {"1", "2", "4"},
{{}, {"v/1", "v/2"}, {"v/2", "v/4"}}, msgs);
ASSERT_TRUE(cpr) << msgs;
}
} // namespace graph
} // namespace tests
} // namespace arangodb