diff --git a/CMakeLists.txt b/CMakeLists.txt index 57887027a..abd06c1b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -218,6 +218,8 @@ option(MANUAL_TESTS "Add manual test binaries" OFF) message(STATUS "Add manual test binaries: ${MANUAL_TESTS}") option(UNIT_TESTS "Add unit test binaries" OFF) message(STATUS "Add unit test binaries: ${UNIT_TESTS}") +option(PROPERTY_BASED_TESTS "Add property based test binaries" OFF) +message(STATUS "Add property based test binaries: ${PROPERTY_BASED_TESTS}") option(HARDCODED_TARGETS "Make hardcoded query targets" ON) message(STATUS "Make hardcoded query targets: ${HARDCODED_TARGETS}") option(TEST_COVERAGE "Generate coverage reports from unit tests" OFF) @@ -335,7 +337,7 @@ endif() # tests if (ALL_TESTS OR BENCHMARK_TESTS OR CONCURRENT_TEST OR INTEGRATION_TEST - OR MANUAL_TESTS OR UNIT_TESTS) + OR MANUAL_TESTS OR UNIT_TESTS OR PROPERTY_BASED_TESTS) add_subdirectory(tests) endif() # ----------------------------------------------------------------------------- diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 0168eba62..570855c29 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -13,6 +13,9 @@ add_subdirectory(benchmark) # are disabled (reasonable configuration) add_subdirectory(fmt) +# setup rapidcheck +add_subdirectory(rapidcheck) + # setup google test add_subdirectory(googletest) diff --git a/libs/setup.sh b/libs/setup.sh index 0bc29c8c4..916a0df74 100755 --- a/libs/setup.sh +++ b/libs/setup.sh @@ -33,6 +33,13 @@ git checkout ${fmt_tag} git cherry-pick ${fmt_cxx14_fix} cd .. +# rapidcheck +git clone https://github.com/emil-e/rapidcheck.git +rapidcheck_tag="853e14f0f4313a9eb3c71e24848373e7b843dfd1" # Jun 23, 2017 +cd rapidcheck +git checkout ${rapidcheck_tag} +cd .. + # google benchmark git clone https://github.com/google/benchmark.git benchmark_tag="4f8bfeae470950ef005327973f15b0044eceaceb" # v1.1.0 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index de2b5f907..1176ea10a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ include_directories(${catch_source_dir}/include) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/test_results/unit) file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/test_results/benchmark) +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/test_results/property_based) # copy test data file(COPY ${CMAKE_SOURCE_DIR}/tests/data @@ -49,3 +50,9 @@ endif() if (ALL_TESTS OR UNIT_TESTS) add_subdirectory(${PROJECT_SOURCE_DIR}/unit) endif() + +# property based test binaries +if (ALL_TESTS OR PROPERTY_BASED_TESTS) + include_directories(${CMAKE_SOURCE_DIR}/libs/rapidcheck/extras/gtest/include) + add_subdirectory(${PROJECT_SOURCE_DIR}/property_based) +endif() diff --git a/tests/property_based/CMakeLists.txt b/tests/property_based/CMakeLists.txt new file mode 100644 index 000000000..d3c9e0580 --- /dev/null +++ b/tests/property_based/CMakeLists.txt @@ -0,0 +1,32 @@ +# set current directory name as a test type +get_filename_component(test_type ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +# get all cpp abs file names recursively starting from current directory +file(GLOB_RECURSE test_type_cpps *.cpp) +message(STATUS "Available ${test_type} cpp files are: ${test_type_cpps}") + +# for each cpp file build binary and register test +foreach(test_cpp ${test_type_cpps}) + + # get exec name (remove extension from the abs path) + get_filename_component(exec_name ${test_cpp} NAME_WE) + + # set target name in format {project_name}_{test_type}_{exec_name} + set(target_name ${project_name}_${test_type}_${exec_name}) + + # build exec file + add_executable(${target_name} ${test_cpp}) + set_property(TARGET ${target_name} PROPERTY CXX_STANDARD ${cxx_standard}) + + # OUTPUT_NAME sets the real name of a target when it is built and can be + # used to help create two targets of the same name even though CMake + # requires unique logical target names + set_target_properties(${target_name} PROPERTIES OUTPUT_NAME ${exec_name}) + + # link libraries + target_link_libraries(${target_name} memgraph_lib) + # gtest + target_link_libraries(${target_name} gtest gtest_main gmock) + target_link_libraries(${target_name} memgraph_lib rapidcheck) + +endforeach() diff --git a/tests/property_based/random_graph.cpp b/tests/property_based/random_graph.cpp new file mode 100644 index 000000000..752dd0e7f --- /dev/null +++ b/tests/property_based/random_graph.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include "dbms/dbms.hpp" + +/** + * gtest/gtest.h must be included before rapidcheck/gtest.h! + */ +#include "gtest/gtest.h" +#include + + +/** + * It is possible to run test with custom seed with: + * RC_PARAMS="seed=1" ./random_graph + */ +RC_GTEST_PROP(RandomGraph, RandomGraph, (std::vector vertex_labels, + std::vector edge_types)) { + RC_PRE(!vertex_labels.empty()); + RC_PRE(!edge_types.empty()); + + int vertices_num = vertex_labels.size(); + int edges_num = edge_types.size(); + + Dbms dbms; + std::vector vertices; + std::map vertex_label_map; + std::map edge_type_map; + + auto dba = dbms.active(); + + for (auto label : vertex_labels) { + auto vertex_accessor = dba->insert_vertex(); + vertex_accessor.add_label(dba->label(label)); + vertex_label_map.insert({vertex_accessor, label}); + vertices.push_back(vertex_accessor); + } + + for (auto type : edge_types) { + auto from = vertices[*rc::gen::inRange(0, vertices_num)]; + auto to = vertices[*rc::gen::inRange(0, vertices_num)]; + auto edge_accessor = dba->insert_edge(from, to, dba->edge_type(type)); + edge_type_map.insert({edge_accessor, type}); + } + + dba->advance_command(); + + int edges_num_check = 0; + int vertices_num_check = 0; + for (const auto &vertex : dba->vertices(false)) { + auto label = vertex_label_map.at(vertex); + RC_ASSERT(vertex.labels().size() == 1); + RC_ASSERT(*vertex.labels()[0] == label); + vertices_num_check++; + } + for (const auto &edge : dba->edges(false)) { + auto type = edge_type_map.at(edge); + RC_ASSERT(*edge.edge_type() == type); + edges_num_check++; + } + RC_ASSERT(vertices_num_check == vertices_num); + RC_ASSERT(edges_num_check == edges_num); +}