Compare commits

..

1 Commits

Author SHA1 Message Date
Gordon Lam (SH)
240083bede feat(paste): add setting to show/hide AI paste section 2026-01-30 22:37:05 -08:00
71 changed files with 80 additions and 5924 deletions

View File

@@ -101,7 +101,6 @@
^doc/devdocs/akaLinks\.md$
^NOTICE\.md$
^src/common/CalculatorEngineCommon/exprtk\.hpp$
^src/common/UnitTests-CommonUtils/
^src/common/ManagedCommon/ColorFormatHelper\.cs$
^src/common/notifications/BackgroundActivatorDLL/cpp\.hint$
^src/common/sysinternals/Eula/

View File

@@ -635,7 +635,6 @@ GMEM
GNumber
googleai
googlegemini
Gotchas
gpedit
gpo
GPOCA
@@ -648,6 +647,8 @@ GSM
gtm
guiddata
GUITHREADINFO
Gotcha
Gotchas
GValue
gwl
GWLP
@@ -893,9 +894,9 @@ Lclean
Ldone
Ldr
LEFTALIGN
leftclick
LEFTSCROLLBAR
LEFTTEXT
leftclick
LError
LEVELID
LExit
@@ -1021,12 +1022,9 @@ MENUITEMINFO
MENUITEMINFOW
MERGECOPY
MERGEPAINT
Metacharacter
metadatamatters
Metadatas
Metacharacter
metafile
Metacharacter
mfc
Mgmt
Microwaved
@@ -1073,7 +1071,7 @@ mouseutils
MOVESIZEEND
MOVESIZESTART
MRM
Mrt
MRT
mru
MSAL
msc
@@ -1491,9 +1489,7 @@ regfile
REGISTERCLASSFAILED
REGISTRYHEADER
REGISTRYPREVIEWEXT
registryroot
regkey
regroot
regsvr
REINSTALLMODE
releaseblog
@@ -1538,6 +1534,7 @@ riid
RKey
RNumber
rollups
ROOTOWNER
rop
ROUNDSMALL
ROWSETEXT
@@ -2174,4 +2171,4 @@ Zoneszonabletester
Zoomin
zoomit
ZOOMITX
Zorder
Zorder

View File

@@ -91,7 +91,6 @@ extends:
official: true
codeSign: true
runTests: false
buildTests: false
signingIdentity:
serviceName: $(SigningServiceName)
appId: $(SigningAppId)

View File

@@ -258,7 +258,6 @@ jobs:
-restore -graph
/p:RestorePackagesConfig=true
/p:CIBuild=true
/p:BuildTests=${{ parameters.buildTests }}
/bl:$(LogOutputDirectory)\build-0-main.binlog
${{ parameters.additionalBuildOptions }}
$(MSBuildCacheParameters)

View File

@@ -59,7 +59,6 @@ stages:
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
msBuildCacheIsReadOnly: ${{ parameters.msBuildCacheIsReadOnly }}
runTests: ${{ parameters.runTests }}
buildTests: true
useVSPreview: ${{ parameters.useVSPreview }}
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
${{ if eq(parameters.useLatestWinAppSDK, true) }}:
@@ -79,9 +78,7 @@ stages:
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS18-Preview
${{ else }}:
demands: ImageOverride -equals SHINE-VS18-Latest
demands: ImageOverride -equals SHINE-VS17-Preview
buildConfigurations: [Release]
official: false
codeSign: false

View File

@@ -90,15 +90,9 @@ if ($noticeMatch.Success) {
$currentNoticePackageList = ""
}
# Test-only packages that are allowed to be in NOTICE.md but not in the build
# (e.g., when BuildTests=false, these packages won't appear in the NuGet list)
$allowedExtraPackages = @(
"- Moq"
)
if (!$noticeFile.Trim().EndsWith($returnList.Trim()))
{
Write-Host -ForegroundColor Yellow "Notice.md does not exactly match NuGet list. Analyzing differences..."
Write-Host -ForegroundColor Red "Notice.md does not match NuGet list."
# Show detailed differences
$generatedPackages = $returnList -split "`r`n|`n" | Where-Object { $_.Trim() -ne "" } | Sort-Object
@@ -111,7 +105,7 @@ if (!$noticeFile.Trim().EndsWith($returnList.Trim()))
# Find packages in proj file list but not in NOTICE.md
$missingFromNotice = $generatedPackages | Where-Object { $noticePackages -notcontains $_ }
if ($missingFromNotice.Count -gt 0) {
Write-Host -ForegroundColor Red "MissingFromNotice (ERROR - these must be added to NOTICE.md):"
Write-Host -ForegroundColor Red "MissingFromNotice:"
foreach ($pkg in $missingFromNotice) {
Write-Host -ForegroundColor Red " $pkg"
}
@@ -120,23 +114,10 @@ if (!$noticeFile.Trim().EndsWith($returnList.Trim()))
# Find packages in NOTICE.md but not in proj file list
$extraInNotice = $noticePackages | Where-Object { $generatedPackages -notcontains $_ }
# Filter out allowed extra packages (test-only dependencies)
$unexpectedExtra = $extraInNotice | Where-Object { $allowedExtraPackages -notcontains $_ }
$allowedExtra = $extraInNotice | Where-Object { $allowedExtraPackages -contains $_ }
if ($allowedExtra.Count -gt 0) {
Write-Host -ForegroundColor Green "ExtraInNotice (OK - allowed test-only packages):"
foreach ($pkg in $allowedExtra) {
Write-Host -ForegroundColor Green " $pkg"
}
Write-Host ""
}
if ($unexpectedExtra.Count -gt 0) {
Write-Host -ForegroundColor Red "ExtraInNotice (ERROR - unexpected packages in NOTICE.md):"
foreach ($pkg in $unexpectedExtra) {
Write-Host -ForegroundColor Red " $pkg"
if ($extraInNotice.Count -gt 0) {
Write-Host -ForegroundColor Yellow "ExtraInNotice:"
foreach ($pkg in $extraInNotice) {
Write-Host -ForegroundColor Yellow " $pkg"
}
Write-Host ""
}
@@ -146,17 +127,10 @@ if (!$noticeFile.Trim().EndsWith($returnList.Trim()))
Write-Host " Proj file list has $($generatedPackages.Count) packages"
Write-Host " NOTICE.md has $($noticePackages.Count) packages"
Write-Host " MissingFromNotice: $($missingFromNotice.Count) packages"
Write-Host " ExtraInNotice (allowed): $($allowedExtra.Count) packages"
Write-Host " ExtraInNotice (unexpected): $($unexpectedExtra.Count) packages"
Write-Host " ExtraInNotice: $($extraInNotice.Count) packages"
Write-Host ""
# Fail if there are missing packages OR unexpected extra packages
if ($missingFromNotice.Count -gt 0 -or $unexpectedExtra.Count -gt 0) {
Write-Host -ForegroundColor Red "FAILED: NOTICE.md mismatch detected."
exit 1
} else {
Write-Host -ForegroundColor Green "PASSED: NOTICE.md matches (with allowed test-only packages)."
}
exit 1
}
exit 0

View File

@@ -2,12 +2,6 @@
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Skip building C++ test projects when BuildTests=false -->
<PropertyGroup Condition="'$(_IsSkippedTestProject)' == 'true'">
<UsePrecompiledHeaders>false</UsePrecompiledHeaders>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<!-- Project configurations -->
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">

View File

@@ -19,39 +19,6 @@
<PlatformTarget>$(Platform)</PlatformTarget>
</PropertyGroup>
<!--
Completely skip building test projects when BuildTests=false (e.g., Release pipeline).
This avoids InternalsVisibleTo/signing issues by not compiling test code at all.
Match: projects ending in Test, Tests, UnitTests, UITests, FuzzTests, or in a folder named Tests.
Also matches projects starting with UnitTests- (e.g., UnitTests-CommonLib).
Also removes all PackageReference/ProjectReference to prevent NuGet restore and dependency builds.
Note: Checking both 'false' and 'False' to handle YAML boolean serialization.
-->
<PropertyGroup Condition="'$(BuildTests)' == 'false' or '$(BuildTests)' == 'False'">
<_ProjectName>$(MSBuildProjectName)</_ProjectName>
<!-- Match any project ending with "Test" or "Tests" (covers UnitTests, UITests, FuzzTests, etc.) -->
<_IsSkippedTestProject Condition="$(_ProjectName.EndsWith('Test'))">true</_IsSkippedTestProject>
<_IsSkippedTestProject Condition="$(_ProjectName.EndsWith('Tests'))">true</_IsSkippedTestProject>
<!-- Match projects starting with UnitTests- or UITest- prefix -->
<_IsSkippedTestProject Condition="$(_ProjectName.StartsWith('UnitTests-'))">true</_IsSkippedTestProject>
<_IsSkippedTestProject Condition="$(_ProjectName.StartsWith('UITest-'))">true</_IsSkippedTestProject>
<!-- Match projects in a Tests folder -->
<_IsSkippedTestProject Condition="$(MSBuildProjectDirectory.Contains('\Tests\'))">true</_IsSkippedTestProject>
</PropertyGroup>
<PropertyGroup Condition="'$(_IsSkippedTestProject)' == 'true'">
<EnableDefaultItems>false</EnableDefaultItems>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateGlobalUsings>false</GenerateGlobalUsings>
<ImplicitUsings>disable</ImplicitUsings>
<!-- Disable all code analysis for skipped test projects -->
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<RunAnalyzers>false</RunAnalyzers>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.csproj'">
<Version>$(Version).0</Version>
<RepositoryUrl>https://github.com/microsoft/PowerToys</RepositoryUrl>
@@ -63,9 +30,7 @@
<_PropertySheetDisplayName>PowerToys.Root.Props</_PropertySheetDisplayName>
<ForceImportBeforeCppProps>$(MsbuildThisFileDirectory)\Cpp.Build.props</ForceImportBeforeCppProps>
</PropertyGroup>
<ItemGroup Condition="'$(MSBuildProjectExtension)' == '.csproj' and '$(_IsSkippedTestProject)' != 'true'">
<ItemGroup Condition="'$(MSBuildProjectExtension)' == '.csproj'">
<PackageReference Include="StyleCop.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -28,41 +28,4 @@
<PropertyGroup Condition="'$(IgnoreExperimentalWarnings)' == 'true'">
<NoWarn>$(NoWarn);CS8305;SA1500;CA1852</NoWarn>
</PropertyGroup>
<!-- Skipped test projects when BuildTests=false: no-op build and remove references.
This must be in targets (not props) so it runs AFTER the project file adds its items. -->
<PropertyGroup Condition="'$(_IsSkippedTestProject)' == 'true'">
<BuildDependsOn />
<CoreBuildDependsOn />
<RebuildDependsOn />
</PropertyGroup>
<!-- For C# projects: remove all items -->
<ItemGroup Condition="'$(_IsSkippedTestProject)' == 'true' and '$(MSBuildProjectExtension)' == '.csproj'">
<PackageReference Remove="@(PackageReference)" />
<ProjectReference Remove="@(ProjectReference)" />
<Reference Remove="@(Reference)" />
<Compile Remove="@(Compile)" />
<Content Remove="@(Content)" />
<EmbeddedResource Remove="@(EmbeddedResource)" />
<None Remove="@(None)" />
<Using Remove="@(Using)" />
<GlobalUsing Remove="@(GlobalUsing)" />
</ItemGroup>
<!-- For C++ projects (vcxproj): remove all compile/link items to prevent build -->
<ItemGroup Condition="'$(_IsSkippedTestProject)' == 'true' and '$(MSBuildProjectExtension)' == '.vcxproj'">
<ClCompile Remove="@(ClCompile)" />
<ClInclude Remove="@(ClInclude)" />
<Link Remove="@(Link)" />
<Lib Remove="@(Lib)" />
<ProjectReference Remove="@(ProjectReference)" />
<None Remove="@(None)" />
<ResourceCompile Remove="@(ResourceCompile)" />
<Midl Remove="@(Midl)" />
</ItemGroup>
<!-- Note: For C++ skipped test projects, build is effectively skipped by removing all compile items above.
We don't define empty Build/Rebuild/Clean targets here because MSBuild Target definitions with Condition
on the Target element still override the default targets even when condition is false. -->
</Project>
</Project>

View File

@@ -55,7 +55,6 @@
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/common/UnitTests-CommonLib/UnitTests-CommonLib.vcxproj" Id="1a066c63-64b3-45f8-92fe-664e1cce8077" />
<Project Path="src/common/UnitTests-CommonUtils/UnitTests-CommonUtils.vcxproj" Id="8b5cfb38-ccba-40a8-ad7a-89c57b070884" />
<Project Path="src/common/updating/updating.vcxproj" Id="17da04df-e393-4397-9cf0-84dabe11032e" />
<Project Path="src/common/version/version.vcxproj" Id="cc6e41ac-8174-4e8a-8d22-85dd7f4851df" />
</Folder>

View File

@@ -18,28 +18,13 @@ Advanced Paste is a PowerToys module that provides enhanced clipboard pasting wi
TODO: Add implementation details
### Paste with AI Preview
The "Show preview" setting (`ShowCustomPreview`) controls whether AI-generated results are displayed in a preview window before pasting. **The preview feature does not consume additional AI credits**—the preview displays the same AI response that was already generated, cached locally from a single API call.
The implementation flow:
1. User initiates "Paste with AI" action
2. A single AI API call is made via `ExecutePasteFormatAsync`
3. The result is cached in `GeneratedResponses`
4. If preview is enabled, the cached result is displayed in the preview UI
5. User can paste the cached result without any additional API calls
See the `ExecutePasteFormatAsync(PasteFormat, PasteActionSource)` method in `OptionsViewModel.cs` for the implementation.
## Debugging
TODO: Add debugging information
## Settings
| Setting | Description |
|---------|-------------|
| `ShowCustomPreview` | When enabled, shows AI-generated results in a preview window before pasting. Does not affect AI credit consumption. |
TODO: Add settings documentation
## Future Improvements

View File

@@ -1,120 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <appMutex.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(AppMutexTests)
{
public:
TEST_METHOD(CreateAppMutex_ValidName_ReturnsHandle)
{
std::wstring mutexName = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) + L"_1";
auto handle = createAppMutex(mutexName);
Assert::IsNotNull(handle.get());
}
TEST_METHOD(CreateAppMutex_SameName_ReturnsExistingHandle)
{
std::wstring mutexName = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) + L"_2";
auto handle1 = createAppMutex(mutexName);
Assert::IsNotNull(handle1.get());
auto handle2 = createAppMutex(mutexName);
Assert::IsNull(handle2.get());
}
TEST_METHOD(CreateAppMutex_DifferentNames_ReturnsDifferentHandles)
{
std::wstring mutexName1 = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) + L"_A";
std::wstring mutexName2 = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) + L"_B";
auto handle1 = createAppMutex(mutexName1);
auto handle2 = createAppMutex(mutexName2);
Assert::IsNotNull(handle1.get());
Assert::IsNotNull(handle2.get());
Assert::AreNotEqual(handle1.get(), handle2.get());
}
TEST_METHOD(CreateAppMutex_EmptyName_ReturnsHandle)
{
// Empty name creates unnamed mutex
auto handle = createAppMutex(L"");
// CreateMutexW with empty string should still work
Assert::IsTrue(true);
// Test passes regardless - just checking it doesn't crash
Assert::IsTrue(true);
}
TEST_METHOD(CreateAppMutex_LongName_ReturnsHandle)
{
// Create a long mutex name
std::wstring mutexName = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) + L"_";
for (int i = 0; i < 50; ++i)
{
mutexName += L"LongNameSegment";
}
auto handle = createAppMutex(mutexName);
// Long names might fail, but shouldn't crash
Assert::IsTrue(true);
}
TEST_METHOD(CreateAppMutex_SpecialCharacters_ReturnsHandle)
{
std::wstring mutexName = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) + L"_Special!@#$%";
auto handle = createAppMutex(mutexName);
// Some special characters might not be valid in mutex names
Assert::IsTrue(true);
}
TEST_METHOD(CreateAppMutex_GlobalPrefix_ReturnsHandle)
{
// Global prefix for cross-session mutex
std::wstring mutexName = L"Global\\TestMutex_" + std::to_wstring(GetCurrentProcessId());
auto handle = createAppMutex(mutexName);
// Might require elevation, but shouldn't crash
Assert::IsTrue(true);
}
TEST_METHOD(CreateAppMutex_LocalPrefix_ReturnsHandle)
{
std::wstring mutexName = L"Local\\TestMutex_" + std::to_wstring(GetCurrentProcessId());
auto handle = createAppMutex(mutexName);
Assert::IsNotNull(handle.get());
}
TEST_METHOD(CreateAppMutex_MultipleCalls_AllSucceed)
{
std::vector<wil::unique_mutex_nothrow> handles;
for (int i = 0; i < 10; ++i)
{
std::wstring mutexName = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) +
L"_Multi_" + std::to_wstring(i);
auto handle = createAppMutex(mutexName);
Assert::IsNotNull(handle.get());
handles.push_back(std::move(handle));
}
}
TEST_METHOD(CreateAppMutex_ReleaseAndRecreate_Works)
{
std::wstring mutexName = L"TestMutex_" + std::to_wstring(GetCurrentProcessId()) + L"_Recreate";
auto handle1 = createAppMutex(mutexName);
Assert::IsNotNull(handle1.get());
handle1.reset();
// After closing, should be able to create again
auto handle2 = createAppMutex(mutexName);
Assert::IsNotNull(handle2.get());
}
};
}

View File

@@ -1,220 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <color.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ColorUtilsTests)
{
public:
// checkValidRGB tests
TEST_METHOD(CheckValidRGB_ValidBlack_ReturnsTrue)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#000000", &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(0), r);
Assert::AreEqual(static_cast<uint8_t>(0), g);
Assert::AreEqual(static_cast<uint8_t>(0), b);
}
TEST_METHOD(CheckValidRGB_ValidWhite_ReturnsTrue)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#FFFFFF", &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(255), r);
Assert::AreEqual(static_cast<uint8_t>(255), g);
Assert::AreEqual(static_cast<uint8_t>(255), b);
}
TEST_METHOD(CheckValidRGB_ValidRed_ReturnsTrue)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#FF0000", &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(255), r);
Assert::AreEqual(static_cast<uint8_t>(0), g);
Assert::AreEqual(static_cast<uint8_t>(0), b);
}
TEST_METHOD(CheckValidRGB_ValidGreen_ReturnsTrue)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#00FF00", &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(0), r);
Assert::AreEqual(static_cast<uint8_t>(255), g);
Assert::AreEqual(static_cast<uint8_t>(0), b);
}
TEST_METHOD(CheckValidRGB_ValidBlue_ReturnsTrue)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#0000FF", &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(0), r);
Assert::AreEqual(static_cast<uint8_t>(0), g);
Assert::AreEqual(static_cast<uint8_t>(255), b);
}
TEST_METHOD(CheckValidRGB_ValidMixed_ReturnsTrue)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#AB12CD", &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(0xAB), r);
Assert::AreEqual(static_cast<uint8_t>(0x12), g);
Assert::AreEqual(static_cast<uint8_t>(0xCD), b);
}
TEST_METHOD(CheckValidRGB_MissingHash_ReturnsFalse)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"FFFFFF", &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidRGB_TooShort_ReturnsFalse)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#FFF", &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidRGB_TooLong_ReturnsFalse)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#FFFFFFFF", &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidRGB_InvalidChars_ReturnsFalse)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#GGHHII", &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidRGB_LowercaseInvalid_ReturnsFalse)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#ffffff", &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidRGB_EmptyString_ReturnsFalse)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"", &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidRGB_OnlyHash_ReturnsFalse)
{
uint8_t r, g, b;
bool result = checkValidRGB(L"#", &r, &g, &b);
Assert::IsFalse(result);
}
// checkValidARGB tests
TEST_METHOD(CheckValidARGB_ValidBlackOpaque_ReturnsTrue)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#FF000000", &a, &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(255), a);
Assert::AreEqual(static_cast<uint8_t>(0), r);
Assert::AreEqual(static_cast<uint8_t>(0), g);
Assert::AreEqual(static_cast<uint8_t>(0), b);
}
TEST_METHOD(CheckValidARGB_ValidWhiteOpaque_ReturnsTrue)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#FFFFFFFF", &a, &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(255), a);
Assert::AreEqual(static_cast<uint8_t>(255), r);
Assert::AreEqual(static_cast<uint8_t>(255), g);
Assert::AreEqual(static_cast<uint8_t>(255), b);
}
TEST_METHOD(CheckValidARGB_ValidTransparent_ReturnsTrue)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#00FFFFFF", &a, &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(0), a);
Assert::AreEqual(static_cast<uint8_t>(255), r);
Assert::AreEqual(static_cast<uint8_t>(255), g);
Assert::AreEqual(static_cast<uint8_t>(255), b);
}
TEST_METHOD(CheckValidARGB_ValidSemiTransparent_ReturnsTrue)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#80FF0000", &a, &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(0x80), a);
Assert::AreEqual(static_cast<uint8_t>(255), r);
Assert::AreEqual(static_cast<uint8_t>(0), g);
Assert::AreEqual(static_cast<uint8_t>(0), b);
}
TEST_METHOD(CheckValidARGB_ValidMixed_ReturnsTrue)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#12345678", &a, &r, &g, &b);
Assert::IsTrue(result);
Assert::AreEqual(static_cast<uint8_t>(0x12), a);
Assert::AreEqual(static_cast<uint8_t>(0x34), r);
Assert::AreEqual(static_cast<uint8_t>(0x56), g);
Assert::AreEqual(static_cast<uint8_t>(0x78), b);
}
TEST_METHOD(CheckValidARGB_MissingHash_ReturnsFalse)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"FFFFFFFF", &a, &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidARGB_TooShort_ReturnsFalse)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#FFFFFF", &a, &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidARGB_TooLong_ReturnsFalse)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#FFFFFFFFFF", &a, &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidARGB_InvalidChars_ReturnsFalse)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#GGHHIIJJ", &a, &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidARGB_LowercaseInvalid_ReturnsFalse)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"#ffffffff", &a, &r, &g, &b);
Assert::IsFalse(result);
}
TEST_METHOD(CheckValidARGB_EmptyString_ReturnsFalse)
{
uint8_t a, r, g, b;
bool result = checkValidARGB(L"", &a, &r, &g, &b);
Assert::IsFalse(result);
}
};
}

