memgraph/tests/unit/graph_db_accessor.cpp
florijan 0a33a555a6 GraphDdAccessor and KeyIndex API upgrades. Test refactoring
Summary:
The GraphDbAccessor and KeyIndex APIs can now also return records for the current transaction+command graph state. This is necessary to correctly implement MERGE. The new logic is has increased the MVCC+Accessor related chaos and should be revised when refactoring MVCC (as planned).

Previous index testing was separated into VertexIndex and EdgeIndex testing. This is inappropriate since most of the logic is exaclty the same. Also it was not clearly defined what gets tested via the GraphDbAccessor API, and what directly through the KeyIndex API. This has also been refactored, but it needs additional work (Gleich).

Reviewers: buda, dgleich

Reviewed By: buda, dgleich

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D306
2017-05-03 15:42:04 +02:00

334 lines
10 KiB
C++

#include "gtest/gtest.h"
#include "database/graph_db.hpp"
#include "database/graph_db_accessor.hpp"
#include "dbms/dbms.hpp"
#include "storage/edge_accessor.hpp"
#include "storage/vertex_accessor.hpp"
template <typename TIterable>
auto Count(TIterable iterable) {
return std::distance(iterable.begin(), iterable.end());
}
TEST(GraphDbAccessorTest, DbmsCreateDefault) {
Dbms dbms;
auto accessor = dbms.active();
EXPECT_EQ(accessor->name(), "default");
}
TEST(GraphDbAccessorTest, InsertVertex) {
Dbms dbms;
auto accessor = dbms.active();
EXPECT_EQ(Count(accessor->vertices()), 0);
accessor->insert_vertex();
EXPECT_EQ(Count(accessor->vertices()), 0);
EXPECT_EQ(Count(accessor->vertices(true)), 1);
accessor->advance_command();
EXPECT_EQ(Count(accessor->vertices()), 1);
accessor->insert_vertex();
EXPECT_EQ(Count(accessor->vertices()), 1);
EXPECT_EQ(Count(accessor->vertices(true)), 2);
accessor->advance_command();
EXPECT_EQ(Count(accessor->vertices()), 2);
}
TEST(GraphDbAccessorTest, RemoveVertexSameTransaction) {
Dbms dbms;
auto accessor = dbms.active();
EXPECT_EQ(Count(accessor->vertices()), 0);
auto va1 = accessor->insert_vertex();
accessor->advance_command();
EXPECT_EQ(Count(accessor->vertices()), 1);
EXPECT_TRUE(accessor->remove_vertex(va1));
EXPECT_EQ(Count(accessor->vertices()), 1);
EXPECT_EQ(Count(accessor->vertices(true)), 0);
accessor->advance_command();
EXPECT_EQ(Count(accessor->vertices()), 0);
EXPECT_EQ(Count(accessor->vertices(true)), 0);
}
TEST(GraphDbAccessorTest, RemoveVertexDifferentTransaction) {
Dbms dbms;
// first transaction creates a vertex
auto accessor1 = dbms.active();
accessor1->insert_vertex();
accessor1->commit();
// second transaction checks that it sees it, and deletes it
auto accessor2 = dbms.active();
EXPECT_EQ(Count(accessor2->vertices()), 1);
EXPECT_EQ(Count(accessor2->vertices(true)), 1);
for (auto vertex_accessor : accessor2->vertices())
accessor2->remove_vertex(vertex_accessor);
accessor2->commit();
// third transaction checks that it does not see the vertex
auto accessor3 = dbms.active();
EXPECT_EQ(Count(accessor3->vertices()), 0);
EXPECT_EQ(Count(accessor3->vertices(true)), 0);
}
TEST(GraphDbAccessorTest, InsertEdge) {
Dbms dbms;
auto dba = dbms.active();
auto va1 = dba->insert_vertex();
auto va2 = dba->insert_vertex();
dba->advance_command();
EXPECT_EQ(va1.in_degree(), 0);
EXPECT_EQ(va1.out_degree(), 0);
EXPECT_EQ(va2.in_degree(), 0);
EXPECT_EQ(va2.out_degree(), 0);
// setup (v1) - [:likes] -> (v2)
dba->insert_edge(va1, va2, dba->edge_type("likes"));
EXPECT_EQ(Count(dba->edges()), 0);
EXPECT_EQ(Count(dba->edges(true)), 1);
dba->advance_command();
EXPECT_EQ(Count(dba->edges()), 1);
EXPECT_EQ(Count(dba->edges(true)), 1);
EXPECT_EQ(va1.out().begin()->to(), va2);
EXPECT_EQ(va2.in().begin()->from(), va1);
EXPECT_EQ(va1.in_degree(), 0);
EXPECT_EQ(va1.out_degree(), 1);
EXPECT_EQ(va2.in_degree(), 1);
EXPECT_EQ(va2.out_degree(), 0);
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
auto va3 = dba->insert_vertex();
dba->insert_edge(va3, va2, dba->edge_type("hates"));
EXPECT_EQ(Count(dba->edges(false)), 1);
EXPECT_EQ(Count(dba->edges(true)), 2);
dba->advance_command();
EXPECT_EQ(Count(dba->edges()), 2);
EXPECT_EQ(va3.out().begin()->to(), va2);
EXPECT_EQ(va1.in_degree(), 0);
EXPECT_EQ(va1.out_degree(), 1);
EXPECT_EQ(va2.in_degree(), 2);
EXPECT_EQ(va2.out_degree(), 0);
EXPECT_EQ(va3.in_degree(), 0);
EXPECT_EQ(va3.out_degree(), 1);
}
TEST(GraphDbAccessorTest, RemoveEdge) {
Dbms dbms;
auto dba = dbms.active();
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
auto va1 = dba->insert_vertex();
auto va2 = dba->insert_vertex();
auto va3 = dba->insert_vertex();
dba->insert_edge(va1, va2, dba->edge_type("likes"));
dba->insert_edge(va3, va2, dba->edge_type("hates"));
dba->advance_command();
EXPECT_EQ(Count(dba->edges()), 2);
EXPECT_EQ(Count(dba->edges(true)), 2);
// remove all [:hates] edges
for (auto edge : dba->edges())
if (edge.edge_type() == dba->edge_type("hates")) dba->remove_edge(edge);
EXPECT_EQ(Count(dba->edges()), 2);
EXPECT_EQ(Count(dba->edges(true)), 1);
// current state: (v1) - [:likes] -> (v2), (v3)
dba->advance_command();
EXPECT_EQ(Count(dba->edges()), 1);
EXPECT_EQ(Count(dba->edges(true)), 1);
EXPECT_EQ(Count(dba->vertices()), 3);
EXPECT_EQ(Count(dba->vertices(true)), 3);
for (auto edge : dba->edges()) {
EXPECT_EQ(edge.edge_type(), dba->edge_type("likes"));
auto v1 = edge.from();
auto v2 = edge.to();
// ensure correct connectivity for all the vertices
for (auto vertex : dba->vertices()) {
if (vertex == v1) {
EXPECT_EQ(vertex.in_degree(), 0);
EXPECT_EQ(vertex.out_degree(), 1);
} else if (vertex == v2) {
EXPECT_EQ(vertex.in_degree(), 1);
EXPECT_EQ(vertex.out_degree(), 0);
} else {
EXPECT_EQ(vertex.in_degree(), 0);
EXPECT_EQ(vertex.out_degree(), 0);
}
}
}
}
TEST(GraphDbAccessorTest, DetachRemoveVertex) {
Dbms dbms;
auto dba = dbms.active();
// setup (v0)- []->(v1)<-[]-(v2)<-[]-(v3)
std::vector<VertexAccessor> vertices;
for (int i = 0; i < 4; ++i) vertices.emplace_back(dba->insert_vertex());
auto edge_type = dba->edge_type("type");
dba->insert_edge(vertices[0], vertices[1], edge_type);
dba->insert_edge(vertices[2], vertices[1], edge_type);
dba->insert_edge(vertices[3], vertices[2], edge_type);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
// ensure that plain remove does NOT work
EXPECT_EQ(Count(dba->vertices()), 4);
EXPECT_EQ(Count(dba->edges()), 3);
EXPECT_FALSE(dba->remove_vertex(vertices[0]));
EXPECT_FALSE(dba->remove_vertex(vertices[1]));
EXPECT_FALSE(dba->remove_vertex(vertices[2]));
EXPECT_EQ(Count(dba->vertices()), 4);
EXPECT_EQ(Count(dba->edges()), 3);
dba->detach_remove_vertex(vertices[2]);
EXPECT_EQ(Count(dba->vertices()), 4);
EXPECT_EQ(Count(dba->vertices(true)), 3);
EXPECT_EQ(Count(dba->edges()), 3);
EXPECT_EQ(Count(dba->edges(true)), 1);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), 3);
EXPECT_EQ(Count(dba->edges()), 1);
EXPECT_TRUE(dba->remove_vertex(vertices[3]));
EXPECT_EQ(Count(dba->vertices(true)), 2);
EXPECT_EQ(Count(dba->vertices()), 3);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), 2);
EXPECT_EQ(Count(dba->edges()), 1);
for (auto va : dba->vertices()) EXPECT_FALSE(dba->remove_vertex(va));
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), 2);
EXPECT_EQ(Count(dba->edges()), 1);
for (auto va : dba->vertices()) {
EXPECT_FALSE(dba->remove_vertex(va));
dba->detach_remove_vertex(va);
break;
}
EXPECT_EQ(Count(dba->vertices(true)), 1);
EXPECT_EQ(Count(dba->vertices()), 2);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), 1);
EXPECT_EQ(Count(dba->edges()), 0);
// remove the last vertex, it has no connections
// so that should work
for (auto va : dba->vertices()) EXPECT_TRUE(dba->remove_vertex(va));
dba->advance_command();
EXPECT_EQ(Count(dba->vertices()), 0);
EXPECT_EQ(Count(dba->edges()), 0);
}
TEST(GraphDbAccessorTest, DetachRemoveVertexMultiple) {
// This test checks that we can detach remove the
// same vertex / edge multiple times
Dbms dbms;
auto dba = dbms.active();
// setup: make a fully connected N graph
// with cycles too!
int N = 7;
std::vector<VertexAccessor> vertices;
auto edge_type = dba->edge_type("edge");
for (int i = 0; i < N; ++i) vertices.emplace_back(dba->insert_vertex());
for (int j = 0; j < N; ++j)
for (int k = 0; k < N; ++k)
dba->insert_edge(vertices[j], vertices[k], edge_type);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), N);
EXPECT_EQ(Count(dba->edges()), N * N);
// detach delete one edge
dba->detach_remove_vertex(vertices[0]);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), N - 1);
EXPECT_EQ(Count(dba->edges()), (N - 1) * (N - 1));
// detach delete two neighboring edges
dba->detach_remove_vertex(vertices[1]);
dba->detach_remove_vertex(vertices[2]);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), N - 3);
EXPECT_EQ(Count(dba->edges()), (N - 3) * (N - 3));
// detach delete everything, buwahahahaha
for (int l = 3; l < N; ++l) dba->detach_remove_vertex(vertices[l]);
dba->advance_command();
for (auto &vertex : vertices) vertex.Reconstruct();
EXPECT_EQ(Count(dba->vertices()), 0);
EXPECT_EQ(Count(dba->edges()), 0);
}
TEST(GraphDbAccessorTest, Labels) {
Dbms dbms;
auto dba1 = dbms.active();
GraphDbTypes::Label label_friend = dba1->label("friend");
EXPECT_EQ(label_friend, dba1->label("friend"));
EXPECT_NE(label_friend, dba1->label("friend2"));
EXPECT_EQ(dba1->label_name(label_friend), "friend");
// test that getting labels through a different accessor works
EXPECT_EQ(label_friend, dbms.active()->label("friend"));
EXPECT_NE(label_friend, dbms.active()->label("friend2"));
}
TEST(GraphDbAccessorTest, EdgeTypes) {
Dbms dbms;
auto dba1 = dbms.active();
GraphDbTypes::EdgeType edge_type = dba1->edge_type("likes");
EXPECT_EQ(edge_type, dba1->edge_type("likes"));
EXPECT_NE(edge_type, dba1->edge_type("hates"));
EXPECT_EQ(dba1->edge_type_name(edge_type), "likes");
// test that getting labels through a different accessor works
EXPECT_EQ(edge_type, dbms.active()->edge_type("likes"));
EXPECT_NE(edge_type, dbms.active()->edge_type("hates"));
}
TEST(GraphDbAccessorTest, Properties) {
Dbms dbms;
auto dba1 = dbms.active();
GraphDbTypes::EdgeType prop = dba1->property("name");
EXPECT_EQ(prop, dba1->property("name"));
EXPECT_NE(prop, dba1->property("surname"));
EXPECT_EQ(dba1->property_name(prop), "name");
// test that getting labels through a different accessor works
EXPECT_EQ(prop, dbms.active()->property("name"));
EXPECT_NE(prop, dbms.active()->property("surname"));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
// ::testing::GTEST_FLAG(filter) = "*.DetachRemoveVertex";
return RUN_ALL_TESTS();
}