// Copyright (c) 2012 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 "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" namespace { // Used to test depth subtyping. class ConDecLoggerParent { public: virtual ~ConDecLoggerParent() {} virtual void set_ptr(int* ptr) = 0; virtual int SomeMeth(int x) const = 0; }; class ConDecLogger : public ConDecLoggerParent { public: ConDecLogger() : ptr_(NULL) { } explicit ConDecLogger(int* ptr) { set_ptr(ptr); } virtual ~ConDecLogger() { --*ptr_; } virtual void set_ptr(int* ptr) { ptr_ = ptr; ++*ptr_; } virtual int SomeMeth(int x) const { return x; } private: int* ptr_; DISALLOW_COPY_AND_ASSIGN(ConDecLogger); }; scoped_ptr PassThru(scoped_ptr logger) { return logger.Pass(); } void GrabAndDrop(scoped_ptr logger) { } // Do not delete this function! It's existence is to test that you can // return a temporarily constructed version of the scoper. scoped_ptr TestReturnOfType(int* constructed) { return scoped_ptr(new ConDecLogger(constructed)); } scoped_ptr UpcastUsingPassAs( scoped_ptr object) { return object.PassAs(); } } // namespace TEST(ScopedPtrTest, ScopedPtr) { int constructed = 0; { scoped_ptr scoper(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); EXPECT_EQ(10, scoper->SomeMeth(10)); EXPECT_EQ(10, scoper.get()->SomeMeth(10)); EXPECT_EQ(10, (*scoper).SomeMeth(10)); } EXPECT_EQ(0, constructed); // Test reset() and release() { scoped_ptr scoper(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoper.reset(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoper.reset(); EXPECT_EQ(0, constructed); EXPECT_FALSE(scoper.get()); scoper.reset(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); ConDecLogger* take = scoper.release(); EXPECT_EQ(1, constructed); EXPECT_FALSE(scoper.get()); delete take; EXPECT_EQ(0, constructed); scoper.reset(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); } EXPECT_EQ(0, constructed); // Test swap(), == and != { scoped_ptr scoper1; scoped_ptr scoper2; EXPECT_TRUE(scoper1 == scoper2.get()); EXPECT_FALSE(scoper1 != scoper2.get()); ConDecLogger* logger = new ConDecLogger(&constructed); scoper1.reset(logger); EXPECT_EQ(logger, scoper1.get()); EXPECT_FALSE(scoper2.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); scoper2.swap(scoper1); EXPECT_EQ(logger, scoper2.get()); EXPECT_FALSE(scoper1.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); } EXPECT_EQ(0, constructed); } TEST(ScopedPtrTest, ScopedPtrDepthSubtyping) { int constructed = 0; // Test construction from a scoped_ptr to a derived class. { scoped_ptr scoper(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoped_ptr scoper_parent(scoper.Pass()); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper_parent.get()); EXPECT_FALSE(scoper.get()); EXPECT_EQ(10, scoper_parent->SomeMeth(10)); EXPECT_EQ(10, scoper_parent.get()->SomeMeth(10)); EXPECT_EQ(10, (*scoper_parent).SomeMeth(10)); } EXPECT_EQ(0, constructed); // Test assignment from a scoped_ptr to a derived class. { scoped_ptr scoper(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoped_ptr scoper_parent; scoper_parent = scoper.Pass(); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper_parent.get()); EXPECT_FALSE(scoper.get()); } EXPECT_EQ(0, constructed); // Test construction of a scoped_ptr with an additional const annotation. { scoped_ptr scoper(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoped_ptr scoper_const(scoper.Pass()); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper_const.get()); EXPECT_FALSE(scoper.get()); EXPECT_EQ(10, scoper_const->SomeMeth(10)); EXPECT_EQ(10, scoper_const.get()->SomeMeth(10)); EXPECT_EQ(10, (*scoper_const).SomeMeth(10)); } EXPECT_EQ(0, constructed); // Test assignment to a scoped_ptr with an additional const annotation. { scoped_ptr scoper(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoped_ptr scoper_const; scoper_const = scoper.Pass(); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper_const.get()); EXPECT_FALSE(scoper.get()); } EXPECT_EQ(0, constructed); } TEST(ScopedPtrTest, ScopedArray) { static const int kNumLoggers = 12; int constructed = 0; { scoped_array scoper(new ConDecLogger[kNumLoggers]); EXPECT_TRUE(scoper.get()); EXPECT_EQ(&scoper[0], scoper.get()); for (int i = 0; i < kNumLoggers; ++i) { scoper[i].set_ptr(&constructed); } EXPECT_EQ(12, constructed); EXPECT_EQ(10, scoper.get()->SomeMeth(10)); EXPECT_EQ(10, scoper[2].SomeMeth(10)); } EXPECT_EQ(0, constructed); // Test reset() and release() { scoped_array scoper; EXPECT_FALSE(scoper.get()); EXPECT_FALSE(scoper.release()); EXPECT_FALSE(scoper.get()); scoper.reset(); EXPECT_FALSE(scoper.get()); scoper.reset(new ConDecLogger[kNumLoggers]); for (int i = 0; i < kNumLoggers; ++i) { scoper[i].set_ptr(&constructed); } EXPECT_EQ(12, constructed); scoper.reset(); EXPECT_EQ(0, constructed); scoper.reset(new ConDecLogger[kNumLoggers]); for (int i = 0; i < kNumLoggers; ++i) { scoper[i].set_ptr(&constructed); } EXPECT_EQ(12, constructed); ConDecLogger* ptr = scoper.release(); EXPECT_EQ(12, constructed); delete[] ptr; EXPECT_EQ(0, constructed); } EXPECT_EQ(0, constructed); // Test swap(), == and != { scoped_array scoper1; scoped_array scoper2; EXPECT_TRUE(scoper1 == scoper2.get()); EXPECT_FALSE(scoper1 != scoper2.get()); ConDecLogger* loggers = new ConDecLogger[kNumLoggers]; for (int i = 0; i < kNumLoggers; ++i) { loggers[i].set_ptr(&constructed); } scoper1.reset(loggers); EXPECT_EQ(loggers, scoper1.get()); EXPECT_FALSE(scoper2.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); scoper2.swap(scoper1); EXPECT_EQ(loggers, scoper2.get()); EXPECT_FALSE(scoper1.get()); EXPECT_FALSE(scoper1 == scoper2.get()); EXPECT_TRUE(scoper1 != scoper2.get()); } EXPECT_EQ(0, constructed); } TEST(ScopedPtrTest, PassBehavior) { int constructed = 0; { ConDecLogger* logger = new ConDecLogger(&constructed); scoped_ptr scoper(logger); EXPECT_EQ(1, constructed); // Test Pass() with constructor; scoped_ptr scoper2(scoper.Pass()); EXPECT_EQ(1, constructed); // Test Pass() with assignment; scoped_ptr scoper3; scoper3 = scoper2.Pass(); EXPECT_EQ(1, constructed); EXPECT_FALSE(scoper.get()); EXPECT_FALSE(scoper2.get()); EXPECT_TRUE(scoper3.get()); } // Test uncaught Pass() does not leak. { ConDecLogger* logger = new ConDecLogger(&constructed); scoped_ptr scoper(logger); EXPECT_EQ(1, constructed); // Should auto-destruct logger by end of scope. scoper.Pass(); EXPECT_FALSE(scoper.get()); } EXPECT_EQ(0, constructed); // Test that passing to function which does nothing does not leak. { ConDecLogger* logger = new ConDecLogger(&constructed); scoped_ptr scoper(logger); EXPECT_EQ(1, constructed); // Should auto-destruct logger by end of scope. GrabAndDrop(scoper.Pass()); EXPECT_FALSE(scoper.get()); } EXPECT_EQ(0, constructed); } TEST(ScopedPtrTest, ReturnTypeBehavior) { int constructed = 0; // Test that we can return a scoped_ptr. { ConDecLogger* logger = new ConDecLogger(&constructed); scoped_ptr scoper(logger); EXPECT_EQ(1, constructed); PassThru(scoper.Pass()); EXPECT_FALSE(scoper.get()); } EXPECT_EQ(0, constructed); // Test uncaught return type not leak. { ConDecLogger* logger = new ConDecLogger(&constructed); scoped_ptr scoper(logger); EXPECT_EQ(1, constructed); // Should auto-destruct logger by end of scope. PassThru(scoper.Pass()); EXPECT_FALSE(scoper.get()); } EXPECT_EQ(0, constructed); // Call TestReturnOfType() so the compiler doesn't warn for an unused // function. { TestReturnOfType(&constructed); } EXPECT_EQ(0, constructed); } TEST(ScopedPtrTest, PassAs) { int constructed = 0; { scoped_ptr scoper(new ConDecLogger(&constructed)); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper.get()); scoped_ptr scoper_parent; scoper_parent = UpcastUsingPassAs(scoper.Pass()); EXPECT_EQ(1, constructed); EXPECT_TRUE(scoper_parent.get()); EXPECT_FALSE(scoper.get()); } EXPECT_EQ(0, constructed); } // TODO scoped_ptr_malloc