summaryrefslogtreecommitdiffstats
path: root/storage/test/gtest/test_interruptSynchronousConnection.cpp
blob: b29337e720f4195c4642379c54b556e305d5ea29 (plain)
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
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "gtest/gtest.h"

#include "storage_test_harness.h"

#include "mozilla/Atomics.h"
#include "mozilla/SpinEventLoopUntil.h"

class SynchronousConnectionInterruptionTest : public ::testing::Test {
 protected:
  void SetUp() override {
    mConnection =
        getDatabase(nullptr, mozIStorageService::CONNECTION_INTERRUPTIBLE);
    ASSERT_TRUE(mConnection);

    ASSERT_EQ(NS_OK, NS_NewNamedThread("Test Thread", getter_AddRefs(mThread)));
  }

  void TearDown() override {
    // We might close the database connection early in test cases.
    mozilla::Unused << mConnection->Close();

    ASSERT_EQ(NS_OK, mThread->Shutdown());
  }

  nsCOMPtr<mozIStorageConnection> mConnection;

  nsCOMPtr<nsIThread> mThread;

  mozilla::Atomic<nsresult> mRv = mozilla::Atomic<nsresult>(NS_ERROR_FAILURE);

  mozilla::Atomic<bool> mDone{false};
};

TEST_F(SynchronousConnectionInterruptionTest,
       shouldBeAbleToInterruptInfiniteOperation) {
  // Delay is modest because we don't want to get interrupted by
  // some unrelated hang or memory guard
  const uint32_t delayMs = 500;

  ASSERT_EQ(NS_OK, mThread->DelayedDispatch(
                       NS_NewRunnableFunction("InterruptRunnable",
                                              [this]() {
                                                mRv = mConnection->Interrupt();
                                                mDone = true;
                                              }),
                       delayMs));

  const nsCString infiniteQuery =
      "WITH RECURSIVE test(n) "
      "AS (VALUES(1) UNION ALL SELECT n + 1 FROM test) "
      "SELECT t.n FROM test, test AS t;"_ns;
  nsCOMPtr<mozIStorageStatement> stmt;
  ASSERT_EQ(NS_OK,
            mConnection->CreateStatement(infiniteQuery, getter_AddRefs(stmt)));

  ASSERT_EQ(NS_ERROR_ABORT, stmt->Execute());
  ASSERT_EQ(NS_OK, stmt->Finalize());

  ASSERT_TRUE(mDone);
  ASSERT_EQ(NS_OK, mRv);

  ASSERT_EQ(NS_OK, mConnection->Close());
}

TEST_F(SynchronousConnectionInterruptionTest, interruptAfterCloseWillFail) {
  ASSERT_EQ(NS_OK, mConnection->Close());

  ASSERT_EQ(NS_OK, mThread->Dispatch(
                       NS_NewRunnableFunction("InterruptRunnable", [this]() {
                         mRv = mConnection->Interrupt();
                         mDone = true;
                       })));

  ASSERT_TRUE(mozilla::SpinEventLoopUntil("interruptAfterCloseWillFail"_ns,
                                          [this]() -> bool { return mDone; }));

  ASSERT_EQ(NS_ERROR_NOT_INITIALIZED, mRv);

  ASSERT_EQ(NS_ERROR_NOT_INITIALIZED, mConnection->Close());
}