Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing framework v2 #1548

Merged
merged 11 commits into from
May 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ endfunction()
function(add_kuzu_test TEST_NAME)
set(SRCS ${ARGN})
add_executable(${TEST_NAME} ${SRCS})
target_link_libraries(${TEST_NAME} PRIVATE test_helper graph_test)
target_link_libraries(${TEST_NAME} PRIVATE test_helper test_runner graph_test)
target_include_directories(${TEST_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/test/include)
include(GoogleTest)
gtest_discover_tests(${TEST_NAME})
Expand Down
11 changes: 0 additions & 11 deletions src/common/file_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,5 @@ std::vector<std::string> FileUtils::globFilePath(const std::string& path) {
return result;
}

std::vector<std::string> FileUtils::findAllDirectories(const std::string& path) {
std::vector<std::string> directories;
directories.push_back(path);
for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
if (entry.is_directory()) {
directories.push_back(entry.path().string());
}
}
return directories;
}

} // namespace common
} // namespace kuzu
9 changes: 9 additions & 0 deletions src/common/string_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,14 @@ std::vector<std::string> StringUtils::split(
return result;
}

std::vector<std::string> StringUtils::splitBySpace(const std::string& input) {
std::istringstream iss(input);
std::vector<std::string> result;
std::string token;
while (iss >> token)
result.push_back(token);
return result;
}

} // namespace common
} // namespace kuzu
10 changes: 0 additions & 10 deletions src/include/common/file_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,6 @@ class FileUtils {

static std::vector<std::string> globFilePath(const std::string& path);

static std::vector<std::string> findAllDirectories(const std::string& path);

static inline std::string getParentPath(const std::filesystem::path& path) {
return path.parent_path().string();
}

static inline std::string getParentPathStem(const std::filesystem::path& path) {
return path.parent_path().stem().string();
}

static inline std::string getFileExtension(const std::filesystem::path& path) {
return path.extension().string();
}
Expand Down
9 changes: 9 additions & 0 deletions src/include/common/string_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class StringUtils {
static std::vector<std::string> split(
const std::string& input, const std::string& delimiter, bool ignoreEmptyStringParts = true);

static std::vector<std::string> splitBySpace(const std::string& input);

static void toUpper(std::string& input) {
std::transform(input.begin(), input.end(), input.begin(), ::toupper);
}
Expand All @@ -41,6 +43,13 @@ class StringUtils {
return string_format("Maximum length of strings is {}. Input string's length is {}.",
maxAllowedStrSize, strlen(strToInsert), strToInsert);
}

static inline std::string ltrim(const std::string& input) {
auto s = input;
s.erase(
s.begin(), find_if(s.begin(), s.end(), [](unsigned char ch) { return !isspace(ch); }));
return s;
}
};

} // namespace common
Expand Down
1 change: 1 addition & 0 deletions src/include/main/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Connection {
friend class kuzu::testing::ApiTest;
friend class kuzu::testing::BaseGraphTest;
friend class kuzu::testing::TestHelper;
friend class kuzu::testing::TestRunner;
friend class kuzu::benchmark::Benchmark;

public:
Expand Down
1 change: 1 addition & 0 deletions src/include/main/kuzu_fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace testing {
class ApiTest;
class BaseGraphTest;
class TestHelper;
class TestRunner;
class TinySnbDDLTest;
class TinySnbCopyCSVTransactionTest;
} // namespace testing
Expand Down
1 change: 1 addition & 0 deletions src/include/main/prepared_statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace main {
class PreparedStatement {
friend class Connection;
friend class testing::TestHelper;
friend class testing::TestRunner;
friend class testing::TinySnbDDLTest;
friend class testing::TinySnbCopyCSVTransactionTest;

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ target_link_libraries(GTest::GTest INTERFACE gmock_main)

enable_testing()
add_subdirectory(test_helper)
add_subdirectory(test_runner)
add_subdirectory(graph_test)
add_subdirectory(binder)
add_subdirectory(c_api)
Expand Down
1 change: 1 addition & 0 deletions test/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_kuzu_test(types_test
date_test.cpp
interval_test.cpp
string_test.cpp
time_test.cpp
timestamp_test.cpp
types_test.cpp)
22 changes: 22 additions & 0 deletions test/common/string_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <string>

#include "common/types/types_include.h"
#include "gtest/gtest.h"

using namespace kuzu::common;

TEST(StringTest, splitBySpace) {
std::string str = " a b c\td ";
std::vector<std::string> result = StringUtils::splitBySpace(str);
EXPECT_EQ(result.size(), 4);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
EXPECT_EQ(result[3], "d");
}

TEST(StringTest, leftTrim) {
std::string str = " command ";
std::string result = StringUtils::ltrim(str);
EXPECT_EQ(result, "command ");
}
6 changes: 6 additions & 0 deletions test/include/graph_test/graph_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "planner/logical_plan/logical_plan_util.h"
#include "planner/planner.h"
#include "test_helper/test_helper.h"
#include "test_runner/test_runner.h"

using ::testing::Test;

Expand Down Expand Up @@ -155,6 +156,11 @@ class DBTest : public BaseGraphTest {
initGraph();
}

inline void runTest(const std::vector<std::unique_ptr<TestStatement>>& statements) {
TestRunner::runTest(statements, *conn);
}

// Deprecated
inline void runTest(const std::string& queryFile) {
auto queryConfigs = TestHelper::parseTestFile(queryFile);
ASSERT_TRUE(TestHelper::testQueries(queryConfigs, *conn));
Expand Down
19 changes: 0 additions & 19 deletions test/include/test_helper/test_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,6 @@ using namespace kuzu::main;
namespace kuzu {
namespace testing {

struct TestConfig {
std::string testGroup;
std::string testName;
std::string dataset;
bool checkOrder = false;
std::vector<std::string> files;

bool isValid() const {
return !testGroup.empty() && !testName.empty() && !dataset.empty() && !files.empty();
}
};

struct TestQueryConfig {
std::string name;
std::string query;
Expand All @@ -38,8 +26,6 @@ struct TestQueryConfig {

class TestHelper {
public:
static TestConfig parseGroupFile(const std::string& path);

static std::vector<std::unique_ptr<TestQueryConfig>> parseTestFile(
const std::string& path, bool checkOutputOrder = false);

Expand All @@ -66,11 +52,6 @@ class TestHelper {
private:
static void initializeConnection(TestQueryConfig* config, Connection& conn);
static bool testQuery(TestQueryConfig* config, Connection& conn);
static void setConfigValue(
const std::string& line, std::string& configItem, const std::string& configKey);
static void setConfigValue(
const std::string& line, bool& configItem, const std::string& configKey);
static std::string extractConfigValue(const std::string& line, const std::string& configKey);
};

} // namespace testing
Expand Down
37 changes: 37 additions & 0 deletions test/include/test_runner/test_case.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include "main/kuzu.h"

namespace kuzu {
namespace testing {

struct TestStatement {
std::string name;
std::string query;
uint64_t numThreads = 4;
std::string encodedJoin;
uint64_t expectedNumTuples = 0;
bool expectedError = false;
bool expectedOk = false;
std::vector<std::string> expectedTuples;
std::string errorMessage;
bool enumerate = false;
bool checkOutputOrder = false;
};

struct TestCase {
std::string group;
std::string name;
std::string dataset;
std::vector<std::unique_ptr<TestStatement>> statements;
std::unordered_map<std::string, std::vector<std::unique_ptr<TestStatement>>> variableStatements;

bool skipTest = false;

bool isValid() const { return !group.empty() && !name.empty() && !dataset.empty(); }

bool hasStatements() const { return !statements.empty(); }
};

} // namespace testing
} // namespace kuzu
93 changes: 93 additions & 0 deletions test/include/test_runner/test_parser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include <cstring>

#include "common/file_utils.h"
#include "test_runner/test_case.h"

namespace kuzu {
namespace testing {

enum class TokenType {
GROUP,
DATASET,
TEST,
CASE,
CHECK_ORDER,
DEFINE_STATEMENT_BLOCK,
EMPTY,
ENCODED_JOIN,
END_OF_STATEMENT_BLOCK,
ENUMERATE,
NAME,
PARALLELISM,
QUERY,
READ_ONLY,
RESULT,
SEPARATOR,
SKIP,
STATEMENT,
STATEMENT_BLOCK
};

const std::unordered_map<std::string, TokenType> tokenMap = {{"-GROUP", TokenType::GROUP},
{"-TEST", TokenType::TEST}, {"-DATASET", TokenType::DATASET}, {"-CASE", TokenType::CASE},
{"-CHECK_ORDER", TokenType::CHECK_ORDER}, {"-ENCODED_JOIN", TokenType::ENCODED_JOIN},
{"-DEFINE_STATEMENT_BLOCK", TokenType::DEFINE_STATEMENT_BLOCK},
{"-ENUMERATE", TokenType::ENUMERATE}, {"-NAME", TokenType::NAME},
{"-PARALLELISM", TokenType::PARALLELISM}, {"-QUERY", TokenType::QUERY},
{"-READ_ONLY", TokenType::READ_ONLY}, {"-SKIP", TokenType::SKIP},
{"-STATEMENT", TokenType::STATEMENT}, {"-STATEMENT_BLOCK", TokenType::STATEMENT_BLOCK},
{"]", TokenType::END_OF_STATEMENT_BLOCK}, {"----", TokenType::RESULT},
{"--", TokenType::SEPARATOR}, {"#", TokenType::EMPTY}};

class LogicToken {
public:
TokenType type;
std::vector<std::string> params;
};

class TestParser {
public:
TestParser() : testCase(std::make_unique<TestCase>()) {}

std::unique_ptr<TestCase> parseTestFile(const std::string& path);

private:
std::ifstream fileStream;
std::string line;
std::string name;
LogicToken currentToken;
std::unique_ptr<TestCase> testCase;

std::string paramsToString();

void openFile(const std::string& path);

void tokenize();

void parseHeader();

void parseBody();

void extractExpectedResult(TestStatement* currentStatement);

void extractStatementBlock();

void addStatementBlock(const std::string& blockName);

inline bool endOfFile() { return fileStream.eof(); }

inline bool nextLine() { return static_cast<bool>(getline(fileStream, line)); }

inline void checkMinimumParams(int minimumParams) {
if (currentToken.params.size() < minimumParams) {
throw common::Exception("Invalid number of parameters for statement [" + line + "]");
}
}

TestStatement* extractStatement(TestStatement* currentStatement);

TestStatement* addNewStatement();
};

} // namespace testing
} // namespace kuzu
29 changes: 29 additions & 0 deletions test/include/test_runner/test_runner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "common/file_utils.h"
#include "gtest/gtest.h"
#include "parser/parser.h"
#include "planner/logical_plan/logical_plan_util.h"
#include "planner/planner.h"
#include "test_runner/test_case.h"

namespace kuzu {
namespace testing {

class TestRunner {
public:
static void runTest(
const std::vector<std::unique_ptr<TestStatement>>& statements, main::Connection& conn);

static std::unique_ptr<planner::LogicalPlan> getLogicalPlan(
const std::string& query, main::Connection& conn);

private:
static void initializeConnection(TestStatement* statement, main::Connection& conn);
static bool testStatement(TestStatement* statement, main::Connection& conn);
static bool checkLogicalPlans(std::unique_ptr<main::PreparedStatement>& preparedStatement,
TestStatement* statement, main::Connection& conn);
static std::vector<std::string> convertResultToString(
main::QueryResult& queryResult, bool checkOutputOrder = false);
};

} // namespace testing
} // namespace kuzu
Loading