fix build when sanitizers are enabled and jemalloc is disabled
[folly.git] / folly / test / SynchronizedTest.cpp
index 375b0c165cc09e533eaf76d00c918b1b487f7f59..db0f9c93ce6509a8736008d9008b24263223f9e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 // Test bed for folly/Synchronized.h
 
+#include <folly/LockTraitsBoost.h>
 #include <folly/Portability.h>
 #include <folly/RWSpinLock.h>
 #include <folly/SharedMutex.h>
 #include <folly/SpinLock.h>
 #include <folly/Synchronized.h>
+#include <folly/portability/GTest.h>
 #include <folly/test/SynchronizedTestLib.h>
-#include <gtest/gtest.h>
 
-namespace {
+using namespace folly::sync_tests;
 
 template <class Mutex>
 class SynchronizedTest : public testing::Test {};
 
-using SynchronizedTestTypes = testing::Types
-  < folly::SharedMutexReadPriority
-  , folly::SharedMutexWritePriority
-  , std::mutex
-  , std::recursive_mutex
-#if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
-  , std::timed_mutex
-  , std::recursive_timed_mutex
+using SynchronizedTestTypes = testing::Types<
+    folly::SharedMutexReadPriority,
+    folly::SharedMutexWritePriority,
+    std::mutex,
+    std::recursive_mutex,
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+    std::timed_mutex,
+    std::recursive_timed_mutex,
 #endif
-  , boost::mutex
-  , boost::recursive_mutex
-#if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
-  , boost::timed_mutex
-  , boost::recursive_timed_mutex
+    boost::mutex,
+    boost::recursive_mutex,
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+    boost::timed_mutex,
+    boost::recursive_timed_mutex,
 #endif
-  , boost::shared_mutex
-  , folly::SpinLock
 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
-  , folly::RWTicketSpinLock32
-  , folly::RWTicketSpinLock64
+    folly::RWTicketSpinLock32,
+    folly::RWTicketSpinLock64,
 #endif
-  >;
+    boost::shared_mutex,
+    folly::SpinLock>;
 TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
 
 TYPED_TEST(SynchronizedTest, Basic) {
   testBasic<TypeParam>();
 }
 
+TYPED_TEST(SynchronizedTest, WithLock) {
+  testWithLock<TypeParam>();
+}
+
+TYPED_TEST(SynchronizedTest, Unlock) {
+  testUnlock<TypeParam>();
+}
+
+TYPED_TEST(SynchronizedTest, Deprecated) {
+  testDeprecated<TypeParam>();
+}
+
 TYPED_TEST(SynchronizedTest, Concurrency) {
   testConcurrency<TypeParam>();
 }
 
+TYPED_TEST(SynchronizedTest, AcquireLocked) {
+  testAcquireLocked<TypeParam>();
+}
+
+TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
+  testAcquireLockedWithConst<TypeParam>();
+}
+
 TYPED_TEST(SynchronizedTest, DualLocking) {
   testDualLocking<TypeParam>();
 }
@@ -78,23 +98,26 @@ TYPED_TEST(SynchronizedTest, ConstCopy) {
 template <class Mutex>
 class SynchronizedTimedTest : public testing::Test {};
 
-using SynchronizedTimedTestTypes = testing::Types
-  < folly::SharedMutexReadPriority
-  , folly::SharedMutexWritePriority
-#if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
-  , std::timed_mutex
-  , std::recursive_timed_mutex
-  , boost::timed_mutex
-  , boost::recursive_timed_mutex
-  , boost::shared_mutex
+using SynchronizedTimedTestTypes = testing::Types<
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+    std::timed_mutex,
+    std::recursive_timed_mutex,
+    boost::timed_mutex,
+    boost::recursive_timed_mutex,
+    boost::shared_mutex,
 #endif
 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
-  , folly::RWTicketSpinLock32
-  , folly::RWTicketSpinLock64
+    folly::RWTicketSpinLock32,
+    folly::RWTicketSpinLock64,
 #endif
-  >;
+    folly::SharedMutexReadPriority,
+    folly::SharedMutexWritePriority>;
 TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
 
+TYPED_TEST(SynchronizedTimedTest, Timed) {
+  testTimed<TypeParam>();
+}
+
 TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
   testTimedSynchronized<TypeParam>();
 }
