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
1 change: 1 addition & 0 deletions Tests/ModernThreadSafeStack.Tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>
#include <atomic>
#include <string>

#include "../../ThreadSafeStack/ModernThreadSafeStack.h"

/**
Expand Down
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>{763327fa-3786-480d-8bf6-856899243cd6}</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/WaitingThreadSafeStack.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/WaitingThreadSafeStack.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/WaitingThreadSafeStack.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/WaitingThreadSafeStack.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 <chrono>
#include <vector>
#include <atomic>

#include "../../ThreadSafeStack/WaitingThreadSafeStack.h"

using namespace std::chrono_literals;

/**
* @category Unit Tests - Basic Functionality
* @brief Tests the standard stack operations in a single-threaded context.
*/

// @test Ensures the stack is correctly initialized as empty.
TEST(WaitingThreadSafeStackTest, InitialStateIsEmpty) {
WaitingThreadSafeStack<int> stack;
EXPECT_TRUE(stack.isEmpty());
EXPECT_EQ(stack.getSize(), 0);
}

// @test Verifies basic push and pop operations with LIFO (Last-In-First-Out) order.
TEST(WaitingThreadSafeStackTest, PushAndPopVerifiesLIFO) {
WaitingThreadSafeStack<int> stack;
stack.push(10);
stack.push(20);

EXPECT_EQ(stack.getSize(), 2);

int result = 0;
stack.pop(result);
EXPECT_EQ(result, 20);

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

// @test Checks that top() provides access to the element without removing it.
TEST(WaitingThreadSafeStackTest, TopReturnsElementWithoutPopping) {
WaitingThreadSafeStack<std::string> stack;
stack.push("first");

EXPECT_EQ(stack.top(), "first");
EXPECT_EQ(stack.getSize(), 1); // Size should remain 1
}

// @test Verifies that pushing multiple elements via initializer list works and notifies correctly.
TEST(WaitingThreadSafeStackTest, PushMultipleInitializesCorrectly) {
WaitingThreadSafeStack<int> stack;
stack.push_multiple({ 1, 2, 3, 4, 5 });

EXPECT_EQ(stack.getSize(), 5);
EXPECT_EQ(stack.top(), 5);
}

// @test Ensures proper exception handling when accessing an empty stack.
TEST(WaitingThreadSafeStackTest, ThrowsExceptionOnEmptyStack) {
WaitingThreadSafeStack<int> stack;
int value;

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

/**
* @category Concurrency Tests - Blocking and Timed Operations
* @brief Tests how the stack handles threads waiting for data and time-based operations.
*/

// @test Verifies that waitAndPop blocks until another thread pushes data.
TEST(WaitingThreadSafeStackTest, WaitAndPopBlocksUntilDataIsAvailable) {
WaitingThreadSafeStack<int> stack;
int poppedValue = 0;

// Run waitAndPop in a separate thread
std::thread consumer([&]() {
stack.waitAndPop(poppedValue);
});

// Simulate some work
std::this_thread::sleep_for(100ms);
EXPECT_EQ(poppedValue, 0); // Still 0 because stack is empty

stack.push(42); // This should wake up the consumer

if (consumer.joinable()) {
consumer.join();
}

EXPECT_EQ(poppedValue, 42);
}

// @test Verifies that waitAndPopFor returns false after the specified timeout.
TEST(WaitingThreadSafeStackTest, WaitAndPopForTimesOutOnEmptyStack) {
WaitingThreadSafeStack<int> stack;
int value = -1;

auto start = std::chrono::steady_clock::now();
bool success = stack.waitAndPopFor(value, 200ms);
auto end = std::chrono::steady_clock::now();

EXPECT_FALSE(success);
EXPECT_GE(end - start, 200ms); // Ensure it actually waited
}

// @test Verifies that waitAndPopFor returns true if data is pushed before timeout.
TEST(WaitingThreadSafeStackTest, WaitAndPopForSucceedsWithinTimeout) {
WaitingThreadSafeStack<int> stack;

std::thread producer([&]() {
std::this_thread::sleep_for(50ms);
stack.push(99);
});

auto result = stack.waitAndPopFor(500ms);

ASSERT_NE(result, nullptr);
EXPECT_EQ(*result, 99);

if (producer.joinable()) producer.join();
}

/**
* @category Thread-Safety Stress Tests
* @brief Heavy contention tests to verify data integrity and absence of race conditions.
*/

// @test Stress test with multiple producers and consumers.
TEST(WaitingThreadSafeStackTest, MultiThreadedProducersAndConsumers) {
WaitingThreadSafeStack<int> stack;
const int numProducers = 4;
const int numConsumers = 4;
const int itemsPerProducer = 1000;
const int totalItems = numProducers * itemsPerProducer;

std::atomic<int> consumeCount{ 0 };
std::atomic<long long> checksum{ 0 };

auto producerFunc = [&](int id) {
for (int i = 0; i < itemsPerProducer; ++i) {
stack.push(id * 10000 + i);
}
};

auto consumerFunc = [&]() {
for (int i = 0; i < itemsPerProducer; ++i) {
int val;
stack.waitAndPop(val);
checksum += val;
consumeCount++;
}
};

std::vector<std::thread> threads;
for (int i = 0; i < numProducers; ++i) threads.emplace_back(producerFunc, i);
for (int i = 0; i < numConsumers; ++i) threads.emplace_back(consumerFunc);

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

EXPECT_EQ(consumeCount.load(), totalItems);
EXPECT_TRUE(stack.isEmpty());
}

// @test Verifies that copy constructor safely captures the state under contention.
TEST(WaitingThreadSafeStackTest, CopyConstructorIsThreadSafe) {
WaitingThreadSafeStack<int> stack;

// Fill the stack
for (int i = 0; i < 100; ++i) stack.push(i);

// Thread trying to push more elements while we copy
std::thread pusher([&]() {
for (int i = 100; i < 200; ++i) stack.push(i);
});

// Perform copy
WaitingThreadSafeStack<int> copyStack(stack);

pusher.join();

// The copy should have at least the initial 100 elements
EXPECT_GE(copyStack.getSize(), 100);
}

// @test Verifies that move constructor transfers ownership and leaves source valid.
TEST(WaitingThreadSafeStackTest, MoveConstructorTransfersData) {
WaitingThreadSafeStack<int> source;
source.push(777);

size_t oldSize = source.getSize();
WaitingThreadSafeStack<int> destination(std::move(source));

EXPECT_EQ(destination.getSize(), oldSize);
EXPECT_EQ(destination.top(), 777);
// Note: source internal container state depends on std::stack move,
// but the object itself remains in a valid state.
}
5 changes: 3 additions & 2 deletions ThreadSafeStack.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="Tests/ModernThreadSafeStack.Tests/ModernThreadSafeStack.Tests.vcxproj" />
<Project Path="ThreadSafeStack/ThreadSafeStack.vcxproj" />
<Project Path="Tests/ModernThreadSafeStack.Tests/ModernThreadSafeStack.Tests.vcxproj" Id="7f150933-516e-4c74-89b3-73754086ff32" />
<Project Path="Tests/WaitingThreadSafeStack.Tests/WaitingThreadSafeStack.Tests.vcxproj" Id="763327fa-3786-480d-8bf6-856899243cd6" />
<Project Path="ThreadSafeStack/ThreadSafeStack.vcxproj" Id="fcf27f67-3832-4d60-a114-fd618ddb9116" />
</Solution>
2 changes: 2 additions & 0 deletions ThreadSafeStack/ThreadSafeStack.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,11 @@
<ItemGroup>
<ClCompile Include="ModernThreadSafeStack.cpp" />
<ClCompile Include="ThreadSafeStack.cpp" />
<ClCompile Include="WaitingThreadSafeStack.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ModernThreadSafeStack.h" />
<ClInclude Include="WaitingThreadSafeStack.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
Expand Down
6 changes: 6 additions & 0 deletions ThreadSafeStack/ThreadSafeStack.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,16 @@
<ClCompile Include="ModernThreadSafeStack.cpp">
<Filter>Исходные файлы</Filter>
</ClCompile>
<ClCompile Include="WaitingThreadSafeStack.cpp">
<Filter>Исходные файлы</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ModernThreadSafeStack.h">
<Filter>Файлы заголовков</Filter>
</ClInclude>
<ClInclude Include="WaitingThreadSafeStack.h">
<Filter>Файлы заголовков</Filter>
</ClInclude>
</ItemGroup>
</Project>
Loading