chromium/chrome/browser/webshare/win/fake_data_transfer_manager_interop_unittest.cc

// Copyright 2020 The Chromium Authors
// 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/functional/bind.h"
#include "base/functional/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:
  void SetUp() override {
    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) {
  // 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) {
  // 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) {
  // 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) {
  // 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) {
  // 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) {
  // 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) {
  // 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) {
  // 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