terminal/src/cascadia/LocalTests_TerminalApp/CppWinrtTailored.h
Mike Griese 9145903e11 Fix the TabTests! (#3833)
## Summary of the Pull Request

Fix the `TabTests`, and enable testing of types with XAML content. The `TabTests` were written many, many moons ago. they were intended to be our tests of XAML-like content within the Terminal app, so we could have unittests of Tabs, Panes, etc. Between their initial authoring and the day they were checked in, we had a bunch of build changes come in and break them irreperably. 

We've gotten them fixed now with _one weird trick_ <sup>doctors hate me</sup>. As long as there isn't an `App.xbf` in the test's output directory, then the tests will deploy just fine.

We also needed a bit of magic, cribbed straight from TAEF, to enable running test code synchronously on the UI thread. Hence, `CppwinrtTailored.h`.

## References

## PR Checklist
* [x] Closes #2472
* [x] I work here
* [x] Tests added/passed - you better believe it
* [n/a] Requires documentation to be updated

## Validation Steps Performed

![image](https://user-images.githubusercontent.com/18356694/70185192-ef1d0b00-16ae-11ea-8799-b77061e3cdb0.png)
2019-12-06 20:45:08 +00:00

104 lines
3 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// Module Name:
// - CppWinrtTailored.h
//
// Abstract:
// - This is effectively a copy-paste of TAEF's Tailored.h, ported to cppwinrt.
// Unfortunately, TAEF only provides a CX or pure c++ version of the
// RunOnUIThread function, which doesn't compile for us. This version has been
// ported to cppwinrt for our uses.
// - RunOnUIThread is a helper function for running test code on the UI thread
//
// Author:
// - Mike Griese (zadjii-msft) 04-Dec-2019
#pragma once
#include "pch.h"
extern "C" __declspec(dllimport) HRESULT __stdcall Thread_Wait_For(HANDLE handle, unsigned long milliseconds);
namespace details
{
class Event
{
public:
Event() :
m_handle(::CreateEvent(nullptr, FALSE, FALSE, nullptr))
{
}
~Event()
{
if (IsValid())
{
::CloseHandle(m_handle);
}
}
void Set()
{
::SetEvent(m_handle);
}
HRESULT Wait()
{
return Thread_Wait_For(m_handle, INFINITE);
}
bool IsValid()
{
return m_handle != nullptr;
}
HANDLE m_handle;
};
};
// Function Description:
// - This is a helper function for running a bit of test code on the UI thread.
// It will synchonously dispatch the provided function to the UI thread, and
// wait for that function to complete, before returning to the caller. Callers
// should make sure to VERIFY_SUCCEEDED the result of this function, to ensure
// the code executed successfully.
// Arguments:
// - function: A pointer to some function to run. This should accept no params,
// and any return value will be ignored.
// Return Value:
// - S_OK _after_ we successfully execute the provided function, or another
// HRESULT indicating failure.
template<typename TFunction>
HRESULT RunOnUIThread(const TFunction& function)
{
auto m = winrt::Windows::ApplicationModel::Core::CoreApplication::MainView();
auto cw = m.CoreWindow();
auto d = cw.Dispatcher();
// Create an event so we can wait for the callback to complete
details::Event completedEvent;
if (!completedEvent.IsValid())
{
return HRESULT_FROM_WIN32(::GetLastError());
}
HRESULT invokeResult = E_FAIL;
auto asyncAction = d.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal,
[&invokeResult, &function]() {
invokeResult = WEX::SafeInvoke([&]() -> bool { function(); return true; });
});
asyncAction.Completed([&completedEvent](auto&&, auto&&) {
completedEvent.Set();
return S_OK;
});
// Wait for the callback to complete
HRESULT hr = completedEvent.Wait();
if (FAILED(hr))
{
return hr;
}
return invokeResult;
}