View File

@@ -1,228 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <com_object_factory.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
// Test COM object for testing the factory
class TestComObject : public IUnknown
{
public:
TestComObject() : m_refCount(1) {}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override
{
if (riid == IID_IUnknown)
{
*ppvObject = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppvObject = nullptr;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() override
{
return InterlockedIncrement(&m_refCount);
}
ULONG STDMETHODCALLTYPE Release() override
{
ULONG count = InterlockedDecrement(&m_refCount);
if (count == 0)
{
delete this;
}
return count;
}
private:
LONG m_refCount;
};
TEST_CLASS(ComObjectFactoryTests)
{
public:
TEST_METHOD(ComObjectFactory_Construction_DoesNotCrash)
{
com_object_factory<TestComObject> factory;
Assert::IsTrue(true);
}
TEST_METHOD(ComObjectFactory_QueryInterface_IUnknown_Succeeds)
{
com_object_factory<TestComObject> factory;
IUnknown* pUnknown = nullptr;
HRESULT hr = factory.QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&pUnknown));
Assert::AreEqual(S_OK, hr);
Assert::IsNotNull(pUnknown);
if (pUnknown)
{
pUnknown->Release();
}
}
TEST_METHOD(ComObjectFactory_QueryInterface_IClassFactory_Succeeds)
{
com_object_factory<TestComObject> factory;
IClassFactory* pFactory = nullptr;
HRESULT hr = factory.QueryInterface(IID_IClassFactory, reinterpret_cast<void**>(&pFactory));
Assert::AreEqual(S_OK, hr);
Assert::IsNotNull(pFactory);
if (pFactory)
{
pFactory->Release();
}
}
TEST_METHOD(ComObjectFactory_QueryInterface_InvalidInterface_Fails)
{
com_object_factory<TestComObject> factory;
void* pInterface = nullptr;
// Random GUID that we don't support
GUID randomGuid = { 0x12345678, 0x1234, 0x1234, { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 } };
HRESULT hr = factory.QueryInterface(randomGuid, &pInterface);
Assert::AreEqual(E_NOINTERFACE, hr);
Assert::IsNull(pInterface);
}
TEST_METHOD(ComObjectFactory_AddRef_IncreasesRefCount)
{
com_object_factory<TestComObject> factory;
ULONG count1 = factory.AddRef();
ULONG count2 = factory.AddRef();
Assert::IsTrue(count2 > count1);
// Clean up
factory.Release();
factory.Release();
}
TEST_METHOD(ComObjectFactory_Release_DecreasesRefCount)
{
com_object_factory<TestComObject> factory;
factory.AddRef();
factory.AddRef();
ULONG count1 = factory.Release();
ULONG count2 = factory.Release();
Assert::IsTrue(count2 < count1);
}
TEST_METHOD(ComObjectFactory_CreateInstance_NoAggregation_Succeeds)
{
com_object_factory<TestComObject> factory;
IUnknown* pObj = nullptr;
HRESULT hr = factory.CreateInstance(nullptr, IID_IUnknown, reinterpret_cast<void**>(&pObj));
Assert::AreEqual(S_OK, hr);
Assert::IsNotNull(pObj);
if (pObj)
{
pObj->Release();
}
}
TEST_METHOD(ComObjectFactory_CreateInstance_WithAggregation_Fails)
{
com_object_factory<TestComObject> factory;
TestComObject outer;
IUnknown* pObj = nullptr;
// Aggregation should fail for our simple test object
HRESULT hr = factory.CreateInstance(&outer, IID_IUnknown, reinterpret_cast<void**>(&pObj));
Assert::AreEqual(CLASS_E_NOAGGREGATION, hr);
Assert::IsNull(pObj);
}
TEST_METHOD(ComObjectFactory_CreateInstance_NullOutput_Fails)
{
com_object_factory<TestComObject> factory;
HRESULT hr = factory.CreateInstance(nullptr, IID_IUnknown, nullptr);
Assert::AreEqual(E_POINTER, hr);
}
TEST_METHOD(ComObjectFactory_LockServer_Lock_Succeeds)
{
com_object_factory<TestComObject> factory;
HRESULT hr = factory.LockServer(TRUE);
Assert::AreEqual(S_OK, hr);
// Unlock
factory.LockServer(FALSE);
}
TEST_METHOD(ComObjectFactory_LockServer_Unlock_Succeeds)
{
com_object_factory<TestComObject> factory;
factory.LockServer(TRUE);
HRESULT hr = factory.LockServer(FALSE);
Assert::AreEqual(S_OK, hr);
}
TEST_METHOD(ComObjectFactory_LockServer_MultipleLocks_Work)
{
com_object_factory<TestComObject> factory;
factory.LockServer(TRUE);
factory.LockServer(TRUE);
factory.LockServer(TRUE);
factory.LockServer(FALSE);
factory.LockServer(FALSE);
HRESULT hr = factory.LockServer(FALSE);
Assert::AreEqual(S_OK, hr);
}
// Thread safety tests
TEST_METHOD(ComObjectFactory_ConcurrentCreateInstance_Works)
{
com_object_factory<TestComObject> factory;
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&factory, &successCount]() {
IUnknown* pObj = nullptr;
HRESULT hr = factory.CreateInstance(nullptr, IID_IUnknown, reinterpret_cast<void**>(&pObj));
if (SUCCEEDED(hr) && pObj)
{
successCount++;
pObj->Release();
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(10, successCount.load());
}
};
}

View File

