tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

runnable_utils_unittest.cpp (9255B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // Original author: ekr@rtfm.com
      8 #include "runnable_utils.h"
      9 
     10 #include <iostream>
     11 
     12 #include "mozilla/RefPtr.h"
     13 #include "mozilla/UniquePtr.h"
     14 #include "nsCOMPtr.h"
     15 #include "nsNetCID.h"
     16 #include "nsServiceManagerUtils.h"
     17 #include "nsThreadUtils.h"
     18 
     19 #define GTEST_HAS_RTTI 0
     20 #include "gtest/gtest.h"
     21 #include "gtest_utils.h"
     22 
     23 using namespace mozilla;
     24 
     25 namespace {
     26 
     27 // Helper used to make sure args are properly copied and/or moved.
     28 struct CtorDtorState {
     29  enum class State { Empty, Copy, Explicit, Move, Moved };
     30 
     31  static const char* ToStr(const State& state) {
     32    switch (state) {
     33      case State::Empty:
     34        return "empty";
     35      case State::Copy:
     36        return "copy";
     37      case State::Explicit:
     38        return "explicit";
     39      case State::Move:
     40        return "move";
     41      case State::Moved:
     42        return "moved";
     43      default:
     44        return "unknown";
     45    }
     46  }
     47 
     48  void DumpState() const { std::cerr << ToStr(state_) << std::endl; }
     49 
     50  CtorDtorState() { DumpState(); }
     51 
     52  explicit CtorDtorState(int* destroyed)
     53      : dtor_count_(destroyed), state_(State::Explicit) {
     54    DumpState();
     55  }
     56 
     57  CtorDtorState(const CtorDtorState& other)
     58      : dtor_count_(other.dtor_count_), state_(State::Copy) {
     59    DumpState();
     60  }
     61 
     62  // Clear the other's dtor counter so it's not counted if moved.
     63  CtorDtorState(CtorDtorState&& other)
     64      : dtor_count_(std::exchange(other.dtor_count_, nullptr)),
     65        state_(State::Move) {
     66    other.state_ = State::Moved;
     67    DumpState();
     68  }
     69 
     70  ~CtorDtorState() {
     71    const char* const state = ToStr(state_);
     72    std::cerr << "Destructor called with end state: " << state << std::endl;
     73 
     74    if (dtor_count_) {
     75      ++*dtor_count_;
     76    }
     77  }
     78 
     79  int* dtor_count_ = nullptr;
     80  State state_ = State::Empty;
     81 };
     82 
     83 class Destructor {
     84 private:
     85  ~Destructor() {
     86    std::cerr << "Destructor called" << std::endl;
     87    *destroyed_ = true;
     88  }
     89 
     90 public:
     91  explicit Destructor(bool* destroyed) : destroyed_(destroyed) {}
     92 
     93  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Destructor)
     94 
     95 private:
     96  bool* destroyed_;
     97 };
     98 
     99 class TargetClass {
    100 public:
    101  explicit TargetClass(int* ran) : ran_(ran) {}
    102 
    103  void m1(int x) {
    104    std::cerr << __FUNCTION__ << " " << x << std::endl;
    105    *ran_ = 1;
    106  }
    107 
    108  void m2(int x, int y) {
    109    std::cerr << __FUNCTION__ << " " << x << " " << y << std::endl;
    110    *ran_ = 2;
    111  }
    112 
    113  void m1set(bool* z) {
    114    std::cerr << __FUNCTION__ << std::endl;
    115    *z = true;
    116  }
    117  int return_int(int x) {
    118    std::cerr << __FUNCTION__ << std::endl;
    119    return x;
    120  }
    121 
    122  void destructor_target_ref(RefPtr<Destructor> destructor) {}
    123 
    124  int* ran_;
    125 };
    126 
    127 class RunnableArgsTest : public MtransportTest {
    128 public:
    129  RunnableArgsTest() : ran_(0), cl_(&ran_) {}
    130 
    131  void Test1Arg() {
    132    Runnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1);
    133    r->Run();
    134    ASSERT_EQ(1, ran_);
    135  }
    136 
    137  void Test2Args() {
    138    Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2);
    139    r->Run();
    140    ASSERT_EQ(2, ran_);
    141  }
    142 
    143 private:
    144  int ran_;
    145  TargetClass cl_;
    146 };
    147 
    148 class DispatchTest : public MtransportTest {
    149 public:
    150  DispatchTest() : ran_(0), cl_(&ran_) {}
    151 
    152  void SetUp() {
    153    MtransportTest::SetUp();
    154 
    155    nsresult rv;
    156    target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
    157    ASSERT_TRUE(NS_SUCCEEDED(rv));
    158  }
    159 
    160  void Test1Arg() {
    161    Runnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1);
    162    NS_DispatchAndSpinEventLoopUntilComplete("DispatchTest::Test1Arg"_ns,
    163                                             target_, do_AddRef(r));
    164    ASSERT_EQ(1, ran_);
    165  }
    166 
    167  void Test2Args() {
    168    Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2);
    169    NS_DispatchAndSpinEventLoopUntilComplete("DispatchTest::Test2Args"_ns,
    170                                             target_, do_AddRef(r));
    171    ASSERT_EQ(2, ran_);
    172  }
    173 
    174  void Test1Set() {
    175    bool x = false;
    176    NS_DispatchAndSpinEventLoopUntilComplete(
    177        "DispatchTest::Test1Set"_ns, target_,
    178        do_AddRef(WrapRunnable(&cl_, &TargetClass::m1set, &x)));
    179    ASSERT_TRUE(x);
    180  }
    181 
    182  void TestRet() {
    183    int z;
    184    int x = 10;
    185 
    186    NS_DispatchAndSpinEventLoopUntilComplete(
    187        "DispatchTest::TestRet"_ns, target_,
    188        do_AddRef(WrapRunnableRet(&z, &cl_, &TargetClass::return_int, x)));
    189    ASSERT_EQ(10, z);
    190  }
    191 
    192 protected:
    193  int ran_;
    194  TargetClass cl_;
    195  nsCOMPtr<nsIEventTarget> target_;
    196 };
    197 
    198 TEST_F(RunnableArgsTest, OneArgument) { Test1Arg(); }
    199 
    200 TEST_F(RunnableArgsTest, TwoArguments) { Test2Args(); }
    201 
    202 TEST_F(DispatchTest, OneArgument) { Test1Arg(); }
    203 
    204 TEST_F(DispatchTest, TwoArguments) { Test2Args(); }
    205 
    206 TEST_F(DispatchTest, Test1Set) { Test1Set(); }
    207 
    208 TEST_F(DispatchTest, TestRet) { TestRet(); }
    209 
    210 void SetNonMethod(TargetClass* cl, int x) { cl->m1(x); }
    211 
    212 int SetNonMethodRet(TargetClass* cl, int x) {
    213  cl->m1(x);
    214 
    215  return x;
    216 }
    217 
    218 TEST_F(DispatchTest, TestNonMethod) {
    219  test_utils_->SyncDispatchToSTS(WrapRunnableNM(SetNonMethod, &cl_, 10));
    220 
    221  ASSERT_EQ(1, ran_);
    222 }
    223 
    224 TEST_F(DispatchTest, TestNonMethodRet) {
    225  int z;
    226 
    227  test_utils_->SyncDispatchToSTS(
    228      WrapRunnableNMRet(&z, SetNonMethodRet, &cl_, 10));
    229 
    230  ASSERT_EQ(1, ran_);
    231  ASSERT_EQ(10, z);
    232 }
    233 
    234 TEST_F(DispatchTest, TestDestructorRef) {
    235  bool destroyed = false;
    236  {
    237    RefPtr<Destructor> destructor = new Destructor(&destroyed);
    238    NS_DispatchAndSpinEventLoopUntilComplete(
    239        "DispatchTest::TestDestructorRef"_ns, target_,
    240        do_AddRef(WrapRunnable(&cl_, &TargetClass::destructor_target_ref,
    241                               destructor)));
    242    ASSERT_FALSE(destroyed);
    243  }
    244  ASSERT_TRUE(destroyed);
    245 
    246  // Now try with a move.
    247  destroyed = false;
    248  {
    249    RefPtr<Destructor> destructor = new Destructor(&destroyed);
    250    NS_DispatchAndSpinEventLoopUntilComplete(
    251        "DispatchTest::TestDestructorRef"_ns, target_,
    252        do_AddRef(WrapRunnable(&cl_, &TargetClass::destructor_target_ref,
    253                               std::move(destructor))));
    254    ASSERT_TRUE(destroyed);
    255  }
    256 }
    257 
    258 TEST_F(DispatchTest, TestMove) {
    259  int destroyed = 0;
    260  {
    261    CtorDtorState state(&destroyed);
    262 
    263    // Dispatch with:
    264    //   - moved arg
    265    //   - by-val capture in function should consume a move
    266    //   - expect destruction in the function scope
    267    NS_DispatchAndSpinEventLoopUntilComplete(
    268        "DispatchTest::TestMove"_ns, target_,
    269        do_AddRef(WrapRunnableNM([](CtorDtorState s) {}, std::move(state))));
    270    ASSERT_EQ(1, destroyed);
    271  }
    272  // Still shouldn't count when we go out of scope as it was moved.
    273  ASSERT_EQ(1, destroyed);
    274 
    275  {
    276    CtorDtorState state(&destroyed);
    277 
    278    // Dispatch with:
    279    //   - copied arg
    280    //   - by-val capture in function should consume a move
    281    //   - expect destruction in the function scope and call scope
    282    NS_DispatchAndSpinEventLoopUntilComplete(
    283        "DispatchTest::TestMove"_ns, target_,
    284        do_AddRef(WrapRunnableNM([](CtorDtorState s) {}, state)));
    285    ASSERT_EQ(2, destroyed);
    286  }
    287  // Original state should be destroyed
    288  ASSERT_EQ(3, destroyed);
    289 
    290  {
    291    CtorDtorState state(&destroyed);
    292 
    293    // Dispatch with:
    294    //   - moved arg
    295    //   - by-ref in function should accept a moved arg
    296    //   - expect destruction in the wrapper invocation scope
    297    NS_DispatchAndSpinEventLoopUntilComplete(
    298        "DispatchTest::TestMove"_ns, target_,
    299        do_AddRef(
    300            WrapRunnableNM([](const CtorDtorState& s) {}, std::move(state))));
    301    ASSERT_EQ(4, destroyed);
    302  }
    303  // Still shouldn't count when we go out of scope as it was moved.
    304  ASSERT_EQ(4, destroyed);
    305 
    306  {
    307    CtorDtorState state(&destroyed);
    308 
    309    // Dispatch with:
    310    //   - moved arg
    311    //   - r-value function should accept a moved arg
    312    //   - expect destruction in the wrapper invocation scope
    313    NS_DispatchAndSpinEventLoopUntilComplete(
    314        "DispatchTest::TestMove"_ns, target_,
    315        do_AddRef(WrapRunnableNM([](CtorDtorState&& s) {}, std::move(state))));
    316    ASSERT_EQ(5, destroyed);
    317  }
    318  // Still shouldn't count when we go out of scope as it was moved.
    319  ASSERT_EQ(5, destroyed);
    320 }
    321 
    322 TEST_F(DispatchTest, TestUniquePtr) {
    323  // Test that holding the class in UniquePtr works
    324  int ran = 0;
    325  auto cl = MakeUnique<TargetClass>(&ran);
    326 
    327  NS_DispatchAndSpinEventLoopUntilComplete(
    328      "DispatchTest::TestUniquePtr"_ns, target_,
    329      do_AddRef(WrapRunnable(std::move(cl), &TargetClass::m1, 1)));
    330  ASSERT_EQ(1, ran);
    331 
    332  // Test that UniquePtr works as a param to the runnable
    333  int destroyed = 0;
    334  {
    335    auto state = MakeUnique<CtorDtorState>(&destroyed);
    336 
    337    // Dispatch with:
    338    //   - moved arg
    339    //   - Function should move construct from arg
    340    //   - expect destruction in the wrapper invocation scope
    341    NS_DispatchAndSpinEventLoopUntilComplete(
    342        "DispatchTest::TestUniquePtr"_ns, target_,
    343        do_AddRef(WrapRunnableNM([](UniquePtr<CtorDtorState> s) {},
    344                                 std::move(state))));
    345    ASSERT_EQ(1, destroyed);
    346  }
    347  // Still shouldn't count when we go out of scope as it was moved.
    348  ASSERT_EQ(1, destroyed);
    349 }
    350 
    351 }  // end of namespace