1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187

base / win / async_operation_unittest.cc [blame]

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/win/async_operation.h"

#include <utility>

#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace WRL = Microsoft::WRL;

using ABI::Windows::Foundation::IAsyncOperation;
using ABI::Windows::Foundation::IAsyncOperationCompletedHandler;

// In order to exercise the interface logic of AsyncOperation we define an empty
// dummy interface, its implementation, and the necessary boilerplate to hook it
// up with IAsyncOperation and IAsyncOperationCompletedHandler.
namespace {

// Chosen by fair `uuidgen` invocation. Also applies to the UUIDs below.
MIDL_INTERFACE("756358C7-8083-4D78-9D27-9278B76096d4")
IFooBar : public IInspectable{};

class FooBar
    : public WRL::RuntimeClass<
          WRL::RuntimeClassFlags<WRL::WinRt | WRL::InhibitRoOriginateError>,
          IFooBar> {};

}  // namespace

namespace ABI {
namespace Windows {
namespace Foundation {

// Provide the required template specializations to register
// IAsyncOperation<Foobar*> as an AggregateType. This is similar to how it is
// done for UWP classes.
template <>
struct DECLSPEC_UUID("124858e4-f97e-409c-86ae-418c4781144c")
    IAsyncOperation<FooBar*>
    : IAsyncOperation_impl<Internal::AggregateType<FooBar*, IFooBar*>> {
  static const wchar_t* z_get_rc_name_impl() {
    return L"Windows.Foundation.IAsyncOperation<FooBar>";
  }
};

template <>
struct DECLSPEC_UUID("9e49373c-200c-4715-abd7-4214ba669c81")
    IAsyncOperationCompletedHandler<FooBar*>
    : IAsyncOperationCompletedHandler_impl<
          Internal::AggregateType<FooBar*, IFooBar*>> {
  static const wchar_t* z_get_rc_name_impl() {
    return L"Windows.Foundation.AsyncOperationCompletedHandler<FooBar>";
  }
};

#ifdef NTDDI_WIN10_VB  // Windows 10.0.19041
// Specialization templates that used to be in windows.foundation.h, removed in
// the 10.0.19041.0 SDK, so placed here instead.
template <>
struct __declspec(uuid("968b9665-06ed-5774-8f53-8edeabd5f7b5"))
    IAsyncOperation<int> : IAsyncOperation_impl<int> {};

template <>
struct __declspec(uuid("d60cae9d-88cb-59f1-8576-3fba44796be8"))
    IAsyncOperationCompletedHandler<int>
    : IAsyncOperationCompletedHandler_impl<int> {};
#endif

}  // namespace Foundation
}  // namespace Windows
}  // namespace ABI

namespace base {
namespace win {

namespace {

// Utility method to add a completion callback to |async_op|. |*called_cb| will
// be set to true once the callback is invoked.
template <typename T>
void PutCallback(AsyncOperation<T>* async_op, bool* called_cb) {
  async_op->put_Completed(
      WRL::Callback<IAsyncOperationCompletedHandler<T>>(
          [=](IAsyncOperation<T>* iasync_op, AsyncStatus status) {
            EXPECT_EQ(async_op, iasync_op);
            *called_cb = true;
            return S_OK;
          })
          .Get());
}

}  // namespace

TEST(AsyncOperationTest, TestInt) {
  bool called_cb = false;

  auto int_op = WRL::Make<AsyncOperation<int>>();
  PutCallback(int_op.Get(), &called_cb);

  int results;
  EXPECT_TRUE(FAILED(int_op->GetResults(&results)));
  EXPECT_FALSE(called_cb);
  int_op->callback().Run(123);

  EXPECT_TRUE(called_cb);
  EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results)));
  EXPECT_EQ(123, results);

  // GetResults should be idempotent.
  EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results)));
  EXPECT_EQ(123, results);
}

TEST(AsyncOperationTest, TestBool) {
  bool called_cb = false;

  auto bool_op = WRL::Make<AsyncOperation<bool>>();
  PutCallback(bool_op.Get(), &called_cb);

  // AsyncOperation<bool> is an aggregate of bool and boolean, and requires a
  // pointer to the latter to get the results.
  boolean results;
  EXPECT_TRUE(FAILED(bool_op->GetResults(&results)));
  EXPECT_FALSE(called_cb);
  bool_op->callback().Run(true);

  EXPECT_TRUE(called_cb);
  EXPECT_TRUE(SUCCEEDED(bool_op->GetResults(&results)));
  EXPECT_TRUE(results);
}

TEST(AsyncOperationTest, TestInterface) {
  bool called_cb = false;

  auto foobar_op = WRL::Make<AsyncOperation<FooBar*>>();
  PutCallback(foobar_op.Get(), &called_cb);

  // AsyncOperation<FooBar*> is an aggregate of FooBar* and IFooBar*.
  WRL::ComPtr<IFooBar> results;
  EXPECT_TRUE(FAILED(foobar_op->GetResults(&results)));
  EXPECT_FALSE(called_cb);

  auto foobar = WRL::Make<FooBar>();
  IFooBar* foobar_ptr = foobar.Get();
  foobar_op->callback().Run(std::move(foobar));

  EXPECT_TRUE(called_cb);
  EXPECT_TRUE(SUCCEEDED(foobar_op->GetResults(&results)));
  EXPECT_EQ(foobar_ptr, results.Get());
}

TEST(AsyncOperationTest, TestIdempotence) {
  bool called_cb = false;

  auto int_op = WRL::Make<AsyncOperation<int>>();
  PutCallback(int_op.Get(), &called_cb);

  int results;
  EXPECT_TRUE(FAILED(int_op->GetResults(&results)));
  EXPECT_FALSE(called_cb);
  // Calling GetResults twice shouldn't change the result.
  EXPECT_TRUE(FAILED(int_op->GetResults(&results)));
  EXPECT_FALSE(called_cb);

  int_op->callback().Run(42);

  EXPECT_TRUE(called_cb);
  EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results)));
  EXPECT_EQ(42, results);
  // Calling GetResults twice shouldn't change the result.
  EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results)));
  EXPECT_EQ(42, results);
}

TEST(AsyncOperationTest, DoubleCallbackFails) {
  auto int_op = WRL::Make<AsyncOperation<int>>();
  auto cb = int_op->callback();

  // Obtaining another callback should result in a DCHECK failure.
  EXPECT_DCHECK_DEATH(int_op->callback());
}

}  // namespace win
}  // namespace base