@@ -102,20 +125,23 @@ TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
 template <class Mutex>
 class SynchronizedTimedWithConstTest : public testing::Test {};
 
-using SynchronizedTimedWithConstTestTypes = testing::Types
-  < folly::SharedMutexReadPriority
-  , folly::SharedMutexWritePriority
-#if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
-  , boost::shared_mutex
+using SynchronizedTimedWithConstTestTypes = testing::Types<
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+    boost::shared_mutex,
 #endif
 #ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
-  , folly::RWTicketSpinLock32
-  , folly::RWTicketSpinLock64
+    folly::RWTicketSpinLock32,
+    folly::RWTicketSpinLock64,
 #endif
-  >;
+    folly::SharedMutexReadPriority,
+    folly::SharedMutexWritePriority>;
 TYPED_TEST_CASE(
     SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
 
+TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
+  testTimedShared<TypeParam>();
+}
+
 TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
   testTimedSynchronizedWithConst<TypeParam>();
 }
@@ -128,14 +154,12 @@ using CountPair = std::pair<int, int>;
 // This class is specialized only to be uesed in SynchronizedLockTest
 class FakeMutex {
  public:
-  bool lock() {
+  void lock() {
     ++lockCount_;
-    return true;
   }
 
-  bool unlock() {
+  void unlock() {
     ++unlockCount_;
-    return true;
   }
 
   static CountPair getLockUnlockCount() {
@@ -152,10 +176,6 @@ class FakeMutex {
   // process
   static FOLLY_TLS int lockCount_;
   static FOLLY_TLS int unlockCount_;
-
-  // Adapters for Synchronized<>
-  friend void acquireReadWrite(FakeMutex& lock) { lock.lock(); }
-  friend void releaseReadWrite(FakeMutex& lock) { lock.unlock(); }
 };
 FOLLY_TLS int FakeMutex::lockCount_{0};
 FOLLY_TLS int FakeMutex::unlockCount_{0};
@@ -169,65 +189,325 @@ class SynchronizedLockTest : public testing::Test {
   }
 };
 
-// Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are
-// super set of it, it is possible single level test passes while nested tests
-// fail
-TEST_F(SynchronizedLockTest, SyncUnSync) {
-  folly::Synchronized<std::vector<int>, FakeMutex> obj;
-  EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
-  SYNCHRONIZED(obj) {
-    EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
-    UNSYNCHRONIZED(obj) {
-      EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount());
-    }
-    EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
-  }
-  EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount());
-}
-
-// Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels for each are used here
-TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
-  folly::Synchronized<std::vector<int>, FakeMutex> obj;
-  EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
-  SYNCHRONIZED(objCopy, obj) {
-    EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
-    SYNCHRONIZED(obj) {
-      EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
-      UNSYNCHRONIZED(obj) {
-        EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
-        UNSYNCHRONIZED(obj) {
-          EXPECT_EQ((CountPair{2, 2}),
-                    FakeMutex::getLockUnlockCount());
-        }
-        EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
-      }
-      EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount());
-    }
-    EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
-  }
-  EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
-}
-
-// Different nesting behavior, UNSYNCHRONIZED called on differen depth of
-// SYNCHRONIZED
-TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
-  folly::Synchronized<std::vector<int>, FakeMutex> obj;
-  EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount());
-  SYNCHRONIZED(objCopy, obj) {
-    EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount());
-    SYNCHRONIZED(obj) {
-      EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount());
-      UNSYNCHRONIZED(obj) {
-        EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
-      }
-      EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
-    }
-    EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount());
-    UNSYNCHRONIZED(obj) {
-      EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount());
-    }
-    EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount());
-  }
-  EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount());
+/**
+ * Test mutex to help to automate assertions, taken from LockTraitsTest.cpp
+ */
+class FakeAllPowerfulAssertingMutexInternal {
+ public:
+  enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
+
+  void lock() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
+    this->lock_state = CurrentLockState::UNIQUE;
+  }
+  void unlock() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
+    this->lock_state = CurrentLockState::UNLOCKED;
+  }
+  void lock_shared() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
+    this->lock_state = CurrentLockState::SHARED;
+  }
+  void unlock_shared() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
+    this->lock_state = CurrentLockState::UNLOCKED;
+  }
+  void lock_upgrade() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
+    this->lock_state = CurrentLockState::UPGRADE;
+  }
+  void unlock_upgrade() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
+    this->lock_state = CurrentLockState::UNLOCKED;
+  }
+
+  void unlock_upgrade_and_lock() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
+    this->lock_state = CurrentLockState::UNIQUE;
+  }
+  void unlock_and_lock_upgrade() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
+    this->lock_state = CurrentLockState::UPGRADE;
+  }
+  void unlock_and_lock_shared() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
+    this->lock_state = CurrentLockState::SHARED;
+  }
+  void unlock_upgrade_and_lock_shared() {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
+    this->lock_state = CurrentLockState::SHARED;
+  }
+
+  template <class Rep, class Period>
+  bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
+    this->lock_state = CurrentLockState::UNIQUE;
+    return true;
+  }
+
+  template <class Rep, class Period>
+  bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
+    this->lock_state = CurrentLockState::UPGRADE;
+    return true;
+  }
+
+  template <class Rep, class Period>
+  bool try_unlock_upgrade_and_lock_for(
+      const std::chrono::duration<Rep, Period>&) {
+    EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
+    this->lock_state = CurrentLockState::UNIQUE;
+    return true;
+  }
+
+  /*
+   * Initialize the FakeMutex with an unlocked state
+   */
+  CurrentLockState lock_state{CurrentLockState::UNLOCKED};
+};
+
+/**
+ * The following works around the internal mutex for synchronized being
+ * private
+ *
+ * This is horridly thread unsafe.
+ */
+static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex;
+
+class FakeAllPowerfulAssertingMutex {
+ public:
+  void lock() {
+    globalAllPowerfulAssertingMutex.lock();
+  }
+  void unlock() {
+    globalAllPowerfulAssertingMutex.unlock();
+  }
+  void lock_shared() {
+    globalAllPowerfulAssertingMutex.lock_shared();
+  }
+  void unlock_shared() {
+    globalAllPowerfulAssertingMutex.unlock_shared();
+  }
+  void lock_upgrade() {
+    globalAllPowerfulAssertingMutex.lock_upgrade();
+  }
+  void unlock_upgrade() {
+    globalAllPowerfulAssertingMutex.unlock_upgrade();
+  }
+
+  void unlock_upgrade_and_lock() {
+    globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock();
+  }
+  void unlock_and_lock_upgrade() {
+    globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade();
+  }
+  void unlock_and_lock_shared() {
+    globalAllPowerfulAssertingMutex.unlock_and_lock_shared();
+  }
+  void unlock_upgrade_and_lock_shared() {
+    globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock_shared();
+  }
+
+  template <class Rep, class Period>
+  bool try_lock_for(const std::chrono::duration<Rep, Period>& arg) {
+    return globalAllPowerfulAssertingMutex.try_lock_for(arg);
+  }
+
+  template <class Rep, class Period>
+  bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>& arg) {
+    return globalAllPowerfulAssertingMutex.try_lock_upgrade_for(arg);
+  }
+
+  template <class Rep, class Period>
+  bool try_unlock_upgrade_and_lock_for(
+      const std::chrono::duration<Rep, Period>& arg) {
+    return globalAllPowerfulAssertingMutex.try_unlock_upgrade_and_lock_for(arg);
+  }
+
+  // reset state on destruction
+  ~FakeAllPowerfulAssertingMutex() {
+    globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{};
+  }
+};
+
+TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) {
+  struct NonCopyConstructible {
+    NonCopyConstructible(const NonCopyConstructible&) = delete;
+    NonCopyConstructible& operator=(const NonCopyConstructible&) = delete;
+  };
+  struct CopyConstructible {};
+  EXPECT_FALSE(std::is_copy_constructible<
+               folly::Synchronized<NonCopyConstructible>>::value);
+  EXPECT_FALSE(std::is_copy_assignable<
+               folly::Synchronized<NonCopyConstructible>>::value);
+  EXPECT_TRUE(std::is_copy_constructible<
+              folly::Synchronized<CopyConstructible>>::value);
+  EXPECT_TRUE(
+      std::is_copy_assignable<folly::Synchronized<CopyConstructible>>::value);
+}
+
+TEST_F(SynchronizedLockTest, UpgradableLocking) {
+  folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
+
+  // sanity assert
+  static_assert(
+      std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
+      "The ulock function was not well configured, blame aary@instagram.com");
+
+  {
+    auto ulock = sync.ulock();
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
+  }
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
+
+  // test going from upgrade to exclusive
+  {
+    auto ulock = sync.ulock();
+    auto wlock = ulock.moveFromUpgradeToWrite();
+    EXPECT_EQ(static_cast<bool>(ulock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
+  }
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
+
+  // test going from upgrade to shared
+  {
+    auto ulock = sync.ulock();
+    auto slock = ulock.moveFromUpgradeToRead();
+    EXPECT_EQ(static_cast<bool>(ulock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
+  }
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
+
+  // test going from exclusive to upgrade
+  {
+    auto wlock = sync.wlock();
+    auto ulock = wlock.moveFromWriteToUpgrade();
+    EXPECT_EQ(static_cast<bool>(wlock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
+  }
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
+
+  // test going from exclusive to shared
+  {
+    auto wlock = sync.wlock();
+    auto slock = wlock.moveFromWriteToRead();
+    EXPECT_EQ(static_cast<bool>(wlock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
+  }
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
 }
+
+TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) {
+  folly::Synchronized<int, FakeAllPowerfulAssertingMutex> sync;
+
+  // sanity assert
+  static_assert(
+      std::is_same<std::decay<decltype(*sync.ulock())>::type, int>::value,
+      "The ulock function was not well configured, blame aary@instagram.com");
+
+  // test from upgrade to write
+  sync.withULockPtr([](auto ulock) {
+    EXPECT_EQ(static_cast<bool>(ulock), true);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
+
+    auto wlock = ulock.moveFromUpgradeToWrite();
+    EXPECT_EQ(static_cast<bool>(ulock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
+  });
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
+
+  // test from write to upgrade
+  sync.withWLockPtr([](auto wlock) {
+    EXPECT_EQ(static_cast<bool>(wlock), true);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
+
+    auto ulock = wlock.moveFromWriteToUpgrade();
+    EXPECT_EQ(static_cast<bool>(wlock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
+  });
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
+
+  // test from upgrade to shared
+  sync.withULockPtr([](auto ulock) {
+    EXPECT_EQ(static_cast<bool>(ulock), true);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE);
+
+    auto slock = ulock.moveFromUpgradeToRead();
+    EXPECT_EQ(static_cast<bool>(ulock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
+  });
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
+
+  // test from write to shared
+  sync.withWLockPtr([](auto wlock) {
+    EXPECT_EQ(static_cast<bool>(wlock), true);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE);
+
+    auto slock = wlock.moveFromWriteToRead();
+    EXPECT_EQ(static_cast<bool>(wlock), false);
+    EXPECT_EQ(
+        globalAllPowerfulAssertingMutex.lock_state,
+        FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED);
+  });
+
+  // should be unlocked here
+  EXPECT_EQ(
+      globalAllPowerfulAssertingMutex.lock_state,
+      FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED);
 }