/* Copyright 2012 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ /** Unit tests for OwnedPointerVector. */ #include "mongo/base/owned_pointer_vector.h" #include #include #include "mongo/unittest/unittest.h" namespace mongo { using std::string; namespace { /** Helper class that logs its constructor argument to a static vector on destruction. */ class DestructionLogger { public: DestructionLogger( const string& name ) : _name( name ) { } ~DestructionLogger() { _destroyedNames.push_back( _name ); } static std::vector& destroyedNames() { return _destroyedNames; } private: string _name; static std::vector _destroyedNames; }; std::vector DestructionLogger::_destroyedNames; TEST(OwnedPointerVectorTest, OwnedPointerDestroyed) { DestructionLogger::destroyedNames().clear(); { OwnedPointerVector owned; owned.mutableVector().push_back( new DestructionLogger( "foo" ) ); // owned destroyed } ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "foo", DestructionLogger::destroyedNames()[ 0 ] ); } TEST(OwnedPointerVectorTest, OwnedConstPointerDestroyed) { DestructionLogger::destroyedNames().clear(); { OwnedPointerVector owned; owned.push_back( new DestructionLogger( "foo" ) ); // owned destroyed } ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "foo", DestructionLogger::destroyedNames()[ 0 ] ); } TEST(OwnedPointerVectorTest, OwnedPointersDestroyedInOrder) { DestructionLogger::destroyedNames().clear(); { OwnedPointerVector owned; owned.push_back( new DestructionLogger( "first" ) ); owned.push_back( new DestructionLogger( "second" ) ); // owned destroyed } ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); } TEST(OwnedPointerVectorTest, ClearDestroyedInOrder) { DestructionLogger::destroyedNames().clear(); { OwnedPointerVector owned; owned.push_back( new DestructionLogger( "first" ) ); owned.push_back( new DestructionLogger( "second" ) ); owned.clear(); ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); ASSERT_EQUALS( 0U, owned.size() ); // owned destroyed } // no additional deletion should have occured when owned was destroyed ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); } TEST(OwnedPointerVectorTest, EraseDestroysAsCalled) { DestructionLogger::destroyedNames().clear(); { // names are order of erasure OwnedPointerVector owned; owned.push_back( new DestructionLogger( "third" ) ); owned.push_back( new DestructionLogger( "first" ) ); owned.push_back( new DestructionLogger( "second" ) ); owned.push_back( new DestructionLogger( "fourth" ) ); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); // erase "first", sliding "second" down to index 1 owned.erase(owned.begin() + 1); ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames().back() ); ASSERT_EQUALS( 3U, owned.size() ); // erase "second" owned.erase(owned.begin() + 1); ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames().back() ); ASSERT_EQUALS( 2U, owned.size() ); // erase "third" owned.erase(owned.begin() + 0); ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "third", DestructionLogger::destroyedNames().back() ); ASSERT_EQUALS( 1U, owned.size() ); // owned destroyed } // only "four" should have been deleted when owned was destroyed ASSERT_EQUALS( 4U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "fourth", DestructionLogger::destroyedNames().back() ); } TEST(OwnedPointerVectorTest, Accessors) { OwnedPointerVector owned; ASSERT_TRUE( owned.empty() ); ASSERT_EQUALS( 0U, owned.size() ); owned.push_back( new int(0) ); owned.push_back( new int(1) ); owned.push_back( new int(2) ); ASSERT_FALSE( owned.empty() ); ASSERT_EQUALS( 3U, owned.size() ); ASSERT_EQUALS( 0, *owned[0] ); ASSERT_EQUALS( 1, *owned[1] ); ASSERT_EQUALS( 2, *owned[2] ); ASSERT_EQUALS( 0, *owned.front() ); ASSERT_EQUALS( 2, *owned.back() ); } TEST(OwnedPointerVectorTest, TransferConstructor) { DestructionLogger::destroyedNames().clear(); { OwnedPointerVector source; source.push_back( new DestructionLogger( "first" ) ); source.push_back( new DestructionLogger( "second" ) ); { OwnedPointerVector dest(source.release()); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 0U, source.size() ); ASSERT_EQUALS( 2U, dest.size() ); // dest destroyed } ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); // source destroyed } // no additional deletions ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); } TEST(OwnedPointerVectorTest, TransferConstructorDoesntModifyArgument) { DestructionLogger::destroyedNames().clear(); { std::vector source; source.push_back( new DestructionLogger( "first" ) ); source.push_back( new DestructionLogger( "second" ) ); { OwnedPointerVector dest(source); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 2U, source.size() ); ASSERT_EQUALS( 2U, dest.size() ); ASSERT( source == dest.vector() ); // can't use ASSERT_EQUALS // dest destroyed } ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); ASSERT_EQUALS( 2U, source.size() ); // source destroyed } // no additional deletions ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); } TEST(OwnedPointerVectorTest, TransferAssignment) { DestructionLogger::destroyedNames().clear(); { OwnedPointerVector dest; { OwnedPointerVector source; source.push_back( new DestructionLogger( "first" ) ); source.push_back( new DestructionLogger( "second" ) ); dest = source.release(); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 0U, source.size() ); ASSERT_EQUALS( 2U, dest.size() ); // source destroyed } ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 2U, dest.size() ); // dest destroyed } ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); } TEST(OwnedPointerVectorTest, TransferAssignmentDoesntModifyArgument) { DestructionLogger::destroyedNames().clear(); { OwnedPointerVector dest; { std::vector source; source.push_back( new DestructionLogger( "first" ) ); source.push_back( new DestructionLogger( "second" ) ); dest = source; ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 2U, source.size() ); ASSERT_EQUALS( 2U, dest.size() ); ASSERT( source == dest.vector() ); // can't use ASSERT_EQUALS // source destroyed } ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 2U, dest.size() ); // dest destroyed } ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); } TEST(OwnedPointerVectorTest, ReleaseAt) { DestructionLogger::destroyedNames().clear(); boost::scoped_ptr holder; { // names are order of deletion OwnedPointerVector owned; owned.push_back( new DestructionLogger( "first" ) ); owned.push_back( new DestructionLogger( "third" ) ); owned.push_back( new DestructionLogger( "second" ) ); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); // transfer ownership of "third" to holder holder.reset(owned.releaseAt(1)); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 3U, owned.size() ); ASSERT_EQUALS( static_cast(NULL), owned[1] ); // owned destroyed } // owned deleted "first" and "second", but not "third" ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[0] ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[1] ); // delete "third" holder.reset(); ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "third", DestructionLogger::destroyedNames().back() ); } TEST(OwnedPointerVectorTest, PopAndReleaseBack) { DestructionLogger::destroyedNames().clear(); { // names are order of deletion OwnedPointerVector owned; owned.push_back( new DestructionLogger( "second" ) ); owned.push_back( new DestructionLogger( "third" ) ); owned.push_back( new DestructionLogger( "first" ) ); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); { // transfer ownership of "third" to holder boost::scoped_ptr holder(owned.popAndReleaseBack()); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( 2U, owned.size() ); // holder destroyed } ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames().back() ); // owned destroyed } // owned destructor deleted "second" and "third", but not "first" ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[1] ); ASSERT_EQUALS( "third", DestructionLogger::destroyedNames()[2] ); } TEST(OwnedPointerVectorTest, PopAndDeleteBack) { DestructionLogger::destroyedNames().clear(); { // names are order of deletion OwnedPointerVector owned; owned.push_back( new DestructionLogger( "second" ) ); owned.push_back( new DestructionLogger( "third" ) ); owned.push_back( new DestructionLogger( "first" ) ); ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); owned.popAndDeleteBack(); ASSERT_EQUALS( 2U, owned.size() ); ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "first", DestructionLogger::destroyedNames().back() ); // owned destroyed } // owned destructor deleted "second" and "third", but not "first" ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[1] ); ASSERT_EQUALS( "third", DestructionLogger::destroyedNames()[2] ); } } // namespace } // namespace mongo