//! \~\file piprotectedvariable_test.cpp //! \~\brief Unit tests for PIProtectedVariable class /* PIP - Platform Independent Primitives Unit tests for PIProtectedVariable This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piprotectedvariable.h" #include "pistring.h" #include "pithread.h" #include "gtest/gtest.h" #include using namespace std; // Basic functionality tests TEST(PIProtectedVariable_Basic, BasicFunctionality) { // Test basic set/get with different types PIProtectedVariable pvInt; PIProtectedVariable pvDouble; PIProtectedVariable pvString; pvInt.set(123); pvDouble.set(3.14159); pvString.set(PIString("Hello, World!")); EXPECT_EQ(pvInt.get(), 123); EXPECT_DOUBLE_EQ(pvDouble.get(), 3.14159); EXPECT_EQ(pvString.get(), PIString("Hello, World!")); // Test operator= pvInt = 999; EXPECT_EQ(pvInt.get(), 999); // Test getRef() with Pointer struct TestStruct { int x = 10; int y = 20; int getValue() const { return x + y; } }; PIProtectedVariable pvStruct; auto ptr = pvStruct.getRef(); EXPECT_EQ(ptr->x, 10); EXPECT_EQ(ptr->y, 20); EXPECT_EQ(ptr->getValue(), 30); // Modify through pointer *ptr = TestStruct(); ptr->x = 100; EXPECT_EQ(pvStruct.get().x, 100); // Test move semantics for Pointer pvInt.set(42); auto ptr1 = pvInt.getRef(); auto ptr2 = std::move(ptr1); EXPECT_EQ(*ptr2, 42); *ptr2 = 100; EXPECT_EQ(pvInt.get(), 100); } // Thread safety tests TEST(PIProtectedVariable_ThreadSafety, ConcurrentReadWrite) { PIProtectedVariable pv; atomic writeCount(0); atomic readCount(0); atomic invalidReads(0); const int NUM_ITERATIONS = 1000; const int NUM_WRITERS = 10; const int NUM_READERS = 20; const int TOTAL_WRITES = NUM_WRITERS * NUM_ITERATIONS; // Collect thread handles for joining PIVector threads; // Create writer threads for (int i = 0; i < NUM_WRITERS; ++i) { threads.push_back(new PIThread([&pv, &writeCount]() { for (int j = 0; j < NUM_ITERATIONS; ++j) { auto val = pv.getRef(); (*val)++; writeCount++; } })); } // Create reader threads for (int i = 0; i < NUM_READERS; ++i) { threads.push_back(new PIThread([&pv, &invalidReads, &readCount]() { for (int j = 0; j < NUM_ITERATIONS; ++j) { auto val = pv.get(); readCount++; // Value should always be in valid range [0, TOTAL_WRITES] if (val < 0 || val > TOTAL_WRITES) { invalidReads++; } } })); } // Start all threads for (auto * t: threads) { t->startOnce(); } // Wait for all threads to finish for (auto * t: threads) { t->waitForFinish(PISystemTime::fromSeconds(5)); delete t; } // Verify results EXPECT_EQ(writeCount, TOTAL_WRITES); EXPECT_EQ(readCount, NUM_READERS * NUM_ITERATIONS); EXPECT_EQ(invalidReads, 0) << "All reads should return valid values in range [0, " << TOTAL_WRITES << "]"; // Final value should be TOTAL_WRITES int finalVal = pv.get(); EXPECT_EQ(finalVal, TOTAL_WRITES); }