summaryrefslogtreecommitdiff
path: root/src/mongo/dbtests/rollbacktests.cpp
diff options
context:
space:
mode:
authorDavid Percy <david.percy@mongodb.com>2014-11-05 11:42:40 -0500
committerDavid Percy <david.percy@mongodb.com>2014-11-06 19:18:16 -0500
commite1fa60e4d8b3f525575b4602b208f2a1ef353735 (patch)
tree634a6debfaa9d1f8e3eb58403f085b1b4607850a /src/mongo/dbtests/rollbacktests.cpp
parenta1fc18243a20cc8c5fd4ef7ee3e9f2078b4139d7 (diff)
downloadmongo-e1fa60e4d8b3f525575b4602b208f2a1ef353735.tar.gz
SERVER-13635 Test collection and index rollback
Diffstat (limited to 'src/mongo/dbtests/rollbacktests.cpp')
-rw-r--r--src/mongo/dbtests/rollbacktests.cpp684
1 files changed, 684 insertions, 0 deletions
diff --git a/src/mongo/dbtests/rollbacktests.cpp b/src/mongo/dbtests/rollbacktests.cpp
new file mode 100644
index 00000000000..324f97c7924
--- /dev/null
+++ b/src/mongo/dbtests/rollbacktests.cpp
@@ -0,0 +1,684 @@
+// rollbacktests.cpp
+
+/**
+ * Copyright (C) 2014 MongoDB 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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.
+ */
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/database_catalog_entry.h"
+#include "mongo/db/catalog/database_holder.h"
+#include "mongo/db/catalog/index_create.h"
+#include "mongo/db/diskloc.h"
+#include "mongo/db/operation_context_impl.h"
+#include "mongo/dbtests/dbtests.h"
+#include "mongo/unittest/unittest.h"
+
+using std::list;
+using std::string;
+
+namespace RollbackTests {
+
+namespace {
+ void dropDatabase( OperationContext* txn, const NamespaceString& nss ) {
+ Lock::GlobalWrite globalWriteLock( txn->lockState() );
+ Database* db = dbHolder().get( txn, nss.db() );
+
+ if ( db ) {
+ dropDatabase( txn, db );
+ }
+ }
+ bool collectionExists( Client::Context* ctx, const string& ns ) {
+ const DatabaseCatalogEntry* dbEntry = ctx->db()->getDatabaseCatalogEntry();
+ list<string> names;
+ dbEntry->getCollectionNamespaces( &names );
+ return std::find( names.begin(), names.end(), ns ) != names.end();
+ }
+ void createCollection( OperationContext* txn, const NamespaceString& nss ) {
+ Lock::DBLock dbXLock( txn->lockState(), nss.db(), MODE_X );
+ Client::Context ctx( txn, nss.ns() );
+ {
+ WriteUnitOfWork uow( txn );
+ ASSERT( !collectionExists( &ctx, nss.ns() ) );
+ ASSERT_OK( userCreateNS( txn, ctx.db(), nss.ns(), BSONObj(), false, false ) );
+ ASSERT( collectionExists( &ctx, nss.ns() ) );
+ uow.commit();
+ }
+ }
+ Status renameCollection( OperationContext* txn,
+ const NamespaceString& source,
+ const NamespaceString& target ) {
+ ASSERT_EQ( source.db(), target.db() );
+ Database* db = dbHolder().get( txn, source.db() );
+ return db->renameCollection( txn, source.ns(), target.ns(), false );
+ }
+ Status truncateCollection( OperationContext* txn, const NamespaceString& nss ) {
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ return coll->truncate( txn );
+ }
+ DiskLoc insertRecord( OperationContext* txn,
+ const NamespaceString& nss,
+ const BSONObj& data ) {
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ StatusWith<DiskLoc> status = coll->insertDocument( txn, data, false );
+ ASSERT_OK( status.getStatus() );
+ return status.getValue();
+ }
+ void assertOnlyRecord( OperationContext* txn,
+ const NamespaceString& nss,
+ const BSONObj& data ) {
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ scoped_ptr<RecordIterator> iter( coll->getIterator( txn ) );
+ ASSERT( !iter->isEOF() );
+ DiskLoc loc = iter->getNext();
+ ASSERT( iter->isEOF() );
+ ASSERT_EQ( data, coll->docFor( txn, loc ) );
+ }
+ void assertEmpty( OperationContext* txn, const NamespaceString& nss ) {
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ scoped_ptr<RecordIterator> iter( coll->getIterator( txn ) );
+ ASSERT( iter->isEOF() );
+ }
+ bool indexExists( OperationContext* txn, const NamespaceString& nss, const string& idxName ) {
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ return coll->getIndexCatalog()->findIndexByName( txn, idxName, true ) != NULL;
+ }
+ bool indexReady( OperationContext* txn, const NamespaceString& nss, const string& idxName ) {
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ return coll->getIndexCatalog()->findIndexByName( txn, idxName, false ) != NULL;
+ }
+ size_t getNumIndexEntries( OperationContext* txn,
+ const NamespaceString& nss,
+ const string& idxName ) {
+ size_t numEntries = 0;
+
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ IndexCatalog* catalog = coll->getIndexCatalog();
+ IndexDescriptor* desc = catalog->findIndexByName( txn, idxName, false );
+
+ if ( desc ) {
+ CursorOptions cursorOptions;
+ cursorOptions.direction = CursorOptions::INCREASING;
+
+ IndexCursor *cursor;
+ ASSERT_OK( catalog->getIndex( desc )->newCursor( txn, cursorOptions, &cursor ) );
+ ASSERT_OK( cursor->seek( minKey ) );
+
+ while ( !cursor->isEOF() ) {
+ numEntries++;
+ cursor->next();
+ }
+ delete cursor;
+ }
+
+ return numEntries;
+ }
+ void dropIndex( OperationContext* txn, const NamespaceString& nss, const string& idxName ) {
+ Collection* coll = dbHolder().get( txn, nss.db() )->getCollection( txn, nss.ns() );
+ IndexDescriptor* desc = coll->getIndexCatalog()->findIndexByName( txn, idxName );
+ ASSERT( desc );
+ ASSERT_OK( coll->getIndexCatalog()->dropIndex( txn, desc ) );
+ }
+} // namespace
+
+ template<bool rollback, bool defaultIndexes>
+ class CreateCollection {
+ public:
+ void run() {
+ string ns = "unittests.rollback_create_collection";
+ OperationContextImpl txn;
+ NamespaceString nss( ns );
+ dropDatabase( &txn, nss );
+
+ Lock::DBLock dbXLock( txn.lockState(), nss.db(), MODE_X );
+ Client::Context ctx( &txn, ns );
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT( !collectionExists( &ctx, ns ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), ns, BSONObj(), false, defaultIndexes ) );
+ ASSERT( collectionExists( &ctx, ns ) );
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ if ( rollback ) {
+ ASSERT( !collectionExists( &ctx, ns ) );
+ }
+ else {
+ ASSERT( collectionExists( &ctx, ns ) );
+ }
+ }
+ };
+
+ template<bool rollback, bool defaultIndexes>
+ class DropCollection {
+ public:
+ void run() {
+ string ns = "unittests.rollback_drop_collection";
+ OperationContextImpl txn;
+ NamespaceString nss( ns );
+ dropDatabase( &txn, nss );
+
+ Lock::DBLock dbXLock( txn.lockState(), nss.db(), MODE_X );
+ Client::Context ctx( &txn, ns );
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT( !collectionExists( &ctx, ns ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), ns, BSONObj(), false, defaultIndexes ) );
+ uow.commit();
+ }
+ ASSERT( collectionExists( &ctx, ns ) );
+
+ // END OF SETUP / START OF TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT( collectionExists( &ctx, ns ) );
+ ASSERT_OK( ctx.db()->dropCollection( &txn, ns ) );
+ ASSERT( !collectionExists( &ctx, ns ) );
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ if ( rollback ) {
+ ASSERT( collectionExists( &ctx, ns ) );
+ }
+ else {
+ ASSERT( !collectionExists( &ctx, ns ) );
+ }
+ }
+ };
+
+ template<bool rollback, bool defaultIndexes>
+ class RenameCollection {
+ public:
+ void run() {
+ NamespaceString source( "unittests.rollback_rename_collection_src" );
+ NamespaceString target( "unittests.rollback_rename_collection_dest" );
+ OperationContextImpl txn;
+
+ dropDatabase( &txn, source );
+ dropDatabase( &txn, target );
+
+ Lock::GlobalWrite globalWriteLock( txn.lockState() );
+ Client::Context ctx( &txn, source );
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT( !collectionExists( &ctx, source ) );
+ ASSERT( !collectionExists( &ctx, target ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), source.ns(), BSONObj(), false,
+ defaultIndexes ) );
+ uow.commit();
+ }
+ ASSERT( collectionExists( &ctx, source ) );
+ ASSERT( !collectionExists( &ctx, target ) );
+
+ // END OF SETUP / START OF TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT_OK( renameCollection( &txn, source, target ) );
+ ASSERT( !collectionExists( &ctx, source ) );
+ ASSERT( collectionExists( &ctx, target ) );
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ if ( rollback ) {
+ ASSERT( collectionExists( &ctx, source ) );
+ ASSERT( !collectionExists( &ctx, target ) );
+ }
+ else {
+ ASSERT( !collectionExists( &ctx, source ) );
+ ASSERT( collectionExists( &ctx, target ) );
+ }
+ }
+ };
+
+ template<bool rollback, bool defaultIndexes>
+ class RenameDropTargetCollection {
+ public:
+ void run() {
+ NamespaceString source( "unittests.rollback_rename_droptarget_collection_src" );
+ NamespaceString target( "unittests.rollback_rename_droptarget_collection_dest" );
+ OperationContextImpl txn;
+
+ dropDatabase( &txn, source );
+ dropDatabase( &txn, target );
+
+ Lock::GlobalWrite globalWriteLock( txn.lockState() );
+ Client::Context ctx( &txn, source );
+
+ BSONObj sourceDoc = BSON( "_id" << "source" );
+ BSONObj targetDoc = BSON( "_id" << "target" );
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT( !collectionExists( &ctx, source ) );
+ ASSERT( !collectionExists( &ctx, target ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), source.ns(), BSONObj(), false,
+ defaultIndexes ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), target.ns(), BSONObj(), false,
+ defaultIndexes ) );
+
+ insertRecord( &txn, source, sourceDoc );
+ insertRecord( &txn, target, targetDoc );
+
+ uow.commit();
+ }
+ ASSERT( collectionExists( &ctx, source ) );
+ ASSERT( collectionExists( &ctx, target ) );
+ assertOnlyRecord( &txn, source, sourceDoc );
+ assertOnlyRecord( &txn, target, targetDoc );
+
+ // END OF SETUP / START OF TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT_OK( ctx.db()->dropCollection( &txn, target.ns() ) );
+ ASSERT_OK( renameCollection( &txn, source, target ) );
+ ASSERT( !collectionExists( &ctx, source ) );
+ ASSERT( collectionExists( &ctx, target ) );
+ assertOnlyRecord( &txn, target, sourceDoc );
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ if ( rollback ) {
+ ASSERT( collectionExists( &ctx, source ) );
+ ASSERT( collectionExists( &ctx, target ) );
+ assertOnlyRecord( &txn, source, sourceDoc );
+ assertOnlyRecord( &txn, target, targetDoc );
+ }
+ else {
+ ASSERT( !collectionExists( &ctx, source ) );
+ ASSERT( collectionExists( &ctx, target ) );
+ assertOnlyRecord( &txn, target, sourceDoc );
+ }
+ }
+ };
+
+ template<bool rollback, bool defaultIndexes>
+ class ReplaceCollection {
+ public:
+ void run() {
+ NamespaceString nss( "unittests.rollback_replace_collection" );
+ OperationContextImpl txn;
+ dropDatabase( &txn, nss );
+
+ Lock::DBLock dbXLock( txn.lockState(), nss.db(), MODE_X );
+ Client::Context ctx( &txn, nss );
+
+ BSONObj oldDoc = BSON( "_id" << "old" );
+ BSONObj newDoc = BSON( "_id" << "new" );
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT( !collectionExists( &ctx, nss ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), nss.ns(), BSONObj(), false,
+ defaultIndexes ) );
+ insertRecord( &txn, nss, oldDoc );
+ uow.commit();
+ }
+ ASSERT( collectionExists( &ctx, nss ) );
+ assertOnlyRecord( &txn, nss, oldDoc );
+
+ // END OF SETUP / START OF TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT_OK( ctx.db()->dropCollection( &txn, nss.ns() ) );
+ ASSERT( !collectionExists( &ctx, nss ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), nss.ns(), BSONObj(), false,
+ defaultIndexes ) );
+ ASSERT( collectionExists( &ctx, nss ) );
+ insertRecord( &txn, nss, newDoc );
+ assertOnlyRecord( &txn, nss, newDoc );
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ ASSERT( collectionExists( &ctx, nss ) );
+ if ( rollback ) {
+ assertOnlyRecord( &txn, nss, oldDoc );
+ }
+ else {
+ assertOnlyRecord( &txn, nss, newDoc );
+ }
+ }
+ };
+
+ template<bool rollback, bool defaultIndexes>
+ class CreateDropCollection {
+ public:
+ void run() {
+ NamespaceString nss( "unittests.rollback_create_drop_collection" );
+ OperationContextImpl txn;
+ dropDatabase( &txn, nss );
+
+ Lock::DBLock dbXLock( txn.lockState(), nss.db(), MODE_X );
+ Client::Context ctx( &txn, nss );
+
+ BSONObj doc = BSON( "_id" << "example string" );
+
+ ASSERT( !collectionExists( &ctx, nss ) );
+ {
+ WriteUnitOfWork uow( &txn );
+
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), nss.ns(), BSONObj(), false,
+ defaultIndexes ) );
+ ASSERT( collectionExists( &ctx, nss ) );
+ insertRecord( &txn, nss, doc );
+ assertOnlyRecord( &txn, nss, doc );
+
+ ASSERT_OK( ctx.db()->dropCollection( &txn, nss.ns() ) );
+ ASSERT( !collectionExists( &ctx, nss ) );
+
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ ASSERT( !collectionExists( &ctx, nss ) );
+ }
+ };
+
+ template<bool rollback, bool defaultIndexes>
+ class TruncateCollection {
+ public:
+ void run() {
+ NamespaceString nss( "unittests.rollback_truncate_collection" );
+ OperationContextImpl txn;
+ dropDatabase( &txn, nss );
+
+ Lock::DBLock dbXLock( txn.lockState(), nss.db(), MODE_X );
+ Client::Context ctx( &txn, nss );
+
+ BSONObj doc = BSON( "_id" << "foo" );
+
+ ASSERT( !collectionExists( &ctx, nss ) );
+ {
+ WriteUnitOfWork uow( &txn );
+
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), nss.ns(), BSONObj(), false,
+ defaultIndexes ) );
+ ASSERT( collectionExists( &ctx, nss ) );
+ insertRecord( &txn, nss, doc );
+ assertOnlyRecord( &txn, nss, doc );
+ uow.commit();
+ }
+ assertOnlyRecord( &txn, nss, doc );
+
+ // END OF SETUP / START OF TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+
+ ASSERT_OK( truncateCollection( &txn, nss ) );
+ ASSERT( collectionExists( &ctx, nss ) );
+ assertEmpty( &txn, nss );
+
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ ASSERT( collectionExists( &ctx, nss ) );
+ if ( rollback ) {
+ assertOnlyRecord( &txn, nss, doc );
+ }
+ else {
+ assertEmpty( &txn, nss );
+ }
+ }
+ };
+
+ template<bool rollback>
+ class CreateIndex {
+ public:
+ void run() {
+ string ns = "unittests.rollback_create_index";
+ OperationContextImpl txn;
+ NamespaceString nss( ns );
+ dropDatabase( &txn, nss );
+ createCollection( &txn, nss );
+
+ Lock::DBLock dbIXLock( txn.lockState(), nss.db(), MODE_IX );
+ Lock::CollectionLock collXLock( txn.lockState(), ns, MODE_X );
+
+ Client::Context ctx( &txn, ns );
+ Collection* coll = ctx.db()->getCollection( &txn, ns );
+ IndexCatalog* catalog = coll->getIndexCatalog();
+
+ string idxName = "a";
+ BSONObj spec = BSON( "ns" << ns << "key" << BSON( "a" << 1 ) << "name" << idxName );
+
+ // END SETUP / START TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT_OK( catalog->createIndexOnEmptyCollection( &txn, spec ) );
+ insertRecord( &txn, nss, BSON( "a" << 1 ) );
+ insertRecord( &txn, nss, BSON( "a" << 2 ) );
+ insertRecord( &txn, nss, BSON( "a" << 3 ) );
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+
+ if ( rollback ) {
+ ASSERT( !indexExists( &txn, nss, idxName ) );
+ }
+ else {
+ ASSERT( indexReady( &txn, nss, idxName ) );
+ }
+ }
+ };
+
+ template<bool rollback>
+ class DropIndex {
+ public:
+ void run() {
+ string ns = "unittests.rollback_drop_index";
+ OperationContextImpl txn;
+ NamespaceString nss( ns );
+ dropDatabase( &txn, nss );
+ createCollection( &txn, nss );
+
+ Lock::DBLock dbIXLock( txn.lockState(), nss.db(), MODE_IX );
+ Lock::CollectionLock collXLock( txn.lockState(), ns, MODE_X );
+
+ Client::Context ctx( &txn, ns );
+ Collection* coll = ctx.db()->getCollection( &txn, ns );
+ IndexCatalog* catalog = coll->getIndexCatalog();
+
+ string idxName = "a";
+ BSONObj spec = BSON( "ns" << ns << "key" << BSON( "a" << 1 ) << "name" << idxName );
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT_OK( catalog->createIndexOnEmptyCollection( &txn, spec ) );
+ insertRecord( &txn, nss, BSON( "a" << 1 ) );
+ insertRecord( &txn, nss, BSON( "a" << 2 ) );
+ insertRecord( &txn, nss, BSON( "a" << 3 ) );
+ uow.commit();
+ }
+ ASSERT( indexReady( &txn, nss, idxName ) );
+ ASSERT_EQ( 3u, getNumIndexEntries( &txn, nss, idxName ) );
+
+ // END SETUP / START TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+
+ dropIndex( &txn, nss, idxName );
+ ASSERT( !indexExists( &txn, nss, idxName ) );
+
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+ if ( rollback ) {
+ ASSERT( indexExists( &txn, nss, idxName ) );
+ ASSERT( indexReady( &txn, nss, idxName ) );
+ ASSERT_EQ( 3u, getNumIndexEntries( &txn, nss, idxName ) );
+ }
+ else {
+ ASSERT( !indexExists( &txn, nss, idxName ) );
+ }
+ }
+ };
+
+ template<bool rollback>
+ class CreateDropIndex {
+ public:
+ void run() {
+ string ns = "unittests.rollback_create_drop_index";
+ OperationContextImpl txn;
+ NamespaceString nss( ns );
+ dropDatabase( &txn, nss );
+ createCollection( &txn, nss );
+
+ Lock::DBLock dbIXLock( txn.lockState(), nss.db(), MODE_IX );
+ Lock::CollectionLock collXLock( txn.lockState(), ns, MODE_X );
+
+ Client::Context ctx( &txn, ns );
+ Collection* coll = ctx.db()->getCollection( &txn, ns );
+ IndexCatalog* catalog = coll->getIndexCatalog();
+
+ string idxName = "a";
+ BSONObj spec = BSON( "ns" << ns << "key" << BSON( "a" << 1 ) << "name" << idxName );
+
+ // END SETUP / START TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+
+ ASSERT_OK( catalog->createIndexOnEmptyCollection( &txn, spec ) );
+ insertRecord( &txn, nss, BSON( "a" << 1 ) );
+ insertRecord( &txn, nss, BSON( "a" << 2 ) );
+ insertRecord( &txn, nss, BSON( "a" << 3 ) );
+ ASSERT( indexExists( &txn, nss, idxName ) );
+ ASSERT_EQ( 3u, getNumIndexEntries( &txn, nss, idxName ) );
+
+ dropIndex( &txn, nss, idxName );
+ ASSERT( !indexExists( &txn, nss, idxName ) );
+
+ if ( !rollback ) {
+ uow.commit();
+ }
+ }
+
+ ASSERT( !indexExists( &txn, nss, idxName ) );
+ }
+ };
+
+ template<bool rollback>
+ class CreateCollectionAndIndexes {
+ public:
+ void run() {
+ string ns = "unittests.rollback_create_collection_and_indexes";
+ OperationContextImpl txn;
+ NamespaceString nss( ns );
+ dropDatabase( &txn, nss );
+ Lock::DBLock dbXLock( txn.lockState(), nss.db(), MODE_X );
+ Client::Context ctx( &txn, nss.ns() );
+ string idxNameA = "indexA";
+ string idxNameB = "indexB";
+ string idxNameC = "indexC";
+ BSONObj specA = BSON( "ns" << ns << "key" << BSON( "a" << 1 ) << "name" << idxNameA );
+ BSONObj specB = BSON( "ns" << ns << "key" << BSON( "b" << 1 ) << "name" << idxNameB );
+ BSONObj specC = BSON( "ns" << ns << "key" << BSON( "c" << 1 ) << "name" << idxNameC );
+
+ // END SETUP / START TEST
+
+ {
+ WriteUnitOfWork uow( &txn );
+ ASSERT( !collectionExists( &ctx, nss.ns() ) );
+ ASSERT_OK( userCreateNS( &txn, ctx.db(), nss.ns(), BSONObj(), false, false ) );
+ ASSERT( collectionExists( &ctx, nss.ns() ) );
+ Collection* coll = ctx.db()->getCollection( &txn, ns );
+ IndexCatalog* catalog = coll->getIndexCatalog();
+
+ ASSERT_OK( catalog->createIndexOnEmptyCollection( &txn, specA ) );
+ ASSERT_OK( catalog->createIndexOnEmptyCollection( &txn, specB ) );
+ ASSERT_OK( catalog->createIndexOnEmptyCollection( &txn, specC ) );
+
+ if ( !rollback ) {
+ uow.commit();
+ }
+ } // uow
+ if ( rollback ) {
+ ASSERT( !collectionExists( &ctx, ns ) );
+ }
+ else {
+ ASSERT( collectionExists( &ctx, ns ) );
+ ASSERT( indexReady( &txn, nss, idxNameA ) );
+ ASSERT( indexReady( &txn, nss, idxNameB ) );
+ ASSERT( indexReady( &txn, nss, idxNameC ) );
+ }
+ }
+ };
+
+
+ class All : public Suite {
+ public:
+ All() : Suite( "rollback" ) {
+ }
+
+ template< template<bool> class T >
+ void addAll() {
+ add< T<false> >();
+ add< T<true> >();
+ }
+
+ template< template<bool, bool> class T >
+ void addAll() {
+ add< T<false, false> >();
+ add< T<true, false> >();
+ add< T<false, true> >();
+ add< T<true, true> >();
+ }
+
+ void setupTests() {
+ addAll< CreateCollection >();
+ addAll< RenameCollection >();
+ addAll< DropCollection >();
+ addAll< RenameDropTargetCollection >();
+ addAll< ReplaceCollection >();
+ addAll< CreateDropCollection >();
+ addAll< TruncateCollection >();
+ addAll< CreateIndex >();
+ addAll< DropIndex >();
+ addAll< CreateDropIndex >();
+ addAll< CreateCollectionAndIndexes >();
+ }
+ };
+
+ SuiteInstance<All> all;
+
+} // namespace RollbackTests
+