@@ -1,146 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <elevation.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ElevationTests)
{
public:
// is_process_elevated tests
TEST_METHOD(IsProcessElevated_ReturnsBoolean)
{
bool result = is_process_elevated(false);
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(IsProcessElevated_CachedValue_ReturnsSameResult)
{
bool result1 = is_process_elevated(true);
bool result2 = is_process_elevated(true);
// Cached value should be consistent
Assert::AreEqual(result1, result2);
}
TEST_METHOD(IsProcessElevated_UncachedValue_ReturnsBoolean)
{
bool result = is_process_elevated(false);
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(IsProcessElevated_CachedAndUncached_AreConsistent)
{
// Both should return the same value for the same process
bool cached = is_process_elevated(true);
bool uncached = is_process_elevated(false);
Assert::AreEqual(cached, uncached);
}
// check_user_is_admin tests
TEST_METHOD(CheckUserIsAdmin_ReturnsBoolean)
{
bool result = check_user_is_admin();
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(CheckUserIsAdmin_ConsistentResults)
{
bool result1 = check_user_is_admin();
bool result2 = check_user_is_admin();
bool result3 = check_user_is_admin();
Assert::AreEqual(result1, result2);
Assert::AreEqual(result2, result3);
}
// Relationship between elevation and admin
TEST_METHOD(ElevationAndAdmin_Relationship)
{
bool elevated = is_process_elevated(false);
bool admin = check_user_is_admin();
(void)admin;
// If elevated, user should typically be admin
// But user can be admin without process being elevated
if (elevated)
{
// Elevated process usually means admin user
// (though there are edge cases)
}
// Just verify both functions return without crashing
Assert::IsTrue(true);
}
// IsProcessOfWindowElevated tests
TEST_METHOD(IsProcessOfWindowElevated_DesktopWindow_ReturnsBoolean)
{
HWND desktop = GetDesktopWindow();
if (desktop)
{
bool result = IsProcessOfWindowElevated(desktop);
Assert::IsTrue(result == true || result == false);
}
Assert::IsTrue(true);
}
TEST_METHOD(IsProcessOfWindowElevated_InvalidHwnd_DoesNotCrash)
{
bool result = IsProcessOfWindowElevated(nullptr);
// Should handle null HWND gracefully
Assert::IsTrue(result == true || result == false);
}
// ProcessInfo struct tests
TEST_METHOD(ProcessInfo_DefaultConstruction)
{
ProcessInfo info{};
Assert::AreEqual(static_cast<DWORD>(0), info.processID);
}
// Thread safety tests
TEST_METHOD(IsProcessElevated_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 10; ++j)
{
is_process_elevated(j % 2 == 0);
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(100, successCount.load());
}
// Performance of cached value
TEST_METHOD(IsProcessElevated_CachedPerformance)
{
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; ++i)
{
is_process_elevated(true);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
// Cached calls should be very fast
Assert::IsTrue(duration.count() < 1000);
}
};
}

View File

@@ -1,182 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <excluded_apps.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ExcludedAppsTests)
{
public:
// find_app_name_in_path tests
TEST_METHOD(FindAppNameInPath_ExactMatch_ReturnsTrue)
{
std::wstring path = L"C:\\Program Files\\App\\notepad.exe";
std::vector<std::wstring> apps = { L"notepad.exe" };
Assert::IsTrue(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_NoMatch_ReturnsFalse)
{
std::wstring path = L"C:\\Program Files\\App\\notepad.exe";
std::vector<std::wstring> apps = { L"calc.exe" };
Assert::IsFalse(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_MultipleApps_FindsMatch)
{
std::wstring path = L"C:\\Program Files\\App\\notepad.exe";
std::vector<std::wstring> apps = { L"calc.exe", L"notepad.exe", L"word.exe" };
Assert::IsTrue(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_EmptyPath_ReturnsFalse)
{
std::wstring path = L"";
std::vector<std::wstring> apps = { L"notepad.exe" };
Assert::IsFalse(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_EmptyApps_ReturnsFalse)
{
std::wstring path = L"C:\\Program Files\\App\\notepad.exe";
std::vector<std::wstring> apps = {};
Assert::IsFalse(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_PartialMatchInFolder_ReturnsFalse)
{
// "notepad" appears in folder name but not as the exe name
std::wstring path = L"C:\\notepad\\other.exe";
std::vector<std::wstring> apps = { L"notepad.exe" };
Assert::IsFalse(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_CaseSensitive_ReturnsFalse)
{
std::wstring path = L"C:\\Program Files\\App\\NOTEPAD.EXE";
std::vector<std::wstring> apps = { L"notepad.exe" };
// The function does rfind which is case-sensitive
Assert::IsFalse(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_MatchWithDifferentExtension_ReturnsFalse)
{
std::wstring path = L"C:\\Program Files\\App\\notepad.com";
std::vector<std::wstring> apps = { L"notepad.exe" };
Assert::IsFalse(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_MatchAtEndOfPath_ReturnsTrue)
{
std::wstring path = L"C:\\Windows\\System32\\notepad.exe";
std::vector<std::wstring> apps = { L"notepad.exe" };
Assert::IsTrue(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_UNCPath_Works)
{
std::wstring path = L"\\\\server\\share\\folder\\app.exe";
std::vector<std::wstring> apps = { L"app.exe" };
Assert::IsTrue(find_app_name_in_path(path, apps));
}
// find_folder_in_path tests
TEST_METHOD(FindFolderInPath_FolderExists_ReturnsTrue)
{
std::wstring path = L"C:\\Program Files\\MyApp\\app.exe";
std::vector<std::wstring> folders = { L"Program Files" };
Assert::IsTrue(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_FolderNotExists_ReturnsFalse)
{
std::wstring path = L"C:\\Windows\\System32\\app.exe";
std::vector<std::wstring> folders = { L"Program Files" };
Assert::IsFalse(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_MultipleFolders_FindsMatch)
{
std::wstring path = L"C:\\Windows\\System32\\app.exe";
std::vector<std::wstring> folders = { L"Program Files", L"System32", L"Users" };
Assert::IsTrue(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_EmptyPath_ReturnsFalse)
{
std::wstring path = L"";
std::vector<std::wstring> folders = { L"Windows" };
Assert::IsFalse(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_EmptyFolders_ReturnsFalse)
{
std::wstring path = L"C:\\Windows\\app.exe";
std::vector<std::wstring> folders = {};
Assert::IsFalse(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_PartialMatch_ReturnsTrue)
{
// find_folder_in_path uses rfind which finds substrings
std::wstring path = L"C:\\Windows\\System32\\app.exe";
std::vector<std::wstring> folders = { L"System" };
Assert::IsTrue(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_NestedFolder_ReturnsTrue)
{
std::wstring path = L"C:\\Program Files\\Company\\Product\\bin\\app.exe";
std::vector<std::wstring> folders = { L"Product" };
Assert::IsTrue(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_RootDrive_ReturnsTrue)
{
std::wstring path = L"C:\\folder\\app.exe";
std::vector<std::wstring> folders = { L"C:\\" };
Assert::IsTrue(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_UNCPath_Works)
{
std::wstring path = L"\\\\server\\share\\folder\\app.exe";
std::vector<std::wstring> folders = { L"share" };
Assert::IsTrue(find_folder_in_path(path, folders));
}
TEST_METHOD(FindFolderInPath_CaseSensitive_ReturnsFalse)
{
std::wstring path = L"C:\\WINDOWS\\app.exe";
std::vector<std::wstring> folders = { L"windows" };
// rfind is case-sensitive
Assert::IsFalse(find_folder_in_path(path, folders));
}
// Edge case tests
TEST_METHOD(FindAppNameInPath_AppNameInMiddleOfPath_HandlesCorrectly)
{
// The app name appears both in folder and as filename
std::wstring path = L"C:\\notepad\\bin\\notepad.exe";
std::vector<std::wstring> apps = { L"notepad.exe" };
Assert::IsTrue(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindAppNameInPath_JustFilename_ReturnsFalse)
{
std::wstring path = L"notepad.exe";
std::vector<std::wstring> apps = { L"notepad.exe" };
// find_app_name_in_path expects a path separator to validate the executable segment
Assert::IsFalse(find_app_name_in_path(path, apps));
}
TEST_METHOD(FindFolderInPath_JustFilename_ReturnsFalse)
{
std::wstring path = L"app.exe";
std::vector<std::wstring> folders = { L"Windows" };
Assert::IsFalse(find_folder_in_path(path, folders));
}
};
}

View File

@@ -1,148 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <exec.h>
#include <cctype>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ExecTests)
{
public:
TEST_METHOD(ExecAndReadOutput_EchoCommand_ReturnsOutput)
{
auto result = exec_and_read_output(L"cmd /c echo hello", 5000);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
// Output should contain "hello"
Assert::IsTrue(result->find("hello") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_WhereCommand_ReturnsPath)
{
auto result = exec_and_read_output(L"where cmd", 5000);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
// Should contain path to cmd.exe
Assert::IsTrue(result->find("cmd") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_DirCommand_ReturnsListing)
{
auto result = exec_and_read_output(L"cmd /c dir /b C:\\Windows", 5000);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
// Should contain some common Windows folder names
std::string output = *result;
std::transform(output.begin(), output.end(), output.begin(), [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
Assert::IsTrue(output.find("system32") != std::string::npos ||
output.find("system") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_InvalidCommand_ReturnsEmptyOrError)
{
auto result = exec_and_read_output(L"nonexistentcommand12345", 5000);
// Invalid command should either return nullopt or an error message
Assert::IsTrue(!result.has_value() || result->empty() ||
result->find("not recognized") != std::string::npos ||
result->find("error") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_EmptyCommand_DoesNotCrash)
{
auto result = exec_and_read_output(L"", 5000);
// Should handle empty command gracefully
Assert::IsTrue(true);
}
TEST_METHOD(ExecAndReadOutput_TimeoutExpires_ReturnsAvailableOutput)
{
// Use a command that produces output slowly
// ping localhost will run for a while
auto start = std::chrono::steady_clock::now();
// Very short timeout
auto result = exec_and_read_output(L"ping localhost -n 10", 100);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start);
// Should return within reasonable time
Assert::IsTrue(elapsed.count() < 5000);
}
TEST_METHOD(ExecAndReadOutput_MultilineOutput_PreservesLines)
{
auto result = exec_and_read_output(L"cmd /c \"echo line1 & echo line2 & echo line3\"", 5000);
Assert::IsTrue(result.has_value());
// Should contain multiple lines
Assert::IsTrue(result->find("line1") != std::string::npos);
Assert::IsTrue(result->find("line2") != std::string::npos);
Assert::IsTrue(result->find("line3") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_UnicodeOutput_Works)
{
// Echo a simple ASCII string (Unicode test depends on system codepage)
auto result = exec_and_read_output(L"cmd /c echo test123", 5000);
Assert::IsTrue(result.has_value());
Assert::IsTrue(result->find("test123") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_LongTimeout_Works)
{
auto result = exec_and_read_output(L"cmd /c echo test", 60000);
Assert::IsTrue(result.has_value());
Assert::IsTrue(result->find("test") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_QuotedArguments_Work)
{
auto result = exec_and_read_output(L"cmd /c echo \"hello world\"", 5000);
Assert::IsTrue(result.has_value());
Assert::IsTrue(result->find("hello") != std::string::npos);
}
TEST_METHOD(ExecAndReadOutput_EnvironmentVariable_Expanded)
{
auto result = exec_and_read_output(L"cmd /c echo %USERNAME%", 5000);
Assert::IsTrue(result.has_value());
// Should not contain the literal %USERNAME% but the actual username
// Or if not expanded, still should not crash
Assert::IsFalse(result->empty());
}
TEST_METHOD(ExecAndReadOutput_ExitCode_CommandFails)
{
// Command that exits with error
auto result = exec_and_read_output(L"cmd /c exit 1", 5000);
// Should still return something (possibly empty)
// Just verify it doesn't crash
Assert::IsTrue(true);
}
TEST_METHOD(ExecAndReadOutput_ZeroTimeout_DoesNotHang)
{
auto start = std::chrono::steady_clock::now();
auto result = exec_and_read_output(L"cmd /c echo test", 0);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start);
// Should complete quickly with zero timeout
Assert::IsTrue(elapsed.count() < 5000);
}
};
}

View File

@@ -1,68 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <game_mode.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(GameModeTests)
{
public:
TEST_METHOD(DetectGameMode_ReturnsBoolean)
{
// This function queries Windows game mode status
bool result = detect_game_mode();
// Result depends on current system state, but should be a valid boolean
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(DetectGameMode_ConsistentResults)
{
// Multiple calls should return consistent results (unless game mode changes)
bool result1 = detect_game_mode();
bool result2 = detect_game_mode();
bool result3 = detect_game_mode();
// Results should be consistent across rapid calls
Assert::AreEqual(result1, result2);
Assert::AreEqual(result2, result3);
}
TEST_METHOD(DetectGameMode_DoesNotCrash)
{
// Call multiple times to ensure no crash or memory leak
for (int i = 0; i < 100; ++i)
{
detect_game_mode();
}
Assert::IsTrue(true);
}
TEST_METHOD(DetectGameMode_ThreadSafe)
{
// Test that calling from multiple threads is safe
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 10; ++j)
{
detect_game_mode();
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(100, successCount.load());
}
};
}

View File

@@ -1,218 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <gpo.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace powertoys_gpo;
namespace UnitTestsCommonUtils
{
TEST_CLASS(GpoTests)
{
public:
// Helper to check if result is a valid gpo_rule_configured_t value
static constexpr bool IsValidGpoResult(gpo_rule_configured_t result)
{
return result == gpo_rule_configured_wrong_value ||
result == gpo_rule_configured_unavailable ||
result == gpo_rule_configured_not_configured ||
result == gpo_rule_configured_disabled ||
result == gpo_rule_configured_enabled;
}
// gpo_rule_configured_t enum tests
TEST_METHOD(GpoRuleConfigured_EnumValues_AreDistinct)
{
Assert::AreNotEqual(static_cast<int>(gpo_rule_configured_not_configured),
static_cast<int>(gpo_rule_configured_enabled));
Assert::AreNotEqual(static_cast<int>(gpo_rule_configured_enabled),
static_cast<int>(gpo_rule_configured_disabled));
Assert::AreNotEqual(static_cast<int>(gpo_rule_configured_not_configured),
static_cast<int>(gpo_rule_configured_disabled));
}
// getConfiguredValue tests
TEST_METHOD(GetConfiguredValue_NonExistentKey_ReturnsNotConfigured)
{
auto result = getConfiguredValue(L"NonExistentPolicyValue12345");
Assert::IsTrue(result == gpo_rule_configured_not_configured ||
result == gpo_rule_configured_unavailable);
}
// Utility enabled getters - these all follow the same pattern
TEST_METHOD(GetAllowExperimentationValue_ReturnsValidState)
{
auto result = getAllowExperimentationValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredAlwaysOnTopEnabledValue_ReturnsValidState)
{
auto result = getConfiguredAlwaysOnTopEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredAwakeEnabledValue_ReturnsValidState)
{
auto result = getConfiguredAwakeEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredColorPickerEnabledValue_ReturnsValidState)
{
auto result = getConfiguredColorPickerEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredFancyZonesEnabledValue_ReturnsValidState)
{
auto result = getConfiguredFancyZonesEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredFileLocksmithEnabledValue_ReturnsValidState)
{
auto result = getConfiguredFileLocksmithEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredImageResizerEnabledValue_ReturnsValidState)
{
auto result = getConfiguredImageResizerEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredKeyboardManagerEnabledValue_ReturnsValidState)
{
auto result = getConfiguredKeyboardManagerEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredPowerRenameEnabledValue_ReturnsValidState)
{
auto result = getConfiguredPowerRenameEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredPowerLauncherEnabledValue_ReturnsValidState)
{
auto result = getConfiguredPowerLauncherEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredShortcutGuideEnabledValue_ReturnsValidState)
{
auto result = getConfiguredShortcutGuideEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredTextExtractorEnabledValue_ReturnsValidState)
{
auto result = getConfiguredTextExtractorEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredHostsFileEditorEnabledValue_ReturnsValidState)
{
auto result = getConfiguredHostsFileEditorEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredMousePointerCrosshairsEnabledValue_ReturnsValidState)
{
auto result = getConfiguredMousePointerCrosshairsEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredMouseHighlighterEnabledValue_ReturnsValidState)
{
auto result = getConfiguredMouseHighlighterEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredMouseJumpEnabledValue_ReturnsValidState)
{
auto result = getConfiguredMouseJumpEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredFindMyMouseEnabledValue_ReturnsValidState)
{
auto result = getConfiguredFindMyMouseEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredMouseWithoutBordersEnabledValue_ReturnsValidState)
{
auto result = getConfiguredMouseWithoutBordersEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredAdvancedPasteEnabledValue_ReturnsValidState)
{
auto result = getConfiguredAdvancedPasteEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredPeekEnabledValue_ReturnsValidState)
{
auto result = getConfiguredPeekEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredRegistryPreviewEnabledValue_ReturnsValidState)
{
auto result = getConfiguredRegistryPreviewEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredScreenRulerEnabledValue_ReturnsValidState)
{
auto result = getConfiguredScreenRulerEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredCropAndLockEnabledValue_ReturnsValidState)
{
auto result = getConfiguredCropAndLockEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
TEST_METHOD(GetConfiguredEnvironmentVariablesEnabledValue_ReturnsValidState)
{
auto result = getConfiguredEnvironmentVariablesEnabledValue();
Assert::IsTrue(IsValidGpoResult(result));
}
// All GPO functions should not crash
TEST_METHOD(AllGpoFunctions_DoNotCrash)
{
getAllowExperimentationValue();
getConfiguredAlwaysOnTopEnabledValue();
getConfiguredAwakeEnabledValue();
getConfiguredColorPickerEnabledValue();
getConfiguredFancyZonesEnabledValue();
getConfiguredFileLocksmithEnabledValue();
getConfiguredImageResizerEnabledValue();
getConfiguredKeyboardManagerEnabledValue();
getConfiguredPowerRenameEnabledValue();
getConfiguredPowerLauncherEnabledValue();
getConfiguredShortcutGuideEnabledValue();
getConfiguredTextExtractorEnabledValue();
getConfiguredHostsFileEditorEnabledValue();
getConfiguredMousePointerCrosshairsEnabledValue();
getConfiguredMouseHighlighterEnabledValue();
getConfiguredMouseJumpEnabledValue();
getConfiguredFindMyMouseEnabledValue();
getConfiguredMouseWithoutBordersEnabledValue();
getConfiguredAdvancedPasteEnabledValue();
getConfiguredPeekEnabledValue();
getConfiguredRegistryPreviewEnabledValue();
getConfiguredScreenRulerEnabledValue();
getConfiguredCropAndLockEnabledValue();
getConfiguredEnvironmentVariablesEnabledValue();
Assert::IsTrue(true);
}
};
}

View File

@@ -1,200 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <HDropIterator.h>
#include <shlobj.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(HDropIteratorTests)
{
public:
// Helper to create a test HDROP structure
static HGLOBAL CreateTestHDrop(const std::vector<std::wstring>& files)
{
// Calculate required size
size_t size = sizeof(DROPFILES);
for (const auto& file : files)
{
size += (file.length() + 1) * sizeof(wchar_t);
}
size += sizeof(wchar_t); // Double null terminator
HGLOBAL hGlobal = GlobalAlloc(GHND, size);
if (!hGlobal) return nullptr;
DROPFILES* pDropFiles = static_cast<DROPFILES*>(GlobalLock(hGlobal));
if (!pDropFiles)
{
GlobalFree(hGlobal);
return nullptr;
}
pDropFiles->pFiles = sizeof(DROPFILES);
pDropFiles->fWide = TRUE;
wchar_t* pData = reinterpret_cast<wchar_t*>(reinterpret_cast<BYTE*>(pDropFiles) + sizeof(DROPFILES));
for (const auto& file : files)
{
wcscpy_s(pData, file.length() + 1, file.c_str());
pData += file.length() + 1;
}
*pData = L'\0'; // Double null terminator
GlobalUnlock(hGlobal);
return hGlobal;
}
TEST_METHOD(HDropIterator_EmptyDrop_IsDoneImmediately)
{
HGLOBAL hGlobal = CreateTestHDrop({});
if (!hGlobal)
{
Assert::IsTrue(true); // Skip if allocation failed
return;
}
STGMEDIUM medium = {};
medium.tymed = TYMED_HGLOBAL;
medium.hGlobal = hGlobal;
// Without a proper IDataObject, we can't fully test
// Just verify the class can be instantiated conceptually
GlobalFree(hGlobal);
Assert::IsTrue(true);
}
TEST_METHOD(HDropIterator_Iteration_Conceptual)
{
// This test verifies the concept of iteration
// Full integration testing requires a proper IDataObject
std::vector<std::wstring> testFiles = {
L"C:\\test\\file1.txt",
L"C:\\test\\file2.txt",
L"C:\\test\\file3.txt"
};
HGLOBAL hGlobal = CreateTestHDrop(testFiles);
if (!hGlobal)
{
Assert::IsTrue(true);
return;
}
// Verify we can create the HDROP structure
DROPFILES* pDropFiles = static_cast<DROPFILES*>(GlobalLock(hGlobal));
Assert::IsNotNull(pDropFiles);
Assert::IsTrue(pDropFiles->fWide);
GlobalUnlock(hGlobal);
GlobalFree(hGlobal);
Assert::IsTrue(true);
}
TEST_METHOD(HDropIterator_SingleFile_Works)
{
std::vector<std::wstring> testFiles = { L"C:\\test\\single.txt" };
HGLOBAL hGlobal = CreateTestHDrop(testFiles);
if (!hGlobal)
{
Assert::IsTrue(true);
return;
}
// Verify structure
DROPFILES* pDropFiles = static_cast<DROPFILES*>(GlobalLock(hGlobal));
Assert::IsNotNull(pDropFiles);
// Read back the file name
wchar_t* pData = reinterpret_cast<wchar_t*>(reinterpret_cast<BYTE*>(pDropFiles) + pDropFiles->pFiles);
Assert::AreEqual(std::wstring(L"C:\\test\\single.txt"), std::wstring(pData));
GlobalUnlock(hGlobal);
GlobalFree(hGlobal);
}
TEST_METHOD(HDropIterator_MultipleFiles_Structure)
{
std::vector<std::wstring> testFiles = {
L"C:\\file1.txt",
L"C:\\file2.txt",
L"C:\\file3.txt"
};
HGLOBAL hGlobal = CreateTestHDrop(testFiles);
if (!hGlobal)
{
Assert::IsTrue(true);
return;
}
DROPFILES* pDropFiles = static_cast<DROPFILES*>(GlobalLock(hGlobal));
Assert::IsNotNull(pDropFiles);
// Count files by iterating through null-terminated strings
wchar_t* pData = reinterpret_cast<wchar_t*>(reinterpret_cast<BYTE*>(pDropFiles) + pDropFiles->pFiles);
int count = 0;
while (*pData)
{
count++;
pData += wcslen(pData) + 1;
}
Assert::AreEqual(3, count);
GlobalUnlock(hGlobal);
GlobalFree(hGlobal);
}
TEST_METHOD(HDropIterator_UnicodeFilenames_Work)
{
std::vector<std::wstring> testFiles = {
L"C:\\test\\file.txt"
};
HGLOBAL hGlobal = CreateTestHDrop(testFiles);
if (!hGlobal)
{
Assert::IsTrue(true);
return;
}
DROPFILES* pDropFiles = static_cast<DROPFILES*>(GlobalLock(hGlobal));
Assert::IsTrue(pDropFiles->fWide == TRUE);
GlobalUnlock(hGlobal);
GlobalFree(hGlobal);
}
TEST_METHOD(HDropIterator_LongFilenames_Work)
{
std::wstring longPath = L"C:\\";
for (int i = 0; i < 20; ++i)
{
longPath += L"LongFolderName\\";
}
longPath += L"file.txt";
std::vector<std::wstring> testFiles = { longPath };
HGLOBAL hGlobal = CreateTestHDrop(testFiles);
if (!hGlobal)
{
Assert::IsTrue(true);
return;
}
DROPFILES* pDropFiles = static_cast<DROPFILES*>(GlobalLock(hGlobal));
Assert::IsNotNull(pDropFiles);
wchar_t* pData = reinterpret_cast<wchar_t*>(reinterpret_cast<BYTE*>(pDropFiles) + pDropFiles->pFiles);
Assert::AreEqual(longPath, std::wstring(pData));
GlobalUnlock(hGlobal);
GlobalFree(hGlobal);
}
};
}

View File

@@ -1,152 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <HttpClient.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(HttpClientTests)
{
public:
// Note: Network tests may fail in offline environments
// These tests are designed to verify the API doesn't crash
TEST_METHOD(HttpClient_DefaultConstruction)
{
http::HttpClient client;
// Should not crash
Assert::IsTrue(true);
}
TEST_METHOD(HttpClient_Request_InvalidUri_ReturnsEmpty)
{
http::HttpClient client;
try
{
// Invalid URI should not crash, may throw or return empty
auto result = client.request(winrt::Windows::Foundation::Uri(L"invalid://not-a-valid-uri"));
// If we get here, result may be empty or contain error
}
catch (...)
{
// Exception is acceptable for invalid URI
}
Assert::IsTrue(true);
}
TEST_METHOD(HttpClient_Download_InvalidUri_DoesNotCrash)
{
http::HttpClient client;
TestHelpers::TempFile tempFile;
try
{
auto result = client.download(
winrt::Windows::Foundation::Uri(L"https://invalid.invalid.invalid"),
tempFile.path());
// May return false or throw
}
catch (...)
{
// Exception is acceptable for invalid/unreachable URI
}
Assert::IsTrue(true);
}
TEST_METHOD(HttpClient_Download_WithCallback_DoesNotCrash)
{
http::HttpClient client;
TestHelpers::TempFile tempFile;
std::atomic<int> callbackCount{ 0 };
try
{
auto result = client.download(
winrt::Windows::Foundation::Uri(L"https://invalid.invalid.invalid"),
tempFile.path(),
[&callbackCount]([[maybe_unused]] float progress) {
callbackCount++;
});
}
catch (...)
{
// Exception is acceptable
}
Assert::IsTrue(true);
}
TEST_METHOD(HttpClient_Download_EmptyPath_DoesNotCrash)
{
http::HttpClient client;
try
{
auto result = client.download(
winrt::Windows::Foundation::Uri(L"https://example.com"),
L"");
}
catch (...)
{
// Exception is acceptable for empty path
}
Assert::IsTrue(true);
}
// These tests require network access and may be skipped in offline environments
TEST_METHOD(HttpClient_Request_ValidUri_ReturnsResult)
{
// Skip this test in most CI environments
// Only run manually to verify network functionality
http::HttpClient client;
try
{
// Use a reliable, fast-responding URL
auto result = client.request(winrt::Windows::Foundation::Uri(L"https://www.microsoft.com"));
// Result may or may not be successful depending on network
}
catch (...)
{
// Network errors are acceptable in test environment
}
Assert::IsTrue(true);
}
// Thread safety test (doesn't require network)
TEST_METHOD(HttpClient_MultipleInstances_DoNotCrash)
{
std::vector<std::unique_ptr<http::HttpClient>> clients;
for (int i = 0; i < 10; ++i)
{
clients.push_back(std::make_unique<http::HttpClient>());
}
// All clients should coexist without crashing
Assert::AreEqual(static_cast<size_t>(10), clients.size());
}
TEST_METHOD(HttpClient_ConcurrentConstruction_DoesNotCrash)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 5; ++i)
{
threads.emplace_back([&successCount]() {
http::HttpClient client;
successCount++;
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(5, successCount.load());
}
};
}

View File

@@ -1,283 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <json.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace winrt::Windows::Data::Json;
namespace UnitTestsCommonUtils
{
TEST_CLASS(JsonTests)
{
public:
// from_file tests
TEST_METHOD(FromFile_NonExistentFile_ReturnsNullopt)
{
auto result = json::from_file(L"C:\\NonExistent\\File\\Path.json");
Assert::IsFalse(result.has_value());
}
TEST_METHOD(FromFile_ValidJsonFile_ReturnsJsonObject)
{
TestHelpers::TempFile tempFile(L"", L".json");
tempFile.write("{\"key\": \"value\"}");
auto result = json::from_file(tempFile.path());
Assert::IsTrue(result.has_value());
}
TEST_METHOD(FromFile_InvalidJson_ReturnsNullopt)
{
TestHelpers::TempFile tempFile(L"", L".json");
tempFile.write("not valid json {{{");
auto result = json::from_file(tempFile.path());
Assert::IsFalse(result.has_value());
}
TEST_METHOD(FromFile_EmptyFile_ReturnsNullopt)
{
TestHelpers::TempFile tempFile(L"", L".json");
// File is empty
auto result = json::from_file(tempFile.path());
Assert::IsFalse(result.has_value());
}
TEST_METHOD(FromFile_ValidComplexJson_ParsesCorrectly)
{
TestHelpers::TempFile tempFile(L"", L".json");
tempFile.write("{\"name\": \"test\", \"value\": 42, \"enabled\": true}");
auto result = json::from_file(tempFile.path());
Assert::IsTrue(result.has_value());
auto& obj = *result;
Assert::IsTrue(obj.HasKey(L"name"));
Assert::IsTrue(obj.HasKey(L"value"));
Assert::IsTrue(obj.HasKey(L"enabled"));
}
// to_file tests
TEST_METHOD(ToFile_ValidObject_WritesFile)
{
TestHelpers::TempFile tempFile(L"", L".json");
JsonObject obj;
obj.SetNamedValue(L"key", JsonValue::CreateStringValue(L"value"));
json::to_file(tempFile.path(), obj);
// Read back and verify
auto result = json::from_file(tempFile.path());
Assert::IsTrue(result.has_value());
Assert::IsTrue(result->HasKey(L"key"));
}
TEST_METHOD(ToFile_ComplexObject_WritesFile)
{
TestHelpers::TempFile tempFile(L"", L".json");
JsonObject obj;
obj.SetNamedValue(L"name", JsonValue::CreateStringValue(L"test"));
obj.SetNamedValue(L"value", JsonValue::CreateNumberValue(42));
obj.SetNamedValue(L"enabled", JsonValue::CreateBooleanValue(true));
json::to_file(tempFile.path(), obj);
auto result = json::from_file(tempFile.path());
Assert::IsTrue(result.has_value());
Assert::AreEqual(std::wstring(L"test"), std::wstring(result->GetNamedString(L"name")));
Assert::AreEqual(42.0, result->GetNamedNumber(L"value"));
Assert::IsTrue(result->GetNamedBoolean(L"enabled"));
}
// has tests
TEST_METHOD(Has_ExistingKey_ReturnsTrue)
{
JsonObject obj;
obj.SetNamedValue(L"key", JsonValue::CreateStringValue(L"value"));
Assert::IsTrue(json::has(obj, L"key", JsonValueType::String));
}
TEST_METHOD(Has_NonExistingKey_ReturnsFalse)
{
JsonObject obj;
Assert::IsFalse(json::has(obj, L"key", JsonValueType::String));
}
TEST_METHOD(Has_WrongType_ReturnsFalse)
{
JsonObject obj;
obj.SetNamedValue(L"key", JsonValue::CreateStringValue(L"value"));
Assert::IsFalse(json::has(obj, L"key", JsonValueType::Number));
}
TEST_METHOD(Has_NumberType_ReturnsTrue)
{
JsonObject obj;
obj.SetNamedValue(L"key", JsonValue::CreateNumberValue(42));
Assert::IsTrue(json::has(obj, L"key", JsonValueType::Number));
}
TEST_METHOD(Has_BooleanType_ReturnsTrue)
{
JsonObject obj;
obj.SetNamedValue(L"key", JsonValue::CreateBooleanValue(true));
Assert::IsTrue(json::has(obj, L"key", JsonValueType::Boolean));
}
TEST_METHOD(Has_ObjectType_ReturnsTrue)
{
JsonObject obj;
JsonObject nested;
obj.SetNamedValue(L"key", nested);
Assert::IsTrue(json::has(obj, L"key", JsonValueType::Object));
}
// value function tests
TEST_METHOD(Value_IntegerType_CreatesNumberValue)
{
auto val = json::value(42);
Assert::IsTrue(val.ValueType() == JsonValueType::Number);
Assert::AreEqual(42.0, val.GetNumber());
}
TEST_METHOD(Value_DoubleType_CreatesNumberValue)
{
auto val = json::value(3.14);
Assert::IsTrue(val.ValueType() == JsonValueType::Number);
Assert::AreEqual(3.14, val.GetNumber());
}
TEST_METHOD(Value_BooleanTrue_CreatesBooleanValue)
{
auto val = json::value(true);
Assert::IsTrue(val.ValueType() == JsonValueType::Boolean);
Assert::IsTrue(val.GetBoolean());
}
TEST_METHOD(Value_BooleanFalse_CreatesBooleanValue)
{
auto val = json::value(false);
Assert::IsTrue(val.ValueType() == JsonValueType::Boolean);
Assert::IsFalse(val.GetBoolean());
}
TEST_METHOD(Value_String_CreatesStringValue)
{
auto val = json::value(L"hello");
Assert::IsTrue(val.ValueType() == JsonValueType::String);
Assert::AreEqual(std::wstring(L"hello"), std::wstring(val.GetString()));
}
TEST_METHOD(Value_JsonObject_ReturnsJsonValue)
{
JsonObject obj;
obj.SetNamedValue(L"key", JsonValue::CreateStringValue(L"value"));
auto val = json::value(obj);
Assert::IsTrue(val.ValueType() == JsonValueType::Object);
}
TEST_METHOD(Value_JsonValue_ReturnsIdentity)
{
auto original = JsonValue::CreateStringValue(L"test");
auto result = json::value(original);
Assert::AreEqual(std::wstring(L"test"), std::wstring(result.GetString()));
}
// get function tests
TEST_METHOD(Get_BooleanValue_ReturnsValue)
{
JsonObject obj;
obj.SetNamedValue(L"enabled", JsonValue::CreateBooleanValue(true));
bool result = false;
json::get(obj, L"enabled", result);
Assert::IsTrue(result);
}
TEST_METHOD(Get_IntValue_ReturnsValue)
{
JsonObject obj;
obj.SetNamedValue(L"count", JsonValue::CreateNumberValue(42));
int result = 0;
json::get(obj, L"count", result);
Assert::AreEqual(42, result);
}
TEST_METHOD(Get_DoubleValue_ReturnsValue)
{
JsonObject obj;
obj.SetNamedValue(L"ratio", JsonValue::CreateNumberValue(3.14));
double result = 0.0;
json::get(obj, L"ratio", result);
Assert::AreEqual(3.14, result);
}
TEST_METHOD(Get_StringValue_ReturnsValue)
{
JsonObject obj;
obj.SetNamedValue(L"name", JsonValue::CreateStringValue(L"test"));
std::wstring result;
json::get(obj, L"name", result);
Assert::AreEqual(std::wstring(L"test"), result);
}
TEST_METHOD(Get_MissingKey_UsesDefault)
{
JsonObject obj;
int result = 0;
json::get(obj, L"missing", result, 99);
Assert::AreEqual(99, result);
}
TEST_METHOD(Get_MissingKeyNoDefault_PreservesOriginal)
{
JsonObject obj;
int result = 42;
json::get(obj, L"missing", result);
// When key is missing and no default, original value is preserved
Assert::AreEqual(42, result);
}
TEST_METHOD(Get_JsonObject_ReturnsObject)
{
JsonObject obj;
JsonObject nested;
nested.SetNamedValue(L"inner", JsonValue::CreateStringValue(L"value"));
obj.SetNamedValue(L"nested", nested);
JsonObject result;
json::get(obj, L"nested", result);
Assert::IsTrue(result.HasKey(L"inner"));
}
// Roundtrip tests
TEST_METHOD(Roundtrip_ComplexObject_PreservesData)
{
TestHelpers::TempFile tempFile(L"", L".json");
JsonObject original;
original.SetNamedValue(L"string", JsonValue::CreateStringValue(L"hello"));
original.SetNamedValue(L"number", JsonValue::CreateNumberValue(42));
original.SetNamedValue(L"boolean", JsonValue::CreateBooleanValue(true));
JsonObject nested;
nested.SetNamedValue(L"inner", JsonValue::CreateStringValue(L"world"));
original.SetNamedValue(L"object", nested);
json::to_file(tempFile.path(), original);
auto loaded = json::from_file(tempFile.path());
Assert::IsTrue(loaded.has_value());
Assert::AreEqual(std::wstring(L"hello"), std::wstring(loaded->GetNamedString(L"string")));
Assert::AreEqual(42.0, loaded->GetNamedNumber(L"number"));
Assert::IsTrue(loaded->GetNamedBoolean(L"boolean"));
Assert::AreEqual(std::wstring(L"world"), std::wstring(loaded->GetNamedObject(L"object").GetNamedString(L"inner")));
}
};
}

View File

@@ -1,180 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <logger_helper.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace LoggerHelpers;
namespace UnitTestsCommonUtils
{
TEST_CLASS(LoggerHelperTests)
{
public:
// get_log_folder_path tests
TEST_METHOD(GetLogFolderPath_ValidAppPath_ReturnsPath)
{
auto result = get_log_folder_path(L"TestApp");
Assert::IsFalse(result.empty());
// Should contain the app name or be a valid path
auto pathStr = result.wstring();
Assert::IsTrue(pathStr.length() > 0);
}
TEST_METHOD(GetLogFolderPath_EmptyAppPath_ReturnsPath)
{
auto result = get_log_folder_path(L"");
// Should still return a base path
Assert::IsTrue(true); // Just verify no crash
}
TEST_METHOD(GetLogFolderPath_SpecialCharacters_Works)
{
auto result = get_log_folder_path(L"Test App With Spaces");
// Should handle spaces in path
Assert::IsTrue(true);
}
TEST_METHOD(GetLogFolderPath_ConsistentResults)
{
auto result1 = get_log_folder_path(L"TestApp");
auto result2 = get_log_folder_path(L"TestApp");
Assert::AreEqual(result1.wstring(), result2.wstring());
}
// dir_exists tests
TEST_METHOD(DirExists_WindowsDirectory_ReturnsTrue)
{
bool result = dir_exists(std::filesystem::path(L"C:\\Windows"));
Assert::IsTrue(result);
}
TEST_METHOD(DirExists_NonExistentDirectory_ReturnsFalse)
{
bool result = dir_exists(std::filesystem::path(L"C:\\NonExistentDir12345"));
Assert::IsFalse(result);
}
TEST_METHOD(DirExists_FileInsteadOfDir_ReturnsTrue)
{
// notepad.exe is a file, not a directory
bool result = dir_exists(std::filesystem::path(L"C:\\Windows\\notepad.exe"));
Assert::IsTrue(result);
}
TEST_METHOD(DirExists_EmptyPath_ReturnsFalse)
{
bool result = dir_exists(std::filesystem::path(L""));
Assert::IsFalse(result);
}
TEST_METHOD(DirExists_TempDirectory_ReturnsTrue)
{
wchar_t tempPath[MAX_PATH];
GetTempPathW(MAX_PATH, tempPath);
bool result = dir_exists(std::filesystem::path(tempPath));
Assert::IsTrue(result);
}
// delete_old_log_folder tests
TEST_METHOD(DeleteOldLogFolder_NonExistentFolder_DoesNotCrash)
{
delete_old_log_folder(std::filesystem::path(L"C:\\NonExistentLogFolder12345"));
Assert::IsTrue(true);
}
TEST_METHOD(DeleteOldLogFolder_ValidEmptyFolder_Works)
{
TestHelpers::TempDirectory tempDir;
// Create a subfolder structure
auto logFolder = std::filesystem::path(tempDir.path()) / L"logs";
std::filesystem::create_directories(logFolder);
Assert::IsTrue(std::filesystem::exists(logFolder));
delete_old_log_folder(logFolder);
// Folder may or may not be deleted depending on implementation
Assert::IsTrue(true);
}
// delete_other_versions_log_folders tests
TEST_METHOD(DeleteOtherVersionsLogFolders_NonExistentPath_DoesNotCrash)
{
delete_other_versions_log_folders(L"C:\\NonExistent\\Path", L"1.0.0");
Assert::IsTrue(true);
}
TEST_METHOD(DeleteOtherVersionsLogFolders_EmptyVersion_DoesNotCrash)
{
wchar_t tempPath[MAX_PATH];
GetTempPathW(MAX_PATH, tempPath);
delete_other_versions_log_folders(tempPath, L"");
Assert::IsTrue(true);
}
// Thread safety tests
TEST_METHOD(GetLogFolderPath_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount, i]() {
auto path = get_log_folder_path(L"TestApp" + std::to_wstring(i));
if (!path.empty())
{
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(10, successCount.load());
}
TEST_METHOD(DirExists_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 10; ++j)
{
dir_exists(std::filesystem::path(L"C:\\Windows"));
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(100, successCount.load());
}
// Path construction tests
TEST_METHOD(GetLogFolderPath_ReturnsValidFilesystemPath)
{
auto result = get_log_folder_path(L"TestApp");
// Should be a valid path that we can use with filesystem operations
Assert::IsTrue(result.is_absolute() || result.has_root_name() || !result.empty());
}
};
}

View File

@@ -1,173 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <modulesRegistry.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
static std::wstring GetInstallDir()
{
wchar_t path[MAX_PATH];
GetModuleFileNameW(nullptr, path, MAX_PATH);
return std::filesystem::path{ path }.parent_path().wstring();
}
TEST_CLASS(ModulesRegistryTests)
{
public:
// Test that all changeset generator functions return valid changesets
TEST_METHOD(GetSvgPreviewHandlerChangeSet_ReturnsChangeSet)
{
auto changeSet = getSvgPreviewHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetSvgThumbnailProviderChangeSet_ReturnsChangeSet)
{
auto changeSet = getSvgThumbnailHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetMarkdownPreviewHandlerChangeSet_ReturnsChangeSet)
{
auto changeSet = getMdPreviewHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetMonacoPreviewHandlerChangeSet_ReturnsChangeSet)
{
auto changeSet = getMonacoPreviewHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetPdfPreviewHandlerChangeSet_ReturnsChangeSet)
{
auto changeSet = getPdfPreviewHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetPdfThumbnailProviderChangeSet_ReturnsChangeSet)
{
auto changeSet = getPdfThumbnailHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetGcodePreviewHandlerChangeSet_ReturnsChangeSet)
{
auto changeSet = getGcodePreviewHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetGcodeThumbnailProviderChangeSet_ReturnsChangeSet)
{
auto changeSet = getGcodeThumbnailHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetStlThumbnailProviderChangeSet_ReturnsChangeSet)
{
auto changeSet = getStlThumbnailHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetQoiPreviewHandlerChangeSet_ReturnsChangeSet)
{
auto changeSet = getQoiPreviewHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
TEST_METHOD(GetQoiThumbnailProviderChangeSet_ReturnsChangeSet)
{
auto changeSet = getQoiThumbnailHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
// Test enabled vs disabled state
TEST_METHOD(ChangeSet_EnabledVsDisabled_MayDiffer)
{
auto enabledSet = getSvgPreviewHandlerChangeSet(GetInstallDir(), true);
auto disabledSet = getSvgPreviewHandlerChangeSet(GetInstallDir(), false);
// Both should be valid change sets
Assert::IsFalse(enabledSet.changes.empty());
Assert::IsFalse(disabledSet.changes.empty());
}
// Test getAllOnByDefaultModulesChangeSets
TEST_METHOD(GetAllOnByDefaultModulesChangeSets_ReturnsMultipleChangeSets)
{
auto changeSets = getAllOnByDefaultModulesChangeSets(GetInstallDir());
// Should return multiple changesets for all default-enabled modules
Assert::IsTrue(changeSets.size() > 0);
}
// Test getAllModulesChangeSets
TEST_METHOD(GetAllModulesChangeSets_ReturnsChangeSets)
{
auto changeSets = getAllModulesChangeSets(GetInstallDir());
// Should return changesets for all modules
Assert::IsTrue(changeSets.size() > 0);
}
TEST_METHOD(GetAllModulesChangeSets_ContainsMoreThanOnByDefault)
{
auto allSets = getAllModulesChangeSets(GetInstallDir());
auto defaultSets = getAllOnByDefaultModulesChangeSets(GetInstallDir());
// All modules should be >= on-by-default modules
Assert::IsTrue(allSets.size() >= defaultSets.size());
}
// Test that changesets have valid structure
TEST_METHOD(ChangeSet_HasValidKeyPath)
{
auto changeSet = getSvgPreviewHandlerChangeSet(GetInstallDir(), false);
Assert::IsFalse(changeSet.changes.empty());
}
// Test all changeset functions don't crash
TEST_METHOD(AllChangeSetFunctions_DoNotCrash)
{
auto installDir = GetInstallDir();
getSvgPreviewHandlerChangeSet(installDir, true);
getSvgPreviewHandlerChangeSet(installDir, false);
getSvgThumbnailHandlerChangeSet(installDir, true);
getSvgThumbnailHandlerChangeSet(installDir, false);
getMdPreviewHandlerChangeSet(installDir, true);
getMdPreviewHandlerChangeSet(installDir, false);
getMonacoPreviewHandlerChangeSet(installDir, true);
getMonacoPreviewHandlerChangeSet(installDir, false);
getPdfPreviewHandlerChangeSet(installDir, true);
getPdfPreviewHandlerChangeSet(installDir, false);
getPdfThumbnailHandlerChangeSet(installDir, true);
getPdfThumbnailHandlerChangeSet(installDir, false);
getGcodePreviewHandlerChangeSet(installDir, true);
getGcodePreviewHandlerChangeSet(installDir, false);
getGcodeThumbnailHandlerChangeSet(installDir, true);
getGcodeThumbnailHandlerChangeSet(installDir, false);
getStlThumbnailHandlerChangeSet(installDir, true);
getStlThumbnailHandlerChangeSet(installDir, false);
getQoiPreviewHandlerChangeSet(installDir, true);
getQoiPreviewHandlerChangeSet(installDir, false);
getQoiThumbnailHandlerChangeSet(installDir, true);
getQoiThumbnailHandlerChangeSet(installDir, false);
Assert::IsTrue(true);
}
};
}

View File

@@ -1,65 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <MsWindowsSettings.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(MsWindowsSettingsTests)
{
public:
TEST_METHOD(GetAnimationsEnabled_ReturnsBoolean)
{
bool result = GetAnimationsEnabled();
// Should return a valid boolean
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(GetAnimationsEnabled_ConsistentResults)
{
// Multiple calls should return consistent results
bool result1 = GetAnimationsEnabled();
bool result2 = GetAnimationsEnabled();
bool result3 = GetAnimationsEnabled();
Assert::AreEqual(result1, result2);
Assert::AreEqual(result2, result3);
}
TEST_METHOD(GetAnimationsEnabled_DoesNotCrash)
{
// Call multiple times to ensure stability
for (int i = 0; i < 100; ++i)
{
GetAnimationsEnabled();
}
Assert::IsTrue(true);
}
TEST_METHOD(GetAnimationsEnabled_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 10; ++j)
{
GetAnimationsEnabled();
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(100, successCount.load());
}
};
}

View File

@@ -1,146 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <MsiUtils.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(MsiUtilsTests)
{
public:
// GetMsiPackageInstalledPath tests
TEST_METHOD(GetMsiPackageInstalledPath_PerUser_DoesNotCrash)
{
auto result = GetMsiPackageInstalledPath(true);
// Result depends on installation state, but should not crash
Assert::IsTrue(true);
}
TEST_METHOD(GetMsiPackageInstalledPath_PerMachine_DoesNotCrash)
{
auto result = GetMsiPackageInstalledPath(false);
// Result depends on installation state, but should not crash
Assert::IsTrue(true);
}
TEST_METHOD(GetMsiPackageInstalledPath_ConsistentResults)
{
auto result1 = GetMsiPackageInstalledPath(true);
auto result2 = GetMsiPackageInstalledPath(true);
// Results should be consistent
Assert::AreEqual(result1.has_value(), result2.has_value());
if (result1.has_value() && result2.has_value())
{
Assert::AreEqual(*result1, *result2);
}
}
TEST_METHOD(GetMsiPackageInstalledPath_PerUserVsPerMachine_MayDiffer)
{
auto perUser = GetMsiPackageInstalledPath(true);
auto perMachine = GetMsiPackageInstalledPath(false);
// These may or may not be equal depending on installation
// Just verify they don't crash
Assert::IsTrue(true);
}
// GetMsiPackagePath tests
TEST_METHOD(GetMsiPackagePath_DoesNotCrash)
{
auto result = GetMsiPackagePath();
// Result depends on installation state, but should not crash
Assert::IsTrue(true);
}
TEST_METHOD(GetMsiPackagePath_ConsistentResults)
{
auto result1 = GetMsiPackagePath();
auto result2 = GetMsiPackagePath();
// Results should be consistent
Assert::AreEqual(result1, result2);
}
// Thread safety tests
TEST_METHOD(GetMsiPackageInstalledPath_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 5; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 5; ++j)
{
GetMsiPackageInstalledPath(j % 2 == 0);
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(25, successCount.load());
}
TEST_METHOD(GetMsiPackagePath_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 5; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 5; ++j)
{
GetMsiPackagePath();
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(25, successCount.load());
}
// Return value format tests
TEST_METHOD(GetMsiPackageInstalledPath_ReturnsValidPathOrEmpty)
{
auto path = GetMsiPackageInstalledPath(true);
if (path.has_value() && !path->empty())
{
// If a path is returned, it should contain backslash or be a valid path format
Assert::IsTrue(path->find(L'\\') != std::wstring::npos ||
path->find(L'/') != std::wstring::npos ||
path->length() >= 2); // At minimum drive letter + colon
}
// No value or empty is also valid (not installed)
Assert::IsTrue(true);
}
TEST_METHOD(GetMsiPackagePath_ReturnsValidPathOrEmpty)
{
auto path = GetMsiPackagePath();
if (!path.empty())
{
// If a path is returned, it should be a valid path format
Assert::IsTrue(path.find(L'\\') != std::wstring::npos ||
path.find(L'/') != std::wstring::npos ||
path.length() >= 2);
}
Assert::IsTrue(true);
}
};
}

View File

@@ -1,107 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <os-detect.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(OsDetectTests)
{
public:
// IsAPIContractVxAvailable tests
TEST_METHOD(IsAPIContractV8Available_ReturnsBoolean)
{
// This test verifies the function runs without crashing
// The actual result depends on the OS version
bool result = IsAPIContractV8Available();
// Result is either true or false, both are valid
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(IsAPIContractVxAvailable_V1_ReturnsTrue)
{
// API contract v1 should be available on any modern Windows
bool result = IsAPIContractVxAvailable<1>();
Assert::IsTrue(result);
}
TEST_METHOD(IsAPIContractVxAvailable_V5_ReturnsBooleanConsistently)
{
// Call multiple times to verify caching works correctly
bool result1 = IsAPIContractVxAvailable<5>();
bool result2 = IsAPIContractVxAvailable<5>();
bool result3 = IsAPIContractVxAvailable<5>();
Assert::AreEqual(result1, result2);
Assert::AreEqual(result2, result3);
}
TEST_METHOD(IsAPIContractVxAvailable_V10_ReturnsBoolean)
{
bool result = IsAPIContractVxAvailable<10>();
// Result depends on Windows version, but should not crash
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(IsAPIContractVxAvailable_V15_ReturnsBoolean)
{
bool result = IsAPIContractVxAvailable<15>();
// Higher API versions, may or may not be available
Assert::IsTrue(result == true || result == false);
}
// Is19H1OrHigher tests
TEST_METHOD(Is19H1OrHigher_ReturnsBoolean)
{
bool result = Is19H1OrHigher();
// Result depends on OS version, but should not crash
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(Is19H1OrHigher_ReturnsSameAsV8Contract)
{
// Is19H1OrHigher is implemented as IsAPIContractV8Available
bool is19H1 = Is19H1OrHigher();
bool isV8 = IsAPIContractV8Available();
Assert::AreEqual(is19H1, isV8);
}
TEST_METHOD(Is19H1OrHigher_ConsistentAcrossMultipleCalls)
{
bool result1 = Is19H1OrHigher();
bool result2 = Is19H1OrHigher();
bool result3 = Is19H1OrHigher();
Assert::AreEqual(result1, result2);
Assert::AreEqual(result2, result3);
}
// Static caching behavior tests
TEST_METHOD(StaticCaching_DifferentContractVersions_IndependentResults)
{
// Each template instantiation has its own static variable
bool v1 = IsAPIContractVxAvailable<1>();
(void)v1; // Suppress unused variable warning
// v1 should be true on any modern Windows
Assert::IsTrue(v1);
}
// Performance test (optional - verifies caching)
TEST_METHOD(Performance_MultipleCallsAreFast)
{
// The static caching should make subsequent calls very fast
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; ++i)
{
Is19H1OrHigher();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
// 10000 calls should complete in well under 1 second due to caching
Assert::IsTrue(duration.count() < 1000);
}
};
}

View File

@@ -1,180 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <package.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace package;
namespace UnitTestsCommonUtils
{
TEST_CLASS(PackageTests)
{
public:
// IsWin11OrGreater tests
TEST_METHOD(IsWin11OrGreater_ReturnsBoolean)
{
bool result = IsWin11OrGreater();
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(IsWin11OrGreater_ConsistentResults)
{
bool result1 = IsWin11OrGreater();
bool result2 = IsWin11OrGreater();
bool result3 = IsWin11OrGreater();
Assert::AreEqual(result1, result2);
Assert::AreEqual(result2, result3);
}
// PACKAGE_VERSION struct tests
TEST_METHOD(PackageVersion_DefaultConstruction)
{
PACKAGE_VERSION version{};
Assert::AreEqual(static_cast<UINT16>(0), version.Major);
Assert::AreEqual(static_cast<UINT16>(0), version.Minor);
Assert::AreEqual(static_cast<UINT16>(0), version.Build);
Assert::AreEqual(static_cast<UINT16>(0), version.Revision);
}
TEST_METHOD(PackageVersion_Assignment)
{
PACKAGE_VERSION version{};
version.Major = 1;
version.Minor = 2;
version.Build = 3;
version.Revision = 4;
Assert::AreEqual(static_cast<UINT16>(1), version.Major);
Assert::AreEqual(static_cast<UINT16>(2), version.Minor);
Assert::AreEqual(static_cast<UINT16>(3), version.Build);
Assert::AreEqual(static_cast<UINT16>(4), version.Revision);
}
// ComInitializer tests
TEST_METHOD(ComInitializer_InitializesAndUninitializesCom)
{
{
ComInitializer comInit;
// COM should be initialized within this scope
}
// COM should be uninitialized after scope
// Verify we can initialize again
{
ComInitializer comInit2;
}
Assert::IsTrue(true);
}
TEST_METHOD(ComInitializer_MultipleInstances)
{
ComInitializer init1;
ComInitializer init2;
ComInitializer init3;
// Multiple initializations should work (COM uses reference counting)
Assert::IsTrue(true);
}
// GetRegisteredPackage tests
TEST_METHOD(GetRegisteredPackage_NonExistentPackage_ReturnsEmpty)
{
auto result = GetRegisteredPackage(L"NonExistentPackage12345", false);
// Should return empty for non-existent package
Assert::IsFalse(result.has_value());
}
TEST_METHOD(GetRegisteredPackage_EmptyName_DoesNotCrash)
{
auto result = GetRegisteredPackage(L"", false);
// Behavior may vary based on package enumeration; just ensure it doesn't crash.
Assert::IsTrue(true);
}
// IsPackageRegisteredWithPowerToysVersion tests
TEST_METHOD(IsPackageRegisteredWithPowerToysVersion_NonExistentPackage_ReturnsFalse)
{
bool result = IsPackageRegisteredWithPowerToysVersion(L"NonExistentPackage12345");
Assert::IsFalse(result);
}
TEST_METHOD(IsPackageRegisteredWithPowerToysVersion_EmptyName_ReturnsFalse)
{
bool result = IsPackageRegisteredWithPowerToysVersion(L"");
Assert::IsFalse(result);
}
// FindMsixFile tests
TEST_METHOD(FindMsixFile_NonExistentDirectory_ReturnsEmpty)
{
auto result = FindMsixFile(L"C:\\NonExistentDirectory12345", false);
Assert::IsTrue(result.empty());
}
TEST_METHOD(FindMsixFile_SystemDirectory_DoesNotCrash)
{
// System32 probably doesn't have MSIX files, but shouldn't crash
auto result = FindMsixFile(L"C:\\Windows\\System32", false);
// May or may not find files, but should not crash
Assert::IsTrue(true);
}
TEST_METHOD(FindMsixFile_RecursiveSearch_DoesNotCrash)
{
// Use temp directory which should exist
wchar_t tempPath[MAX_PATH];
GetTempPathW(MAX_PATH, tempPath);
auto result = FindMsixFile(tempPath, true);
// May or may not find files, but should not crash
Assert::IsTrue(true);
}
// GetPackageNameAndVersionFromAppx tests
TEST_METHOD(GetPackageNameAndVersionFromAppx_NonExistentFile_ReturnsFalse)
{
std::wstring name;
PACKAGE_VERSION version{};
bool result = GetPackageNameAndVersionFromAppx(L"C:\\NonExistent\\file.msix", name, version);
Assert::IsFalse(result);
}
TEST_METHOD(GetPackageNameAndVersionFromAppx_EmptyPath_ReturnsFalse)
{
std::wstring name;
PACKAGE_VERSION version{};
bool result = GetPackageNameAndVersionFromAppx(L"", name, version);
Assert::IsFalse(result);
}
// Thread safety
TEST_METHOD(IsWin11OrGreater_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 10; ++j)
{
IsWin11OrGreater();
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(100, successCount.load());
}
};
}

View File

@@ -1,136 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <processApi.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ProcessApiTests)
{
public:
TEST_METHOD(GetProcessHandlesByName_CurrentProcess_ReturnsHandles)
{
// Get current process executable name
wchar_t path[MAX_PATH];
GetModuleFileNameW(nullptr, path, MAX_PATH);
// Extract just the filename
std::wstring fullPath(path);
auto lastSlash = fullPath.rfind(L'\\');
std::wstring exeName = (lastSlash != std::wstring::npos) ?
fullPath.substr(lastSlash + 1) : fullPath;
auto handles = getProcessHandlesByName(exeName, PROCESS_QUERY_LIMITED_INFORMATION);
// Should find at least our own process
Assert::IsFalse(handles.empty());
// Handles are RAII-managed
}
TEST_METHOD(GetProcessHandlesByName_NonExistentProcess_ReturnsEmpty)
{
auto handles = getProcessHandlesByName(L"NonExistentProcess12345.exe", PROCESS_QUERY_LIMITED_INFORMATION);
Assert::IsTrue(handles.empty());
}
TEST_METHOD(GetProcessHandlesByName_EmptyName_ReturnsEmpty)
{
auto handles = getProcessHandlesByName(L"", PROCESS_QUERY_LIMITED_INFORMATION);
Assert::IsTrue(handles.empty());
}
TEST_METHOD(GetProcessHandlesByName_Explorer_ReturnsHandles)
{
// Explorer.exe should typically be running
auto handles = getProcessHandlesByName(L"explorer.exe", PROCESS_QUERY_LIMITED_INFORMATION);
// Handles are RAII-managed
// May or may not find explorer depending on system state
// Just verify it doesn't crash
Assert::IsTrue(true);
}
TEST_METHOD(GetProcessHandlesByName_CaseInsensitive_Works)
{
// Get current process name in uppercase
wchar_t path[MAX_PATH];
GetModuleFileNameW(nullptr, path, MAX_PATH);
std::wstring fullPath(path);
auto lastSlash = fullPath.rfind(L'\\');
std::wstring exeName = (lastSlash != std::wstring::npos) ?
fullPath.substr(lastSlash + 1) : fullPath;
// Convert to uppercase
std::wstring upperName = exeName;
std::transform(upperName.begin(), upperName.end(), upperName.begin(), ::towupper);
auto handles = getProcessHandlesByName(upperName, PROCESS_QUERY_LIMITED_INFORMATION);
// Handles are RAII-managed
// The function may or may not be case insensitive - just don't crash
Assert::IsTrue(true);
}
TEST_METHOD(GetProcessHandlesByName_DifferentAccessRights_Works)
{
wchar_t path[MAX_PATH];
GetModuleFileNameW(nullptr, path, MAX_PATH);
std::wstring fullPath(path);
auto lastSlash = fullPath.rfind(L'\\');
std::wstring exeName = (lastSlash != std::wstring::npos) ?
fullPath.substr(lastSlash + 1) : fullPath;
// Try with different access rights
auto handles1 = getProcessHandlesByName(exeName, PROCESS_QUERY_INFORMATION);
auto handles2 = getProcessHandlesByName(exeName, PROCESS_VM_READ);
// Handles are RAII-managed
// Just verify no crashes
Assert::IsTrue(true);
}
TEST_METHOD(GetProcessHandlesByName_SystemProcess_MayRequireElevation)
{
// System processes might require elevation
auto handles = getProcessHandlesByName(L"System", PROCESS_QUERY_LIMITED_INFORMATION);
// Handles are RAII-managed
// Just verify no crashes
Assert::IsTrue(true);
}
TEST_METHOD(GetProcessHandlesByName_ValidHandles_AreUsable)
{
wchar_t path[MAX_PATH];
GetModuleFileNameW(nullptr, path, MAX_PATH);
std::wstring fullPath(path);
auto lastSlash = fullPath.rfind(L'\\');
std::wstring exeName = (lastSlash != std::wstring::npos) ?
fullPath.substr(lastSlash + 1) : fullPath;
auto handles = getProcessHandlesByName(exeName, PROCESS_QUERY_LIMITED_INFORMATION);
bool foundValidHandle = false;
for (auto& handle : handles)
{
// Try to use the handle
DWORD exitCode;
if (GetExitCodeProcess(handle.get(), &exitCode))
{
foundValidHandle = true;
}
}
Assert::IsTrue(foundValidHandle || handles.empty());
}
};
}

View File

@@ -1,153 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <process_path.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ProcessPathTests)
{
public:
// get_process_path (by PID) tests
TEST_METHOD(GetProcessPath_CurrentProcess_ReturnsPath)
{
DWORD pid = GetCurrentProcessId();
auto path = get_process_path(pid);
Assert::IsFalse(path.empty());
Assert::IsTrue(path.find(L".exe") != std::wstring::npos ||
path.find(L".dll") != std::wstring::npos);
}
TEST_METHOD(GetProcessPath_InvalidPid_ReturnsEmpty)
{
DWORD invalidPid = 0xFFFFFFFF;
auto path = get_process_path(invalidPid);
// Should return empty for invalid PID
Assert::IsTrue(path.empty());
}
TEST_METHOD(GetProcessPath_ZeroPid_ReturnsEmpty)
{
auto path = get_process_path(static_cast<DWORD>(0));
// PID 0 is the System Idle Process, might return empty or a path
// Just verify it doesn't crash
Assert::IsTrue(true);
}
TEST_METHOD(GetProcessPath_SystemPid_DoesNotCrash)
{
// PID 4 is typically the System process
auto path = get_process_path(static_cast<DWORD>(4));
// May return empty due to access rights, but shouldn't crash
Assert::IsTrue(true);
}
// get_module_filename tests
TEST_METHOD(GetModuleFilename_NullModule_ReturnsExePath)
{
auto path = get_module_filename(nullptr);
Assert::IsFalse(path.empty());
Assert::IsTrue(path.find(L".exe") != std::wstring::npos ||
path.find(L".dll") != std::wstring::npos);
}
TEST_METHOD(GetModuleFilename_Kernel32_ReturnsPath)
{
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
Assert::IsNotNull(kernel32);
auto path = get_module_filename(kernel32);
Assert::IsFalse(path.empty());
// Should contain kernel32 (case insensitive check)
std::wstring lowerPath = path;
std::transform(lowerPath.begin(), lowerPath.end(), lowerPath.begin(), ::towlower);
Assert::IsTrue(lowerPath.find(L"kernel32") != std::wstring::npos);
}
TEST_METHOD(GetModuleFilename_InvalidModule_ReturnsEmpty)
{
auto path = get_module_filename(reinterpret_cast<HMODULE>(0x12345678));
// Invalid module should return empty
Assert::IsTrue(path.empty());
}
// get_module_folderpath tests
TEST_METHOD(GetModuleFolderpath_NullModule_ReturnsFolder)
{
auto folder = get_module_folderpath(nullptr, true);
Assert::IsFalse(folder.empty());
// Should not end with .exe when removeFilename is true
Assert::IsTrue(folder.find(L".exe") == std::wstring::npos);
// Should end with backslash or be a valid folder path
Assert::IsTrue(folder.back() == L'\\' || folder.find(L"\\") != std::wstring::npos);
}
TEST_METHOD(GetModuleFolderpath_KeepFilename_ReturnsFullPath)
{
auto fullPath = get_module_folderpath(nullptr, false);
Assert::IsFalse(fullPath.empty());
// Should contain .exe or .dll when not removing filename
Assert::IsTrue(fullPath.find(L".exe") != std::wstring::npos ||
fullPath.find(L".dll") != std::wstring::npos);
}
TEST_METHOD(GetModuleFolderpath_Kernel32_ReturnsSystem32)
{
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
Assert::IsNotNull(kernel32);
auto folder = get_module_folderpath(kernel32, true);
Assert::IsFalse(folder.empty());
// Should be in system32 folder
std::wstring lowerPath = folder;
std::transform(lowerPath.begin(), lowerPath.end(), lowerPath.begin(), ::towlower);
Assert::IsTrue(lowerPath.find(L"system32") != std::wstring::npos ||
lowerPath.find(L"syswow64") != std::wstring::npos);
}
// get_process_path (by HWND) tests
TEST_METHOD(GetProcessPath_DesktopWindow_ReturnsPath)
{
HWND desktop = GetDesktopWindow();
Assert::IsNotNull(desktop);
auto path = get_process_path(desktop);
// Desktop window should return a path
// (could be explorer.exe or empty depending on system)
Assert::IsTrue(true); // Just verify it doesn't crash
}
TEST_METHOD(GetProcessPath_InvalidHwnd_ReturnsEmpty)
{
auto path = get_process_path(reinterpret_cast<HWND>(0x12345678));
Assert::IsTrue(path.empty());
}
TEST_METHOD(GetProcessPath_NullHwnd_ReturnsEmpty)
{
auto path = get_process_path(static_cast<HWND>(nullptr));
Assert::IsTrue(path.empty());
}
// Consistency tests
TEST_METHOD(Consistency_ModuleFilenameAndFolderpath_AreRelated)
{
auto fullPath = get_module_filename(nullptr);
auto folder = get_module_folderpath(nullptr, true);
Assert::IsFalse(fullPath.empty());
Assert::IsFalse(folder.empty());
// Full path should start with the folder
Assert::IsTrue(fullPath.find(folder) == 0 || folder.find(fullPath.substr(0, folder.length())) == 0);
}
};
}

View File

@@ -1,127 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <ProcessWaiter.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace ProcessWaiter;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ProcessWaiterTests)
{
public:
TEST_METHOD(OnProcessTerminate_InvalidPid_DoesNotCrash)
{
std::atomic<bool> called{ false };
// Use a very unlikely PID (negative value as string will fail conversion)
OnProcessTerminate(L"invalid", [&called](DWORD) {
called = true;
});
// Wait briefly
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Should not crash, callback may or may not be called depending on implementation
Assert::IsTrue(true);
}
TEST_METHOD(OnProcessTerminate_NonExistentPid_DoesNotCrash)
{
std::atomic<bool> called{ false };
// Use a PID that likely doesn't exist
OnProcessTerminate(L"999999999", [&called](DWORD) {
called = true;
});
// Wait briefly
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Should not crash
Assert::IsTrue(true);
}
TEST_METHOD(OnProcessTerminate_ZeroPid_DoesNotCrash)
{
std::atomic<bool> called{ false };
OnProcessTerminate(L"0", [&called](DWORD) {
called = true;
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Assert::IsTrue(true);
}
TEST_METHOD(OnProcessTerminate_CurrentProcessPid_DoesNotTerminate)
{
std::atomic<bool> called{ false };
// Use current process PID - it shouldn't terminate during test
std::wstring pid = std::to_wstring(GetCurrentProcessId());
OnProcessTerminate(pid, [&called](DWORD) {
called = true;
});
// Wait briefly - current process should not terminate
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// Callback should not have been called since process is still running
Assert::IsFalse(called);
}
TEST_METHOD(OnProcessTerminate_EmptyCallback_DoesNotCrash)
{
// Test with an empty function
OnProcessTerminate(L"999999999", std::function<void(DWORD)>());
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Assert::IsTrue(true);
}
TEST_METHOD(OnProcessTerminate_MultipleCallsForSamePid_DoesNotCrash)
{
std::atomic<int> counter{ 0 };
std::wstring pid = std::to_wstring(GetCurrentProcessId());
// Multiple waits on same (running) process
for (int i = 0; i < 5; ++i)
{
OnProcessTerminate(pid, [&counter](DWORD) {
counter++;
});
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// None should have been called since process is running
Assert::AreEqual(0, counter.load());
}
TEST_METHOD(OnProcessTerminate_NegativeNumberString_DoesNotCrash)
{
std::atomic<bool> called{ false };
OnProcessTerminate(L"-1", [&called](DWORD) {
called = true;
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Assert::IsTrue(true);
}
TEST_METHOD(OnProcessTerminate_LargeNumber_DoesNotCrash)
{
std::atomic<bool> called{ false };
OnProcessTerminate(L"18446744073709551615", [&called](DWORD) {
called = true;
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Assert::IsTrue(true);
}
};
}

View File

@@ -1,61 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <registry.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(RegistryTests)
{
public:
// Note: These tests use HKCU which doesn't require elevation
TEST_METHOD(InstallScope_Registry_CanReadAndWrite)
{
TestHelpers::TestRegistryKey testKey(L"RegistryTest");
Assert::IsTrue(testKey.isValid());
// Write a test value
Assert::IsTrue(testKey.setStringValue(L"TestValue", L"TestData"));
Assert::IsTrue(testKey.setDwordValue(L"TestDword", 42));
}
TEST_METHOD(Registry_ValueChange_StringValue)
{
registry::ValueChange change{ HKEY_CURRENT_USER, L"Software\\PowerToys\\Test", L"TestValue", std::wstring{ L"TestData" } };
Assert::AreEqual(std::wstring(L"Software\\PowerToys\\Test"), change.path);
Assert::IsTrue(change.name.has_value());
Assert::AreEqual(std::wstring(L"TestValue"), *change.name);
Assert::AreEqual(std::wstring(L"TestData"), std::get<std::wstring>(change.value));
}
TEST_METHOD(Registry_ValueChange_DwordValue)
{
registry::ValueChange change{ HKEY_CURRENT_USER, L"Software\\PowerToys\\Test", L"TestDword", static_cast<DWORD>(42) };
Assert::AreEqual(std::wstring(L"Software\\PowerToys\\Test"), change.path);
Assert::IsTrue(change.name.has_value());
Assert::AreEqual(std::wstring(L"TestDword"), *change.name);
Assert::AreEqual(static_cast<DWORD>(42), std::get<DWORD>(change.value));
}
TEST_METHOD(Registry_ChangeSet_AddChanges)
{
registry::ChangeSet changeSet;
changeSet.changes.push_back({ HKEY_CURRENT_USER, L"Software\\PowerToys\\Test", L"Value1", std::wstring{ L"Data1" } });
changeSet.changes.push_back({ HKEY_CURRENT_USER, L"Software\\PowerToys\\Test", L"Value2", static_cast<DWORD>(123) });
Assert::AreEqual(static_cast<size_t>(2), changeSet.changes.size());
}
TEST_METHOD(InstallScope_GetCurrentInstallScope_ReturnsValidValue)
{
auto scope = registry::install_scope::get_current_install_scope();
Assert::IsTrue(scope == registry::install_scope::InstallScope::PerMachine ||
scope == registry::install_scope::InstallScope::PerUser);
}
};
}

View File

@@ -1,144 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <resources.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(ResourcesTests)
{
public:
// get_resource_string tests with current module
TEST_METHOD(GetResourceString_NonExistentId_ReturnsFallback)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
auto result = get_resource_string(99999, instance, L"fallback");
Assert::AreEqual(std::wstring(L"fallback"), result);
}
TEST_METHOD(GetResourceString_NullInstance_UsesFallback)
{
auto result = get_resource_string(99999, nullptr, L"fallback");
// Should return fallback or empty string
Assert::IsTrue(result == L"fallback" || result.empty());
}
TEST_METHOD(GetResourceString_EmptyFallback_ReturnsEmpty)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
auto result = get_resource_string(99999, instance, L"");
Assert::IsTrue(result.empty());
}
// get_english_fallback_string tests
TEST_METHOD(GetEnglishFallbackString_NonExistentId_ReturnsEmpty)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
auto result = get_english_fallback_string(99999, instance);
// Should return empty or the resource if it exists
Assert::IsTrue(true); // Just verify no crash
}
TEST_METHOD(GetEnglishFallbackString_NullInstance_DoesNotCrash)
{
auto result = get_english_fallback_string(99999, nullptr);
Assert::IsTrue(true); // Just verify no crash
}
// get_resource_string_language_override tests
TEST_METHOD(GetResourceStringLanguageOverride_NonExistentId_ReturnsEmpty)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
auto result = get_resource_string_language_override(99999, instance);
// Should return empty for non-existent resource
Assert::IsTrue(result.empty() || !result.empty()); // Valid either way
}
TEST_METHOD(GetResourceStringLanguageOverride_NullInstance_DoesNotCrash)
{
auto result = get_resource_string_language_override(99999, nullptr);
Assert::IsTrue(true);
}
// Thread safety tests
TEST_METHOD(GetResourceString_ThreadSafe)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount, instance]() {
for (int j = 0; j < 10; ++j)
{
get_resource_string(99999, instance, L"fallback");
successCount++;
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(100, successCount.load());
}
// Kernel32 resource tests (has known resources)
TEST_METHOD(GetResourceString_Kernel32_DoesNotCrash)
{
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
if (kernel32)
{
// Kernel32 has resources, but we don't know exact IDs
// Just verify it doesn't crash
get_resource_string(1, kernel32, L"fallback");
get_resource_string(100, kernel32, L"fallback");
get_resource_string(1000, kernel32, L"fallback");
}
Assert::IsTrue(true);
}
// Performance test
TEST_METHOD(GetResourceString_Performance_Acceptable)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 1000; ++i)
{
get_resource_string(99999, instance, L"fallback");
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
// 1000 lookups should complete in under 1 second
Assert::IsTrue(duration.count() < 1000);
}
// Edge case tests
TEST_METHOD(GetResourceString_ZeroId_DoesNotCrash)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
auto result = get_resource_string(0, instance, L"fallback");
Assert::IsTrue(true);
}
TEST_METHOD(GetResourceString_MaxUintId_DoesNotCrash)
{
HINSTANCE instance = GetModuleHandleW(nullptr);
auto result = get_resource_string(UINT_MAX, instance, L"fallback");
Assert::IsTrue(true);
}
};
}

View File

@@ -1,286 +0,0 @@
#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);
}
};
}

View File

@@ -1,283 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <string_utils.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(StringUtilsTests)
{
public:
// left_trim tests
TEST_METHOD(LeftTrim_EmptyString_ReturnsEmpty)
{
std::string_view input = "";
auto result = left_trim(input);
Assert::AreEqual(std::string_view(""), result);
}
TEST_METHOD(LeftTrim_NoWhitespace_ReturnsOriginal)
{
std::string_view input = "hello";
auto result = left_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(LeftTrim_LeadingSpaces_TrimsSpaces)
{
std::string_view input = " hello";
auto result = left_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(LeftTrim_LeadingTabs_TrimsTabs)
{
std::string_view input = "\t\thello";
auto result = left_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(LeftTrim_LeadingNewlines_TrimsNewlines)
{
std::string_view input = "\r\n\nhello";
auto result = left_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(LeftTrim_MixedWhitespace_TrimsAll)
{
std::string_view input = " \t\r\nhello";
auto result = left_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(LeftTrim_TrailingWhitespace_PreservesTrailing)
{
std::string_view input = " hello ";
auto result = left_trim(input);
Assert::AreEqual(std::string_view("hello "), result);
}
TEST_METHOD(LeftTrim_OnlyWhitespace_ReturnsEmpty)
{
std::string_view input = " \t\r\n";
auto result = left_trim(input);
Assert::AreEqual(std::string_view(""), result);
}
TEST_METHOD(LeftTrim_CustomChars_TrimsSpecified)
{
std::string_view input = "xxxhello";
auto result = left_trim(input, std::string_view("x"));
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(LeftTrim_WideString_Works)
{
std::wstring_view input = L" hello";
auto result = left_trim(input);
Assert::AreEqual(std::wstring_view(L"hello"), result);
}
// right_trim tests
TEST_METHOD(RightTrim_EmptyString_ReturnsEmpty)
{
std::string_view input = "";
auto result = right_trim(input);
Assert::AreEqual(std::string_view(""), result);
}
TEST_METHOD(RightTrim_NoWhitespace_ReturnsOriginal)
{
std::string_view input = "hello";
auto result = right_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(RightTrim_TrailingSpaces_TrimsSpaces)
{
std::string_view input = "hello ";
auto result = right_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(RightTrim_TrailingTabs_TrimsTabs)
{
std::string_view input = "hello\t\t";
auto result = right_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(RightTrim_TrailingNewlines_TrimsNewlines)
{
std::string_view input = "hello\r\n\n";
auto result = right_trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(RightTrim_LeadingWhitespace_PreservesLeading)
{
std::string_view input = " hello ";
auto result = right_trim(input);
Assert::AreEqual(std::string_view(" hello"), result);
}
TEST_METHOD(RightTrim_OnlyWhitespace_ReturnsEmpty)
{
std::string_view input = " \t\r\n";
auto result = right_trim(input);
Assert::AreEqual(std::string_view(""), result);
}
TEST_METHOD(RightTrim_CustomChars_TrimsSpecified)
{
std::string_view input = "helloxxx";
auto result = right_trim(input, std::string_view("x"));
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(RightTrim_WideString_Works)
{
std::wstring_view input = L"hello ";
auto result = right_trim(input);
Assert::AreEqual(std::wstring_view(L"hello"), result);
}
// trim tests
TEST_METHOD(Trim_EmptyString_ReturnsEmpty)
{
std::string_view input = "";
auto result = trim(input);
Assert::AreEqual(std::string_view(""), result);
}
TEST_METHOD(Trim_NoWhitespace_ReturnsOriginal)
{
std::string_view input = "hello";
auto result = trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(Trim_BothSides_TrimsBoth)
{
std::string_view input = " hello ";
auto result = trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(Trim_MixedWhitespace_TrimsAll)
{
std::string_view input = " \t\r\nhello \t\r\n";
auto result = trim(input);
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(Trim_InternalWhitespace_Preserved)
{
std::string_view input = " hello world ";
auto result = trim(input);
Assert::AreEqual(std::string_view("hello world"), result);
}
TEST_METHOD(Trim_OnlyWhitespace_ReturnsEmpty)
{
std::string_view input = " \t\r\n ";
auto result = trim(input);
Assert::AreEqual(std::string_view(""), result);
}
TEST_METHOD(Trim_CustomChars_TrimsSpecified)
{
std::string_view input = "xxxhelloxxx";
auto result = trim(input, std::string_view("x"));
Assert::AreEqual(std::string_view("hello"), result);
}
TEST_METHOD(Trim_WideString_Works)
{
std::wstring_view input = L" hello ";
auto result = trim(input);
Assert::AreEqual(std::wstring_view(L"hello"), result);
}
// replace_chars tests
TEST_METHOD(ReplaceChars_EmptyString_NoChange)
{
std::string s = "";
replace_chars(s, std::string_view("abc"), 'x');
Assert::AreEqual(std::string(""), s);
}
TEST_METHOD(ReplaceChars_NoMatchingChars_NoChange)
{
std::string s = "hello";
replace_chars(s, std::string_view("xyz"), '_');
Assert::AreEqual(std::string("hello"), s);
}
TEST_METHOD(ReplaceChars_SingleChar_Replaces)
{
std::string s = "hello";
replace_chars(s, std::string_view("l"), '_');
Assert::AreEqual(std::string("he__o"), s);
}
TEST_METHOD(ReplaceChars_MultipleChars_ReplacesAll)
{
std::string s = "hello world";
replace_chars(s, std::string_view("lo"), '_');
Assert::AreEqual(std::string("he___ w_r_d"), s);
}
TEST_METHOD(ReplaceChars_WideString_Works)
{
std::wstring s = L"hello";
replace_chars(s, std::wstring_view(L"l"), L'_');
Assert::AreEqual(std::wstring(L"he__o"), s);
}
// unwide tests
TEST_METHOD(Unwide_EmptyString_ReturnsEmpty)
{
std::wstring input = L"";
auto result = unwide(input);
Assert::AreEqual(std::string(""), result);
}
TEST_METHOD(Unwide_AsciiString_Converts)
{
std::wstring input = L"hello";
auto result = unwide(input);
Assert::AreEqual(std::string("hello"), result);
}
TEST_METHOD(Unwide_WithNumbers_Converts)
{
std::wstring input = L"test123";
auto result = unwide(input);
Assert::AreEqual(std::string("test123"), result);
}
TEST_METHOD(Unwide_WithSpecialChars_Converts)
{
std::wstring input = L"test!@#$%";
auto result = unwide(input);
Assert::AreEqual(std::string("test!@#$%"), result);
}
TEST_METHOD(Unwide_MixedCase_PreservesCase)
{
std::wstring input = L"HeLLo WoRLd";
auto result = unwide(input);
Assert::AreEqual(std::string("HeLLo WoRLd"), result);
}
TEST_METHOD(Unwide_LongString_Works)
{
std::wstring input = L"This is a longer string with multiple words and punctuation!";
auto result = unwide(input);
Assert::AreEqual(std::string("This is a longer string with multiple words and punctuation!"), result);
}
};
}

View File

@@ -1,192 +0,0 @@
#pragma once
#include "pch.h"
#include <string>
#include <filesystem>
#include <fstream>
#include <random>
namespace TestHelpers
{
// RAII helper for creating and cleaning up temporary files
class TempFile
{
public:
TempFile(const std::wstring& content = L"", const std::wstring& extension = L".txt")
{
wchar_t tempPath[MAX_PATH];
GetTempPathW(MAX_PATH, tempPath);
// Generate a unique filename
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(10000, 99999);
m_path = std::wstring(tempPath) + L"test_" + std::to_wstring(dis(gen)) + extension;
if (!content.empty())
{
std::wofstream file(m_path);
file << content;
}
}
~TempFile()
{
if (std::filesystem::exists(m_path))
{
std::filesystem::remove(m_path);
}
}
TempFile(const TempFile&) = delete;
TempFile& operator=(const TempFile&) = delete;
const std::wstring& path() const { return m_path; }
void write(const std::string& content)
{
std::ofstream file(m_path, std::ios::binary);
file << content;
}
void write(const std::wstring& content)
{
std::wofstream file(m_path);
file << content;
}
std::wstring read()
{
std::wifstream file(m_path);
return std::wstring((std::istreambuf_iterator<wchar_t>(file)),
std::istreambuf_iterator<wchar_t>());
}
private:
std::wstring m_path;
};
// RAII helper for creating and cleaning up temporary directories
class TempDirectory
{
public:
TempDirectory()
{
wchar_t tempPath[MAX_PATH];
GetTempPathW(MAX_PATH, tempPath);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(10000, 99999);
m_path = std::wstring(tempPath) + L"testdir_" + std::to_wstring(dis(gen));
std::filesystem::create_directories(m_path);
}
~TempDirectory()
{
if (std::filesystem::exists(m_path))
{
std::filesystem::remove_all(m_path);
}
}
TempDirectory(const TempDirectory&) = delete;
TempDirectory& operator=(const TempDirectory&) = delete;
const std::wstring& path() const { return m_path; }
private:
std::wstring m_path;
};
// Registry test key path - use HKCU for non-elevated tests
inline const std::wstring TestRegistryPath = L"Software\\PowerToys\\UnitTests";
// RAII helper for registry key creation/cleanup
class TestRegistryKey
{
public:
TestRegistryKey(const std::wstring& subKey = L"")
{
m_path = TestRegistryPath;
if (!subKey.empty())
{
m_path += L"\\" + subKey;
}
HKEY key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, m_path.c_str(), 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr,
&key, nullptr) == ERROR_SUCCESS)
{
RegCloseKey(key);
m_created = true;
}
}
~TestRegistryKey()
{
if (m_created)
{
RegDeleteTreeW(HKEY_CURRENT_USER, m_path.c_str());
}
}
TestRegistryKey(const TestRegistryKey&) = delete;
TestRegistryKey& operator=(const TestRegistryKey&) = delete;
bool isValid() const { return m_created; }
const std::wstring& path() const { return m_path; }
bool setStringValue(const std::wstring& name, const std::wstring& value)
{
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, m_path.c_str(), 0, KEY_SET_VALUE, &key) != ERROR_SUCCESS)
{
return false;
}
auto result = RegSetValueExW(key, name.c_str(), 0, REG_SZ,
reinterpret_cast<const BYTE*>(value.c_str()),
static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t)));
RegCloseKey(key);
return result == ERROR_SUCCESS;
}
bool setDwordValue(const std::wstring& name, DWORD value)
{
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, m_path.c_str(), 0, KEY_SET_VALUE, &key) != ERROR_SUCCESS)
{
return false;
}
auto result = RegSetValueExW(key, name.c_str(), 0, REG_DWORD,
reinterpret_cast<const BYTE*>(&value), sizeof(DWORD));
RegCloseKey(key);
return result == ERROR_SUCCESS;
}
private:
std::wstring m_path;
bool m_created = false;
};
// Helper to wait for a condition with timeout
template<typename Predicate>
bool WaitFor(Predicate pred, std::chrono::milliseconds timeout = std::chrono::milliseconds(5000))
{
auto start = std::chrono::steady_clock::now();
while (!pred())
{
if (std::chrono::steady_clock::now() - start > timeout)
{
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return true;
}
}

View File

@@ -1,14 +0,0 @@
#include "pch.h"
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <spdlog/sinks/null_sink.h>
std::shared_ptr<spdlog::logger> Logger::logger = spdlog::null_logger_mt("Common.Utils.UnitTests");
namespace PTSettingsHelper
{
std::wstring get_root_save_folder_location()
{
return L"";
}
}

View File

@@ -1,336 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <OnThreadExecutor.h>
#include <EventWaiter.h>
#include <EventLocker.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(OnThreadExecutorTests)
{
public:
TEST_METHOD(Constructor_CreatesInstance)
{
OnThreadExecutor executor;
// Should not crash
Assert::IsTrue(true);
}
TEST_METHOD(Submit_SingleTask_Executes)
{
OnThreadExecutor executor;
std::atomic<bool> executed{ false };
auto future = executor.submit(OnThreadExecutor::task_t([&executed]() {
executed = true;
}));
future.wait();
Assert::IsTrue(executed);
}
TEST_METHOD(Submit_MultipleTasks_ExecutesAll)
{
OnThreadExecutor executor;
std::atomic<int> counter{ 0 };
std::vector<std::future<void>> futures;
for (int i = 0; i < 10; ++i)
{
futures.push_back(executor.submit(OnThreadExecutor::task_t([&counter]() {
counter++;
})));
}
for (auto& f : futures)
{
f.wait();
}
Assert::AreEqual(10, counter.load());
}
TEST_METHOD(Submit_TasksExecuteInOrder)
{
OnThreadExecutor executor;
std::vector<int> order;
std::mutex orderMutex;
std::vector<std::future<void>> futures;
for (int i = 0; i < 5; ++i)
{
futures.push_back(executor.submit(OnThreadExecutor::task_t([&order, &orderMutex, i]() {
std::lock_guard lock(orderMutex);
order.push_back(i);
})));
}
for (auto& f : futures)
{
f.wait();
}
Assert::AreEqual(static_cast<size_t>(5), order.size());
for (int i = 0; i < 5; ++i)
{
Assert::AreEqual(i, order[i]);
}
}
TEST_METHOD(Submit_TaskReturnsResult)
{
OnThreadExecutor executor;
std::atomic<int> result{ 0 };
auto future = executor.submit(OnThreadExecutor::task_t([&result]() {
result = 42;
}));
future.wait();
Assert::AreEqual(42, result.load());
}
TEST_METHOD(Cancel_ClearsPendingTasks)
{
OnThreadExecutor executor;
std::atomic<int> counter{ 0 };
// Submit a slow task first
executor.submit(OnThreadExecutor::task_t([&counter]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
counter++;
}));
// Submit more tasks
for (int i = 0; i < 5; ++i)
{
executor.submit(OnThreadExecutor::task_t([&counter]() {
counter++;
}));
}
// Cancel pending tasks
executor.cancel();
// Wait a bit for any running task to complete
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// Not all tasks should have executed
Assert::IsTrue(counter < 6);
}
TEST_METHOD(Destructor_WaitsForCompletion)
{
std::atomic<bool> completed{ false };
std::future<void> future;
{
OnThreadExecutor executor;
future = executor.submit(OnThreadExecutor::task_t([&completed]() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
completed = true;
}));
future.wait();
} // Destructor no longer required to wait for completion
Assert::IsTrue(completed);
}
TEST_METHOD(Submit_AfterCancel_StillWorks)
{
OnThreadExecutor executor;
std::atomic<int> counter{ 0 };
executor.submit(OnThreadExecutor::task_t([&counter]() {
counter++;
}));
executor.cancel();
auto future = executor.submit(OnThreadExecutor::task_t([&counter]() {
counter = 42;
}));
future.wait();
Assert::AreEqual(42, counter.load());
}
};
TEST_CLASS(EventWaiterTests)
{
public:
TEST_METHOD(Constructor_CreatesInstance)
{
EventWaiter waiter;
Assert::IsFalse(waiter.is_listening());
}
TEST_METHOD(Start_ValidEvent_ReturnsTrue)
{
EventWaiter waiter;
bool result = waiter.start(L"TestEvent_Start", [](DWORD) {});
Assert::IsTrue(result);
Assert::IsTrue(waiter.is_listening());
waiter.stop();
}
TEST_METHOD(Start_AlreadyListening_ReturnsFalse)
{
EventWaiter waiter;
waiter.start(L"TestEvent_Double1", [](DWORD) {});
bool result = waiter.start(L"TestEvent_Double2", [](DWORD) {});
Assert::IsFalse(result);
waiter.stop();
}
TEST_METHOD(Stop_WhileListening_StopsListening)
{
EventWaiter waiter;
waiter.start(L"TestEvent_Stop", [](DWORD) {});
Assert::IsTrue(waiter.is_listening());
waiter.stop();
Assert::IsFalse(waiter.is_listening());
}
TEST_METHOD(Stop_WhenNotListening_DoesNotCrash)
{
EventWaiter waiter;
waiter.stop(); // Should not crash
Assert::IsFalse(waiter.is_listening());
}
TEST_METHOD(Stop_CalledMultipleTimes_DoesNotCrash)
{
EventWaiter waiter;
waiter.start(L"TestEvent_MultiStop", [](DWORD) {});
waiter.stop();
waiter.stop();
waiter.stop();
Assert::IsFalse(waiter.is_listening());
}
TEST_METHOD(Callback_EventSignaled_CallsCallback)
{
EventWaiter waiter;
std::atomic<bool> called{ false };
std::atomic<DWORD> errorCode{ 0xFFFFFFFF };
// Create a named event we can signal
std::wstring eventName = L"TestEvent_Callback_" + std::to_wstring(GetCurrentProcessId());
HANDLE signalEvent = CreateEventW(nullptr, FALSE, FALSE, eventName.c_str());
Assert::IsNotNull(signalEvent);
waiter.start(eventName, [&called, &errorCode](DWORD err) {
errorCode = err;
called = true;
});
// Signal the event
SetEvent(signalEvent);
// Wait for callback
bool waitResult = TestHelpers::WaitFor([&called]() { return called.load(); }, std::chrono::milliseconds(1000));
waiter.stop();
CloseHandle(signalEvent);
Assert::IsTrue(waitResult);
Assert::AreEqual(static_cast<DWORD>(ERROR_SUCCESS), errorCode.load());
}
TEST_METHOD(Destructor_StopsListening)
{
std::atomic<bool> isListening{ false };
{
EventWaiter waiter;
waiter.start(L"TestEvent_Destructor", [](DWORD) {});
isListening = waiter.is_listening();
}
// After destruction, the waiter should have stopped
Assert::IsTrue(isListening);
}
TEST_METHOD(IsListening_InitialState_ReturnsFalse)
{
EventWaiter waiter;
Assert::IsFalse(waiter.is_listening());
}
TEST_METHOD(IsListening_AfterStart_ReturnsTrue)
{
EventWaiter waiter;
waiter.start(L"TestEvent_IsListening", [](DWORD) {});
Assert::IsTrue(waiter.is_listening());
waiter.stop();
}
TEST_METHOD(IsListening_AfterStop_ReturnsFalse)
{
EventWaiter waiter;
waiter.start(L"TestEvent_AfterStop", [](DWORD) {});
waiter.stop();
Assert::IsFalse(waiter.is_listening());
}
};
TEST_CLASS(EventLockerTests)
{
public:
TEST_METHOD(Get_ValidEventName_ReturnsLocker)
{
std::wstring eventName = L"TestEventLocker_" + std::to_wstring(GetCurrentProcessId());
auto locker = EventLocker::Get(eventName);
Assert::IsTrue(locker.has_value());
}
TEST_METHOD(Get_UniqueNames_CreatesSeparateLockers)
{
auto locker1 = EventLocker::Get(L"TestEventLocker1_" + std::to_wstring(GetCurrentProcessId()));
auto locker2 = EventLocker::Get(L"TestEventLocker2_" + std::to_wstring(GetCurrentProcessId()));
Assert::IsTrue(locker1.has_value());
Assert::IsTrue(locker2.has_value());
}
TEST_METHOD(Destructor_CleansUpHandle)
{
std::wstring eventName = L"TestEventLockerCleanup_" + std::to_wstring(GetCurrentProcessId());
{
auto locker = EventLocker::Get(eventName);
Assert::IsTrue(locker.has_value());
}
// After destruction, the event should be cleaned up
// Creating a new one should succeed
auto newLocker = EventLocker::Get(eventName);
Assert::IsTrue(newLocker.has_value());
}
TEST_METHOD(MoveConstructor_TransfersOwnership)
{
std::wstring eventName = L"TestEventLockerMove_" + std::to_wstring(GetCurrentProcessId());
auto locker1 = EventLocker::Get(eventName);
Assert::IsTrue(locker1.has_value());
EventLocker locker2 = std::move(*locker1);
// Move should transfer ownership without crash
Assert::IsTrue(true);
}
TEST_METHOD(MoveAssignment_TransfersOwnership)
{
std::wstring eventName1 = L"TestEventLockerMoveAssign1_" + std::to_wstring(GetCurrentProcessId());
std::wstring eventName2 = L"TestEventLockerMoveAssign2_" + std::to_wstring(GetCurrentProcessId());
auto locker1 = EventLocker::Get(eventName1);
auto locker2 = EventLocker::Get(eventName2);
Assert::IsTrue(locker1.has_value());
Assert::IsTrue(locker2.has_value());
*locker1 = std::move(*locker2);
// Should not crash
Assert::IsTrue(true);
}
};
}

View File

@@ -1,248 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <timeutil.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(TimeUtilsTests)
{
public:
// to_string tests
TEST_METHOD(ToString_ZeroTime_ReturnsZero)
{
time_t t = 0;
auto result = timeutil::to_string(t);
Assert::AreEqual(std::wstring(L"0"), result);
}
TEST_METHOD(ToString_PositiveTime_ReturnsString)
{
time_t t = 1234567890;
auto result = timeutil::to_string(t);
Assert::AreEqual(std::wstring(L"1234567890"), result);
}
TEST_METHOD(ToString_LargeTime_ReturnsString)
{
time_t t = 1700000000;
auto result = timeutil::to_string(t);
Assert::AreEqual(std::wstring(L"1700000000"), result);
}
// from_string tests
TEST_METHOD(FromString_ZeroString_ReturnsZero)
{
auto result = timeutil::from_string(L"0");
Assert::IsTrue(result.has_value());
Assert::AreEqual(static_cast<time_t>(0), result.value());
}
TEST_METHOD(FromString_ValidNumber_ReturnsTime)
{
auto result = timeutil::from_string(L"1234567890");
Assert::IsTrue(result.has_value());
Assert::AreEqual(static_cast<time_t>(1234567890), result.value());
}
TEST_METHOD(FromString_InvalidString_ReturnsNullopt)
{
auto result = timeutil::from_string(L"invalid");
Assert::IsFalse(result.has_value());
}
TEST_METHOD(FromString_EmptyString_ReturnsNullopt)
{
auto result = timeutil::from_string(L"");
Assert::IsFalse(result.has_value());
}
TEST_METHOD(FromString_MixedAlphaNumeric_ReturnsNullopt)
{
auto result = timeutil::from_string(L"123abc");
Assert::IsFalse(result.has_value());
}
TEST_METHOD(FromString_NegativeNumber_ReturnsNullopt)
{
auto result = timeutil::from_string(L"-1");
Assert::IsFalse(result.has_value());
}
// Roundtrip test
TEST_METHOD(ToStringFromString_Roundtrip_Works)
{
time_t original = 1609459200; // 2021-01-01 00:00:00 UTC
auto str = timeutil::to_string(original);
auto result = timeutil::from_string(str);
Assert::IsTrue(result.has_value());
Assert::AreEqual(original, result.value());
}
// now tests
TEST_METHOD(Now_ReturnsReasonableTime)
{
auto result = timeutil::now();
// Should be after 2020 and before 2100
Assert::IsTrue(result > 1577836800); // 2020-01-01
Assert::IsTrue(result < 4102444800); // 2100-01-01
}
TEST_METHOD(Now_TwoCallsAreCloseInTime)
{
auto first = timeutil::now();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto second = timeutil::now();
// Difference should be less than 2 seconds
Assert::IsTrue(second >= first);
Assert::IsTrue(second - first < 2);
}
// diff::in_seconds tests
TEST_METHOD(DiffInSeconds_SameTime_ReturnsZero)
{
time_t t = 1000000;
auto result = timeutil::diff::in_seconds(t, t);
Assert::AreEqual(static_cast<int64_t>(0), result);
}
TEST_METHOD(DiffInSeconds_OneDifference_ReturnsOne)
{
time_t to = 1000001;
time_t from = 1000000;
auto result = timeutil::diff::in_seconds(to, from);
Assert::AreEqual(static_cast<int64_t>(1), result);
}
TEST_METHOD(DiffInSeconds_60Seconds_Returns60)
{
time_t to = 1000060;
time_t from = 1000000;
auto result = timeutil::diff::in_seconds(to, from);
Assert::AreEqual(static_cast<int64_t>(60), result);
}
TEST_METHOD(DiffInSeconds_NegativeDiff_ReturnsNegative)
{
time_t to = 1000000;
time_t from = 1000060;
auto result = timeutil::diff::in_seconds(to, from);
Assert::AreEqual(static_cast<int64_t>(-60), result);
}
// diff::in_minutes tests
TEST_METHOD(DiffInMinutes_SameTime_ReturnsZero)
{
time_t t = 1000000;
auto result = timeutil::diff::in_minutes(t, t);
Assert::AreEqual(static_cast<int64_t>(0), result);
}
TEST_METHOD(DiffInMinutes_OneMinute_ReturnsOne)
{
time_t to = 1000060;
time_t from = 1000000;
auto result = timeutil::diff::in_minutes(to, from);
Assert::AreEqual(static_cast<int64_t>(1), result);
}
TEST_METHOD(DiffInMinutes_60Minutes_Returns60)
{
time_t to = 1003600;
time_t from = 1000000;
auto result = timeutil::diff::in_minutes(to, from);
Assert::AreEqual(static_cast<int64_t>(60), result);
}
TEST_METHOD(DiffInMinutes_LessThanMinute_ReturnsZero)
{
time_t to = 1000059;
time_t from = 1000000;
auto result = timeutil::diff::in_minutes(to, from);
Assert::AreEqual(static_cast<int64_t>(0), result);
}
// diff::in_hours tests
TEST_METHOD(DiffInHours_SameTime_ReturnsZero)
{
time_t t = 1000000;
auto result = timeutil::diff::in_hours(t, t);
Assert::AreEqual(static_cast<int64_t>(0), result);
}
TEST_METHOD(DiffInHours_OneHour_ReturnsOne)
{
time_t to = 1003600;
time_t from = 1000000;
auto result = timeutil::diff::in_hours(to, from);
Assert::AreEqual(static_cast<int64_t>(1), result);
}
TEST_METHOD(DiffInHours_24Hours_Returns24)
{
time_t to = 1086400;
time_t from = 1000000;
auto result = timeutil::diff::in_hours(to, from);
Assert::AreEqual(static_cast<int64_t>(24), result);
}
TEST_METHOD(DiffInHours_LessThanHour_ReturnsZero)
{
time_t to = 1003599;
time_t from = 1000000;
auto result = timeutil::diff::in_hours(to, from);
Assert::AreEqual(static_cast<int64_t>(0), result);
}
// diff::in_days tests
TEST_METHOD(DiffInDays_SameTime_ReturnsZero)
{
time_t t = 1000000;
auto result = timeutil::diff::in_days(t, t);
Assert::AreEqual(static_cast<int64_t>(0), result);
}
TEST_METHOD(DiffInDays_OneDay_ReturnsOne)
{
time_t to = 1086400;
time_t from = 1000000;
auto result = timeutil::diff::in_days(to, from);
Assert::AreEqual(static_cast<int64_t>(1), result);
}
TEST_METHOD(DiffInDays_7Days_Returns7)
{
time_t to = 1604800;
time_t from = 1000000;
auto result = timeutil::diff::in_days(to, from);
Assert::AreEqual(static_cast<int64_t>(7), result);
}
TEST_METHOD(DiffInDays_LessThanDay_ReturnsZero)
{
time_t to = 1086399;
time_t from = 1000000;
auto result = timeutil::diff::in_days(to, from);
Assert::AreEqual(static_cast<int64_t>(0), result);
}
// format_as_local tests
TEST_METHOD(FormatAsLocal_YearFormat_ReturnsYear)
{
time_t t = 1609459200; // 2021-01-01 00:00:00 UTC
auto result = timeutil::format_as_local("%Y", t);
// Result depends on local timezone, but year should be 2020 or 2021
Assert::IsTrue(result == "2020" || result == "2021");
}
TEST_METHOD(FormatAsLocal_DateFormat_ReturnsDate)
{
time_t t = 0; // 1970-01-01 00:00:00 UTC
auto result = timeutil::format_as_local("%Y-%m-%d", t);
// Result should be a date around 1970-01-01 depending on timezone
Assert::IsTrue(result.length() == 10); // YYYY-MM-DD format
Assert::IsTrue(result.substr(0, 4) == "1969" || result.substr(0, 4) == "1970");
}
};
}

View File

@@ -1,210 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <UnhandledExceptionHandler.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(UnhandledExceptionTests)
{
public:
// exceptionDescription tests
TEST_METHOD(ExceptionDescription_AccessViolation_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_ACCESS_VIOLATION);
Assert::IsTrue(result && *result != '\0');
// Should contain meaningful description
std::string desc{ result };
Assert::IsTrue(desc.find("ACCESS") != std::string::npos ||
desc.find("access") != std::string::npos ||
desc.find("violation") != std::string::npos ||
desc.length() > 0);
}
TEST_METHOD(ExceptionDescription_StackOverflow_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_STACK_OVERFLOW);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_DivideByZero_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_INT_DIVIDE_BY_ZERO);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_IllegalInstruction_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_ILLEGAL_INSTRUCTION);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_ArrayBoundsExceeded_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_Breakpoint_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_BREAKPOINT);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_SingleStep_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_SINGLE_STEP);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_FloatDivideByZero_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_FLT_DIVIDE_BY_ZERO);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_FloatOverflow_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_FLT_OVERFLOW);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_FloatUnderflow_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_FLT_UNDERFLOW);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_FloatInvalidOperation_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_FLT_INVALID_OPERATION);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_PrivilegedInstruction_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_PRIV_INSTRUCTION);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_InPageError_ReturnsDescription)
{
auto result = exceptionDescription(EXCEPTION_IN_PAGE_ERROR);
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_UnknownCode_ReturnsDescription)
{
auto result = exceptionDescription(0x12345678);
// Should return something (possibly "Unknown exception" or similar)
Assert::IsTrue(result && *result != '\0');
}
TEST_METHOD(ExceptionDescription_ZeroCode_ReturnsDescription)
{
auto result = exceptionDescription(0);
// Should handle zero gracefully
Assert::IsTrue(result && *result != '\0');
}
// GetFilenameStart tests (if accessible)
TEST_METHOD(GetFilenameStart_ValidPath_ReturnsFilename)
{
wchar_t path[] = L"C:\\folder\\subfolder\\file.exe";
int start = GetFilenameStart(path);
Assert::IsTrue(start >= 0);
Assert::AreEqual(std::wstring(L"file.exe"), std::wstring(path + start));
}
TEST_METHOD(GetFilenameStart_NoPath_ReturnsOriginal)
{
wchar_t path[] = L"file.exe";
int start = GetFilenameStart(path);
Assert::IsTrue(start >= 0);
Assert::AreEqual(std::wstring(L"file.exe"), std::wstring(path + start));
}
TEST_METHOD(GetFilenameStart_TrailingBackslash_ReturnsEmpty)
{
wchar_t path[] = L"C:\\folder\\";
int start = GetFilenameStart(path);
// Should point to empty string after last backslash
Assert::IsTrue(start >= 0);
}
TEST_METHOD(GetFilenameStart_NullPath_HandlesGracefully)
{
// This might crash or return null depending on implementation
// Just document the behavior
int start = GetFilenameStart(nullptr);
(void)start;
// Result is implementation-defined for null input
Assert::IsTrue(true);
}
// Thread safety tests
TEST_METHOD(ExceptionDescription_ThreadSafe)
{
std::vector<std::thread> threads;
std::atomic<int> successCount{ 0 };
for (int i = 0; i < 10; ++i)
{
threads.emplace_back([&successCount]() {
for (int j = 0; j < 10; ++j)
{
auto desc = exceptionDescription(EXCEPTION_ACCESS_VIOLATION);
if (desc && *desc != '\0')
{
successCount++;
}
}
});
}
for (auto& t : threads)
{
t.join();
}
Assert::AreEqual(100, successCount.load());
}
// All exception codes test
TEST_METHOD(ExceptionDescription_AllCommonCodes_ReturnDescriptions)
{
std::vector<DWORD> codes = {
EXCEPTION_ACCESS_VIOLATION,
EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
EXCEPTION_BREAKPOINT,
EXCEPTION_DATATYPE_MISALIGNMENT,
EXCEPTION_FLT_DENORMAL_OPERAND,
EXCEPTION_FLT_DIVIDE_BY_ZERO,
EXCEPTION_FLT_INEXACT_RESULT,
EXCEPTION_FLT_INVALID_OPERATION,
EXCEPTION_FLT_OVERFLOW,
EXCEPTION_FLT_STACK_CHECK,
EXCEPTION_FLT_UNDERFLOW,
EXCEPTION_ILLEGAL_INSTRUCTION,
EXCEPTION_IN_PAGE_ERROR,
EXCEPTION_INT_DIVIDE_BY_ZERO,
EXCEPTION_INT_OVERFLOW,
EXCEPTION_INVALID_DISPOSITION,
EXCEPTION_NONCONTINUABLE_EXCEPTION,
EXCEPTION_PRIV_INSTRUCTION,
EXCEPTION_SINGLE_STEP,
EXCEPTION_STACK_OVERFLOW
};
for (DWORD code : codes)
{
auto desc = exceptionDescription(code);
Assert::IsTrue(desc && *desc != '\0', (L"Empty description for code: " + std::to_wstring(code)).c_str());
}
}
};
}

View File

@@ -1,36 +0,0 @@
#include <windows.h>
#include "resource.h"
#include "../version/version.h"
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

View File

@@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{8B5CFB38-CCBA-40A8-AD7A-89C57B070884}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>UnitTestsCommonUtils</RootNamespace>
<ProjectSubType>NativeUnitTestProject</ProjectSubType>
<ProjectName>Common.Utils.UnitTests</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v143</PlatformToolset>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\tests\UnitTestsCommonUtils\</OutDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\;..\utils;..\Telemetry;..\..\;..\..\..\deps\;..\..\..\deps\spdlog\include;..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp23</LanguageStandard>
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_HEADER_ONLY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>RuntimeObject.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="StringUtils.Tests.cpp" />
<ClCompile Include="ColorUtils.Tests.cpp" />
<ClCompile Include="TimeUtils.Tests.cpp" />
<ClCompile Include="WinApiError.Tests.cpp" />
<ClCompile Include="Serialized.Tests.cpp" />
<ClCompile Include="Json.Tests.cpp" />
<ClCompile Include="OsDetect.Tests.cpp" />
<ClCompile Include="Threading.Tests.cpp" />
<ClCompile Include="ProcessPath.Tests.cpp" />
<ClCompile Include="Window.Tests.cpp" />
<ClCompile Include="GameMode.Tests.cpp" />
<ClCompile Include="Gpo.Tests.cpp" />
<ClCompile Include="MsiUtils.Tests.cpp" />
<ClCompile Include="HttpClient.Tests.cpp" />
<ClCompile Include="ComObjectFactory.Tests.cpp" />
<ClCompile Include="AppMutex.Tests.cpp" />
<ClCompile Include="Elevation.Tests.cpp" />
<ClCompile Include="Exec.Tests.cpp" />
<ClCompile Include="ExcludedApps.Tests.cpp" />
<ClCompile Include="HDropIterator.Tests.cpp" />
<ClCompile Include="LoggerHelper.Tests.cpp" />
<ClCompile Include="ModulesRegistry.Tests.cpp" />
<ClCompile Include="MsWindowsSettings.Tests.cpp" />
<ClCompile Include="Package.Tests.cpp" />
<ClCompile Include="ProcessApi.Tests.cpp" />
<ClCompile Include="ProcessWaiter.Tests.cpp" />
<ClCompile Include="Registry.Tests.cpp" />
<ClCompile Include="Resources.Tests.cpp" />
<ClCompile Include="TestStubs.cpp" />
<ClCompile Include="UnhandledException.Tests.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="TestHelpers.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="UnitTests-CommonUtils.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -1,143 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\Pure Functions">
<UniqueIdentifier>{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Threading">
<UniqueIdentifier>{B2C3D4E5-F6A7-4B6C-9D0E-1F2A3B4C5D6E}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Process">
<UniqueIdentifier>{C3D4E5F6-A7B8-4C7D-0E1F-2A3B4C5D6E7F}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Registry">
<UniqueIdentifier>{D4E5F6A7-B8C9-4D8E-1F2A-3B4C5D6E7F8A}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Integration">
<UniqueIdentifier>{E5F6A7B8-C9D0-4E9F-2A3B-4C5D6E7F8A9B}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="StringUtils.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="ColorUtils.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="TimeUtils.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="WinApiError.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="Serialized.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="Json.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="ExcludedApps.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="OsDetect.Tests.cpp">
<Filter>Source Files\Pure Functions</Filter>
</ClCompile>
<ClCompile Include="Threading.Tests.cpp">
<Filter>Source Files\Threading</Filter>
</ClCompile>
<ClCompile Include="AppMutex.Tests.cpp">
<Filter>Source Files\Threading</Filter>
</ClCompile>
<ClCompile Include="ProcessWaiter.Tests.cpp">
<Filter>Source Files\Threading</Filter>
</ClCompile>
<ClCompile Include="ProcessPath.Tests.cpp">
<Filter>Source Files\Process</Filter>
</ClCompile>
<ClCompile Include="ProcessApi.Tests.cpp">
<Filter>Source Files\Process</Filter>
</ClCompile>
<ClCompile Include="Window.Tests.cpp">
<Filter>Source Files\Process</Filter>
</ClCompile>
<ClCompile Include="Exec.Tests.cpp">
<Filter>Source Files\Process</Filter>
</ClCompile>
<ClCompile Include="GameMode.Tests.cpp">
<Filter>Source Files\Process</Filter>
</ClCompile>
<ClCompile Include="MsWindowsSettings.Tests.cpp">
<Filter>Source Files\Process</Filter>
</ClCompile>
<ClCompile Include="Registry.Tests.cpp">
<Filter>Source Files\Registry</Filter>
</ClCompile>
<ClCompile Include="Gpo.Tests.cpp">
<Filter>Source Files\Registry</Filter>
</ClCompile>
<ClCompile Include="ModulesRegistry.Tests.cpp">
<Filter>Source Files\Registry</Filter>
</ClCompile>
<ClCompile Include="Elevation.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="Package.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="MsiUtils.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="HttpClient.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="Resources.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="LoggerHelper.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="ComObjectFactory.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="HDropIterator.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
<ClCompile Include="UnhandledException.Tests.cpp">
<Filter>Source Files\Integration</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TestHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="UnitTests-CommonUtils.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -1,130 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <winapi_error.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(WinApiErrorTests)
{
public:
// get_last_error_message tests
TEST_METHOD(GetLastErrorMessage_Success_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_SUCCESS);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_FileNotFound_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_FILE_NOT_FOUND);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_AccessDenied_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_ACCESS_DENIED);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_PathNotFound_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_PATH_NOT_FOUND);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_InvalidHandle_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_INVALID_HANDLE);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_NotEnoughMemory_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_NOT_ENOUGH_MEMORY);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_InvalidParameter_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_INVALID_PARAMETER);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
// get_last_error_or_default tests
TEST_METHOD(GetLastErrorOrDefault_Success_ReturnsMessage)
{
auto result = get_last_error_or_default(ERROR_SUCCESS);
Assert::IsFalse(result.empty());
}
TEST_METHOD(GetLastErrorOrDefault_FileNotFound_ReturnsMessage)
{
auto result = get_last_error_or_default(ERROR_FILE_NOT_FOUND);
Assert::IsFalse(result.empty());
}
TEST_METHOD(GetLastErrorOrDefault_AccessDenied_ReturnsMessage)
{
auto result = get_last_error_or_default(ERROR_ACCESS_DENIED);
Assert::IsFalse(result.empty());
}
TEST_METHOD(GetLastErrorOrDefault_UnknownError_ReturnsEmptyOrMessage)
{
// For an unknown error code, should return empty string or a default message
auto result = get_last_error_or_default(0xFFFFFFFF);
// Either empty or has content, both are valid
Assert::IsTrue(result.empty() || !result.empty());
}
// Comparison tests
TEST_METHOD(BothFunctions_SameError_ProduceSameContent)
{
auto message = get_last_error_message(ERROR_FILE_NOT_FOUND);
auto defaultMessage = get_last_error_or_default(ERROR_FILE_NOT_FOUND);
Assert::IsTrue(message.has_value());
Assert::AreEqual(*message, defaultMessage);
}
TEST_METHOD(BothFunctions_SuccessError_ProduceSameContent)
{
auto message = get_last_error_message(ERROR_SUCCESS);
auto defaultMessage = get_last_error_or_default(ERROR_SUCCESS);
Assert::IsTrue(message.has_value());
Assert::AreEqual(*message, defaultMessage);
}
// Error code specific tests
TEST_METHOD(GetLastErrorMessage_SharingViolation_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_SHARING_VIOLATION);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_FileExists_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_FILE_EXISTS);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
TEST_METHOD(GetLastErrorMessage_DirNotEmpty_ReturnsMessage)
{
auto result = get_last_error_message(ERROR_DIR_NOT_EMPTY);
Assert::IsTrue(result.has_value());
Assert::IsFalse(result->empty());
}
};
}

View File

@@ -1,159 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include <window.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestsCommonUtils
{
TEST_CLASS(WindowTests)
{
public:
// is_system_window tests
TEST_METHOD(IsSystemWindow_DesktopWindow_ReturnsResult)
{
HWND desktop = GetDesktopWindow();
Assert::IsNotNull(desktop);
// Get class name
char className[256] = {};
GetClassNameA(desktop, className, sizeof(className));
bool result = is_system_window(desktop, className);
// Just verify it doesn't crash and returns a boolean
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(IsSystemWindow_NullHwnd_ReturnsFalse)
{
auto shell = GetShellWindow();
auto desktop = GetDesktopWindow();
bool result = is_system_window(nullptr, "ClassName");
bool expected = (shell == nullptr) || (desktop == nullptr);
Assert::AreEqual(expected, result);
}
TEST_METHOD(IsSystemWindow_InvalidHwnd_ReturnsFalse)
{
bool result = is_system_window(reinterpret_cast<HWND>(0x12345678), "ClassName");
Assert::IsFalse(result);
}
TEST_METHOD(IsSystemWindow_EmptyClassName_DoesNotCrash)
{
HWND desktop = GetDesktopWindow();
bool result = is_system_window(desktop, "");
// Just verify it doesn't crash
Assert::IsTrue(result == true || result == false);
}
TEST_METHOD(IsSystemWindow_NullClassName_DoesNotCrash)
{
HWND desktop = GetDesktopWindow();
bool result = is_system_window(desktop, nullptr);
// Should handle null className gracefully
Assert::IsTrue(result == true || result == false);
}
// GetWindowCreateParam tests
TEST_METHOD(GetWindowCreateParam_ValidLparam_ReturnsValue)
{
struct TestData
{
int value;
};
TestData data{ 42 };
CREATESTRUCT cs{};
cs.lpCreateParams = &data;
auto result = GetWindowCreateParam<TestData*>(reinterpret_cast<LPARAM>(&cs));
Assert::IsNotNull(result);
Assert::AreEqual(42, result->value);
}
// Window data storage tests
TEST_METHOD(WindowData_StoreAndRetrieve_Works)
{
// Create a simple message-only window for testing
WNDCLASSW wc = {};
wc.lpfnWndProc = DefWindowProcW;
wc.hInstance = GetModuleHandleW(nullptr);
wc.lpszClassName = L"TestWindowClass_DataTest";
RegisterClassW(&wc);
HWND hwnd = CreateWindowExW(0, L"TestWindowClass_DataTest", L"Test",
0, 0, 0, 0, 0, HWND_MESSAGE, nullptr,
GetModuleHandleW(nullptr), nullptr);
if (hwnd)
{
int value = 42;
int* testValue = &value;
StoreWindowParam(hwnd, testValue);
auto retrieved = GetWindowParam<int*>(hwnd);
Assert::AreEqual(testValue, retrieved);
DestroyWindow(hwnd);
}
UnregisterClassW(L"TestWindowClass_DataTest", GetModuleHandleW(nullptr));
Assert::IsTrue(true); // Window creation might fail in test environment
}
// run_message_loop tests
TEST_METHOD(RunMessageLoop_UntilIdle_Completes)
{
// Run message loop until idle with a timeout
// This should complete quickly since there are no messages
auto start = std::chrono::steady_clock::now();
run_message_loop(true, 100);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start);
// Should complete within reasonable time
Assert::IsTrue(elapsed.count() < 500);
}
TEST_METHOD(RunMessageLoop_WithTimeout_RespectsTimeout)
{
auto start = std::chrono::steady_clock::now();
run_message_loop(false, 50);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start);
// Should take at least the timeout duration
// Allow some tolerance for timing
Assert::IsTrue(elapsed.count() >= 40 && elapsed.count() < 500);
}
TEST_METHOD(RunMessageLoop_ZeroTimeout_CompletesImmediately)
{
auto start = std::chrono::steady_clock::now();
run_message_loop(false, 0);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start);
// Should complete very quickly
Assert::IsTrue(elapsed.count() < 100);
}
TEST_METHOD(RunMessageLoop_NoTimeout_ProcessesMessages)
{
// Post a quit message before starting the loop
PostQuitMessage(0);
// Should process the quit message and exit
run_message_loop(false, std::nullopt);
Assert::IsTrue(true);
}
};
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
</packages>

View File

@@ -1,5 +0,0 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View File

@@ -1,39 +0,0 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include <Windows.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Foundation.Metadata.h>
#include <winrt/Windows.Data.Json.h>
#include <string>
#include <vector>
#include <optional>
#include <functional>
#include <thread>
#include <atomic>
#include <mutex>
#include <shared_mutex>
#include <future>
#include <queue>
#include <filesystem>
#include <fstream>
#include <chrono>
#include <ctime>
// Suppressing 26466 - Don't use static_cast downcasts - in CppUnitTest.h
#pragma warning(push)
#pragma warning(disable : 26466)
#include "CppUnitTest.h"
#pragma warning(pop)
#endif //PCH_H

View File

@@ -1,13 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by UnitTests-CommonUtils.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys UnitTests-CommonUtils"
#define INTERNAL_NAME "UnitTests-CommonUtils"
#define ORIGINAL_FILENAME "UnitTests-CommonUtils.dll"
// Non-localizable
//////////////////////////////

View File

@@ -34,7 +34,6 @@ public:
{
this->eventHandle = e.eventHandle;
e.eventHandle = nullptr;
return *this;
}
~EventLocker()

View File

@@ -1,8 +1,5 @@
#pragma once
#include <Windows.h>
#include "../logger/logger.h"
inline bool GetAnimationsEnabled()
{
BOOL enabled = 0;
@@ -13,4 +10,4 @@ inline bool GetAnimationsEnabled()
Logger::error("SystemParametersInfo SPI_GETCLIENTAREAANIMATION failed.");
}
return enabled;
}
}

