Skip to content
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
123 changes: 123 additions & 0 deletions Tests/ModernThreadSafeStack.Tests/ModernThreadSafeStack.Tests.vcxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{7f150933-516e-4c74-89b3-73754086ff32}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v145</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="test.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets" Condition="Exists('..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Данный проект ссылается на пакеты NuGet, отсутствующие на этом компьютере. Используйте восстановление пакетов NuGet, чтобы скачать их. Дополнительную информацию см. по адресу: http://go.microsoft.com/fwlink/?LinkID=322105. Отсутствует следующий файл: {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets'))" />
</Target>
</Project>
4 changes: 4 additions & 0 deletions Tests/ModernThreadSafeStack.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn" version="1.8.1.8" targetFramework="native" />
</packages>
5 changes: 5 additions & 0 deletions Tests/ModernThreadSafeStack.Tests/pch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//
// pch.cpp
//

#include "pch.h"
7 changes: 7 additions & 0 deletions Tests/ModernThreadSafeStack.Tests/pch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//
// pch.h
//

#pragma once

#include "gtest/gtest.h"
201 changes: 201 additions & 0 deletions Tests/ModernThreadSafeStack.Tests/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#include "pch.h"

#include <gtest/gtest.h>
#include <thread>
#include <vector>
#include <atomic>
#include <string>
#include "../../ThreadSafeStack/ModernThreadSafeStack.h"

/**
* @category Functional Tests (Single-threaded)
* @brief These tests verify the core logic of the stack in a deterministic,
* single-threaded environment to ensure basic LIFO behavior.
*/

// @test Verifies that a newly created stack is empty and has a size of zero.
TEST(ModernThreadSafeStackTest, InitialStateIsCorrect) {
ModernThreadSafeStack<int> stack;
EXPECT_TRUE(stack.isEmpty());
EXPECT_EQ(stack.getSize(), 0);
}

// @test Validates basic push and pop operations, ensuring LIFO order and proper size updates.
TEST(ModernThreadSafeStackTest, PushAndPopValues) {
ModernThreadSafeStack<int> stack;
stack.push(10);
stack.push(20);

EXPECT_FALSE(stack.isEmpty());
EXPECT_EQ(stack.getSize(), 2);

int val = 0;
stack.pop(val); // Pop 20
EXPECT_EQ(val, 20);

auto ptr = stack.pop(); // Pop 10
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(*ptr, 10);
EXPECT_TRUE(stack.isEmpty());
}

// @test Ensures that popping from an empty stack throws a std::out_of_range exception.
TEST(ModernThreadSafeStackTest, PopEmptyStackThrowsException) {
ModernThreadSafeStack<int> stack;
int val = 0;

EXPECT_THROW(stack.pop(), std::out_of_range);
EXPECT_THROW(stack.pop(val), std::out_of_range);
}

// @test Tests non-throwing pop variants (tryPop) for both empty and non-empty stack scenarios.
TEST(ModernThreadSafeStackTest, TryPopWorksCorrectly) {
ModernThreadSafeStack<int> stack;
int val = 0;

// Test tryPop on empty stack
EXPECT_FALSE(stack.tryPop(val));
EXPECT_EQ(stack.tryPop(), nullptr);

stack.push(42);

// Test successful tryPop
EXPECT_TRUE(stack.tryPop(val));
EXPECT_EQ(val, 42);
EXPECT_TRUE(stack.isEmpty());
}

// @test Confirms that the clear() method removes all elements and resets the stack state.
TEST(ModernThreadSafeStackTest, ClearEmptiesTheStack) {
ModernThreadSafeStack<std::string> stack;
stack.push("data");
stack.clear();

EXPECT_TRUE(stack.isEmpty());
EXPECT_EQ(stack.getSize(), 0);
}

// @test Verifies thread-safe deep copying and ensures the source stack remains intact.
TEST(ModernThreadSafeStackTest, CopyConstructorWorks) {
ModernThreadSafeStack<int> original;
original.push(100);

ModernThreadSafeStack<int> copy(original);

EXPECT_EQ(copy.getSize(), 1);
int val = 0;
copy.pop(val);
EXPECT_EQ(val, 100);
EXPECT_EQ(original.getSize(), 1); // Source must be unchanged
}

