From e90ccb9d55f0968797dd5d69365c8c8c1b130a97 Mon Sep 17 00:00:00 2001 From: monkey Date: Wed, 13 Sep 2023 11:35:56 +0800 Subject: [PATCH] use one EndToEndTest class add reloadDB --- test/graph_test/graph_test.cpp | 34 +- test/include/graph_test/graph_test.h | 27 +- test/include/test_runner/test_group.h | 11 + test/include/test_runner/test_parser.h | 12 +- test/include/test_runner/test_runner.h | 4 +- test/runner/e2e_test.cpp | 20 +- .../int_delete_create_transaction.test | 352 ++++++++++++++++++ .../transaction/set_transaction.test | 79 ++++ test/test_runner/test_parser.cpp | 46 ++- test/test_runner/test_runner.cpp | 13 +- 10 files changed, 571 insertions(+), 27 deletions(-) create mode 100644 test/test_files/transaction/int_delete_create_transaction.test create mode 100644 test/test_files/transaction/set_transaction.test diff --git a/test/graph_test/graph_test.cpp b/test/graph_test/graph_test.cpp index 40b18c2bdf6..e5b86d51dd9 100644 --- a/test/graph_test/graph_test.cpp +++ b/test/graph_test/graph_test.cpp @@ -158,6 +158,28 @@ void TestHelper::executeScript(const std::string& cypherScript, Connection& conn } } } +void BaseGraphTest::createDB(uint64_t checkpointWaitTimeout) { + if (database != nullptr) { + database.reset(); + } + database = std::make_unique(databasePath, *systemConfig); + getTransactionManager(*database)->setCheckPointWaitTimeoutForTransactionsToLeaveInMicros( + checkpointWaitTimeout /* 10ms */); + spdlog::set_level(spdlog::level::info); +} + +void BaseGraphTest::createConns(const std::set& connNames) { + if (connNames.size() == 0) { // impart a default connName + conn = std::make_unique(database.get()); + } else { + for (auto connName : connNames) { + if (connMap[connName] != nullptr) { + connMap[connName].reset(); + } + connMap[connName] = std::make_unique(database.get()); + } + } +} void BaseGraphTest::createDBAndConn() { if (database != nullptr) { @@ -169,8 +191,16 @@ void BaseGraphTest::createDBAndConn() { } void BaseGraphTest::initGraph() { - TestHelper::executeScript(getInputDir() + TestHelper::SCHEMA_FILE_NAME, *conn); - TestHelper::executeScript(getInputDir() + TestHelper::COPY_FILE_NAME, *conn); + if (conn) { // normal conn + TestHelper::executeScript(getInputDir() + TestHelper::SCHEMA_FILE_NAME, *conn); + TestHelper::executeScript(getInputDir() + TestHelper::COPY_FILE_NAME, *conn); + } else { + // choose a conn from connMap + TestHelper::executeScript( + getInputDir() + TestHelper::SCHEMA_FILE_NAME, *(connMap.begin()->second)); + TestHelper::executeScript( + getInputDir() + TestHelper::COPY_FILE_NAME, *(connMap.begin()->second)); + } } void BaseGraphTest::commitOrRollbackConnection( diff --git a/test/include/graph_test/graph_test.h b/test/include/graph_test/graph_test.h index e871119e577..8ea26d8bf9b 100644 --- a/test/include/graph_test/graph_test.h +++ b/test/include/graph_test/graph_test.h @@ -39,6 +39,10 @@ class BaseGraphTest : public Test { void createDBAndConn(); + // multiple conns test + void createDB(uint64_t checkpointWaitTimeout); + void createConns(const std::set& connNames); + void initGraph(); void commitOrRollbackConnection(bool isCommit, TransactionTestType transactionTestType) const; @@ -140,7 +144,10 @@ class BaseGraphTest : public Test { std::string databasePath; std::unique_ptr systemConfig; std::unique_ptr database; + // for normal conn; if it is null, respresents multiple conns, need to use connMap std::unique_ptr conn; + // for multiple conns + std::unordered_map> connMap; }; // This class starts database without initializing graph. @@ -157,10 +164,24 @@ class DBTest : public BaseGraphTest { initGraph(); } - inline void runTest(const std::vector>& statements) { - TestRunner::runTest(statements, *conn, databasePath); + inline void runTest(const std::vector>& statements, + uint64_t checkpointWaitTimeout = + common::DEFAULT_CHECKPOINT_WAIT_TIMEOUT_FOR_TRANSACTIONS_TO_LEAVE_IN_MICROS, + std::set connNames = std::set()) { + for (auto& statement : statements) { + auto conn_name = *statement->conn_name; + if (statement->reLoadDBFlag) { + createDB(checkpointWaitTimeout); + createConns(connNames); + continue; + } + if (conn) { + TestRunner::runTest(statement.get(), *conn, databasePath); + } else { + TestRunner::runTest(statement.get(), *connMap[conn_name], databasePath); + } + } } }; - } // namespace testing } // namespace kuzu diff --git a/test/include/test_runner/test_group.h b/test/include/test_runner/test_group.h index c34f8038687..131e156c205 100644 --- a/test/include/test_runner/test_group.h +++ b/test/include/test_runner/test_group.h @@ -1,4 +1,6 @@ #pragma once +#include +#include #include "main/kuzu.h" @@ -18,6 +20,9 @@ struct TestStatement { bool enumerate = false; bool checkOutputOrder = false; std::string expectedTuplesCSVFile; + // for multiple conns + std::optional conn_name; // default conntion name conn0 + bool reLoadDBFlag = false; }; // Test group is a collection of test cases in a single file. @@ -31,6 +36,12 @@ struct TestGroup { std::unordered_map>> testCasesStatementBlocks; uint64_t bufferPoolSize = common::BufferPoolConstants::DEFAULT_BUFFER_POOL_SIZE_FOR_TESTING; + // for multiple connections + uint64_t checkpointWaitTimeout = + common::DEFAULT_CHECKPOINT_WAIT_TIMEOUT_FOR_TRANSACTIONS_TO_LEAVE_IN_MICROS; + ; + bool multipleConns = false; + std::unordered_map> testCasesConnNames; enum class DatasetType { CSV, PARQUET, NPY, CSV_TO_PARQUET }; DatasetType datasetType; diff --git a/test/include/test_runner/test_parser.h b/test/include/test_runner/test_parser.h index afb05006708..ee091f88d52 100644 --- a/test/include/test_runner/test_parser.h +++ b/test/include/test_runner/test_parser.h @@ -34,7 +34,11 @@ enum class TokenType { ROLLBACK, SEPARATOR, STATEMENT, - _SKIP_LINE + _SKIP_LINE, + + CHECKPOINT_WAIT_TIMEOUT, + CREATE_CONNECTION, + RELOADDB }; const std::unordered_map tokenMap = {{"-GROUP", TokenType::GROUP}, @@ -48,6 +52,8 @@ const std::unordered_map tokenMap = {{"-GROUP", TokenTyp {"-DEFINE", TokenType::DEFINE}, {"-STATEMENT", TokenType::STATEMENT}, {"-INSERT_STATEMENT_BLOCK", TokenType::INSERT_STATEMENT_BLOCK}, {"-ROLLBACK", TokenType::ROLLBACK}, {"-BUFFER_POOL_SIZE", TokenType::BUFFER_POOL_SIZE}, + {"-CHECKPOINT_WAIT_TIMEOUT", TokenType::CHECKPOINT_WAIT_TIMEOUT}, + {"-RELOADDB", TokenType::RELOADDB}, {"-CREATE_CONNECTION", TokenType::CREATE_CONNECTION}, {"]", TokenType::END_OF_STATEMENT_BLOCK}, {"----", TokenType::RESULT}, {"--", TokenType::SEPARATOR}, {"#", TokenType::EMPTY}}; @@ -85,6 +91,7 @@ class TestParser { void extractDataset(); void addStatementBlock(const std::string& blockName, const std::string& testGroupName); void replaceVariables(std::string& str); + bool extractConnName(std::string& query, TestStatement* statement); inline bool endOfFile() { return fileStream.eof(); } inline void setCursorToPreviousLine() { fileStream.seekg(previousFilePosition); } @@ -110,7 +117,8 @@ class TestParser { }); } - TestStatement* extractStatement(TestStatement* currentStatement); + TestStatement* extractStatement( + TestStatement* currentStatement, const std::string& testCaseName); TestStatement* addNewStatement(std::string& name); // Any value here will be replaced inside the .test files diff --git a/test/include/test_runner/test_runner.h b/test/include/test_runner/test_runner.h index e70714341d3..b699f46052f 100644 --- a/test/include/test_runner/test_runner.h +++ b/test/include/test_runner/test_runner.h @@ -10,8 +10,8 @@ namespace testing { class TestRunner { public: - static void runTest(const std::vector>& statements, - main::Connection& conn, std::string& databasePath); + static void runTest( + TestStatement* statement, main::Connection& conn, std::string& databasePath); static std::unique_ptr getLogicalPlan( const std::string& query, main::Connection& conn); diff --git a/test/runner/e2e_test.cpp b/test/runner/e2e_test.cpp index 8e6f35673a1..52872142e00 100644 --- a/test/runner/e2e_test.cpp +++ b/test/runner/e2e_test.cpp @@ -10,15 +10,19 @@ using namespace kuzu::common; class EndToEndTest : public DBTest { public: explicit EndToEndTest(TestGroup::DatasetType datasetType, std::string dataset, - uint64_t bufferPoolSize, std::vector> testStatements) + uint64_t bufferPoolSize, uint64_t checkpointWaitTimeout, + const std::set& connNames, + std::vector> testStatements) : datasetType{datasetType}, dataset{std::move(dataset)}, bufferPoolSize{bufferPoolSize}, + checkpointWaitTimeout{checkpointWaitTimeout}, connNames{connNames}, testStatements{std::move(testStatements)} {} void SetUp() override { setUpDataset(); BaseGraphTest::SetUp(); systemConfig->bufferPoolSize = bufferPoolSize; - createDBAndConn(); + createDB(checkpointWaitTimeout); + createConns(connNames); if (dataset != "empty") { initGraph(); } @@ -38,7 +42,7 @@ class EndToEndTest : public DBTest { FileUtils::removeDir(parquetTempDatasetPath); } - void TestBody() override { runTest(testStatements); } + void TestBody() override { runTest(testStatements, checkpointWaitTimeout, connNames); } std::string getInputDir() override { return dataset + "/"; } private: @@ -46,7 +50,9 @@ class EndToEndTest : public DBTest { std::string dataset; std::string parquetTempDatasetPath; uint64_t bufferPoolSize; + uint64_t checkpointWaitTimeout; std::vector> testStatements; + std::set connNames; std::string generateParquetTempDatasetPath() { return TestHelper::appendKuzuRootPath( @@ -64,6 +70,7 @@ void parseAndRegisterTestGroup(const std::string& path, bool generateTestList = auto dataset = testGroup->dataset; auto testCases = std::move(testGroup->testCases); auto bufferPoolSize = testGroup->bufferPoolSize; + auto checkpointWaitTimeout = testGroup->checkpointWaitTimeout; for (auto& [testCaseName, testStatements] : testCases) { if (generateTestList) { std::ofstream testList(TestHelper::getTestListFile(), std::ios_base::app); @@ -72,12 +79,13 @@ void parseAndRegisterTestGroup(const std::string& path, bool generateTestList = if (empty(testCaseName)) { throw TestException("Missing test case name (-CASE) [" + path + "]."); } + auto connNames = testGroup->testCasesConnNames[testCaseName]; testing::RegisterTest(testGroup->group.c_str(), testCaseName.c_str(), nullptr, nullptr, __FILE__, __LINE__, - [datasetType, dataset, bufferPoolSize, + [datasetType, dataset, bufferPoolSize, checkpointWaitTimeout, connNames, testStatements = std::move(testStatements)]() mutable -> DBTest* { - return new EndToEndTest( - datasetType, dataset, bufferPoolSize, std::move(testStatements)); + return new EndToEndTest(datasetType, dataset, bufferPoolSize, + checkpointWaitTimeout, connNames, std::move(testStatements)); }); } } else { diff --git a/test/test_files/transaction/int_delete_create_transaction.test b/test/test_files/transaction/int_delete_create_transaction.test new file mode 100644 index 00000000000..375a56fc5e6 --- /dev/null +++ b/test/test_files/transaction/int_delete_create_transaction.test @@ -0,0 +1,352 @@ +-GROUP CreateDeleteInt64NodeTrxTest +-DATASET CSV node-insertion-deletion-tests/int64-pk + +-- + +#MixedInsertDeleteCommitNormalExecution +-CASE MixedInsertDeleteCommitNormalExecution +-LOG Mixed Insert Delete Commit Normal Execution +#-CHECKPOINT_WAIT_TIMEOUT 100000 +-CREATE_CONNECTION conn1 +-CREATE_CONNECTION conn2 +-STATEMENT [conn2] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn2] MATCH (a:person) WHERE a.ID=8000 DELETE a; +---- ok +-STATEMENT [conn2] MATCH (a:person) WHERE a.ID=9000 DELETE a; +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:8000}); +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:9000}); +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:10001}); +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:10002}); +---- ok +-STATEMENT [conn2] Match (:person) RETURN COUNT(*); +---- 1 +10002 +-STATEMENT [conn1] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn2] COMMIT +---- ok +-STATEMENT [conn2] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn1] Match (:person) RETURN COUNT(*); +---- 1 +10002 +-STATEMENT [conn2] MATCH (a:person) DELETE a; +---- ok +-STATEMENT [conn1] Match (:person) RETURN COUNT(*); +---- 1 +10002 +-STATEMENT [conn2] Match (:person) RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn2] COMMIT +---- ok +-STATEMENT [conn1] Match (:person) RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn2] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:10000099}); +---- ok +-STATEMENT [conn1] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn2] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn2] COMMIT +---- ok +-STATEMENT [conn1] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +1 + +#MixedInsertDeleteCommitRecovery +-CASE MixedInsertDeleteCommitRecovery +-LOG Mixed Insert Delete Commit Recovery +-CREATE_CONNECTION conn1 +-CREATE_CONNECTION conn2 +-STATEMENT [conn2] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn2] MATCH (a:person) WHERE a.ID=8000 DELETE a; +---- ok +-STATEMENT [conn2] MATCH (a:person) WHERE a.ID=9000 DELETE a; +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:8000}); +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:9000}); +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:10001}); +---- ok +-STATEMENT [conn2] CREATE (a:person {ID:10002}); +---- ok +-STATEMENT [conn1] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn2] Match (:person) RETURN COUNT(*); +---- 1 +10002 + +#RECOVERY +#init db? +-STATEMENT [conn2] COMMIT_SKIP_CHECKPOINT +---- ok +-RELOADDB +-STATEMENT [conn2] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn2] Match (:person) RETURN COUNT(*); +---- 1 +10002 + +-STATEMENT [conn2] MATCH (a:person) DELETE a; +---- ok +-STATEMENT [conn1] Match (:person) RETURN COUNT(*); +---- 1 +10002 +-STATEMENT [conn2] Match (:person) RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn2] COMMIT_SKIP_CHECKPOINT +---- ok +-RELOADDB +-STATEMENT [conn2] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn1] Match (:person) RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn2] CREATE (a:person {ID:10000099}); +---- ok +-STATEMENT [conn1] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn2] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn2] COMMIT_SKIP_CHECKPOINT +---- ok +-RELOADDB +-STATEMENT [conn2] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn1] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +1 + + +#MixedInsertDeleteRollbackNormalExecution +-CASE MixedInsertDeleteRollbackNormalExecution +-LOG Mixed Insert Delete Rollback Normal Execution +-CREATE_CONNECTION conn_read +-CREATE_CONNECTION conn_write +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] MATCH (a:person) WHERE a.ID=8000 DELETE a; +---- ok +-STATEMENT [conn_write] MATCH (a:person) WHERE a.ID=9000 DELETE a; +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:8000}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:9000}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10001}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10002}); +---- ok +-STATEMENT [conn_read] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +10002 + +#Rollback false:is_commit NORMAL_EXECUTION +-STATEMENT [conn_write] Rollback +---- ok +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +10000 + +-STATEMENT [conn_write] MATCH (a:person) DELETE a; +---- ok +-STATEMENT [conn_read] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn_write] Rollback +---- ok + + +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn_write] CREATE (a:person {ID:10000099}); +---- ok +-STATEMENT [conn_read] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn_write] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_write] Rollback +---- ok + +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_read] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +0 + +#MixedInsertDeleteRollbackRecovery +-CASE MixedInsertDeleteRollbackRecovery +-LOG Mixed Insert Delete Rollback Recovery +-CREATE_CONNECTION conn_read +-CREATE_CONNECTION conn_write +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] MATCH (a:person) WHERE a.ID=8000 DELETE a; +---- ok +-STATEMENT [conn_write] MATCH (a:person) WHERE a.ID=9000 DELETE a; +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:8000}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:9000}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10001}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10002}); +---- ok +-STATEMENT [conn_read] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +10002 + +#Rollback false:is_commit RECOVERY +#init_db and conn? +-STATEMENT [conn_write] ROLLBACK_SKIP_CHECKPOINT +---- ok +-RELOADDB +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +10000 + +-STATEMENT [conn_write] MATCH (a:person) DELETE a; +---- ok +-STATEMENT [conn_read] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn_write] ROLLBACK_SKIP_CHECKPOINT +---- ok +-RELOADDB + +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] Match (:person) RETURN COUNT(*); +---- 1 +10000 +-STATEMENT [conn_write] CREATE (a:person {ID:10000099}); +---- ok +-STATEMENT [conn_read] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn_write] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_write] ROLLBACK_SKIP_CHECKPOINT +---- ok +-RELOADDB +-STATEMENT [conn_read] Match (a:person) where a.ID=10000099 RETURN COUNT(*); +---- 1 +0 + + +#IndexScanAfterInsertionCommitNormalExecution +-CASE IndexScanAfterInsertionCommitNormalExecution +-LOG Index Scan After Insertion Commit Normal Execution +-CREATE_CONNECTION conn_read +-CREATE_CONNECTION conn_write +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10003}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10005}); +---- ok +-STATEMENT [conn_write] Match (a:person) WHERE a.ID=10003 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_write] Match (a:person) WHERE a.ID=10005 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_read] Match (a:person) WHERE a.ID=10003 RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn_read] Match (a:person) WHERE a.ID=10005 RETURN COUNT(*); +---- 1 +0 + +#commit +-STATEMENT [conn_write] COMMIT +---- ok +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok + +-STATEMENT [conn_read] Match (a:person) WHERE a.ID=10003 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_read] Match (a:person) WHERE a.ID=10005 RETURN COUNT(*); +---- 1 +1 + +#IndexScanAfterInsertionCommitRecovery +-CASE IndexScanAfterInsertionCommitRecovery +-LOG Index Scan After Insertion Commit Recovery +-CREATE_CONNECTION conn_read +-CREATE_CONNECTION conn_write +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10003}); +---- ok +-STATEMENT [conn_write] CREATE (a:person {ID:10005}); +---- ok +-STATEMENT [conn_write] Match (a:person) WHERE a.ID=10003 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_write] Match (a:person) WHERE a.ID=10005 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_read] Match (a:person) WHERE a.ID=10003 RETURN COUNT(*); +---- 1 +0 +-STATEMENT [conn_read] Match (a:person) WHERE a.ID=10005 RETURN COUNT(*); +---- 1 +0 + +#true recovery +-STATEMENT [conn_write] COMMIT_SKIP_CHECKPOINT +---- ok +-RELOADDB +-STATEMENT [conn_write] BEGIN WRITE TRANSACTION +---- ok + +-STATEMENT [conn_write] Match (a:person) WHERE a.ID=10003 RETURN COUNT(*); +---- 1 +1 +-STATEMENT [conn_write] Match (a:person) WHERE a.ID=10005 RETURN COUNT(*); +---- 1 +1 + diff --git a/test/test_files/transaction/set_transaction.test b/test/test_files/transaction/set_transaction.test new file mode 100644 index 00000000000..29affbd68f3 --- /dev/null +++ b/test/test_files/transaction/set_transaction.test @@ -0,0 +1,79 @@ +-GROUP Set_Transaction +-DATASET CSV tinysnb + +-- + + +#SingleTransactionReadWriteToFixedLengthStructuredNodePropertyNonNullTest +-CASE NonNullTest +-LOG Single Transaction Read Write To Fixed Length Structured Node Property NonNull Test +-STATEMENT BEGIN WRITE TRANSACTION +---- ok +-STATEMENT MATCH (a:person) WHERE a.ID=0 RETURN a.age; +---- 1 +35 + +-STATEMENT MATCH (a:person) WHERE a.ID = 0 SET a.age = 70; +---- ok + +-STATEMENT MATCH (a:person) WHERE a.ID=0 RETURN a.age; +---- 1 +70 + +#NullTest +-CASE NullTest +-LOG Single Transaction Read Write To Fixed Length Structured Node Property Null Test +-STATEMENT BEGIN WRITE TRANSACTION +---- ok +-STATEMENT MATCH (a:person) WHERE a.ID=0 RETURN a.age; +---- 1 +35 + +-STATEMENT MATCH (a:person) WHERE a.ID = 0 SET a.age = null; +---- ok + +-STATEMENT MATCH (a:person) WHERE a.ID=0 RETURN a.age; +---- 1 + +#Timeout +-CASE TimeoutErrorTest +-LOG Open Read Only Transaction Triggers Timeout Error For Write Transaction +-CHECKPOINT_WAIT_TIMEOUT 10000 +-CREATE_CONNECTION conn1 +-STATEMENT [conn1] BEGIN READ TRANSACTION; +---- ok +-CREATE_CONNECTION conn2 +-STATEMENT [conn2] BEGIN WRITE TRANSACTION; +---- ok +-STATEMENT [conn2] MATCH (a:person) WHERE a.ID=0 set a.age=70; +---- ok +-STATEMENT [conn2] COMMIT +---- error +Timeout waiting for read transactions to leave the system before committing and checkpointing a write transaction. If you have an open read transaction close and try again. + +-STATEMENT [conn1] MATCH (a:person) WHERE a.ID=0 RETURN a.age; +---- 1 +35 + + +#RollbackTest +-CASE RollbackTest +-LOG Set Node Long String Prop Rollback Test +-STATEMENT BEGIN WRITE TRANSACTION; +---- ok +-STATEMENT MATCH (a:person) WHERE a.ID=0 SET a.fName='abcdefghijklmnopqrstuvwxyz' +---- ok +-STATEMENT ROLLBACK +---- ok +-STATEMENT MATCH (a:person) WHERE a.ID=0 RETURN a.fName; +---- 1 +Alice + +#VeryLongStringErrorsTest +-CASE VeryLongStringErrorsTest +-LOG Set Very Long String Errors Test +-STATEMENT BEGIN WRITE TRANSACTION; +---- ok +-DEFINE test_long_string REPEAT 4096 "a" +-STATEMENT MATCH (a:person) WHERE a.ID=0 SET a.fName='${test_long_string}'; +---- ok \ No newline at end of file diff --git a/test/test_runner/test_parser.cpp b/test/test_runner/test_parser.cpp index 328aed3c460..84ca160da8d 100644 --- a/test/test_runner/test_parser.cpp +++ b/test/test_runner/test_parser.cpp @@ -132,7 +132,8 @@ std::string TestParser::extractTextBeforeNextStatement(bool ignoreLineBreak) { return extractedText; } -TestStatement* TestParser::extractStatement(TestStatement* statement) { +TestStatement* TestParser::extractStatement( + TestStatement* statement, const std::string& testCaseName) { if (endOfFile()) { return statement; } @@ -143,8 +144,28 @@ TestStatement* TestParser::extractStatement(TestStatement* statement) { statement->logMessage = paramsToString(1); break; } + case TokenType::CHECKPOINT_WAIT_TIMEOUT: { + checkMinimumParams(1); + testGroup->checkpointWaitTimeout = stoi(currentToken.params[1]); + break; + } + case TokenType::CREATE_CONNECTION: { + checkMinimumParams(1); + testGroup->testCasesConnNames[testCaseName].insert(currentToken.params[1]); + break; + } + case TokenType::RELOADDB: { + statement->reLoadDBFlag = true; + return statement; + } case TokenType::STATEMENT: { std::string query = paramsToString(1); + bool statement_status = extractConnName(query, statement); + auto& caseStatus = testGroup->multipleConns; + caseStatus = caseStatus ? caseStatus : statement_status; + if (caseStatus) { + statement->conn_name = statement_status ? statement->conn_name : "default"; + } query += extractTextBeforeNextStatement(true); replaceVariables(query); statement->query = query; @@ -179,7 +200,7 @@ TestStatement* TestParser::extractStatement(TestStatement* statement) { } } nextLine(); - return extractStatement(statement); + return extractStatement(statement, testCaseName); } void TestParser::extractStatementBlock() { @@ -190,7 +211,7 @@ void TestParser::extractStatementBlock() { break; } else { auto statement = std::make_unique(); - extractStatement(statement.get()); + extractStatement(statement.get(), blockName); testGroup->testCasesStatementBlocks[blockName].push_back(std::move(statement)); } } @@ -277,7 +298,7 @@ void TestParser::parseBody() { default: { // if its not a special case, then it has to be a statement TestStatement* statement = addNewStatement(testCaseName); - extractStatement(statement); + extractStatement(statement, testCaseName); } } } @@ -289,6 +310,8 @@ void TestParser::addStatementBlock(const std::string& blockName, const std::stri for (const auto& statementPtr : testGroup->testCasesStatementBlocks[blockName]) { testGroup->testCases[testCaseName].push_back( std::make_unique(*statementPtr)); + testGroup->testCasesConnNames[testCaseName] = + std::move(testGroup->testCasesConnNames[blockName]); } } else { throw TestException("Statement block not found [" + path + ":" + blockName + "]."); @@ -299,6 +322,7 @@ TestStatement* TestParser::addNewStatement(std::string& testGroupName) { auto statement = std::make_unique(); TestStatement* currentStatement = statement.get(); testGroup->testCases[testGroupName].push_back(std::move(statement)); + // testCaseConnNames=testGroup->testCasesConnNames[testGroupName]; return currentStatement; } @@ -327,5 +351,19 @@ void TestParser::tokenize() { } } +bool TestParser::extractConnName(std::string& query, TestStatement* statement) { + std::regex pattern(R"(\[(conn.*?)\]\s*(.*))"); + std::smatch matches; + bool statement_status = false; + if (std::regex_search(query, matches, pattern)) { + if (matches.size() == 3) { + statement_status = true; + statement->conn_name = matches[1]; + query = matches[2]; + } + } + return statement_status; +} + } // namespace testing } // namespace kuzu diff --git a/test/test_runner/test_runner.cpp b/test/test_runner/test_runner.cpp index 890095ebd66..1f8520e2e95 100644 --- a/test/test_runner/test_runner.cpp +++ b/test/test_runner/test_runner.cpp @@ -12,14 +12,11 @@ using namespace kuzu::common; namespace kuzu { namespace testing { -void TestRunner::runTest(const std::vector>& statements, - Connection& conn, std::string& databasePath) { - for (auto& statement : statements) { - spdlog::info("DEBUG LOG: {}", statement->logMessage); - spdlog::info("QUERY: {}", statement->query); - conn.setMaxNumThreadForExec(statement->numThreads); - ASSERT_TRUE(testStatement(statement.get(), conn, databasePath)); - } +void TestRunner::runTest(TestStatement* statement, Connection& conn, std::string& databasePath) { + spdlog::info("DEBUG LOG: {}", statement->logMessage); + spdlog::info("QUERY: {}", statement->query); + conn.setMaxNumThreadForExec(statement->numThreads); + ASSERT_TRUE(testStatement(statement, conn, databasePath)); } bool TestRunner::testStatement(