View File

@@ -7,19 +7,7 @@ namespace ProcessWaiter
{
void OnProcessTerminate(std::wstring parent_pid, std::function<void(DWORD)> callback)
{
DWORD pid = 0;
try
{
pid = std::stol(parent_pid);
}
catch (...)
{
if (callback)
{
callback(ERROR_INVALID_PARAMETER);
}
return;
}
DWORD pid = std::stol(parent_pid);
std::thread([=]() {
HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (process != nullptr)
@@ -27,26 +15,17 @@ namespace ProcessWaiter
if (WaitForSingleObject(process, INFINITE) == WAIT_OBJECT_0)
{
CloseHandle(process);
if (callback)
{
callback(ERROR_SUCCESS);
}
callback(ERROR_SUCCESS);
}
else
{
CloseHandle(process);
if (callback)
{
callback(GetLastError());
}
callback(GetLastError());
}
}
else
{
if (callback)
{
callback(GetLastError());
}
callback(GetLastError());
}
}).detach();
}

View File

@@ -31,10 +31,6 @@ public:
HRESULT __stdcall CreateInstance(IUnknown* punkOuter, const IID& riid, void** ppv)
{
if (!ppv)
{
return E_POINTER;
}
*ppv = nullptr;
if (punkOuter)
@@ -59,4 +55,4 @@ public:
private:
std::atomic<long> _refCount;
};
};