// @test Verifies move semantics, ensuring resources are transferred and the source is left valid.
TEST(ModernThreadSafeStackTest, MoveConstructorWorks) {
ModernThreadSafeStack<int> original;
original.push(500);

ModernThreadSafeStack<int> moved(std::move(original));

EXPECT_EQ(moved.getSize(), 1);
EXPECT_TRUE(original.isEmpty()); // Source should be empty after move
}

/**
* @category Concurrency Tests (Multi-threaded)
* @brief These tests stress the implementation under high contention to detect
* race conditions, deadlocks, or data corruption.
*/

// @test Verifies thread safety during simultaneous push operations from multiple threads.
TEST(ModernThreadSafeStackTest, ConcurrentPushes) {
ModernThreadSafeStack<int> stack;
const int numThreads = 10;
const int numPushesPerThread = 1000;
std::vector<std::thread> threads;

for (int i = 0; i < numThreads; ++i) {
threads.emplace_back([&stack, numPushesPerThread]() {
for (int j = 0; j < numPushesPerThread; ++j) {
stack.push(j);
}
});
}

for (auto& t : threads) t.join();

EXPECT_EQ(stack.getSize(), numThreads * numPushesPerThread);
}

// @test Ensures no data loss or double-popping occurs when multiple threads consume the stack.
TEST(ModernThreadSafeStackTest, ConcurrentPops) {
ModernThreadSafeStack<int> stack;
const int totalElements = 5000;
for (int i = 0; i < totalElements; ++i) stack.push(i);

std::atomic<int> popCount{ 0 };
const int numThreads = 8;
std::vector<std::thread> threads;

for (int i = 0; i < numThreads; ++i) {
threads.emplace_back([&stack, &popCount]() {
int val;
while (stack.tryPop(val)) {
popCount++;
}
});
}

for (auto& t : threads) t.join();

EXPECT_EQ(popCount.load(), totalElements);
EXPECT_TRUE(stack.isEmpty());
}

/**
* @test Comprehensive stress test for simultaneous producers and consumers.
* Verifies overall data integrity by comparing the sum of pushed values
* against the sum of popped values (checksum validation).
*/
TEST(ModernThreadSafeStackTest, ConcurrentPushAndPopDataIntegrity) {
ModernThreadSafeStack<int> stack;
const int numThreads = 4;
const int itemsPerThread = 2000;

std::atomic<int> totalPopped{ 0 };
std::atomic<bool> producersDone{ false };
std::atomic<long long> sumPushed{ 0 };
std::atomic<long long> sumPopped{ 0 };

auto producer = [&]() {
for (int j = 1; j <= itemsPerThread; ++j) {
stack.push(j);
sumPushed += j;
}
};

auto consumer = [&]() {
int val;
while (!producersDone || !stack.isEmpty()) {
if (stack.tryPop(val)) {
totalPopped++;
sumPopped += val;
}
else {
std::this_thread::yield();
}
}
};

std::vector<std::thread> producers, consumers;
for (int i = 0; i < numThreads; ++i) {
producers.emplace_back(producer);
consumers.emplace_back(consumer);
}

for (auto& t : producers) t.join();
producersDone = true;
for (auto& t : consumers) t.join();

EXPECT_EQ(totalPopped.load(), numThreads * itemsPerThread);
EXPECT_EQ(sumPushed.load(), sumPopped.load());
}
1 change: 1 addition & 0 deletions ThreadSafeStack.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="Tests/ModernThreadSafeStack.Tests/ModernThreadSafeStack.Tests.vcxproj" />
<Project Path="ThreadSafeStack/ThreadSafeStack.vcxproj" />
</Solution>
9 changes: 9 additions & 0 deletions ThreadSafeStack/ModernThreadSafeStack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "ModernThreadSafeStack.h"

// Explicit template instantiations for commonly used types.
// This forces the compiler to generate implementations for these types
// in this translation unit, which can reduce compilation times and
// prevent linker errors when the template is used across multiple files.
template class ModernThreadSafeStack<int>;
template class ModernThreadSafeStack<double>;
template class ModernThreadSafeStack<std::string>;
Loading