terminal/src/cascadia/UnitTests_Remoting/RemotingTests.cpp
Mike Griese fb597ed304
Add support for renaming windows (#9662)
## Summary of the Pull Request

This PR adds support for renaming windows.

![window-renaming-000](https://user-images.githubusercontent.com/18356694/113034344-9a30be00-9157-11eb-9443-975f3c294f56.gif)
![window-renaming-001](https://user-images.githubusercontent.com/18356694/113034452-b5033280-9157-11eb-9e35-e5ac80fef0bc.gif)


It does so through two new actions:
* `renameWindow` takes a `name` parameter, and attempts to set the window's name
  to the provided name. This is useful if you always want to hit <kbd>F3</kbd>
  and rename a window to "foo" (READ: probably not that useful)
* `openWindowRenamer` is more interesting: it opens a `TeachingTip` with a
  `TextBox`. When the user hits Ok, it'll request a rename for the provided
  value. This lets the user pick a new name for the window at runtime.

In both cases, if there's already a window with that name, then the monarch will
reject the rename, and pop a `Toast` in the window informing the user that the
rename failed. Nifty!

## References
* Builds on the toasts from #9523
* #5000 - process model megathread

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50771747
* [x] I work here
* [x] Tests addded (and pass with the help of #9660)
* [ ] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

I'm sending this PR while finishing up the tests. I figured I'll have time to sneak them in before I get the necessary reviews.

> PAIN: We can't immediately focus the textbox in the TeachingTip. It's
> not technically focusable until it is opened. However, it doesn't
> provide an even tto tell us when it is opened. That's tracked in
> microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to
> click on the text box manually.
> We're also not using a ContentDialog for this, because in Xaml
> Islands a text box in a ContentDialog won't recieve _any_ keypresses.
> Fun!

## Validation Steps Performed

I've been playing with 

```json
        { "keys": "f1", "command": "identifyWindow" },
        { "keys": "f2", "command": "identifyWindows" },
        { "keys": "f3", "command": "openWindowRenamer" },
        { "keys": "f4", "command": { "action": "renameWindow", "name": "foo" } },
        { "keys": "f5", "command": { "action": "renameWindow", "name": "bar" } },
```

and they seem to work as expected
2021-04-02 16:00:04 +00:00

1510 lines
62 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "../Remoting/Monarch.h"
#include "../Remoting/CommandlineArgs.h"
#include "../Remoting/FindTargetWindowArgs.h"
#include "../Remoting/ProposeCommandlineResult.h"
#include "../inc/WindowingBehavior.h"
using namespace Microsoft::Console;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
using namespace winrt;
using namespace winrt::Microsoft::Terminal;
// These are some gross macros that let us call a private ctor for
// Monarch/Peasant. We can't just use make_self, because that doesn't let us
// call a private ctor. We can use com_ptr::attach, but since we're allocating
// the thing on the stack, we need to make sure to call detach before the object
// is destructed.
#define MAKE_MONARCH(name, pid) \
Remoting::implementation::Monarch _local_##name##{ pid }; \
com_ptr<Remoting::implementation::Monarch> name; \
name.attach(&_local_##name##); \
auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); });
#define MAKE_PEASANT(name, pid) \
Remoting::implementation::Peasant _local_##name##{ pid }; \
com_ptr<Remoting::implementation::Peasant> name; \
name.attach(&_local_##name##); \
auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); });
namespace RemotingUnitTests
{
// This is a silly helper struct.
// It will always throw an hresult_error on any of its methods.
//
// In the tests, it's hard to emulate a peasant process being totally dead
// once the Monarch has captured a reference to it. Since everything's
// in-proc in the tests, we can't decrement the refcount in such a way that
// the monarch's reference will throw a catchable exception. Instead, this
// class can be used to replace a peasant inside a Monarch, to emulate that
// peasant process dying. Any time the monarch tries to do something to this
// peasant, it'll throw an exception.
struct DeadPeasant : implements<DeadPeasant, winrt::Microsoft::Terminal::Remoting::IPeasant>
{
DeadPeasant() = default;
void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; };
uint64_t GetID() { throw winrt::hresult_error{}; };
winrt::hstring WindowName() { throw winrt::hresult_error{}; };
uint64_t GetPID() { throw winrt::hresult_error{}; };
bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; }
void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { throw winrt::hresult_error{}; }
void RequestIdentifyWindows() { throw winrt::hresult_error{}; };
void DisplayWindowId() { throw winrt::hresult_error{}; };
Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; }
Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; }
void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error{}; }
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs);
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs);
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs);
};
class RemotingTests
{
BEGIN_TEST_CLASS(RemotingTests)
END_TEST_CLASS()
TEST_METHOD(CreateMonarch);
TEST_METHOD(CreatePeasant);
TEST_METHOD(CreatePeasantWithNew);
TEST_METHOD(AddPeasants);
TEST_METHOD(GetPeasantsByID);
TEST_METHOD(AddPeasantsToNewMonarch);
TEST_METHOD(RemovePeasantFromMonarchWhenFreed);
TEST_METHOD(ProposeCommandlineNoWindow);
TEST_METHOD(ProposeCommandlineGivenWindow);
TEST_METHOD(ProposeCommandlineNegativeWindow);
TEST_METHOD(ProposeCommandlineCurrentWindow);
TEST_METHOD(ProposeCommandlineNonExistentWindow);
TEST_METHOD(ProposeCommandlineDeadWindow);
TEST_METHOD(MostRecentWindowSameDesktops);
TEST_METHOD(MostRecentWindowDifferentDesktops);
TEST_METHOD(MostRecentWindowMoveDesktops);
TEST_METHOD(GetMostRecentAnyDesktop);
TEST_METHOD(MostRecentIsDead);
TEST_METHOD(GetPeasantsByName);
TEST_METHOD(AddNamedPeasantsToNewMonarch);
TEST_METHOD(LookupNamedPeasantWhenOthersDied);
TEST_METHOD(LookupNamedPeasantWhenItDied);
TEST_METHOD(GetMruPeasantAfterNameLookupForDeadPeasant);
TEST_METHOD(ProposeCommandlineForNamedDeadWindow);
TEST_METHOD(TestRenameWindowSuccessfully);
TEST_METHOD(TestRenameSameNameAsAnother);
TEST_METHOD(TestRenameSameNameAsADeadPeasant);
TEST_CLASS_SETUP(ClassSetup)
{
return true;
}
static void _killPeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID);
static void _findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args);
static void _findTargetWindowByNameHelper(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args);
};
// Helper to replace the specified peasant in a monarch with a
// "DeadPeasant", which will emulate what happens when the peasant process
// dies.
void RemotingTests::_killPeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID)
{
if (peasantID <= 0)
{
return;
}
com_ptr<DeadPeasant> tombstone;
tombstone.attach(new DeadPeasant());
m->_peasants[peasantID] = *tombstone;
}
// Helper to get the first argument out of the commandline, and try to
// convert it to an int.
void RemotingTests::_findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args)
{
const auto arguments = args.Args().Commandline();
if (arguments.size() > 0)
{
const auto index = std::stoi(arguments.at(0).c_str());
args.ResultTargetWindow(index >= 0 ? index : -1);
}
}
// Helper to get the first argument out of the commandline, and return it as
// a name to use.
void RemotingTests::_findTargetWindowByNameHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args)
{
const auto arguments = args.Args().Commandline();
if (arguments.size() > 0)
{
args.ResultTargetWindow(WindowingBehaviorUseName);
args.ResultTargetWindowName(arguments.at(0));
}
}
void RemotingTests::CreateMonarch()
{
auto m1 = winrt::make_self<Remoting::implementation::Monarch>();
VERIFY_IS_NOT_NULL(m1);
VERIFY_ARE_EQUAL(GetCurrentProcessId(),
m1->GetPID(),
L"A Monarch without an explicit PID should use the current PID");
Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs.");
auto expectedFakePID = 1234u;
MAKE_MONARCH(m2, expectedFakePID);
VERIFY_IS_NOT_NULL(m2);
VERIFY_ARE_EQUAL(expectedFakePID,
m2->GetPID(),
L"A Monarch with an explicit PID should use the one we provided");
}
void RemotingTests::CreatePeasant()
{
auto p1 = winrt::make_self<Remoting::implementation::Peasant>();
VERIFY_IS_NOT_NULL(p1);
VERIFY_ARE_EQUAL(GetCurrentProcessId(),
p1->GetPID(),
L"A Peasant without an explicit PID should use the current PID");
Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs.");
auto expectedFakePID = 2345u;
MAKE_PEASANT(p2, expectedFakePID);
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(expectedFakePID,
p2->GetPID(),
L"A Peasant with an explicit PID should use the one we provided");
}
void RemotingTests::CreatePeasantWithNew()
{
Log::Comment(L"The same thing as the above test, but with `new` instead of insanity on the stack");
auto p1 = winrt::make_self<Remoting::implementation::Peasant>();
VERIFY_IS_NOT_NULL(p1);
VERIFY_ARE_EQUAL(GetCurrentProcessId(),
p1->GetPID(),
L"A Peasant without an explicit PID should use the current PID");
auto expectedFakePID = 2345u;
com_ptr<Remoting::implementation::Peasant> p2;
VERIFY_IS_NULL(p2);
p2.attach(new Remoting::implementation::Peasant(expectedFakePID));
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(expectedFakePID,
p2->GetPID(),
L"A Peasant with an explicit PID should use the one we provided");
}
void RemotingTests::AddPeasants()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
}
void RemotingTests::GetPeasantsByID()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
auto maybeP1 = m0->_getPeasant(1);
VERIFY_IS_NOT_NULL(maybeP1);
VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID());
auto maybeP2 = m0->_getPeasant(2);
VERIFY_IS_NOT_NULL(maybeP2);
VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID());
}
void RemotingTests::AddPeasantsToNewMonarch()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
const auto monarch3PID = 45678u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
com_ptr<Remoting::implementation::Monarch> m3;
m3.attach(new Remoting::implementation::Monarch(monarch3PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_IS_NOT_NULL(m3);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
m3->AddPeasant(*p1);
m3->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
}
void RemotingTests::RemovePeasantFromMonarchWhenFreed()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch.");
RemotingTests::_killPeasant(m0, p1->GetID());
auto maybeP2 = m0->_getPeasant(2);
VERIFY_IS_NOT_NULL(maybeP2);
VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID());
auto maybeP1 = m0->_getPeasant(1);
VERIFY_IS_NULL(maybeP1);
VERIFY_ARE_EQUAL(1u, m0->_peasants.size());
}
void RemotingTests::ProposeCommandlineNoWindow()
{
Log::Comment(L"Test proposing a commandline that doesn't have a window specified in it");
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
std::vector<winrt::hstring> args{};
Remoting::CommandlineArgs eventArgs{ { args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
Log::Comment(L"Propose the same args again after adding a peasant - we should still return {create new window, no ID}");
result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
void RemotingTests::ProposeCommandlineGivenWindow()
{
Log::Comment(L"Test proposing a commandline for a window that currently exists");
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) {
Log::Comment(L"Commandline dispatched to p1");
VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u);
VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1));
});
std::vector<winrt::hstring> args{ L"1", L"arg[1]" };
Remoting::CommandlineArgs eventArgs{ { args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
void RemotingTests::ProposeCommandlineNegativeWindow()
{
Log::Comment(L"Test proposing a commandline for an invalid window ID, like -1");
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
{
std::vector<winrt::hstring> args{ L"-1" };
Remoting::CommandlineArgs eventArgs{ { args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
{
std::vector<winrt::hstring> args{ L"-2" };
Remoting::CommandlineArgs eventArgs{ { args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
}
void RemotingTests::ProposeCommandlineCurrentWindow()
{
Log::Comment(L"Test proposing a commandline for the current window (ID=0)");
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) {
Log::Comment(L"Commandline dispatched to p1");
VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u);
VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1));
});
std::vector<winrt::hstring> p1Args{ L"0", L"arg[1]" };
std::vector<winrt::hstring> p2Args{ L"0", L"this is for p2" };
{
Log::Comment(L"Manually activate the first peasant");
// This would usually happen immediately when the window is created, but
// there's no actual window in these tests.
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
winrt::guid{},
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
Log::Comment(L"Add a second peasant");
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(p2);
m0->AddPeasant(*p2);
p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) {
Log::Comment(L"Commandline dispatched to p2");
VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u);
VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1));
});
{
Log::Comment(L"Activate the second peasant");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
winrt::guid{},
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
Log::Comment(L"Send a commandline to the current window, which should be p2");
Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
{
Log::Comment(L"Reactivate the first peasant");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
winrt::guid{},
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
Log::Comment(L"Send a commandline to the current window, which should be p1 again");
Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
}
void RemotingTests::ProposeCommandlineNonExistentWindow()
{
Log::Comment(L"Test proposing a commandline for an ID that doesn't have a current peasant");
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
{
std::vector<winrt::hstring> args{ L"2" };
Remoting::CommandlineArgs eventArgs{ { args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(true, (bool)result.Id());
VERIFY_ARE_EQUAL(2u, result.Id().Value());
}
{
std::vector<winrt::hstring> args{ L"10" };
Remoting::CommandlineArgs eventArgs{ { args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(true, (bool)result.Id());
VERIFY_ARE_EQUAL(10u, result.Id().Value());
}
}
void RemotingTests::ProposeCommandlineDeadWindow()
{
Log::Comment(L"Test proposing a commandline for a peasant that previously died");
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& /*cmdlineArgs*/) {
Log::Comment(L"Commandline dispatched to p1");
VERIFY_IS_TRUE(false, L"This should not happen, this peasant should be dead.");
});
Log::Comment(L"Add a second peasant");
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(p2);
m0->AddPeasant(*p2);
p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) {
Log::Comment(L"Commandline dispatched to p2");
VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u);
VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1));
});
std::vector<winrt::hstring> p1Args{ L"1", L"arg[1]" };
std::vector<winrt::hstring> p2Args{ L"2", L"this is for p2" };
Log::Comment(L"Kill peasant 1");
_killPeasant(m0, 1);
{
Log::Comment(L"Send a commandline to p2, who is still alive. We won't create a new window.");
Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id());
}
{
Log::Comment(L"Send a commandline to p1, who is dead. We will create a new window.");
Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(true, (bool)result.Id());
VERIFY_ARE_EQUAL(1u, result.Id().Value());
}
}
// TODO:projects/5
//
// In order to test WindowingBehaviorUseExisting, we'll have to
// create our own IVirtualDesktopManager implementation that can be subbed
// in for testing. We can't _actually_ create HWNDs as a part of the test
// and move them to different desktops. Instead, we'll have to create a stub
// impl that can fake a result for IsWindowOnCurrentVirtualDesktop.
void RemotingTests::MostRecentWindowSameDesktops()
{
Log::Comment(L"Make windows on the same desktop. Validate the contents of _mruPeasants are as expected.");
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
Log::Comment(L"Add a second peasant");
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(p2);
m0->AddPeasant(*p2);
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
guid1,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID());
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID());
}
void RemotingTests::MostRecentWindowDifferentDesktops()
{
Log::Comment(L"Make windows on different desktops. Validate the contents of _mruPeasants are as expected.");
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
Log::Comment(L"Add a second peasant");
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(p2);
m0->AddPeasant(*p2);
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
guid2,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID());
Log::Comment(L"Add a third peasant");
const auto peasant3PID = 45678u;
com_ptr<Remoting::implementation::Peasant> p3;
p3.attach(new Remoting::implementation::Peasant(peasant3PID));
VERIFY_IS_NOT_NULL(p3);
m0->AddPeasant(*p3);
{
Log::Comment(L"Activate the third peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
guid1,
winrt::clock().now() };
p3->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID());
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID());
}
void RemotingTests::MostRecentWindowMoveDesktops()
{
Log::Comment(L"Make windows on different desktops. Move one to another "
L"desktop. Validate the contents of _mruPeasants are as expected.");
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
Log::Comment(L"Add a second peasant");
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(p2);
m0->AddPeasant(*p2);
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
guid2,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID());
Log::Comment(L"Add a third peasant");
const auto peasant3PID = 45678u;
com_ptr<Remoting::implementation::Peasant> p3;
p3.attach(new Remoting::implementation::Peasant(peasant3PID));
VERIFY_IS_NOT_NULL(p3);
m0->AddPeasant(*p3);
{
Log::Comment(L"Activate the third peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
guid1,
winrt::clock().now() };
p3->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID());
{
Log::Comment(L"Activate the first peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid2,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID());
{
Log::Comment(L"Activate the third peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
guid2,
winrt::clock().now() };
p3->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID());
{
Log::Comment(L"Activate the second peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
guid1,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID());
}
void RemotingTests::GetMostRecentAnyDesktop()
{
Log::Comment(L"Make windows on different desktops. Confirm that "
L"getting the most recent of all windows works as expected.");
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
Log::Comment(L"Add a second peasant");
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(p2);
m0->AddPeasant(*p2);
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
guid2,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(p2->GetID(), m0->_getMostRecentPeasantID(false));
Log::Comment(L"Add a third peasant");
const auto peasant3PID = 45678u;
com_ptr<Remoting::implementation::Peasant> p3;
p3.attach(new Remoting::implementation::Peasant(peasant3PID));
VERIFY_IS_NOT_NULL(p3);
m0->AddPeasant(*p3);
{
Log::Comment(L"Activate the third peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
guid1,
winrt::clock().now() };
p3->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(p3->GetID(), m0->_getMostRecentPeasantID(false));
{
Log::Comment(L"Activate the first peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid2,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false));
}
void RemotingTests::MostRecentIsDead()
{
Log::Comment(L"Make two windows. Activate the first, then the second. "
L"Kill the second. The most recent should be the _first_ window.");
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
const auto monarch0PID = 12345u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper);
Log::Comment(L"Add a peasant");
const auto peasant1PID = 23456u;
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
VERIFY_IS_NOT_NULL(p1);
m0->AddPeasant(*p1);
Log::Comment(L"Add a second peasant");
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(p2);
m0->AddPeasant(*p2);
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
guid1,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID());
Log::Comment(L"Kill peasant 2");
RemotingTests::_killPeasant(m0, p2->GetID());
Log::Comment(L"Peasant 1 should be the new MRU peasant");
VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false));
Log::Comment(L"Peasant 2 should not be in the monarch at all anymore");
VERIFY_ARE_EQUAL(1u, m0->_peasants.size());
VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID());
}
void RemotingTests::GetPeasantsByName()
{
Log::Comment(L"Test that looking up a peasant by name finds the window we expect");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
VERIFY_ARE_EQUAL(L"one", p1->WindowName());
VERIFY_ARE_EQUAL(L"two", p2->WindowName());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(L"one", p1->WindowName());
VERIFY_ARE_EQUAL(L"two", p2->WindowName());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
Log::Comment(L"Rename p2");
p2->WindowName(L"foo");
VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"two"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"foo"));
}
void RemotingTests::AddNamedPeasantsToNewMonarch()
{
Log::Comment(L"Test that moving peasants to a new monarch persists their original names");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
const auto monarch3PID = 45678u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
com_ptr<Remoting::implementation::Monarch> m3;
m3.attach(new Remoting::implementation::Monarch(monarch3PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_IS_NOT_NULL(m3);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(L"one", p1->WindowName());
VERIFY_ARE_EQUAL(L"two", p2->WindowName());
Log::Comment(L"When the peasants go to a new monarch, make sure they have the same name");
m3->AddPeasant(*p1);
m3->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(L"one", p1->WindowName());
VERIFY_ARE_EQUAL(L"two", p2->WindowName());
VERIFY_ARE_EQUAL(p1->GetID(), m3->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m3->_lookupPeasantIdForName(L"two"));
}
void RemotingTests::LookupNamedPeasantWhenOthersDied()
{
Log::Comment(L"Test that looking for a peasant by name when a different"
L" peasant has died cleans up the corpses of any peasants "
L"we may have tripped over.");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch.");
RemotingTests::_killPeasant(m0, p1->GetID());
// By killing 1, then looking for "two", we happen to iterate over the
// corpse of 1 when looking for the peasant named "two". This causes us
// to remove 1 while looking for "two". Technically, we shouldn't be
// relying on any sort of ordering for an unordered_map iterator, but
// this one just so happens to work.
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
Log::Comment(L"Peasant 1 should have been pruned");
VERIFY_ARE_EQUAL(1u, m0->_peasants.size());
}
void RemotingTests::LookupNamedPeasantWhenItDied()
{
Log::Comment(L"Test that looking up a dead peasant by name returns 0, "
L"indicating there's no peasant with that name.");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch.");
RemotingTests::_killPeasant(m0, p1->GetID());
VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"one"));
Log::Comment(L"Peasant 1 should have been pruned");
VERIFY_ARE_EQUAL(1u, m0->_peasants.size());
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
}
void RemotingTests::GetMruPeasantAfterNameLookupForDeadPeasant()
{
// This test is trying to hit the catch in Monarch::_lookupPeasantIdForName.
//
// We need to:
// * add some peasants,
// * make one the mru, then make a named two the mru
// * then kill two
// * then try to get the mru peasant -> it should be one
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
guid1,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
Log::Comment(L"Kill peasant 2.");
RemotingTests::_killPeasant(m0, p2->GetID());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false));
VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(true));
}
void RemotingTests::ProposeCommandlineForNamedDeadWindow()
{
Log::Comment(L"Test proposing a commandline for a named window that's "
L"currently dead. This should result in a new window with "
L"the given name.");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
VERIFY_IS_NOT_NULL(m0);
m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowByNameHelper);
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) {
Log::Comment(L"Commandline dispatched to p1");
VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u);
VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1));
});
p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) {
Log::Comment(L"Commandline dispatched to p2");
VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u);
VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1));
});
p1->WindowName(L"one");
p2->WindowName(L"two");
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
std::vector<winrt::hstring> p1Args{ L"one", L"arg[1]" };
std::vector<winrt::hstring> p2Args{ L"two", L"this is for p2" };
{
Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value
VERIFY_ARE_EQUAL(L"", result.WindowName());
}
{
Log::Comment(L"Send a commandline to \"two\", which should be p2");
Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value
VERIFY_ARE_EQUAL(L"", result.WindowName());
}
Log::Comment(L"Kill peasant 2.");
RemotingTests::_killPeasant(m0, p2->GetID());
{
Log::Comment(L"Send a commandline to \"two\", who is now dead.");
Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } };
auto result = m0->ProposeCommandline(eventArgs);
VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow());
VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value
VERIFY_ARE_EQUAL(L"two", result.WindowName());
}
}
void RemotingTests::TestRenameWindowSuccessfully()
{
Log::Comment(L"Attempt to rename a window. This should succeed.");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
Remoting::RenameRequestArgs eventArgs{ L"foo" };
p1->RequestRename(eventArgs);
VERIFY_IS_TRUE(eventArgs.Succeeded());
VERIFY_ARE_EQUAL(L"foo", p1->WindowName());
VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"foo"));
}
void RemotingTests::TestRenameSameNameAsAnother()
{
Log::Comment(L"Try renaming a window to a name used by another peasant."
L" This should fail.");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
Remoting::RenameRequestArgs eventArgs{ L"two" };
p1->RequestRename(eventArgs);
VERIFY_IS_FALSE(eventArgs.Succeeded());
VERIFY_ARE_EQUAL(L"one", p1->WindowName());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
}
void RemotingTests::TestRenameSameNameAsADeadPeasant()
{
Log::Comment(L"We'll try renaming a window to the name of a window that"
L" has died. This should succeed, without crashing.");
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
p1->WindowName(L"one");
p2->WindowName(L"two");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
Remoting::RenameRequestArgs eventArgs{ L"two" };
p1->RequestRename(eventArgs);
VERIFY_IS_FALSE(eventArgs.Succeeded());
VERIFY_ARE_EQUAL(L"one", p1->WindowName());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one"));
VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two"));
RemotingTests::_killPeasant(m0, p2->GetID());
p1->RequestRename(eventArgs);
VERIFY_IS_TRUE(eventArgs.Succeeded());
VERIFY_ARE_EQUAL(L"two", p1->WindowName());
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"two"));
}
}