mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-24 04:00:02 +01:00
287 lines
7.8 KiB
C++
287 lines
7.8 KiB
C++
|
|
#include "pch.h"
|
||
|
|
#include "TestHelpers.h"
|
||
|
|
#include <serialized.h>
|
||
|
|
|
||
|
|
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||
|
|
|
||
|
|
namespace UnitTestsCommonUtils
|
||
|
|
{
|
||
|
|
TEST_CLASS(SerializedTests)
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
// Basic Read tests
|
||
|
|
TEST_METHOD(Read_DefaultState_ReturnsDefaultValue)
|
||
|
|
{
|
||
|
|
Serialized<int> s;
|
||
|
|
int value = -1;
|
||
|
|
s.Read([&value](const int& v) {
|
||
|
|
value = v;
|
||
|
|
});
|
||
|
|
Assert::AreEqual(0, value); // Default constructed int is 0
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_METHOD(Read_StringType_ReturnsEmpty)
|
||
|
|
{
|
||
|
|
Serialized<std::string> s;
|
||
|
|
std::string value = "initial";
|
||
|
|
s.Read([&value](const std::string& v) {
|
||
|
|
value = v;
|
||
|
|
});
|
||
|
|
Assert::AreEqual(std::string(""), value);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Basic Access tests
|
||
|
|
TEST_METHOD(Access_ModifyValue_ValueIsModified)
|
||
|
|
{
|
||
|
|
Serialized<int> s;
|
||
|
|
s.Access([](int& v) {
|
||
|
|
v = 42;
|
||
|
|
});
|
||
|
|
|
||
|
|
int value = 0;
|
||
|
|
s.Read([&value](const int& v) {
|
||
|
|
value = v;
|
||
|
|
});
|
||
|
|
Assert::AreEqual(42, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_METHOD(Access_ModifyString_StringIsModified)
|
||
|
|
{
|
||
|
|
Serialized<std::string> s;
|
||
|
|
s.Access([](std::string& v) {
|
||
|
|
v = "hello";
|
||
|
|
});
|
||
|
|
|
||
|
|
std::string value;
|
||
|
|
s.Read([&value](const std::string& v) {
|
||
|
|
value = v;
|
||
|
|
});
|
||
|
|
Assert::AreEqual(std::string("hello"), value);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_METHOD(Access_MultipleModifications_LastValuePersists)
|
||
|
|
{
|
||
|
|
Serialized<int> s;
|
||
|
|
s.Access([](int& v) { v = 1; });
|
||
|
|
s.Access([](int& v) { v = 2; });
|
||
|
|
s.Access([](int& v) { v = 3; });
|
||
|
|
|
||
|
|
int value = 0;
|
||
|
|
s.Read([&value](const int& v) {
|
||
|
|
value = v;
|
||
|
|
});
|
||
|
|
Assert::AreEqual(3, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reset tests
|
||
|
|
TEST_METHOD(Reset_AfterModification_ReturnsDefault)
|
||
|
|
{
|
||
|
|
Serialized<int> s;
|
||
|
|
s.Access([](int& v) { v = 42; });
|
||
|
|
s.Reset();
|
||
|
|
|
||
|
|
int value = -1;
|
||
|
|
s.Read([&value](const int& v) {
|
||
|
|
value = v;
|
||
|
|
});
|
||
|
|
Assert::AreEqual(0, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_METHOD(Reset_String_ReturnsEmpty)
|
||
|
|
{
|
||
|
|
Serialized<std::string> s;
|
||
|
|
s.Access([](std::string& v) { v = "hello"; });
|
||
|
|
s.Reset();
|
||
|
|
|
||
|
|
std::string value = "initial";
|
||
|
|
s.Read([&value](const std::string& v) {
|
||
|
|
value = v;
|
||
|
|
});
|
||
|
|
Assert::AreEqual(std::string(""), value);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Complex type tests
|
||
|
|
TEST_METHOD(Serialized_VectorType_Works)
|
||
|
|
{
|
||
|
|
Serialized<std::vector<int>> s;
|
||
|
|
s.Access([](std::vector<int>& v) {
|
||
|
|
v.push_back(1);
|
||
|
|
v.push_back(2);
|
||
|
|
v.push_back(3);
|
||
|
|
});
|
||
|
|
|
||
|
|
size_t size = 0;
|
||
|
|
int sum = 0;
|
||
|
|
s.Read([&size, &sum](const std::vector<int>& v) {
|
||
|
|
size = v.size();
|
||
|
|
for (int i : v) sum += i;
|
||
|
|
});
|
||
|
|
|
||
|
|
Assert::AreEqual(static_cast<size_t>(3), size);
|
||
|
|
Assert::AreEqual(6, sum);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_METHOD(Serialized_MapType_Works)
|
||
|
|
{
|
||
|
|
Serialized<std::map<std::string, int>> s;
|
||
|
|
s.Access([](std::map<std::string, int>& v) {
|
||
|
|
v["one"] = 1;
|
||
|
|
v["two"] = 2;
|
||
|
|
});
|
||
|
|
|
||
|
|
int value = 0;
|
||
|
|
s.Read([&value](const std::map<std::string, int>& v) {
|
||
|
|
auto it = v.find("two");
|
||
|
|
if (it != v.end()) {
|
||
|
|
value = it->second;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
Assert::AreEqual(2, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Thread safety tests
|
||
|
|
TEST_METHOD(ThreadSafety_ConcurrentReads_NoDataRace)
|
||
|
|
{
|
||
|
|
Serialized<int> s;
|
||
|
|
s.Access([](int& v) { v = 42; });
|
||
|
|
|
||
|
|
std::atomic<int> readCount{ 0 };
|
||
|
|
std::vector<std::thread> threads;
|
||
|
|
|
||
|
|
for (int i = 0; i < 10; ++i)
|
||
|
|
{
|
||
|
|
threads.emplace_back([&s, &readCount]() {
|
||
|
|
for (int j = 0; j < 100; ++j)
|
||
|
|
{
|
||
|
|
s.Read([&readCount](const int& v) {
|
||
|
|
if (v == 42) {
|
||
|
|
readCount++;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (auto& t : threads)
|
||
|
|
{
|
||
|
|
t.join();
|
||
|
|
}
|
||
|
|
|
||
|
|
Assert::AreEqual(1000, readCount.load());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_METHOD(ThreadSafety_ConcurrentAccessAndRead_NoDataRace)
|
||
|
|
{
|
||
|
|
Serialized<int> s;
|
||
|
|
std::atomic<bool> done{ false };
|
||
|
|
std::atomic<int> accessCount{ 0 };
|
||
|
|
std::atomic<int> readersReady{ 0 };
|
||
|
|
std::atomic<bool> start{ false };
|
||
|
|
|
||
|
|
// Writer thread
|
||
|
|
std::thread writer([&s, &done, &accessCount, &readersReady, &start]() {
|
||
|
|
while (readersReady.load() < 5)
|
||
|
|
{
|
||
|
|
std::this_thread::yield();
|
||
|
|
}
|
||
|
|
start = true;
|
||
|
|
for (int i = 0; i < 100; ++i)
|
||
|
|
{
|
||
|
|
s.Access([i](int& v) {
|
||
|
|
v = i;
|
||
|
|
});
|
||
|
|
accessCount++;
|
||
|
|
}
|
||
|
|
done = true;
|
||
|
|
});
|
||
|
|
|
||
|
|
// Reader threads
|
||
|
|
std::vector<std::thread> readers;
|
||
|
|
std::atomic<int> readAttempts{ 0 };
|
||
|
|
|
||
|
|
for (int i = 0; i < 5; ++i)
|
||
|
|
{
|
||
|
|
readers.emplace_back([&s, &done, &readAttempts, &readersReady, &start]() {
|
||
|
|
readersReady++;
|
||
|
|
while (!start)
|
||
|
|
{
|
||
|
|
std::this_thread::yield();
|
||
|
|
}
|
||
|
|
while (!done)
|
||
|
|
{
|
||
|
|
s.Read([](const int& v) {
|
||
|
|
// Just read the value
|
||
|
|
(void)v;
|
||
|
|
});
|
||
|
|
readAttempts++;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
writer.join();
|
||
|
|
for (auto& t : readers)
|
||
|
|
{
|
||
|
|
t.join();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Verify all access calls completed
|
||
|
|
Assert::AreEqual(100, accessCount.load());
|
||
|
|
// Verify reads happened
|
||
|
|
Assert::IsTrue(readAttempts > 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Struct type test
|
||
|
|
TEST_METHOD(Serialized_StructType_Works)
|
||
|
|
{
|
||
|
|
struct TestStruct
|
||
|
|
{
|
||
|
|
int x = 0;
|
||
|
|
std::string name;
|
||
|
|
};
|
||
|
|
|
||
|
|
Serialized<TestStruct> s;
|
||
|
|
s.Access([](TestStruct& v) {
|
||
|
|
v.x = 10;
|
||
|
|
v.name = "test";
|
||
|
|
});
|
||
|
|
|
||
|
|
int x = 0;
|
||
|
|
std::string name;
|
||
|
|
s.Read([&x, &name](const TestStruct& v) {
|
||
|
|
x = v.x;
|
||
|
|
name = v.name;
|
||
|
|
});
|
||
|
|
|
||
|
|
Assert::AreEqual(10, x);
|
||
|
|
Assert::AreEqual(std::string("test"), name);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_METHOD(Reset_StructType_ResetsToDefault)
|
||
|
|
{
|
||
|
|
struct TestStruct
|
||
|
|
{
|
||
|
|
int x = 0;
|
||
|
|
std::string name;
|
||
|
|
};
|
||
|
|
|
||
|
|
Serialized<TestStruct> s;
|
||
|
|
s.Access([](TestStruct& v) {
|
||
|
|
v.x = 10;
|
||
|
|
v.name = "test";
|
||
|
|
});
|
||
|
|
s.Reset();
|
||
|
|
|
||
|
|
int x = -1;
|
||
|
|
std::string name = "not empty";
|
||
|
|
s.Read([&x, &name](const TestStruct& v) {
|
||
|
|
x = v.x;
|
||
|
|
name = v.name;
|
||
|
|
});
|
||
|
|
|
||
|
|
Assert::AreEqual(0, x);
|
||
|
|
Assert::AreEqual(std::string(""), name);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|