// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <string> #include "base/bind.h" #include "base/callback.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/test_globals.h" #include "testing/gtest/include/gtest/gtest.h" namespace ppapi { namespace { bool expect_to_be_locked = false; void CheckLockState() { if (expect_to_be_locked) { ProxyLock::AssertAcquired(); } else { // If we expect to be unlocked, try to lock. We rely on the checking inside // base::Lock that prevents recursive locking. ProxyAutoLock lock; } } int called_num = 0; class CheckLockStateInDestructor : public base::RefCounted<CheckLockStateInDestructor> { public: CheckLockStateInDestructor() {} void Method() { ++called_num; } private: friend class base::RefCounted<CheckLockStateInDestructor>; ~CheckLockStateInDestructor() { CheckLockState(); } DISALLOW_COPY_AND_ASSIGN(CheckLockStateInDestructor); }; void TestCallback_0() { CheckLockState(); ++called_num; } void TestCallback_1(int p1) { CheckLockState(); ++called_num; } void TestCallback_2(int p1, const std::string& p2) { CheckLockState(); ++called_num; } struct Param {}; void TestCallback_3(int p1, const std::string& p2, Param p3) { CheckLockState(); ++called_num; } } // namespace TEST(PpapiProxyLockTest, Locking) { TestGlobals globals; expect_to_be_locked = true; base::Callback<void()> cb0; { ProxyAutoLock lock; cb0 = RunWhileLocked(base::Bind(TestCallback_0)); } cb0.Run(); ASSERT_EQ(1, called_num); called_num = 0; { ProxyAutoLock lock; cb0 = RunWhileLocked(base::Bind(TestCallback_1, 123)); } cb0.Run(); ASSERT_EQ(1, called_num); called_num = 0; { ProxyAutoLock lock; scoped_refptr<CheckLockStateInDestructor> object = new CheckLockStateInDestructor(); cb0 = RunWhileLocked(base::Bind(&CheckLockStateInDestructor::Method, object)); // Note after this scope, the Callback owns the only reference. } cb0.Run(); ASSERT_EQ(1, called_num); called_num = 0; base::Callback<void(int)> cb1; { ProxyAutoLock lock; cb1 = RunWhileLocked(base::Bind(TestCallback_1)); } cb1.Run(123); ASSERT_EQ(1, called_num); called_num = 0; base::Callback<void(int, const std::string&)> cb2; { ProxyAutoLock lock; cb2 = RunWhileLocked(base::Bind(TestCallback_2)); } cb2.Run(123, std::string("yo")); ASSERT_EQ(1, called_num); called_num = 0; base::Callback<void(int, const std::string&, Param)> cb3; { ProxyAutoLock lock; cb3 = RunWhileLocked(base::Bind(TestCallback_3)); } cb3.Run(123, std::string("yo"), Param()); ASSERT_EQ(1, called_num); called_num = 0; base::Callback<void(const std::string&)> cb1_string; { ProxyAutoLock lock; cb1_string = RunWhileLocked(base::Bind(TestCallback_2, 123)); } cb1_string.Run(std::string("yo")); ASSERT_EQ(1, called_num); called_num = 0; { ProxyAutoLock lock; cb0 = RunWhileLocked(base::Bind(TestCallback_2, 123, std::string("yo"))); } cb0.Run(); ASSERT_EQ(1, called_num); called_num = 0; } TEST(PpapiProxyLockTest, Unlocking) { TestGlobals globals; expect_to_be_locked = false; // These calls should all try to _unlock_, so we must be locked before // entering them. ProxyAutoLock auto_lock; { CallWhileUnlocked(TestCallback_0); ASSERT_EQ(1, called_num); called_num = 0; } { CallWhileUnlocked(TestCallback_1, 123); ASSERT_EQ(1, called_num); called_num = 0; } { // TODO(dmichael): Make const-ref arguments work properly with type // deduction. CallWhileUnlocked<void, int, const std::string&>( TestCallback_2, 123, std::string("yo")); ASSERT_EQ(1, called_num); called_num = 0; } { base::Callback<void()> callback(base::Bind(TestCallback_0)); CallWhileUnlocked(callback); ASSERT_EQ(1, called_num); called_num = 0; } } } // namespace ppapi