tor-browser

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

TestThreadUtils.cpp (6056B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=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 #include "QuotaManagerDependencyFixture.h"
      8 #include "mozilla/dom/quota/QuotaManager.h"
      9 #include "mozilla/dom/quota/ThreadUtils.h"
     10 #include "mozilla/gtest/MozAssertions.h"
     11 
     12 namespace mozilla::dom::quota::test {
     13 
     14 namespace {
     15 
     16 MozExternalRefCountType GetRefCount(nsISupports* aSupports) {
     17  if (!aSupports) {
     18    return 0;
     19  }
     20  aSupports->AddRef();
     21  return aSupports->Release();
     22 }
     23 
     24 class OneTimeRunnable final : public Runnable {
     25 public:
     26  enum class State { None, Initial, Created, Destroyed };
     27 
     28  static void Init() {
     29    ASSERT_TRUE(sState == State::None || sState == State::Destroyed);
     30    ASSERT_FALSE(sCurrent);
     31 
     32    sState = State::Initial;
     33  }
     34 
     35  static RefPtr<OneTimeRunnable> Create(std::function<void()>&& aTask) {
     36    EXPECT_EQ(sState, State::Initial);
     37 
     38    RefPtr<OneTimeRunnable> runnable = new OneTimeRunnable(std::move(aTask));
     39    return runnable;
     40  }
     41 
     42  static State GetState() { return sState; }
     43 
     44  static OneTimeRunnable* GetCurrent() { return sCurrent; }
     45 
     46  static MozExternalRefCountType GetCurrentRefCount() {
     47    return GetRefCount(static_cast<nsIRunnable*>(sCurrent));
     48  }
     49 
     50  NS_IMETHOD
     51  Run() override {
     52    auto task = std::move(mTask);
     53    task();
     54 
     55    return NS_OK;
     56  }
     57 
     58 private:
     59  explicit OneTimeRunnable(std::function<void()>&& aTask)
     60      : Runnable("dom::quota::test::OneTimeRunnable"), mTask(std::move(aTask)) {
     61    sCurrent = this;
     62    sState = State::Created;
     63  }
     64 
     65  ~OneTimeRunnable() override {
     66    sState = State::Destroyed;
     67    sCurrent = nullptr;
     68  }
     69 
     70  static Atomic<State> sState;
     71  static OneTimeRunnable* sCurrent;
     72 
     73  std::function<void()> mTask;
     74 };
     75 
     76 }  // namespace
     77 
     78 Atomic<OneTimeRunnable::State> OneTimeRunnable::sState(
     79    OneTimeRunnable::State::None);
     80 OneTimeRunnable* OneTimeRunnable::sCurrent(nullptr);
     81 
     82 class TestThreadUtils : public QuotaManagerDependencyFixture {
     83 public:
     84  static void SetUpTestCase() { ASSERT_NO_FATAL_FAILURE(InitializeFixture()); }
     85 
     86  static void TearDownTestCase() { ASSERT_NO_FATAL_FAILURE(ShutdownFixture()); }
     87 };
     88 
     89 TEST_F(TestThreadUtils, RunAfterProcessingCurrentEvent_Release) {
     90  bool runnableCalled = false;
     91  bool callbackCalled = false;
     92 
     93  QuotaManager* quotaManager = QuotaManager::Get();
     94  ASSERT_TRUE(quotaManager);
     95 
     96  OneTimeRunnable::Init();
     97 
     98  auto runnable = OneTimeRunnable::Create([&runnableCalled, &callbackCalled]() {
     99    runnableCalled = true;
    100 
    101    EXPECT_EQ(OneTimeRunnable::GetState(), OneTimeRunnable::State::Created);
    102    EXPECT_EQ(OneTimeRunnable::GetCurrentRefCount(), 2u);
    103 
    104    RunAfterProcessingCurrentEvent([&callbackCalled]() {
    105      callbackCalled = true;
    106 
    107      // The runnable shouldn't be yet destroyed because we still have a strong
    108      // reference to it in `runnable`.
    109      EXPECT_EQ(OneTimeRunnable::GetState(), OneTimeRunnable::State::Created);
    110 
    111      // The runnable should be released once (by the event queue processing
    112      // code).
    113      EXPECT_EQ(OneTimeRunnable::GetCurrentRefCount(), 1u);
    114    });
    115 
    116    EXPECT_EQ(OneTimeRunnable::GetState(), OneTimeRunnable::State::Created);
    117    EXPECT_EQ(OneTimeRunnable::GetCurrentRefCount(), 2u);
    118  });
    119 
    120  ASSERT_FALSE(runnableCalled);
    121  ASSERT_FALSE(callbackCalled);
    122 
    123  // Note that we are keeping ownership of the runnable here.
    124  ASSERT_EQ(NS_OK,
    125            quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL));
    126 
    127  // Do a round-trip to the QM IO thread to ensure the test doesn't finish
    128  // before the OneTimeRunnable is fully processed.
    129  //
    130  // Note: In theory, we would use SyncRunnable wrapper here, but the code
    131  // reads better when both tests use the same way for blocking the current
    132  // thread.
    133  ASSERT_NO_FATAL_FAILURE(PerformOnIOThread([]() {}));
    134 
    135  // Check that the runnable and the callback were actually called.
    136  ASSERT_TRUE(runnableCalled);
    137  ASSERT_TRUE(callbackCalled);
    138 }
    139 
    140 TEST_F(TestThreadUtils, RunAfterProcessingCurrentEvent_ReleaseAndDestory) {
    141  bool runnableCalled = false;
    142  bool callbackCalled = false;
    143 
    144  QuotaManager* quotaManager = QuotaManager::Get();
    145  ASSERT_TRUE(quotaManager);
    146 
    147  OneTimeRunnable::Init();
    148 
    149  auto runnable = OneTimeRunnable::Create([&runnableCalled, &callbackCalled]() {
    150    runnableCalled = true;
    151 
    152    EXPECT_EQ(OneTimeRunnable::GetState(), OneTimeRunnable::State::Created);
    153    EXPECT_EQ(OneTimeRunnable::GetCurrentRefCount(), 1u);
    154 
    155    RunAfterProcessingCurrentEvent([&callbackCalled]() {
    156      callbackCalled = true;
    157 
    158      // The runnable should be destroyed because we don't have any other strong
    159      // references to it.
    160      EXPECT_EQ(OneTimeRunnable::GetState(), OneTimeRunnable::State::Destroyed);
    161 
    162      // The runnable should be released once (by the event queue processing
    163      // code).
    164      EXPECT_EQ(OneTimeRunnable::GetCurrentRefCount(), 0u);
    165    });
    166 
    167    EXPECT_EQ(OneTimeRunnable::GetState(), OneTimeRunnable::State::Created);
    168    EXPECT_EQ(OneTimeRunnable::GetCurrentRefCount(), 1u);
    169  });
    170 
    171  ASSERT_FALSE(runnableCalled);
    172  ASSERT_FALSE(callbackCalled);
    173 
    174  // Note that we are tranferring ownership of the runnable here.
    175  ASSERT_EQ(NS_OK, quotaManager->IOThread()->Dispatch(runnable.forget(),
    176                                                      NS_DISPATCH_NORMAL));
    177 
    178  // Do a round-trip to the QM IO thread to ensure the test doesn't finish
    179  // before the OneTimeRunnable is fully processed.
    180  //
    181  // Note: SyncRunnable wrapper can't be used here because that would hold our
    182  // runnable longer, and we couldn't test that our runnable is destroyed when
    183  // the callback for RunAfterProcessingCurrentEvent is executed.
    184  ASSERT_NO_FATAL_FAILURE(PerformOnIOThread([]() {}));
    185 
    186  // Check that the runnable and the callback were actually called.
    187  ASSERT_TRUE(runnableCalled);
    188  ASSERT_TRUE(callbackCalled);
    189 }
    190 
    191 }  // namespace mozilla::dom::quota::test