Commit 9f1e8b17 authored by Hoch Hochkeppel's avatar Hoch Hochkeppel Committed by Commit Bot

WebShare: Add fake DataTransferManagerInterop for testing

Adding a class to represent the Windows DataTransferManagerInterop APIs
within tests, as well as basic unit tests to validate the test class
behaviors.

Bug: 1035527
Change-Id: I926078f3b54b350c622adb40fc84fe3614fa89d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2397315
Commit-Queue: Hoch Hochkeppel <mhochk@microsoft.com>
Reviewed-by: default avatarEric Willigers <ericwilligers@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805564}
parent 60844759
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/webshare/win/fake_data_transfer_manager_interop.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/task/post_task.h"
#include "chrome/browser/webshare/win/fake_data_transfer_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace webshare {
FakeDataTransferManagerInterop::FakeDataTransferManagerInterop() = default;
FakeDataTransferManagerInterop::~FakeDataTransferManagerInterop() {
// Though it is legal for consuming code to hold on to a DataTransferManager
// after releasing all references to the DataTransferManagerInterop, in a
// test environment the DataTransferManagerInterop is only expected to be
// torn down as part of the test cleanup, at which point the
// DataTransferManager references should also have been cleaned up.
for (auto& manager : managers_)
EXPECT_EQ(0u, manager.second.Reset());
}
IFACEMETHODIMP
FakeDataTransferManagerInterop::GetForWindow(HWND app_window,
REFIID riid,
void** data_transfer_manager) {
auto it = managers_.find(app_window);
if (it != managers_.end()) {
*data_transfer_manager = it->second.Get();
it->second->AddRef();
} else {
auto mock = Microsoft::WRL::Make<FakeDataTransferManager>();
managers_.insert({app_window, mock});
*data_transfer_manager = mock.Get();
mock->AddRef();
}
return S_OK;
}
IFACEMETHODIMP FakeDataTransferManagerInterop::ShowShareUIForWindow(
HWND app_window) {
auto it = managers_.find(app_window);
if (it == managers_.end()) {
ADD_FAILURE() << "ShowShareUIForWindow called for HWND with no "
"DataTransferManager (or DataRequested handler) defined.";
return E_FAIL;
}
switch (show_share_ui_for_window_behavior_) {
case ShowShareUIForWindowBehavior::FailImmediately:
return E_FAIL;
case ShowShareUIForWindowBehavior::InvokeEventSynchronously:
std::move(it->second->GetDataRequestedInvoker()).Run();
return S_OK;
case ShowShareUIForWindowBehavior::InvokeEventSynchronouslyAndReturnFailure:
std::move(it->second->GetDataRequestedInvoker()).Run();
return E_FAIL;
case ShowShareUIForWindowBehavior::ScheduleEvent:
EXPECT_TRUE(base::PostTask(FROM_HERE, {content::BrowserThread::UI},
it->second->GetDataRequestedInvoker()));
return S_OK;
case ShowShareUIForWindowBehavior::SucceedWithoutAction:
return S_OK;
}
NOTREACHED();
return E_UNEXPECTED;
}
base::OnceClosure FakeDataTransferManagerInterop::GetDataRequestedInvoker(
HWND app_window) {
auto it = managers_.find(app_window);
if (it == managers_.end()) {
ADD_FAILURE() << "GetDataRequestedInvoker called when no DataRequested "
"event handler has been registered";
return base::DoNothing();
}
return it->second->GetDataRequestedInvoker();
}
bool FakeDataTransferManagerInterop::HasDataRequestedListener(HWND app_window) {
auto it = managers_.find(app_window);
if (it == managers_.end())
return false;
return it->second->HasDataRequestedListener();
}
void FakeDataTransferManagerInterop::SetShowShareUIForWindowBehavior(
ShowShareUIForWindowBehavior behavior) {
show_share_ui_for_window_behavior_ = behavior;
}
} // namespace webshare
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEBSHARE_WIN_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_
#define CHROME_BROWSER_WEBSHARE_WIN_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_
#include <shlobj.h>
#include <wrl/implements.h>
#include <map>
#include "base/callback_forward.h"
namespace webshare {
class FakeDataTransferManager;
// Provides an implementation of IDataTransferManagerInterop for use in GTests.
//
// Like the Windows implementation, this class cloaks its implementation of
// IDataTransferManagerInterop to closely match casting behaviors.
class FakeDataTransferManagerInterop
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<
Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>,
Microsoft::WRL::CloakedIid<IDataTransferManagerInterop>> {
public:
// Behavior options for the ShowShareUIForWindow API
enum ShowShareUIForWindowBehavior {
// Returns a failed value without invoking/scheduling the DataRequested
// event/handler.
FailImmediately,
// Invokes the DataRequested event/handler synchronously as part of the
// original invoking call. This matches the behavior exposed by Windows
// under various edge-case scenarios.
InvokeEventSynchronously,
// Invokes the DataRequested event/handler synchronously as part of the
// original invoking call, but then returns a failure result.
InvokeEventSynchronouslyAndReturnFailure,
// Schedules the invocation of the DataRequested event/handler to happen
// automatically, outside of the original invoking call. This matches the
// the most common behavior exposed by Windows.
ScheduleEvent,
// Returns a success value without invoking/scheduling the DataRequested
// event/handler. To later invoke the DataRequested event/handler, see
// |GetDataRequestedInvoker|.
SucceedWithoutAction
};
FakeDataTransferManagerInterop();
FakeDataTransferManagerInterop(const FakeDataTransferManagerInterop&) =
delete;
FakeDataTransferManagerInterop& operator=(
const FakeDataTransferManagerInterop&) = delete;
~FakeDataTransferManagerInterop() override;
// IDataTransferManagerInterop:
IFACEMETHODIMP GetForWindow(HWND app_window,
REFIID riid,
void** data_transfer_manager) override;
IFACEMETHODIMP ShowShareUIForWindow(HWND app_window) override;
// Returns a callback that captures a reference to the current DataRequested
// event handler and, when invoked, triggers that handler.
//
// If the registered handler changes after this method is called the callback
// will still trigger the previous event handler, not a newly registered one.
base::OnceClosure GetDataRequestedInvoker(HWND app_window);
// Checks if there are any listeners registered for the DataRequested event
// on the given |app_window|.
bool HasDataRequestedListener(HWND app_window);
void SetShowShareUIForWindowBehavior(ShowShareUIForWindowBehavior behavior);
private:
ShowShareUIForWindowBehavior show_share_ui_for_window_behavior_ =
ShowShareUIForWindowBehavior::ScheduleEvent;
std::map<HWND, Microsoft::WRL::ComPtr<FakeDataTransferManager>> managers_;
};
} // namespace webshare
#endif // CHROME_BROWSER_WEBSHARE_WIN_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/webshare/win/fake_data_transfer_manager_interop.h"
#include <windows.applicationmodel.datatransfer.h>
#include <wrl/event.h>
#include <wrl/implements.h>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/win/core_winrt_util.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest-spi.h"
#include "testing/gtest/include/gtest/gtest.h"
using ABI::Windows::ApplicationModel::DataTransfer::DataRequestedEventArgs;
using ABI::Windows::ApplicationModel::DataTransfer::DataTransferManager;
using ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs;
using ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager;
using ABI::Windows::Foundation::ITypedEventHandler;
using Microsoft::WRL::Callback;
using Microsoft::WRL::ComPtr;
namespace webshare {
using ShowShareUIForWindowBehavior =
FakeDataTransferManagerInterop::ShowShareUIForWindowBehavior;
// Provides a DataRequested callback and records the number of times it is
// invoked
class DataRequestedTestCallback {
public:
DataRequestedTestCallback() {
auto weak_ptr = weak_factory_.GetWeakPtr();
callback_ = Callback<
ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs*>>(
[weak_ptr](IDataTransferManager* data_transfer_manager,
IDataRequestedEventArgs* event_args) -> HRESULT {
if (weak_ptr.get())
weak_ptr->invocation_count_++;
return S_OK;
});
}
int invocation_count_ = 0;
ComPtr<ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs*>>
callback_;
private:
base::WeakPtrFactory<DataRequestedTestCallback> weak_factory_{this};
};
class FakeDataTransferManagerInteropTest : public ::testing::Test {
protected:
bool IsSupportedEnvironment() {
return base::win::ResolveCoreWinRTDelayload() &&
base::win::ScopedHString::ResolveCoreWinRTStringDelayload();
}
void SetUp() override {
if (!IsSupportedEnvironment())
return;
fake_data_transfer_manager_interop_ =
Microsoft::WRL::Make<FakeDataTransferManagerInterop>();
}
ComPtr<FakeDataTransferManagerInterop> fake_data_transfer_manager_interop_;
const HWND hwnd_1_ = reinterpret_cast<HWND>(1);
const HWND hwnd_2_ = reinterpret_cast<HWND>(2);
content::BrowserTaskEnvironment task_environment;
};
TEST_F(FakeDataTransferManagerInteropTest, GetDataRequestedInvoker) {
if (!IsSupportedEnvironment())
return;
// Verify failure when called without a listener in place
base::OnceClosure invoker;
EXPECT_NONFATAL_FAILURE(
invoker =
fake_data_transfer_manager_interop_->GetDataRequestedInvoker(hwnd_1_),
"GetDataRequestedInvoker");
// Set up a listener for |hwnd_1_|
ComPtr<IDataTransferManager> data_transfer_manager;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_1_, IID_PPV_ARGS(&data_transfer_manager)));
EventRegistrationToken token;
DataRequestedTestCallback test_callback;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->add_DataRequested(
test_callback.callback_.Get(), &token));
// Verify failure when called with a different HWND
EXPECT_NONFATAL_FAILURE(
invoker =
fake_data_transfer_manager_interop_->GetDataRequestedInvoker(hwnd_2_),
"GetDataRequestedInvoker");
// Verify success when called with a matching HWND
EXPECT_NO_FATAL_FAILURE(
invoker = fake_data_transfer_manager_interop_->GetDataRequestedInvoker(
hwnd_1_));
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->remove_DataRequested(token));
}
TEST_F(FakeDataTransferManagerInteropTest, HasDataRequestedListener) {
if (!IsSupportedEnvironment())
return;
// Verify values before any listeners are attached
ASSERT_FALSE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_1_));
ASSERT_FALSE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_2_));
// Add a listener for |hwnd_1_| and verify values
ComPtr<IDataTransferManager> data_transfer_manager_1;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_1_, IID_PPV_ARGS(&data_transfer_manager_1)));
EventRegistrationToken token_1;
DataRequestedTestCallback test_callback;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager_1->add_DataRequested(
test_callback.callback_.Get(), &token_1));
ASSERT_TRUE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_1_));
ASSERT_FALSE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_2_));
// Add a listener for |hwnd_2_| and verify values
ComPtr<IDataTransferManager> data_transfer_manager_2;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_2_, IID_PPV_ARGS(&data_transfer_manager_2)));
EventRegistrationToken token_2;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager_2->add_DataRequested(
test_callback.callback_.Get(), &token_2));
ASSERT_TRUE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_1_));
ASSERT_TRUE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_2_));
// Add an additional listener for |hwnd_2_| and verify values
EventRegistrationToken token_3;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager_2->add_DataRequested(
test_callback.callback_.Get(), &token_3));
ASSERT_TRUE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_1_));
ASSERT_TRUE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_2_));
// Remove the original listener for |hwnd_1_| and verify values
ASSERT_HRESULT_SUCCEEDED(
data_transfer_manager_1->remove_DataRequested(token_1));
ASSERT_FALSE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_1_));
ASSERT_TRUE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_2_));
// Remove the original listener for |hwnd_2_| and verify values
ASSERT_HRESULT_SUCCEEDED(
data_transfer_manager_2->remove_DataRequested(token_2));
ASSERT_FALSE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_1_));
ASSERT_TRUE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_2_));
// Remove the second listener for |hwnd_2_| and verify values
ASSERT_HRESULT_SUCCEEDED(
data_transfer_manager_2->remove_DataRequested(token_3));
ASSERT_FALSE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_1_));
ASSERT_FALSE(
fake_data_transfer_manager_interop_->HasDataRequestedListener(hwnd_2_));
}
TEST_F(FakeDataTransferManagerInteropTest,
ShowShareUIForWindow_FailImmediately) {
if (!IsSupportedEnvironment())
return;
// Set up a listener for |hwnd_1_|
ComPtr<IDataTransferManager> data_transfer_manager;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_1_, IID_PPV_ARGS(&data_transfer_manager)));
EventRegistrationToken token;
DataRequestedTestCallback test_callback;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->add_DataRequested(
test_callback.callback_.Get(), &token));
// Validate that ShowShareUIForWindow fails without invoking the DataRequested
// event
ASSERT_NO_FATAL_FAILURE(
fake_data_transfer_manager_interop_->SetShowShareUIForWindowBehavior(
ShowShareUIForWindowBehavior::FailImmediately));
base::RunLoop run_loop;
ASSERT_HRESULT_FAILED(
fake_data_transfer_manager_interop_->ShowShareUIForWindow(hwnd_1_));
run_loop.RunUntilIdle();
ASSERT_EQ(test_callback.invocation_count_, 0);
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->remove_DataRequested(token));
}
TEST_F(FakeDataTransferManagerInteropTest,
ShowShareUIForWindow_InvokeEventSynchronously) {
if (!IsSupportedEnvironment())
return;
// Set up a listener for |hwnd_1_|
ComPtr<IDataTransferManager> data_transfer_manager;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_1_, IID_PPV_ARGS(&data_transfer_manager)));
EventRegistrationToken token;
DataRequestedTestCallback test_callback;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->add_DataRequested(
test_callback.callback_.Get(), &token));
// Validate that ShowShareUIForWindow succeeds and invokes the DataRequested
// event in a synchronous fashion
ASSERT_NO_FATAL_FAILURE(
fake_data_transfer_manager_interop_->SetShowShareUIForWindowBehavior(
ShowShareUIForWindowBehavior::InvokeEventSynchronously));
ASSERT_HRESULT_SUCCEEDED(
fake_data_transfer_manager_interop_->ShowShareUIForWindow(hwnd_1_));
ASSERT_EQ(test_callback.invocation_count_, 1);
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->remove_DataRequested(token));
}
TEST_F(FakeDataTransferManagerInteropTest,
ShowShareUIForWindow_InvokeEventSynchronouslyAndReturnFailure) {
if (!IsSupportedEnvironment())
return;
// Set up a listener for |hwnd_1_|
ComPtr<IDataTransferManager> data_transfer_manager;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_1_, IID_PPV_ARGS(&data_transfer_manager)));
EventRegistrationToken token;
DataRequestedTestCallback test_callback;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->add_DataRequested(
test_callback.callback_.Get(), &token));
// Validate that ShowShareUIForWindow invokes the DataRequested
// event in a synchronous fashion, but still fails
ASSERT_NO_FATAL_FAILURE(
fake_data_transfer_manager_interop_->SetShowShareUIForWindowBehavior(
ShowShareUIForWindowBehavior::
InvokeEventSynchronouslyAndReturnFailure));
ASSERT_HRESULT_FAILED(
fake_data_transfer_manager_interop_->ShowShareUIForWindow(hwnd_1_));
ASSERT_EQ(test_callback.invocation_count_, 1);
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->remove_DataRequested(token));
}
TEST_F(FakeDataTransferManagerInteropTest, ShowShareUIForWindow_ScheduleEvent) {
if (!IsSupportedEnvironment())
return;
// Set up a listener for |hwnd_1_|
ComPtr<IDataTransferManager> data_transfer_manager;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_1_, IID_PPV_ARGS(&data_transfer_manager)));
EventRegistrationToken token;
DataRequestedTestCallback test_callback;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->add_DataRequested(
test_callback.callback_.Get(), &token));
// Validate that ShowShareUIForWindow succeeds and invokes the DataRequested
// event in an async fashion
ASSERT_NO_FATAL_FAILURE(
fake_data_transfer_manager_interop_->SetShowShareUIForWindowBehavior(
ShowShareUIForWindowBehavior::ScheduleEvent));
base::RunLoop run_loop;
ASSERT_HRESULT_SUCCEEDED(
fake_data_transfer_manager_interop_->ShowShareUIForWindow(hwnd_1_));
ASSERT_EQ(test_callback.invocation_count_, 0);
run_loop.RunUntilIdle();
ASSERT_EQ(test_callback.invocation_count_, 1);
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->remove_DataRequested(token));
}
TEST_F(FakeDataTransferManagerInteropTest,
ShowShareUIForWindow_SucceedWithoutAction) {
if (!IsSupportedEnvironment())
return;
// Set up a listener for |hwnd_1_|
ComPtr<IDataTransferManager> data_transfer_manager;
ASSERT_HRESULT_SUCCEEDED(fake_data_transfer_manager_interop_->GetForWindow(
hwnd_1_, IID_PPV_ARGS(&data_transfer_manager)));
EventRegistrationToken token;
DataRequestedTestCallback test_callback;
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->add_DataRequested(
test_callback.callback_.Get(), &token));
// Validate that ShowShareUIForWindow succeeds, but does not invoke the
// DataRequested event
ASSERT_NO_FATAL_FAILURE(
fake_data_transfer_manager_interop_->SetShowShareUIForWindowBehavior(
ShowShareUIForWindowBehavior::SucceedWithoutAction));
base::RunLoop run_loop;
ASSERT_HRESULT_SUCCEEDED(
fake_data_transfer_manager_interop_->ShowShareUIForWindow(hwnd_1_));
run_loop.RunUntilIdle();
ASSERT_EQ(test_callback.invocation_count_, 0);
ASSERT_HRESULT_SUCCEEDED(data_transfer_manager->remove_DataRequested(token));
}
TEST_F(FakeDataTransferManagerInteropTest,
ShowShareUIForWindow_WithoutListener) {
if (!IsSupportedEnvironment())
return;
// Validate that ShowShareUIForWindow fails and causes a test failure when
// called without a listener
ASSERT_NO_FATAL_FAILURE(
fake_data_transfer_manager_interop_->SetShowShareUIForWindowBehavior(
ShowShareUIForWindowBehavior::SucceedWithoutAction));
HRESULT hr;
EXPECT_NONFATAL_FAILURE(
hr = fake_data_transfer_manager_interop_->ShowShareUIForWindow(hwnd_1_),
"ShowShareUIForWindow");
ASSERT_HRESULT_FAILED(hr);
}
} // namespace webshare
......@@ -299,6 +299,8 @@ static_library("test_support") {
sources += [
"../browser/webshare/win/fake_data_transfer_manager.cc",
"../browser/webshare/win/fake_data_transfer_manager.h",
"../browser/webshare/win/fake_data_transfer_manager_interop.cc",
"../browser/webshare/win/fake_data_transfer_manager_interop.h",
"//chrome/app/chrome_crash_reporter_client_win.cc",
]
public_deps += [
......@@ -5552,6 +5554,7 @@ test("unit_tests") {
"../browser/notifications/win/notification_image_retainer_unittest.cc",
"../browser/notifications/win/notification_template_builder_unittest.cc",
"../browser/ui/views/uninstall_view_unittest.cc",
"../browser/webshare/win/fake_data_transfer_manager_interop_unittest.cc",
"../browser/webshare/win/fake_data_transfer_manager_unittest.cc",
]
deps += [
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment