blob: 9901ea47d74db77d432794db73730d88e0b30083 [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 <cstdio>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include "./util.h"
#include "graphar/api/info.h"
#include "graphar/fwd.h"
#include "graphar/status.h"
#include <catch2/catch_test_macros.hpp>
namespace graphar {
TEST_CASE_METHOD(GlobalFixture, "InfoVersion") {
InfoVersion info_version(1);
REQUIRE(info_version.version() == 1);
REQUIRE(info_version.user_define_types() == std::vector<std::string>({}));
REQUIRE(info_version.ToString() == "gar/v1");
REQUIRE(info_version.CheckType("int32") == true);
REQUIRE(info_version.CheckType("date32") == false);
InfoVersion info_version_2(1, {"t1", "t2"});
REQUIRE(info_version_2.version() == 1);
REQUIRE(info_version_2.user_define_types() ==
std::vector<std::string>({"t1", "t2"}));
REQUIRE(info_version_2.ToString() == "gar/v1 (t1,t2)");
REQUIRE(info_version_2.CheckType("t1") == true);
// raise error if version is not 1
CHECK_THROWS_AS(InfoVersion(2), std::invalid_argument);
SECTION("Parse") {
std::string version_str = "gar/v1 (t1,t2)";
auto info_version_result = InfoVersion::Parse(version_str);
REQUIRE(!info_version_result.has_error());
auto& info_version_3 = info_version_result.value();
REQUIRE(info_version_3->version() == 1);
REQUIRE(info_version_3->user_define_types() ==
std::vector<std::string>({"t1", "t2"}));
REQUIRE(info_version_3->ToString() == version_str);
REQUIRE(info_version_3->CheckType("t1") == true);
}
}
TEST_CASE_METHOD(GlobalFixture, "Property") {
Property p0("p0", int32(), true);
Property p1("p1", int32(), false);
REQUIRE(p0.name == "p0");
REQUIRE(p0.type->ToTypeName() == int32()->ToTypeName());
REQUIRE(p0.is_primary == true);
REQUIRE(p0.is_nullable == false);
REQUIRE(p1.is_primary == false);
REQUIRE(p1.is_nullable == true);
}
TEST_CASE_METHOD(GlobalFixture, "PropertyGroup") {
Property p0("p0", int32(), true);
Property p1("p1", int32(), false, true, Cardinality::SINGLE);
Property p2("p2", string(), false, true, Cardinality::LIST);
Property p3("p3", float32(), false, true, Cardinality::SET);
Property p4("p4", float64(), false);
PropertyGroup pg0({p0, p1}, FileType::CSV, "p0_and_p1/");
PropertyGroup pg1({p2, p3, p4}, FileType::PARQUET);
SECTION("Properties") {
REQUIRE(pg0.GetProperties().size() == 2);
REQUIRE(pg1.GetProperties().size() == 3);
REQUIRE(pg0.HasProperty("p0") == true);
REQUIRE(pg0.HasProperty("p2") == false);
REQUIRE(pg1.HasProperty("p2") == true);
REQUIRE(pg1.HasProperty("p0") == false);
auto& p = pg0.GetProperties()[0];
REQUIRE(p.name == "p0");
REQUIRE(p.type->ToTypeName() == int32()->ToTypeName());
REQUIRE(p.is_primary == true);
REQUIRE(p.is_nullable == false);
// cardinality
REQUIRE(p0.cardinality == Cardinality::SINGLE);
REQUIRE(p1.cardinality == Cardinality::SINGLE);
REQUIRE(p2.cardinality == Cardinality::LIST);
REQUIRE(p3.cardinality == Cardinality::SET);
}
SECTION("FileType") {
REQUIRE(pg0.GetFileType() == FileType::CSV);
REQUIRE(pg1.GetFileType() == FileType::PARQUET);
}
SECTION("Prefix") {
REQUIRE(pg0.GetPrefix() == "p0_and_p1/");
REQUIRE(pg1.GetPrefix() == "p2_p3_p4/");
}
SECTION("IsValidate") {
REQUIRE(pg0.IsValidated() == true);
REQUIRE(pg1.IsValidated() == true);
Property invalid_p0("invalid", nullptr, false);
Property invalid_p1("", int32(), false);
PropertyGroup invalid_pg0({invalid_p0}, FileType::CSV);
PropertyGroup invalid_pg1({invalid_p1}, FileType::CSV);
PropertyGroup invalid_pg2({p0, p0}, FileType::PARQUET);
PropertyGroup invalid_pg3({}, FileType::CSV, "empty/");
REQUIRE(invalid_pg0.IsValidated() == false);
REQUIRE(invalid_pg1.IsValidated() == false);
REQUIRE(invalid_pg2.IsValidated() == false);
REQUIRE(invalid_pg3.IsValidated() == false);
}
SECTION("CreatePropertyGroup") {
auto pg2 = CreatePropertyGroup({p0, p1}, FileType::CSV, "p0_and_p1/");
REQUIRE(*pg2.get() == pg0);
REQUIRE(!(pg0 == pg1));
// not allow empty property group
auto pg3 = CreatePropertyGroup({}, FileType::PARQUET);
REQUIRE(pg3 == nullptr);
}
SECTION("Ostream") {
std::stringstream ss;
ss << pg0;
REQUIRE(ss.str() == "p0_p1");
ss.str("");
ss << pg1;
REQUIRE(ss.str() == "p2_p3_p4");
}
}
TEST_CASE_METHOD(GlobalFixture, "AdjacentList") {
AdjacentList adj_list0(AdjListType::unordered_by_source, FileType::CSV,
"adj_list0/");
AdjacentList adj_list1(AdjListType::ordered_by_source, FileType::PARQUET);
SECTION("AdjListType") {
REQUIRE(adj_list0.GetType() == AdjListType::unordered_by_source);
REQUIRE(adj_list1.GetType() == AdjListType::ordered_by_source);
}
SECTION("FileType") {
REQUIRE(adj_list0.GetFileType() == FileType::CSV);
REQUIRE(adj_list1.GetFileType() == FileType::PARQUET);
}
SECTION("Prefix") {
REQUIRE(adj_list0.GetPrefix() == "adj_list0/");
REQUIRE(adj_list1.GetPrefix() == "ordered_by_source/");
}
SECTION("IsValidate") {
REQUIRE(adj_list0.IsValidated() == true);
REQUIRE(adj_list1.IsValidated() == true);
}
SECTION("CreateAdjacentList") {
auto adj_list2 = CreateAdjacentList(AdjListType::unordered_by_source,
FileType::CSV, "unordered_by_source/");
REQUIRE(adj_list2->GetType() == AdjListType::unordered_by_source);
REQUIRE(adj_list2->GetFileType() == FileType::CSV);
REQUIRE(adj_list2->GetPrefix() == "unordered_by_source/");
}
}
TEST_CASE_METHOD(GlobalFixture, "VertexInfo") {
std::string type = "test_vertex";
int chunk_size = 100;
auto version = std::make_shared<InfoVersion>(1);
auto pg = CreatePropertyGroup(
{Property("p0", int32(), true),
Property("p1", string(), false, true, Cardinality::LIST),
Property("p2", string(), false, true, Cardinality::SET)},
FileType::PARQUET, "p0_p1/");
auto vertex_info =
CreateVertexInfo(type, chunk_size, {pg}, {}, "test_vertex", version);
SECTION("Basics") {
REQUIRE(vertex_info->GetType() == type);
REQUIRE(vertex_info->GetChunkSize() == chunk_size);
REQUIRE(vertex_info->GetPrefix() == "test_vertex");
REQUIRE(vertex_info->version()->ToString() == "gar/v1");
}
SECTION("PropertyGroup") {
REQUIRE(vertex_info->PropertyGroupNum() == 1);
REQUIRE(*vertex_info->GetPropertyGroupByIndex(0) == *pg);
REQUIRE(vertex_info->HasProperty("p0") == true);
REQUIRE(vertex_info->HasPropertyGroup(pg) == true);
REQUIRE(*vertex_info->GetPropertyGroup("p0") == *pg);
REQUIRE(vertex_info->GetPropertyGroups().size() == 1);
REQUIRE(*(vertex_info->GetPropertyGroups()[0]) == *pg);
REQUIRE(vertex_info->GetPropertyType("p0").value()->ToTypeName() ==
int32()->ToTypeName());
REQUIRE(vertex_info->IsPrimaryKey("p0") == true);
REQUIRE(vertex_info->IsPrimaryKey("p1") == false);
REQUIRE(vertex_info->IsNullableKey("p0") == false);
REQUIRE(vertex_info->IsNullableKey("p1") == true);
REQUIRE(vertex_info->GetPropertyCardinality("p0").value() ==
Cardinality::SINGLE);
REQUIRE(vertex_info->GetPropertyCardinality("p1").value() ==
Cardinality::LIST);
REQUIRE(vertex_info->GetPropertyCardinality("p2").value() ==
Cardinality::SET);
REQUIRE(vertex_info->HasProperty("not_exist") == false);
REQUIRE(vertex_info->IsPrimaryKey("not_exist") == false);
REQUIRE(vertex_info->HasPropertyGroup(nullptr) == false);
}
SECTION("Path") {
REQUIRE(vertex_info->GetPathPrefix(pg).value() == "test_vertex/p0_p1/");
REQUIRE(vertex_info->GetFilePath(pg, 0).value() ==
"test_vertex/p0_p1/chunk0");
REQUIRE(vertex_info->GetVerticesNumFilePath().value() ==
"test_vertex/vertex_count");
}
SECTION("IsValidate") {
REQUIRE(vertex_info->IsValidated() == true);
auto invalid_pg = CreatePropertyGroup(
{Property("p0", list(string()), true, false, Cardinality::SET)},
FileType::CSV);
auto invalid_vertex_info0 = CreateVertexInfo(type, chunk_size, {invalid_pg},
{}, "test_vertex/", version);
REQUIRE(invalid_vertex_info0->IsValidated() == false);
invalid_pg =
CreatePropertyGroup({Property("p0", nullptr, true)}, FileType::CSV);
invalid_vertex_info0 = CreateVertexInfo(type, chunk_size, {invalid_pg}, {},
"test_vertex/", version);
REQUIRE(invalid_vertex_info0->IsValidated() == false);
VertexInfo invalid_vertex_info1("", chunk_size, {pg}, {}, "test_vertex/",
version);
REQUIRE(invalid_vertex_info1.IsValidated() == false);
VertexInfo invalid_vertex_info2(type, 0, {pg}, {}, "test_vertex/", version);
REQUIRE(invalid_vertex_info2.IsValidated() == false);
// check if prefix empty
auto vertex_info_empty_prefix =
CreateVertexInfo(type, chunk_size, {pg}, {}, "", version);
REQUIRE(vertex_info_empty_prefix->IsValidated() == true);
}
SECTION("CreateVertexInfo") {
auto vertex_info3 =
CreateVertexInfo("", chunk_size, {pg}, {}, "test_vertex/");
REQUIRE(vertex_info3 == nullptr);
auto vertex_info4 = CreateVertexInfo(type, 0, {pg}, {}, "test_vertex/");
REQUIRE(vertex_info4 == nullptr);
}
SECTION("Dump") {
auto dump_result = vertex_info->Dump();
REQUIRE(dump_result.status().ok());
std::string expected = R"(chunk_size: 100
prefix: test_vertex
property_groups:
- file_type: parquet
prefix: p0_p1/
properties:
- data_type: int32
is_nullable: false
is_primary: true
name: p0
- cardinality: list
data_type: string
is_nullable: true
is_primary: false
name: p1
- cardinality: set
data_type: string
is_nullable: true
is_primary: false
name: p2
type: test_vertex
version: gar/v1
)";
REQUIRE(dump_result.value() == expected);
auto vertex_info_empty_version =
CreateVertexInfo(type, chunk_size, {pg}, {}, "test_vertex/");
REQUIRE(vertex_info_empty_version->Dump().status().ok());
}
SECTION("Save") {
// save to a simple output path
REQUIRE(vertex_info->Save("/tmp/" + type + ".vertex.yml").ok());
// save to a URI path
REQUIRE(vertex_info->Save("file:///tmp/" + type + ".vertex.yml").ok());
}
SECTION("AddPropertyGroup") {
auto pg3 = CreatePropertyGroup({Property("p3", int32(), false)},
FileType::CSV, "p3/");
auto maybe_extend_info = vertex_info->AddPropertyGroup(pg3);
REQUIRE(maybe_extend_info.status().ok());
auto extend_info = maybe_extend_info.value();
REQUIRE(extend_info->PropertyGroupNum() == 2);
REQUIRE(extend_info->HasProperty("p2") == true);
REQUIRE(extend_info->HasPropertyGroup(pg3) == true);
REQUIRE(extend_info->GetPropertyGroups().size() == 2);
REQUIRE(*(extend_info->GetPropertyGroups()[1]) == *pg3);
REQUIRE(extend_info->GetPropertyType("p3").value()->ToTypeName() ==
int32()->ToTypeName());
REQUIRE(extend_info->IsPrimaryKey("p3") == false);
REQUIRE(extend_info->IsNullableKey("p3") == true);
REQUIRE(extend_info->GetPropertyCardinality("p3") == Cardinality::SINGLE);
auto extend_info3 = extend_info->AddPropertyGroup(pg3);
REQUIRE(!extend_info3.status().ok());
}
}
TEST_CASE_METHOD(GlobalFixture, "EdgeInfo") {
std::string src_type = "person", edge_type = "knows", dst_type = "person";
int chunk_size = 1024;
int src_chunk_size = 100;
int dst_chunk_size = 100;
bool directed = true;
auto version = std::make_shared<InfoVersion>(1);
auto adj_list_type = AdjListType::ordered_by_source;
auto adj_list =
CreateAdjacentList(adj_list_type, FileType::CSV, "ordered_by_source/");
auto pg = CreatePropertyGroup(
{Property("p0", int32(), true), Property("p1", string(), false)},
FileType::CSV, "p0_p1/");
auto edge_info = CreateEdgeInfo(src_type, edge_type, dst_type, chunk_size,
src_chunk_size, dst_chunk_size, directed,
{adj_list}, {pg}, "test_edge/", version);
SECTION("Basics") {
REQUIRE(edge_info->GetSrcType() == src_type);
REQUIRE(edge_info->GetEdgeType() == edge_type);
REQUIRE(edge_info->GetDstType() == dst_type);
REQUIRE(edge_info->GetChunkSize() == chunk_size);
REQUIRE(edge_info->GetSrcChunkSize() == src_chunk_size);
REQUIRE(edge_info->GetDstChunkSize() == dst_chunk_size);
REQUIRE(edge_info->IsDirected() == directed);
REQUIRE(edge_info->GetPrefix() == "test_edge/");
REQUIRE(edge_info->version()->ToString() == "gar/v1");
}
SECTION("AdjacentList") {
REQUIRE(edge_info->HasAdjacentListType(adj_list_type) == true);
REQUIRE(edge_info->HasAdjacentListType(AdjListType::unordered_by_source) ==
false);
REQUIRE(edge_info->GetAdjacentList(adj_list_type)->GetType() ==
adj_list_type);
REQUIRE(edge_info->GetAdjacentList(adj_list_type)->GetFileType() ==
FileType::CSV);
REQUIRE(edge_info->GetAdjacentList(AdjListType::unordered_by_source) ==
nullptr);
}
SECTION("PropertyGroup") {
REQUIRE(edge_info->PropertyGroupNum() == 1);
REQUIRE(*edge_info->GetPropertyGroupByIndex(0) == *pg);
REQUIRE(edge_info->HasProperty("p0") == true);
REQUIRE(edge_info->HasPropertyGroup(pg) == true);
REQUIRE(*edge_info->GetPropertyGroup("p0") == *pg);
REQUIRE(edge_info->GetPropertyGroups().size() == 1);
REQUIRE(*(edge_info->GetPropertyGroups()[0]) == *pg);
REQUIRE(edge_info->GetPropertyType("p0").value()->ToTypeName() ==
int32()->ToTypeName());
REQUIRE(edge_info->IsPrimaryKey("p0") == true);
REQUIRE(edge_info->IsPrimaryKey("p1") == false);
REQUIRE(edge_info->IsNullableKey("p0") == false);
REQUIRE(edge_info->IsNullableKey("p1") == true);
REQUIRE(edge_info->HasProperty("not_exist") == false);
REQUIRE(edge_info->IsPrimaryKey("not_exist") == false);
REQUIRE(edge_info->IsNullableKey("not_exist") == false);
REQUIRE(edge_info->HasPropertyGroup(nullptr) == false);
}
SECTION("Path") {
REQUIRE(edge_info->GetAdjListPathPrefix(adj_list_type).value() ==
"test_edge/ordered_by_source/adj_list/");
REQUIRE(edge_info->GetAdjListFilePath(0, 0, adj_list_type).value() ==
"test_edge/ordered_by_source/adj_list/part0/chunk0");
REQUIRE(edge_info->GetOffsetPathPrefix(adj_list_type).value() ==
"test_edge/ordered_by_source/offset/");
REQUIRE(edge_info->GetAdjListOffsetFilePath(0, adj_list_type).value() ==
"test_edge/ordered_by_source/offset/chunk0");
REQUIRE(edge_info->GetEdgesNumFilePath(0, adj_list_type).value() ==
"test_edge/ordered_by_source/edge_count0");
REQUIRE(edge_info->GetVerticesNumFilePath(adj_list_type).value() ==
"test_edge/ordered_by_source/vertex_count");
REQUIRE(edge_info->GetPropertyGroupPathPrefix(pg, adj_list_type).value() ==
"test_edge/ordered_by_source/p0_p1/");
REQUIRE(edge_info->GetPropertyFilePath(pg, adj_list_type, 0, 0).value() ==
"test_edge/ordered_by_source/p0_p1/part0/chunk0");
}
SECTION("IsValidated") {
REQUIRE(edge_info->IsValidated() == true);
auto invalid_pg =
CreatePropertyGroup({Property("p0", nullptr, true)}, FileType::CSV);
auto invalid_edge_info0 =
CreateEdgeInfo(src_type, edge_type, dst_type, chunk_size,
src_chunk_size, dst_chunk_size, directed, {adj_list},
{invalid_pg}, "test_edge/", version);
REQUIRE(invalid_edge_info0->IsValidated() == false);
// edgeInfo does not support list/set cardinality
invalid_pg = CreatePropertyGroup(
{Property("p_cardinality", string(), false, true, Cardinality::LIST)},
FileType::PARQUET);
auto cardinality_invalid_edge_info0 =
CreateEdgeInfo(src_type, edge_type, dst_type, chunk_size,
src_chunk_size, dst_chunk_size, directed, {adj_list},
{invalid_pg}, "test_edge/", version);
REQUIRE(invalid_edge_info0->IsValidated() == false);
for (int i = 0; i < 3; i++) {
std::vector<std::string> types = {src_type, edge_type, dst_type};
types[i] = "";
EdgeInfo invalid_edge_info1(types[0], types[1], types[2], chunk_size,
src_chunk_size, dst_chunk_size, directed,
{adj_list}, {pg}, "test_edge/", version);
REQUIRE(invalid_edge_info1.IsValidated() == false);
}
for (int i = 0; i < 3; i++) {
std::vector<int> sizes = {chunk_size, src_chunk_size, dst_chunk_size};
sizes[i] = 0;
EdgeInfo invalid_edge_info2(src_type, edge_type, dst_type, sizes[0],
sizes[1], sizes[2], directed, {adj_list},
{pg}, "test_edge/", version);
REQUIRE(invalid_edge_info2.IsValidated() == false);
}
// check if prefix empty
auto edge_info_with_empty_prefix = CreateEdgeInfo(
src_type, edge_type, dst_type, chunk_size, src_chunk_size,
dst_chunk_size, directed, {adj_list}, {pg}, "", version);
REQUIRE(edge_info_with_empty_prefix->IsValidated() == true);
}
SECTION("CreateEdgeInfo") {
for (int i = 0; i < 3; i++) {
std::vector<std::string> types = {src_type, edge_type, dst_type};
types[i] = "";
auto edge_info = CreateEdgeInfo(types[0], types[1], types[2], chunk_size,
src_chunk_size, dst_chunk_size, directed,
{adj_list}, {pg}, "test_edge/", version);
REQUIRE(edge_info == nullptr);
}
for (int i = 0; i < 3; i++) {
std::vector<int> sizes = {chunk_size, src_chunk_size, dst_chunk_size};
sizes[i] = 0;
auto edge_info = CreateEdgeInfo(src_type, edge_type, dst_type, sizes[0],
sizes[1], sizes[2], directed, {adj_list},
{pg}, "test_edge/", version);
REQUIRE(edge_info == nullptr);
}
auto edge_info_empty_adjlist = CreateEdgeInfo(
src_type, edge_type, dst_type, chunk_size, src_chunk_size,
dst_chunk_size, directed, {}, {pg}, "test_edge/");
REQUIRE(edge_info_empty_adjlist == nullptr);
}
SECTION("Dump") {
auto dump_result = edge_info->Dump();
REQUIRE(dump_result.status().ok());
std::string expected = R"(adj_lists:
- aligned_by: src
file_type: csv
ordered: true
prefix: ordered_by_source/
chunk_size: 1024
directed: true
dst_chunk_size: 100
dst_type: person
edge_type: knows
prefix: test_edge/
property_groups:
- file_type: csv
prefix: p0_p1/
properties:
- data_type: int32
is_nullable: false
is_primary: true
name: p0
- data_type: string
is_nullable: true
is_primary: false
name: p1
src_chunk_size: 100
src_type: person
version: gar/v1
)";
REQUIRE(dump_result.value() == expected);
auto edge_info_empty_version = CreateEdgeInfo(
src_type, edge_type, dst_type, chunk_size, src_chunk_size,
dst_chunk_size, directed, {adj_list}, {pg});
REQUIRE(edge_info_empty_version->Dump().status().ok());
}
SECTION("Save") {
// save to a simple output path
REQUIRE(edge_info->Save("/tmp/" + edge_type + ".edge.yml").ok());
// save to a URI path
REQUIRE(edge_info->Save("file:///tmp/" + edge_type + ".edge.yml").ok());
}
SECTION("AddAdjacentList") {
auto adj_list2 = CreateAdjacentList(AdjListType::ordered_by_dest,
FileType::CSV, "ordered_by_dest/");
auto maybe_extend_info = edge_info->AddAdjacentList(adj_list2);
REQUIRE(maybe_extend_info.status().ok());
auto extend_info = maybe_extend_info.value();
REQUIRE(extend_info->HasAdjacentListType(AdjListType::ordered_by_dest) ==
true);
REQUIRE(
extend_info->GetAdjacentList(AdjListType::ordered_by_dest)->GetType() ==
AdjListType::ordered_by_dest);
REQUIRE(extend_info->GetAdjacentList(AdjListType::ordered_by_dest)
->GetFileType() == FileType::CSV);
auto extend_info2 = extend_info->AddAdjacentList(adj_list2);
REQUIRE(!extend_info2.status().ok());
}
SECTION("AddPropertyGroup") {
auto pg2 = CreatePropertyGroup({Property("p2", int32(), false)},
FileType::CSV, "p2/");
auto maybe_extend_info = edge_info->AddPropertyGroup(pg2);
REQUIRE(maybe_extend_info.status().ok());
auto extend_info = maybe_extend_info.value();
REQUIRE(extend_info->PropertyGroupNum() == 2);
REQUIRE(extend_info->HasProperty("p2") == true);
REQUIRE(extend_info->HasPropertyGroup(pg2) == true);
REQUIRE(extend_info->GetPropertyGroups().size() == 2);
REQUIRE(*(extend_info->GetPropertyGroups()[1]) == *pg2);
REQUIRE(extend_info->GetPropertyType("p2").value()->ToTypeName() ==
int32()->ToTypeName());
REQUIRE(extend_info->IsPrimaryKey("p2") == false);
auto extend_info2 = extend_info->AddPropertyGroup(pg2);
REQUIRE(!extend_info2.status().ok());
}
}
TEST_CASE_METHOD(GlobalFixture, "GraphInfo") {
std::string name = "test_graph";
auto version = std::make_shared<InfoVersion>(1);
auto pg = CreatePropertyGroup(
{Property("p0", int32(), true), Property("p1", string(), false)},
FileType::CSV, "p0_p1/");
auto vertex_info =
CreateVertexInfo("test_vertex", 100, {pg}, {}, "test_vertex/", version);
std::unordered_map<std::string, std::string> extra_info = {
{"category", "test graph"}};
auto edge_info =
CreateEdgeInfo("person", "knows", "person", 1024, 100, 100, true,
{CreateAdjacentList(AdjListType::ordered_by_source,
FileType::CSV, "adj_list/")},
{pg}, "test_edge/", version);
auto graph_info = CreateGraphInfo(name, {vertex_info}, {edge_info}, {},
"test_graph/", version, extra_info);
SECTION("Basics") {
REQUIRE(graph_info->GetName() == name);
REQUIRE(graph_info->GetPrefix() == "test_graph/");
REQUIRE(graph_info->version()->ToString() == "gar/v1");
REQUIRE(graph_info->GetExtraInfo().size() == 1);
REQUIRE(graph_info->GetExtraInfo().find("category") !=
graph_info->GetExtraInfo().end());
REQUIRE(graph_info->GetExtraInfo().at("category") == "test graph");
}
SECTION("ExtraInfo") {
auto graph_info_with_extra_info =
CreateGraphInfo(name, {vertex_info}, {edge_info}, {}, "test_graph/",
version, {{"key1", "value1"}, {"key2", "value2"}});
const auto& extra_info = graph_info_with_extra_info->GetExtraInfo();
REQUIRE(extra_info.size() == 2);
REQUIRE(extra_info.find("key1") != extra_info.end());
REQUIRE(extra_info.at("key1") == "value1");
REQUIRE(extra_info.find("key2") != extra_info.end());
REQUIRE(extra_info.at("key2") == "value2");
}
SECTION("VertexInfo") {
REQUIRE(graph_info->VertexInfoNum() == 1);
REQUIRE(graph_info->GetVertexInfoByIndex(0)->GetType() == "test_vertex");
REQUIRE(graph_info->GetVertexInfoByIndex(1) == nullptr);
REQUIRE(graph_info->GetVertexInfo("test_vertex")->GetType() ==
"test_vertex");
REQUIRE(graph_info->GetVertexInfo("not_exist") == nullptr);
REQUIRE(graph_info->GetVertexInfos().size() == 1);
REQUIRE(graph_info->GetVertexInfos()[0]->GetType() == "test_vertex");
}
SECTION("EdgeInfo") {
REQUIRE(graph_info->EdgeInfoNum() == 1);
REQUIRE(graph_info->GetEdgeInfoByIndex(0)->GetEdgeType() == "knows");
REQUIRE(graph_info->GetEdgeInfoByIndex(1) == nullptr);
REQUIRE(
graph_info->GetEdgeInfo("person", "knows", "person")->GetEdgeType() ==
"knows");
REQUIRE(graph_info->GetEdgeInfo("not_exist", "knows", "person") == nullptr);
REQUIRE(graph_info->GetEdgeInfos().size() == 1);
REQUIRE(graph_info->GetEdgeInfos()[0]->GetEdgeType() == "knows");
}
SECTION("IsValidated") {
REQUIRE(graph_info->IsValidated() == true);
auto invalid_vertex_info =
CreateVertexInfo("", 100, {pg}, {}, "test_vertex/", version);
auto invalid_graph_info0 = CreateGraphInfo(
name, {invalid_vertex_info}, {edge_info}, {}, "test_graph/", version);
REQUIRE(invalid_graph_info0->IsValidated() == false);
auto invalid_edge_info =
CreateEdgeInfo("", "knows", "person", 1024, 100, 100, true,
{CreateAdjacentList(AdjListType::ordered_by_source,
FileType::CSV, "adj_list/")},
{pg}, "test_edge/", version);
auto invalid_graph_info1 = CreateGraphInfo(
name, {vertex_info}, {invalid_edge_info}, {}, "test_graph/", version);
REQUIRE(invalid_graph_info1->IsValidated() == false);
GraphInfo invalid_graph_info2("", {vertex_info}, {edge_info}, {},
"test_graph/", version);
REQUIRE(invalid_graph_info2.IsValidated() == false);
GraphInfo invalid_graph_info3(name, {vertex_info}, {edge_info}, {}, "",
version);
REQUIRE(invalid_graph_info3.IsValidated() == false);
// check if prefix empty, graph_info with empty prefix is invalid
auto graph_info_with_empty_prefix =
CreateGraphInfo(name, {vertex_info}, {edge_info}, {}, "", version);
REQUIRE(graph_info_with_empty_prefix->IsValidated() == false);
}
SECTION("CreateGraphInfo") {
auto graph_info_empty_name =
CreateGraphInfo("", {vertex_info}, {edge_info}, {}, "test_graph/");
REQUIRE(graph_info_empty_name == nullptr);
}
SECTION("Dump") {
auto dump_result = graph_info->Dump();
REQUIRE(dump_result.status().ok());
std::string expected = R"(edges:
- person_knows_person.edge.yaml
extra_info:
- key: category
value: test graph
name: test_graph
prefix: test_graph/
version: gar/v1
vertices:
- test_vertex.vertex.yaml
)";
REQUIRE(dump_result.value() == expected);
auto graph_info_empty_version =
CreateGraphInfo(name, {vertex_info}, {edge_info}, {}, "test_graph/");
REQUIRE(graph_info_empty_version->Dump().status().ok());
}
SECTION("Save") {
// save to a simple output path
REQUIRE(graph_info->Save("/tmp/" + name + ".graph.yml").ok());
// save to a URI path
REQUIRE(graph_info->Save("file:///tmp/" + name + ".graph.yml").ok());
}
SECTION("AddVertex") {
auto vertex_info2 = CreateVertexInfo("test_vertex2", 100, {pg}, {},
"test_vertex2/", version);
auto maybe_extend_info = graph_info->AddVertex(vertex_info2);
REQUIRE(maybe_extend_info.status().ok());
auto extend_info = maybe_extend_info.value();
REQUIRE(extend_info->VertexInfoNum() == 2);
REQUIRE(extend_info->GetVertexInfoByIndex(1)->GetType() == "test_vertex2");
REQUIRE(extend_info->GetVertexInfoByIndex(2) == nullptr);
REQUIRE(extend_info->GetVertexInfo("test_vertex2")->GetType() ==
"test_vertex2");
REQUIRE(extend_info->GetVertexInfo("not_exist") == nullptr);
REQUIRE(extend_info->GetVertexInfos().size() == 2);
REQUIRE(extend_info->GetVertexInfos()[1]->GetType() == "test_vertex2");
auto extend_info2 = extend_info->AddVertex(vertex_info2);
REQUIRE(!extend_info2.status().ok());
}
SECTION("AddEdge") {
auto edge_info2 =
CreateEdgeInfo("person", "knows2", "person", 1024, 100, 100, true,
{CreateAdjacentList(AdjListType::ordered_by_source,
FileType::CSV, "adj_list/")},
{pg}, "test_edge/", version);
auto maybe_extend_info = graph_info->AddEdge(edge_info2);
REQUIRE(maybe_extend_info.status().ok());
auto extend_info = maybe_extend_info.value();
REQUIRE(extend_info->EdgeInfoNum() == 2);
REQUIRE(extend_info->GetEdgeInfoByIndex(1)->GetEdgeType() == "knows2");
REQUIRE(extend_info->GetEdgeInfoByIndex(2) == nullptr);
REQUIRE(
extend_info->GetEdgeInfo("person", "knows2", "person")->GetEdgeType() ==
"knows2");
REQUIRE(extend_info->GetEdgeInfo("not_exist", "knows2", "person") ==
nullptr);
REQUIRE(extend_info->GetEdgeInfos().size() == 2);
REQUIRE(extend_info->GetEdgeInfos()[1]->GetEdgeType() == "knows2");
auto extend_info2 = extend_info->AddEdge(edge_info2);
REQUIRE(!extend_info2.status().ok());
}
}
TEST_CASE_METHOD(GlobalFixture, "LoadFromYaml") {
std::string vertex_info_yaml = R"(type: person
chunk_size: 100
prefix: vertex/person/
property_groups:
- properties:
- name: id
data_type: int64
is_primary: true
is_nullable: false
file_type: parquet
- properties:
- name: email
data_type: string
is_primary: false
is_nullable: true
cardinality: list
file_type: parquet
- properties:
- name: firstName
data_type: string
is_primary: false
is_nullable: true
- name: lastName
data_type: string
is_primary: false
is_nullable: true
- name: gender
data_type: string
is_primary: false
is_nullable: true
file_type: parquet
version: gar/v1
)";
std::string edge_info_yaml = R"(src_type: person
edge_type: knows
dst_type: person
chunk_size: 1024
src_chunk_size: 100
dst_chunk_size: 100
directed: false
prefix: edge/person_knows_person/
adj_lists:
- ordered: false
aligned_by: src
file_type: parquet
- ordered: true
aligned_by: src
file_type: parquet
- ordered: true
aligned_by: dst
file_type: parquet
property_groups:
- file_type: parquet
properties:
- name: creationDate
data_type: string
is_primary: false
is_nullable: true
version: gar/v1
)";
std::string graph_info_yaml = R"(name: ldbc_sample
prefix: /tmp/ldbc/
version: gar/v1
extra_info:
- key: category
value: test graph
)";
SECTION("VertexInfo::Load") {
auto maybe_vertex_info = VertexInfo::Load(vertex_info_yaml);
REQUIRE(!maybe_vertex_info.has_error());
auto vertex_info = maybe_vertex_info.value();
REQUIRE(vertex_info->GetType() == "person");
REQUIRE(vertex_info->GetChunkSize() == 100);
REQUIRE(vertex_info->GetPrefix() == "vertex/person/");
REQUIRE(vertex_info->version()->ToString() == "gar/v1");
REQUIRE(vertex_info->GetPropertyCardinality("id").value() ==
Cardinality::SINGLE);
REQUIRE(vertex_info->GetPropertyCardinality("email").value() ==
Cardinality::LIST);
}
SECTION("EdgeInfo::Load") {
auto maybe_edge_info = EdgeInfo::Load(edge_info_yaml);
REQUIRE(!maybe_edge_info.has_error());
auto edge_info = maybe_edge_info.value();
REQUIRE(edge_info->GetSrcType() == "person");
REQUIRE(edge_info->GetEdgeType() == "knows");
REQUIRE(edge_info->GetDstType() == "person");
}
edge_info_yaml = R"(src_type: person
edge_type: knows
dst_type: person
chunk_size: 1024
src_chunk_size: 100
dst_chunk_size: 100
directed: false
prefix: edge/person_knows_person/
adj_lists:
- ordered: false
aligned_by: src
file_type: parquet
property_groups:
- file_type: parquet
properties:
- name: creationDate
data_type: string
is_primary: false
is_nullable: true
cardinality: list
version: gar/v1
)";
SECTION("EdgeInfo::Load with cardinality") {
auto maybe_edge_info = EdgeInfo::Load(edge_info_yaml);
REQUIRE(maybe_edge_info.has_error());
REQUIRE(maybe_edge_info.status().code() == StatusCode::kYamlError);
std::cout << maybe_edge_info.status().message() << std::endl;
}
SECTION("GraphInfo::Load") {
auto maybe_graph_info = GraphInfo::Load(graph_info_yaml, "/");
std::cout << maybe_graph_info.status().message() << std::endl;
REQUIRE(!maybe_graph_info.has_error());
auto graph_info = maybe_graph_info.value();
REQUIRE(graph_info->GetName() == "ldbc_sample");
REQUIRE(graph_info->GetPrefix() == "/tmp/ldbc/");
REQUIRE(graph_info->version()->ToString() == "gar/v1");
const auto& extra_info = graph_info->GetExtraInfo();
REQUIRE(extra_info.size() == 1);
REQUIRE(extra_info.find("category") != extra_info.end());
REQUIRE(extra_info.at("category") == "test graph");
}
}
/*
TODO(acezen): need to mock S3 server to test this case, this private
service is not available for public access.
*/
// TEST_CASE_METHOD(GlobalFixture, "LoadFromS3") {
// // explicitly call InitS3 to initialize S3 APIs before using
// // S3 file system.
// InitializeS3();
// std::string path =
// "s3://graphar/ldbc/ldbc.graph.yml"
// "?endpoint_override=graphscope.oss-cn-beijing.aliyuncs.com";
// auto graph_info_result = GraphInfo::Load(path);
// std::cout << graph_info_result.status().message() << std::endl;
// REQUIRE(!graph_info_result.has_error());
// auto graph_info = graph_info_result.value();
// REQUIRE(graph_info->GetName() == "ldbc");
// const auto& vertex_infos = graph_info->GetVertexInfos();
// const auto& edge_infos = graph_info->GetEdgeInfos();
// REQUIRE(vertex_infos.size() == 8);
// REQUIRE(edge_infos.size() == 23);
// // explicitly call FinalizeS3 to avoid memory leak
// FinalizeS3();
// }
} // namespace graphar