diff --git a/test/storagetransaction.cpp b/test/storagetransaction.cpp index e756931..eac7ac2 100644 --- a/test/storagetransaction.cpp +++ b/test/storagetransaction.cpp @@ -1,5 +1,7 @@ #include +#include + #include "base.h" #include "storage.h" @@ -12,6 +14,24 @@ protected: ~StorageTransactionsTest() {} + int waitForChildFork(int pid) { + int status; + if (0 > waitpid(pid, &status, 0)) { + std::cerr << "[----------] Waitpid error!" << std::endl; + return (-1); + } + if (WIFEXITED(status)) { + const int exit_status = WEXITSTATUS(status); + if (exit_status != 0) { + std::cerr << "[----------] Non-zero exit status " << exit_status << " from test!" << std::endl; + } + return exit_status; + } else { + std::cerr << "[----------] Non-normal exit from child!" << std::endl; + return (-2); + } + } + static void SetUpTestSuite() { if (db == nullptr) { db = new LMDBAL::Base("storageTrnansactionsTestBase"); @@ -20,6 +40,7 @@ protected: } db->open(); + db->drop(); } static void TearDownTestSuite() { @@ -109,3 +130,100 @@ TEST_F(StorageTransactionsTest, Reading) { db->abortTransaction(txn); } + +TEST_F(StorageTransactionsTest, ConcurentReading) { + EXPECT_EQ(db->ready(), true); + + LMDBAL::SizeType size = t1->count(); + LMDBAL::TransactionID txn = db->beginTransaction(); + EXPECT_EQ(t1->getRecord(5, txn), 13); + EXPECT_EQ(t1->getRecord(5), 13); + + t1->removeRecord(5, txn); + + EXPECT_FALSE(t1->checkRecord(5, txn)); + EXPECT_EQ(t1->getRecord(5), 13); + + t1->addRecord(5, 571, txn); + EXPECT_EQ(t1->getRecord(5, txn), 571); + EXPECT_EQ(t1->getRecord(5), 13); + + t1->forceRecord(5, -472, txn); + EXPECT_EQ(t1->getRecord(5, txn), -472); + EXPECT_EQ(t1->getRecord(5), 13); + + t1->replaceAll({ + {1, 75} + }, txn); + EXPECT_FALSE(t1->checkRecord(5, txn)); + EXPECT_EQ(t1->getRecord(5), 13); + EXPECT_EQ(t1->count(txn), 1); + EXPECT_EQ(t1->count(), size); + + db->commitTransaction(txn); + + EXPECT_FALSE(t1->checkRecord(5)); + EXPECT_EQ(t1->count(), 1); +} + +TEST_F(StorageTransactionsTest, ConcurentModification) { + EXPECT_EQ(db->ready(), true); + + //if you start one writable transaction after another + //in a single thread like so: + // + //LMDBAL::TransactionID txn1 = db->beginTransaction(); + //LMDBAL::TransactionID txn2 = db->beginTransaction(); + // + //the execution should block on the second transaction + //so this test should preform in a sequence + //first the parent, then the child + + int pid = fork(); + if (pid == 0) { // I am the child + std::cout << "beggining second transaction" << std::endl; + LMDBAL::TransactionID txn2 = db->beginTransaction(); //<--- this is where the execution should pause + //and wait for the first transaction to get finished + std::cout << "checking result of the first transaction value" << std::endl; + EXPECT_EQ(t1->getRecord(5, txn2), 812); + + std::cout << "forcing second transaction value" << std::endl; + t1->forceRecord(5, -46, txn2); + + std::cout << "checking second transaction value" << std::endl; + EXPECT_EQ(t1->getRecord(5, txn2), -46); + + std::cout << "checking value independently" << std::endl; + EXPECT_EQ(t1->getRecord(5), 812); + + std::cout << "commiting second transaction" << std::endl; + db->commitTransaction(txn2); + + std::cout << "quitting child thread" << std::endl; + exit(testing::Test::HasFailure()); + } else { // I am the parent + std::cout << "beggining first transaction" << std::endl; + LMDBAL::TransactionID txn1 = db->beginTransaction(); + + std::cout << "putting parent thread to sleep for 5 ms" << std::endl; + usleep(5); + + std::cout << "adding first transaction value" << std::endl; + t1->addRecord(5, 812, txn1); + + std::cout << "checking first transaction value" << std::endl; + EXPECT_EQ(t1->getRecord(5, txn1), 812); + + std::cout << "checking value independently" << std::endl; + EXPECT_FALSE(t1->checkRecord(5)); + + std::cout << "commiting first transaction" << std::endl; + db->commitTransaction(txn1); + + std::cout << "waiting for the other thread to finish" << std::endl; + ASSERT_EQ(0, waitForChildFork(pid)); //child process should have no problems + } + + std::cout << "checking final result" << std::endl; + EXPECT_EQ(t1->getRecord(5), -46); +}