-
Notifications
You must be signed in to change notification settings - Fork 1.6k
tests: add Wsladiag CLI tests #13975
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: feature/wsl-for-apps
Are you sure you want to change the base?
Changes from all commits
d86895d
e80ddfc
6d6cd1e
0e577a5
660b381
d0c24c2
800b26f
3803739
94c5275
d98de6f
8522c2d
0069655
ea16203
f8b6771
1f32d46
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 |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| /*++ | ||
| Copyright (c) Microsoft. All rights reserved. | ||
| Module Name: | ||
| WsladiagTests.cpp | ||
| Abstract: | ||
| This file contains smoke tests for wsladiag. | ||
| --*/ | ||
|
|
||
| #include "precomp.h" | ||
| #include "Common.h" | ||
| #include "Localization.h" | ||
| #include <format> | ||
|
|
||
| namespace WsladiagTests { | ||
| class WsladiagTests | ||
| { | ||
| WSL_TEST_CLASS(WsladiagTests) | ||
|
|
||
| // Use localized usage text at runtime | ||
| static std::wstring GetUsageText() | ||
| { | ||
| return std::wstring(wsl::shared::Localization::MessageWsladiagUsage()); | ||
| } | ||
|
|
||
| // Test that wsladiag list command shows either sessions or "no sessions" message | ||
| TEST_METHOD(List_ShowsSessionsOrNoSessions) | ||
|
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. I think it's better to explicitly test that we get no list sessions output here, and then have another test to explicitly validate that |
||
| { | ||
| auto [out, err, code] = RunWsladiag(L"list"); | ||
| VERIFY_ARE_EQUAL(0, code); | ||
| VERIFY_ARE_EQUAL(L"", NormalizeForCompare(err)); | ||
|
|
||
| ValidateListOutput(out); | ||
| } | ||
|
|
||
| // Test that wsladiag --help shows usage information | ||
| TEST_METHOD(Help_ShowsUsage) | ||
| { | ||
| ValidateWsladiagOutput(L"--help", 0, L"", GetUsageText()); | ||
| } | ||
|
|
||
| // Test that wsladiag with no arguments shows usage information | ||
| TEST_METHOD(EmptyCommand_ShowsUsage) | ||
| { | ||
| ValidateWsladiagOutput(L"", 0, L"", GetUsageText()); | ||
| } | ||
|
|
||
| // Test that -h and --help flags produce identical output | ||
| TEST_METHOD(Help_ShortAndLongFlags_Match) | ||
| { | ||
| auto [outH, errH, codeH] = RunWsladiag(L"-h"); | ||
| auto [outLong, errLong, codeLong] = RunWsladiag(L"--help"); | ||
|
|
||
| VERIFY_ARE_EQUAL(0, codeH); | ||
| VERIFY_ARE_EQUAL(0, codeLong); | ||
|
|
||
| VERIFY_ARE_EQUAL(L"", outH); | ||
| VERIFY_ARE_EQUAL(L"", outLong); | ||
|
|
||
| VERIFY_ARE_EQUAL(NormalizeForCompare(errH), NormalizeForCompare(errLong)); | ||
| ValidateUsage(errH); | ||
| } | ||
|
|
||
| // Test that unknown commands show error message and usage | ||
| TEST_METHOD(UnknownCommand_ShowsError) | ||
| { | ||
| const std::wstring verb = L"blah"; | ||
| const std::wstring errorMsg = std::wstring(wsl::shared::Localization::MessageWslaUnknownCommand(verb.c_str())); | ||
| const std::wstring usage = GetUsageText(); | ||
|
|
||
| auto [out, err, code] = RunWsladiag(verb); | ||
|
|
||
| VERIFY_ARE_EQUAL(1, code); | ||
| VERIFY_ARE_EQUAL(L"", NormalizeForCompare(out)); | ||
|
|
||
| const auto nerr = NormalizeForCompare(err); | ||
| const auto nerrorMsg = NormalizeForCompare(errorMsg); | ||
| const auto nusage = NormalizeForCompare(usage); | ||
|
|
||
| VERIFY_IS_TRUE(nerr.find(nerrorMsg) != std::wstring::npos); | ||
| VERIFY_IS_TRUE(nerr.find(nusage) != std::wstring::npos); | ||
| } | ||
|
|
||
| // Test that shell command without session name shows error | ||
| TEST_METHOD(Shell_MissingName_ShowsError) | ||
| { | ||
| auto [out, err, code] = RunWsladiag(L"shell"); | ||
| VERIFY_ARE_EQUAL(1, code); | ||
| VERIFY_ARE_EQUAL(L"", out); | ||
| const std::wstring missingArgMsg = | ||
| std::wstring(wsl::shared::Localization::MessageMissingArgument(L"<SessionName>", L"wsladiag shell")); | ||
| VERIFY_IS_TRUE(NormalizeForCompare(err).find(NormalizeForCompare(missingArgMsg)) != std::wstring::npos); | ||
| } | ||
|
|
||
| // Test shell command with invalid session name (silent mode) | ||
| TEST_METHOD(Shell_InvalidSessionName_Silent) | ||
|
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. What does "silent mode" mean here?
Author
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. by silent mode, I meant the default non-verbose behavior. I’ll rename this to “no verbose output” to make it clearer. |
||
| { | ||
| const auto expectedErr = | ||
| std::wstring(wsl::shared::Localization::MessageWslaSessionNotFound(L"DefinitelyNotARealSession")); | ||
| ValidateWsladiagOutput(L"shell DefinitelyNotARealSession", 1, L"", expectedErr); | ||
| } | ||
|
|
||
| // Test shell command with invalid session name (verbose mode) | ||
| TEST_METHOD(Shell_InvalidSessionName_Verbose) | ||
| { | ||
| const std::wstring name = L"DefinitelyNotARealSession"; | ||
| const auto expectedErr = std::wstring(wsl::shared::Localization::MessageWslaSessionNotFound(name.c_str())); | ||
| ValidateWsladiagOutput(std::format(L"shell {} --verbose", name), 1, L"", expectedErr); | ||
| } | ||
|
|
||
| // Build command line for wsladiag.exe with given arguments | ||
| static std::wstring BuildWsladiagCmd(const std::wstring& args) | ||
| { | ||
| const auto msiPathOpt = wsl::windows::common::wslutil::GetMsiPackagePath(); | ||
| VERIFY_IS_TRUE(msiPathOpt.has_value()); | ||
|
|
||
| const auto exePath = std::filesystem::path(*msiPathOpt) / L"wsladiag.exe"; | ||
| const auto exe = exePath.wstring(); | ||
|
|
||
| return args.empty() ? std::format(L"\"{}\"", exe) : std::format(L"\"{}\" {}", exe, args); | ||
| } | ||
|
|
||
| // Execute wsladiag with given arguments and return output, error, and exit code | ||
| static std::tuple<std::wstring, std::wstring, int> RunWsladiag(const std::wstring& args) | ||
| { | ||
| std::wstring cmd = BuildWsladiagCmd(args); | ||
| return LxsstuLaunchCommandAndCaptureOutputWithResult(cmd.data()); | ||
| } | ||
|
|
||
| // Normalize for reliable comparisons: | ||
| // - Remove '\r' so CRLF -> LF | ||
| // - Trim trailing whitespace (usually just final newline) | ||
| static std::wstring NormalizeForCompare(std::wstring s) | ||
| { | ||
| s.erase(std::remove(s.begin(), s.end(), L'\r'), s.end()); | ||
| while (!s.empty() && iswspace(s.back())) | ||
| { | ||
| s.pop_back(); | ||
| } | ||
| return s; | ||
| } | ||
|
|
||
| static void ValidateWsladiagOutput(const std::wstring& cmd, int expectedExitCode, const std::wstring& expectedStdout, const std::wstring& expectedStderr) | ||
| { | ||
| auto [out, err, code] = RunWsladiag(cmd); | ||
| VERIFY_ARE_EQUAL(expectedExitCode, code); | ||
|
|
||
| VERIFY_ARE_EQUAL(NormalizeForCompare(expectedStdout), NormalizeForCompare(out)); | ||
| VERIFY_ARE_EQUAL(NormalizeForCompare(expectedStderr), NormalizeForCompare(err)); | ||
| } | ||
|
|
||
| // Validate that list command output shows either no sessions message or session table | ||
| static void ValidateListOutput(const std::wstring& out) | ||
| { | ||
| const bool noSessions = out.find(std::wstring(wsl::shared::Localization::MessageWslaNoSessionsFound())) != std::wstring::npos; | ||
| const auto idHeader = std::wstring(wsl::shared::Localization::MessageWslaHeaderId()); | ||
| const auto pidHeader = std::wstring(wsl::shared::Localization::MessageWslaHeaderCreatorPid()); | ||
| const auto nameHeader = std::wstring(wsl::shared::Localization::MessageWslaHeaderDisplayName()); | ||
|
|
||
| const bool hasTable = (out.find(idHeader) != std::wstring::npos) && (out.find(pidHeader) != std::wstring::npos) && | ||
| (out.find(nameHeader) != std::wstring::npos); | ||
|
|
||
| VERIFY_IS_TRUE(noSessions || hasTable); | ||
| } | ||
|
|
||
| // Validate that usage information contains expected command descriptions | ||
| static void ValidateUsage(const std::wstring& err) | ||
| { | ||
| const std::wstring nerr = NormalizeForCompare(err); | ||
| const std::wstring usage = NormalizeForCompare(GetUsageText()); | ||
| VERIFY_IS_TRUE(nerr.find(usage) != std::wstring::npos); | ||
|
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. Might make more sense to check if the strings are exactly equal here.
Author
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. Sounds good, I’ll update the test to check for exact string equality here. |
||
| } | ||
| }; | ||
| } // namespace WsladiagTests | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test class uses WSL_TEST_CLASS macro which doesn't include wsladiag.exe in the BinaryUnderTest property list. While wslaservice.exe is included (which wsladiag depends on), the test is directly executing wsladiag.exe and should declare it as a binary under test. Consider adding a TEST_CLASS_PROPERTY for wsladiag.exe after the WSL_TEST_CLASS macro, similar to how other test classes declare their specific binaries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@OneBlue does BinaryUnderTest actually do something useful in WSL? Most docs I see on it seem related to internal infra.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've always assumed that TE would watch said binaries in case they crash and fail the test case if that happens, but I'm not actually not sure that it is the case