View File

@@ -3,7 +3,6 @@
#include <filesystem>
#include <common/version/version.h>
#include <common/SettingsAPI/settings_helpers.h>
#include "../logger/logger.h"
namespace LoggerHelpers
{

View File

@@ -21,16 +21,9 @@
namespace package
{
using winrt::Windows::ApplicationModel::Package;
using winrt::Windows::Foundation::IAsyncOperationWithProgress;
using winrt::Windows::Foundation::AsyncStatus;
using winrt::Windows::Foundation::Uri;
using winrt::Windows::Foundation::Collections::IVector;
using winrt::Windows::Management::Deployment::AddPackageOptions;
using winrt::Windows::Management::Deployment::DeploymentOptions;
using winrt::Windows::Management::Deployment::DeploymentProgress;
using winrt::Windows::Management::Deployment::DeploymentResult;
using winrt::Windows::Management::Deployment::PackageManager;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::Management::Deployment;
using Microsoft::WRL::ComPtr;
inline BOOL IsWin11OrGreater()
@@ -442,7 +435,7 @@ namespace package
// Declare use of an external location
DeploymentOptions options = DeploymentOptions::ForceTargetApplicationShutdown;
IVector<Uri> uris = winrt::single_threaded_vector<Uri>();
Collections::IVector<Uri> uris = winrt::single_threaded_vector<Uri>();
if (!dependencies.empty())
{
for (const auto& dependency : dependencies)

View File

@@ -4,7 +4,6 @@
#include <cinttypes>
#include <string>
#include <optional>
#include <cwctype>
#include <winrt/base.h>
@@ -28,17 +27,6 @@ namespace timeutil
{
try
{
if (s.empty())
{
return std::nullopt;
}
for (wchar_t ch : s)
{
if (!iswdigit(ch))
{
return std::nullopt;
}
}
uint64_t i = std::stoull(s);
return static_cast<std::time_t>(i);
}

View File

@@ -47,6 +47,8 @@ internal sealed class IntegrationTestUserSettings : IUserSettings
public bool ShowCustomPreview => false;
public bool ShowAIPaste => true;
public bool CloseAfterLosingFocus => false;
public bool EnableClipboardPreview => true;

View File

@@ -251,7 +251,8 @@
Margin="20,0,20,0"
x:FieldModifier="public"
IsEnabled="{x:Bind ViewModel.IsCustomAIServiceEnabled, Mode=OneWay}"
TabIndex="0">
TabIndex="0"
Visibility="{x:Bind ViewModel.ShowAIPasteSection, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<controls:PromptBox.Footer>
<StackPanel Orientation="Horizontal">
<TextBlock

View File

@@ -17,6 +17,8 @@ namespace AdvancedPaste.Settings
public bool ShowCustomPreview { get; }
public bool ShowAIPaste { get; }
public bool CloseAfterLosingFocus { get; }
public bool EnableClipboardPreview { get; }

View File

@@ -38,6 +38,8 @@ namespace AdvancedPaste.Settings
public bool ShowCustomPreview { get; private set; }
public bool ShowAIPaste { get; private set; }
public bool CloseAfterLosingFocus { get; private set; }
public bool EnableClipboardPreview { get; private set; }
@@ -54,6 +56,7 @@ namespace AdvancedPaste.Settings
IsAIEnabled = false;
ShowCustomPreview = true;
ShowAIPaste = true;
CloseAfterLosingFocus = false;
EnableClipboardPreview = true;
PasteAIConfiguration = new PasteAIConfiguration();
@@ -109,6 +112,7 @@ namespace AdvancedPaste.Settings
IsAIEnabled = properties.IsAIEnabled;
ShowCustomPreview = properties.ShowCustomPreview;
ShowAIPaste = properties.ShowAIPaste;
CloseAfterLosingFocus = properties.CloseAfterLosingFocus;
EnableClipboardPreview = properties.EnableClipboardPreview;
PasteAIConfiguration = properties.PasteAIConfiguration ?? new PasteAIConfiguration();

View File

@@ -234,6 +234,8 @@ namespace AdvancedPaste.ViewModels
public bool ShowClipboardHistoryButton => ClipboardHistoryEnabled;
public bool ShowAIPasteSection => _userSettings.ShowAIPaste && IsAllowedByGPO;
public bool HasIndeterminateTransformProgress => double.IsNaN(TransformProgress);
private PasteFormats CustomAIFormat =>
@@ -320,6 +322,7 @@ namespace AdvancedPaste.ViewModels
OnPropertyChanged(nameof(AIProviders));
OnPropertyChanged(nameof(AllowedAIProviders));
OnPropertyChanged(nameof(ShowClipboardPreview));
OnPropertyChanged(nameof(ShowAIPasteSection));
NotifyActiveProviderChanged();

View File

@@ -1 +1 @@
{"properties":{"IsAIEnabled":{"value":false},"ShowCustomPreview":{"value":true},"CloseAfterLosingFocus":{"value":false},"advanced-paste-ui-hotkey":{"win":true,"ctrl":false,"alt":false,"shift":true,"code":86,"key":""},"paste-as-plain-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":79,"key":""},"paste-as-markdown-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":77,"key":""},"paste-as-json-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":74,"key":""},"custom-actions":{"value":[]},"additional-actions":{"image-to-text":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-file":{"isShown":true,"paste-as-txt-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-png-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-html-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}},"transcode":{"isShown":true,"transcode-to-mp3":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"transcode-to-mp4":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}}},"paste-ai-configuration":{"active-provider-id":"","providers":[],"use-shared-credentials":true}},"name":"AdvancedPaste","version":"1"}
{"properties":{"IsAIEnabled":{"value":false},"ShowCustomPreview":{"value":true},"ShowAIPaste":{"value":true},"CloseAfterLosingFocus":{"value":false},"advanced-paste-ui-hotkey":{"win":true,"ctrl":false,"alt":false,"shift":true,"code":86,"key":""},"paste-as-plain-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":79,"key":""},"paste-as-markdown-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":77,"key":""},"paste-as-json-hotkey":{"win":true,"ctrl":true,"alt":true,"shift":false,"code":74,"key":""},"custom-actions":{"value":[]},"additional-actions":{"image-to-text":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-file":{"isShown":true,"paste-as-txt-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-png-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"paste-as-html-file":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}},"transcode":{"isShown":true,"transcode-to-mp3":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true},"transcode-to-mp4":{"shortcut":{"win":false,"ctrl":false,"alt":false,"shift":false,"code":0,"key":""},"isShown":true}}},"paste-ai-configuration":{"active-provider-id":"","providers":[],"use-shared-credentials":true}},"name":"AdvancedPaste","version":"1"}

