-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add 38 C++ MSTest tests for FileLocksmith #46936
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4bab47d
00e6ca0
1063733
f3bc361
73a2d64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -204,7 +204,7 @@ Zoltan | |
| Zykova | ||
|
|
||
| # OTHERS | ||
|
|
||
| admins | ||
| Bilibili | ||
| BVID | ||
| capturevideosample | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,158 @@ | ||
| #include "pch.h" | ||
| #include "CppUnitTest.h" | ||
| #include "../CLILogic.h" | ||
| #include "../../FileLocksmithLib/Constants.h" | ||
| #include <map> | ||
|
|
||
| using namespace Microsoft::VisualStudio::CppUnitTestFramework; | ||
|
|
||
| namespace FileLocksmithCLIUnitTests | ||
| { | ||
| // ======================================================================== | ||
| // Constants validation — guards against silent config corruption | ||
| // ======================================================================== | ||
| TEST_CLASS(ConstantsTests) | ||
| { | ||
| public: | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::PowerToyKey | ||
| // What: Validates the module's registry/settings key matches expected value | ||
| // Why: A mismatch breaks settings persistence, module registration, and GPO detection | ||
| // Risk: Silent config corruption if key changes without updating all consumers | ||
| TEST_METHOD(PowerToyKey_IsFileLocksmith) | ||
| { | ||
| Assert::AreEqual(L"File Locksmith", constants::nonlocalizable::PowerToyKey); | ||
| } | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::PowerToyName | ||
| // What: Validates the display/log name for the module | ||
| // Why: Used in logging, telemetry, and user-visible strings; must match expectations | ||
| // Risk: Incorrect name breaks log filtering and telemetry dashboards | ||
| TEST_METHOD(PowerToyName_IsFileLocksmith) | ||
| { | ||
| Assert::AreEqual(L"File Locksmith", constants::nonlocalizable::PowerToyName); | ||
| } | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::JsonKeyEnabled | ||
| // What: Validates the JSON settings key for the enabled flag | ||
| // Why: PowerToys settings framework reads this key to determine if module is on | ||
| // Risk: Typo or rename silently disables the module for all users | ||
| TEST_METHOD(JsonKeyEnabled_IsEnabled) | ||
| { | ||
| Assert::AreEqual(L"Enabled", constants::nonlocalizable::JsonKeyEnabled); | ||
| } | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::JsonKeyShowInExtendedContextMenu | ||
| // What: Validates the JSON key controlling extended context menu visibility | ||
| // Why: Settings UI writes this key; mismatch means user toggle has no effect | ||
| // Risk: Context menu always shows (or never shows) regardless of user preference | ||
| TEST_METHOD(JsonKeyExtendedMenu_IsCorrect) | ||
| { | ||
| Assert::AreEqual(L"showInExtendedContextMenu", | ||
| constants::nonlocalizable::JsonKeyShowInExtendedContextMenu); | ||
| } | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::DataFilePath | ||
| // What: Validates settings file path contains .json extension | ||
| // Why: Settings framework expects JSON; wrong extension breaks serialization | ||
| // Risk: Settings silently fail to load/save, reverting to defaults on every launch | ||
| TEST_METHOD(DataFilePath_ContainsJson) | ||
| { | ||
| std::wstring path(constants::nonlocalizable::DataFilePath); | ||
| Assert::IsTrue(path.find(L".json") != std::wstring::npos, | ||
| L"Data file path should end in .json"); | ||
| } | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::LastRunPath | ||
| // What: Validates the last-run file path contains .log extension | ||
| // Why: IPC between shell extension and UI depends on this path being correct | ||
| // Risk: Shell extension writes to wrong file; UI never receives the file list | ||
| TEST_METHOD(LastRunPath_ContainsLog) | ||
| { | ||
| std::wstring path(constants::nonlocalizable::LastRunPath); | ||
| Assert::IsTrue(path.find(L".log") != std::wstring::npos, | ||
| L"Last run path should end in .log"); | ||
| } | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::FileNameUIExe | ||
| // What: Validates the UI executable name follows PowerToys naming convention | ||
| // Why: Runner uses this name to launch the UI; mismatch prevents UI from opening | ||
| // Risk: Context menu "What's using this file?" does nothing visible to the user | ||
| TEST_METHOD(UIExe_ContainsPowerToys) | ||
| { | ||
| std::wstring exe(constants::nonlocalizable::FileNameUIExe); | ||
| Assert::IsTrue(exe.find(L"PowerToys") != std::wstring::npos, | ||
| L"UI executable should contain PowerToys in name"); | ||
| } | ||
|
|
||
| // Product code: Constants.h — constants::nonlocalizable::RegistryKeyDescription | ||
| // What: Validates the registry description string is not empty | ||
| // Why: Empty description in registry looks broken to admins and may fail GPO validation | ||
| // Risk: Enterprise policy tools flag the extension as malformed | ||
| TEST_METHOD(RegistryKeyDescription_NotEmpty) | ||
| { | ||
| std::wstring desc(constants::nonlocalizable::RegistryKeyDescription); | ||
| Assert::IsFalse(desc.empty(), | ||
| L"Registry key description must not be empty"); | ||
| } | ||
| }; | ||
|
|
||
| // ======================================================================== | ||
| // ProcessResult struct — validates the data layout used by | ||
| // find_processes_recursive() and IPC serialization | ||
| // ======================================================================== | ||
| TEST_CLASS(ProcessResultTests) | ||
| { | ||
| public: | ||
|
|
||
| // Product code: ProcessResult.h — ProcessResult struct | ||
| // What: Validates all four fields (name, pid, user, files) round-trip correctly | ||
| // Why: find_processes_recursive() populates these; get_json()/get_text() reads them | ||
| // Risk: Field misalignment silently corrupts IPC data between CLI and UI | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test is unable to actually verify field alignment over the IPC channel, because it does not use an IPC channel. It is only capable of testing that two However, it is not even capable of that, because only the structure definition visible to this cpp file matters inside this test. This test boils down to... which is... i mean. not going to prevent a regression |
||
| TEST_METHOD(FieldAssignment) | ||
| { | ||
| ProcessResult pr; | ||
| pr.name = L"test.exe"; | ||
| pr.pid = 1234; | ||
| pr.user = L"SYSTEM"; | ||
| pr.files = { L"C:\\file1.txt", L"C:\\file2.txt" }; | ||
|
|
||
| Assert::AreEqual(std::wstring(L"test.exe"), pr.name); | ||
| Assert::AreEqual((DWORD)1234, pr.pid); | ||
| Assert::AreEqual(std::wstring(L"SYSTEM"), pr.user); | ||
| Assert::AreEqual((size_t)2, pr.files.size()); | ||
| } | ||
|
|
||
| // Product code: ProcessResult.h — ProcessResult::files default state | ||
| // What: Validates a default-constructed ProcessResult has an empty files vector | ||
| // Why: Callers check files.empty() to skip display; must be true by default | ||
| // Risk: Uninitialized vector could contain garbage, causing spurious output | ||
| TEST_METHOD(EmptyFiles) | ||
| { | ||
| ProcessResult pr; | ||
| pr.name = L"proc.exe"; | ||
| pr.pid = 0; | ||
| pr.user = L""; | ||
|
Comment on lines
+133
to
+135
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is extraneous and not related to the test condition |
||
| Assert::IsTrue(pr.files.empty(), | ||
| L"Default-constructed files vector must be empty"); | ||
| } | ||
|
|
||
| // Product code: ProcessResult.h — ProcessResult::files with multiple entries | ||
| // What: Validates that files vector correctly stores and indexes multiple paths | ||
| // Why: A single process can lock many files; UI must display all of them | ||
| // Risk: Vector reallocation or index errors could drop paths from the display | ||
| TEST_METHOD(MultiplePaths) | ||
| { | ||
| ProcessResult pr; | ||
| pr.files = { L"A", L"B", L"C", L"D" }; | ||
| Assert::AreEqual((size_t)4, pr.files.size()); | ||
| Assert::AreEqual(std::wstring(L"C"), pr.files[2]); | ||
|
Comment on lines
+147
to
+149
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a test to make sure |
||
| } | ||
| }; | ||
|
|
||
| // ======================================================================== | ||
| // Mock infrastructure for CLI tests | ||
| // ======================================================================== | ||
| struct MockProcessFinder : IProcessFinder | ||
| { | ||
| std::vector<ProcessResult> results; | ||
|
|
@@ -126,5 +272,58 @@ namespace FileLocksmithCLIUnitTests | |
|
|
||
| Assert::AreEqual(1, result.exit_code); | ||
| } | ||
|
|
||
| // Product code: CLILogic.cpp — get_json() with empty results | ||
| // What: Validates JSON output is well-formed when no processes lock the files | ||
| // Why: Consumers (UI, scripts) parse this JSON; malformed output breaks IPC | ||
| // Risk: Empty-array edge case could produce invalid JSON or crash serialization | ||
| TEST_METHOD(TestJsonOutput_EmptyResults) | ||
| { | ||
| MockProcessFinder finder; | ||
| MockProcessTerminator terminator; | ||
| MockStringProvider strings; | ||
|
|
||
| wchar_t* argv[] = { (wchar_t*)L"exe", (wchar_t*)L"somefile.txt", (wchar_t*)L"--json" }; | ||
| auto result = run_command(3, argv, finder, terminator, strings); | ||
|
|
||
| Assert::AreEqual(0, result.exit_code); | ||
| Assert::IsTrue(result.output.find(L"processes") != std::wstring::npos, | ||
| L"JSON output should contain 'processes' key even when empty"); | ||
| } | ||
|
|
||
| // Product code: CLILogic.cpp — run_command() path collection loop | ||
| // What: Verifies that multiple file path arguments are all accepted and processed | ||
| // Why: Users often right-click multiple files; all paths must reach the finder | ||
| // Risk: Off-by-one in arg parsing could silently drop trailing paths | ||
| TEST_METHOD(TestMultiplePaths) | ||
| { | ||
| MockProcessFinder finder; | ||
| finder.results = { { L"proc.exe", 42, L"user", { L"a.txt", L"b.txt", L"c.txt" } } }; | ||
| MockProcessTerminator terminator; | ||
| MockStringProvider strings; | ||
|
|
||
| wchar_t* argv[] = { (wchar_t*)L"exe", (wchar_t*)L"a.txt", (wchar_t*)L"b.txt", (wchar_t*)L"c.txt" }; | ||
| auto result = run_command(4, argv, finder, terminator, strings); | ||
|
|
||
| Assert::AreEqual(0, result.exit_code); | ||
| Assert::IsTrue(result.output.find(L"42") != std::wstring::npos, | ||
| L"Output should contain the PID from mock results"); | ||
| } | ||
|
|
||
| // Product code: CLILogic.cpp — run_command() empty-paths guard | ||
| // What: Verifies that flags-only invocation (no file paths) returns an error | ||
| // Why: --json alone is meaningless without paths; must fail gracefully | ||
| // Risk: Missing guard could pass empty paths to finder, causing undefined behavior | ||
| TEST_METHOD(TestNoPathsAfterFlags) | ||
| { | ||
| MockProcessFinder finder; | ||
| MockProcessTerminator terminator; | ||
| MockStringProvider strings; | ||
|
|
||
| wchar_t* argv[] = { (wchar_t*)L"exe", (wchar_t*)L"--json" }; | ||
| auto result = run_command(2, argv, finder, terminator, strings); | ||
|
|
||
| Assert::AreEqual(1, result.exit_code); | ||
| } | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.