View File

@@ -2,11 +2,8 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Windows;
using WorkspacesEditor.Utils;
namespace WorkspacesEditor
{
/// <summary>
@@ -14,40 +11,9 @@ namespace WorkspacesEditor
/// </summary>
public partial class OverlayWindow : Window
{
private int _targetX;
private int _targetY;
private int _targetWidth;
private int _targetHeight;
public OverlayWindow()
{
InitializeComponent();
SourceInitialized += OnWindowSourceInitialized;
}
/// <summary>
/// Sets the target bounds for the overlay window.
/// The window will be positioned using DPI-unaware context after initialization.
/// </summary>
public void SetTargetBounds(int x, int y, int width, int height)
{
_targetX = x;
_targetY = y;
_targetWidth = width;
_targetHeight = height;
// Set initial WPF properties (will be corrected after HWND creation)
Left = x;
Top = y;
Width = width;
Height = height;
}
private void OnWindowSourceInitialized(object sender, EventArgs e)
{
// Reposition window using DPI-unaware context to match the virtual coordinates.
// This fixes overlay positioning on mixed-DPI multi-monitor setups.
NativeMethods.SetWindowPositionDpiUnaware(this, _targetX, _targetY, _targetWidth, _targetHeight);
}
}
}

View File

@@ -4,8 +4,6 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace WorkspacesEditor.Utils
{
@@ -19,39 +17,6 @@ namespace WorkspacesEditor.Utils
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_NOACTIVATE = 0x0010;
private static readonly IntPtr DPI_AWARENESS_CONTEXT_UNAWARE = new IntPtr(-1);
/// <summary>
/// Positions a WPF window using DPI-unaware context to match the virtual coordinates.
/// This fixes overlay positioning on mixed-DPI multi-monitor setups.
/// </summary>
public static void SetWindowPositionDpiUnaware(Window window, int x, int y, int width, int height)
{
var helper = new WindowInteropHelper(window).Handle;
if (helper != IntPtr.Zero)
{
// Temporarily switch to DPI-unaware context to position window.
IntPtr oldContext = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
try
{
SetWindowPos(helper, IntPtr.Zero, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
}
finally
{
SetThreadDpiAwarenessContext(oldContext);
}
}
}
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);

View File

@@ -495,10 +495,10 @@ namespace WorkspacesEditor.ViewModels
{
var bounds = screen.Bounds;
OverlayWindow overlayWindow = new OverlayWindow();
// Use DPI-unaware positioning to fix overlay on mixed-DPI multi-monitor setups
overlayWindow.SetTargetBounds(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
overlayWindow.Top = bounds.Top;
overlayWindow.Left = bounds.Left;
overlayWindow.Width = bounds.Width;
overlayWindow.Height = bounds.Height;
overlayWindow.ShowActivated = true;
overlayWindow.Topmost = true;
overlayWindow.Show();

View File

@@ -1,9 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\CoreCommonProps.props" />
<PropertyGroup>
<EnableCoreMrtTooling>false</EnableCoreMrtTooling>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Common" />

View File

@@ -84,13 +84,7 @@
<WarningsNotAsErrors>IL2081;$(WarningsNotAsErrors)</WarningsNotAsErrors>
</PropertyGroup>
<!-- InternalsVisibleTo with public key for CI builds (signed assemblies) -->
<ItemGroup Condition="'$(CIBuild)'=='true'">
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.CommandPalette.Extensions.Toolkit.UnitTests, PublicKey=002400000c80000014010000060200000024000052534131000800000100010085aad0bef0688d1b994a0d78e1fd29fc24ac34ed3d3ac3fb9b3d0c48386ba834aa880035060a8848b2d8adf58e670ed20914be3681a891c9c8c01eef2ab22872547c39be00af0e6c72485d7cfd1a51df8947d36ceba9989106b58abe79e6a3e71a01ed6bdc867012883e0b1a4d35b1b5eeed6df21e401bb0c22f2246ccb69979dc9e61eef262832ed0f2064853725a75485fa8a3efb7e027319c86dec03dc3b1bca2b5081bab52a627b9917450dfad534799e1c7af58683bdfa135f1518ff1ea60e90d7b993a6c87fd3dd93408e35d1296f9a7f9a97c5db56c0f3cc25ad11e9777f94d138b3cea53b9a8331c2e6dcb8d2ea94e18bf1163ff112a22dbd92d429a" />
</ItemGroup>
<!-- InternalsVisibleTo without public key for local builds (unsigned assemblies) -->
<ItemGroup Condition="'$(CIBuild)'!='true'">
<InternalsVisibleTo Include="Microsoft.CommandPalette.Extensions.Toolkit.UnitTests" />
</ItemGroup>
</Project>

View File

@@ -26,6 +26,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
AdditionalActions = new();
IsAIEnabled = false;
ShowCustomPreview = true;
ShowAIPaste = true;
CloseAfterLosingFocus = false;
EnableClipboardPreview = true;
PasteAIConfiguration = new();
@@ -73,6 +74,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool ShowCustomPreview { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool ShowAIPaste { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool CloseAfterLosingFocus { get; set; }

View File

@@ -183,6 +183,9 @@
<tkcontrols:SettingsCard Name="AdvancedPasteShowCustomPreviewSettingsCard" ContentAlignment="Left">
<controls:CheckBoxWithDescriptionControl x:Uid="AdvancedPaste_ShowCustomPreviewSettingsCard" IsChecked="{x:Bind ViewModel.ShowCustomPreview, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard Name="AdvancedPasteShowAIPasteSettingsCard" ContentAlignment="Left">
<controls:CheckBoxWithDescriptionControl x:Uid="AdvancedPaste_ShowAIPasteSettingsCard" IsChecked="{x:Bind ViewModel.ShowAIPaste, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>

View File

@@ -4117,11 +4117,17 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="AdvancedPaste_ShowCustomPreviewSettingsCard.Description" xml:space="preserve">
<value>Preview the output of AI formats and Image to text before pasting</value>
</data>
<data name="AdvancedPaste_ShowAIPasteSettingsCard.Header" xml:space="preserve">
<value>Show AI paste section</value>
</data>
<data name="AdvancedPaste_ShowAIPasteSettingsCard.Description" xml:space="preserve">
<value>Show the AI paste input box in the Advanced Paste window</value>
</data>
<data name="AdvancedPaste_EnableAdvancedAI.Text" xml:space="preserve">
<value>Advanced AI</value>
</data>
<data name="Oobe_AdvancedPaste.Description" xml:space="preserve">
<value>Advanced Paste is a tool to put your clipboard content into any format you need, focused towards developer workflows. It can paste as plain text, Markdown, or JSON directly with the UX or with a direct keystroke invoke. These are fully locally executed. In addition, it has an opt-in AI feature that can use an online or local language model endpoint. Note: this will replace the formatted text in your clipboard with the selected format.</value>
<value>Advanced Paste is a tool to put your clipboard content into any format you need, focused towards developer workflows. It can paste as plain text, markdown, or json directly with the UX or with a direct keystroke invoke. These are fully locally executed. In addition, it has an AI powered option that is 100% opt-in and requires an Open AI key. Note: this will replace the formatted text in your clipboard with the selected format.</value>
</data>
<data name="Oobe_AdvancedPaste.Title" xml:space="preserve">
<value>Advanced Paste</value>

View File

@@ -543,6 +543,19 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public bool ShowAIPaste
{
get => _advancedPasteSettings.Properties.ShowAIPaste;
set
{
if (value != _advancedPasteSettings.Properties.ShowAIPaste)
{
_advancedPasteSettings.Properties.ShowAIPaste = value;
NotifySettingsChanged();
}
}
}
public bool CloseAfterLosingFocus
{
get => _advancedPasteSettings.Properties.CloseAfterLosingFocus;
@@ -1221,6 +1234,12 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
OnPropertyChanged(nameof(ShowCustomPreview));
}
if (target.ShowAIPaste != source.ShowAIPaste)
{
target.ShowAIPaste = source.ShowAIPaste;
OnPropertyChanged(nameof(ShowAIPaste));
}
if (target.CloseAfterLosingFocus != source.CloseAfterLosingFocus)
{
target.CloseAfterLosingFocus = source.CloseAfterLosingFocus;