diff options
21 files changed, 2667 insertions, 2 deletions
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript index 591c8e4a0af..3bfcb27e074 100644 --- a/src/mongo/db/storage/SConscript +++ b/src/mongo/db/storage/SConscript @@ -41,7 +41,22 @@ env.Library( env.Library( target='record_store_test_harness', source=[ + 'record_store_test_customoption.cpp', + 'record_store_test_datafor.cpp', + 'record_store_test_datasize.cpp', + 'record_store_test_deleterecord.cpp', 'record_store_test_harness.cpp', + 'record_store_test_insertrecord.cpp', + 'record_store_test_manyiter.cpp', + 'record_store_test_recorditer.cpp', + 'record_store_test_recordstore.cpp', + 'record_store_test_repairiter.cpp', + 'record_store_test_storagesize.cpp', + 'record_store_test_touch.cpp', + 'record_store_test_truncate.cpp', + 'record_store_test_updaterecord.cpp', + 'record_store_test_updatewithdamages.cpp', + 'record_store_test_validate.cpp', ], LIBDEPS=[] ) diff --git a/src/mongo/db/storage/heap1/record_store_heap.cpp b/src/mongo/db/storage/heap1/record_store_heap.cpp index 98f41926be3..84fe77ddb6b 100644 --- a/src/mongo/db/storage/heap1/record_store_heap.cpp +++ b/src/mongo/db/storage/heap1/record_store_heap.cpp @@ -328,7 +328,7 @@ namespace mongo { return Status::OK(); } - return Status( ErrorCodes::BadValue, + return Status( ErrorCodes::InvalidOptions, mongoutils::str::stream() << "unknown custom option to HeapRecordStore: " << name ); @@ -462,7 +462,10 @@ namespace mongo { _it = _records.rbegin(); } else { - _it = HeapRecordStore::Records::const_reverse_iterator(_records.find(start)); + // The reverse iterator will point to the preceding element, so we + // increment the base iterator to make it point past the found element + HeapRecordStore::Records::const_iterator baseIt(++_records.find(start)); + _it = HeapRecordStore::Records::const_reverse_iterator(baseIt); invariant(_it != _records.rend()); } } diff --git a/src/mongo/db/storage/record_data.h b/src/mongo/db/storage/record_data.h index 61428b7ca61..6da51edec3e 100644 --- a/src/mongo/db/storage/record_data.h +++ b/src/mongo/db/storage/record_data.h @@ -30,6 +30,10 @@ #pragma once +#include <boost/shared_array.hpp> + +#include "mongo/bson/bsonobj.h" + namespace mongo { /** diff --git a/src/mongo/db/storage/record_store_test_customoption.cpp b/src/mongo/db/storage/record_store_test_customoption.cpp new file mode 100644 index 00000000000..ab5e51be113 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_customoption.cpp @@ -0,0 +1,89 @@ +// record_store_test_customoption.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/db/storage/record_store_test_harness.h" + +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/bsonmisc.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { + + // Verify that calling setCustomOption() with { "usePowerOf2Sizes": true } + // returns an OK status. + TEST( RecordStoreTestHarness, SetPowerOf2SizesOptionTrue ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + BSONObjBuilder info; + BSONObj option = BSON( "usePowerOf2Sizes" << true ); + ASSERT_OK( rs->setCustomOption( opCtx.get(), option.firstElement(), &info ) ); + } + } + } + + // Verify that calling setCustomOption() with { "usePowerOf2Sizes": false } + // returns an OK status. + TEST( RecordStoreTestHarness, SetPowerOf2SizesOptionFalse ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + BSONObjBuilder info; + BSONObj option = BSON( "usePowerOf2Sizes" << false ); + ASSERT_OK( rs->setCustomOption( opCtx.get(), option.firstElement(), &info ) ); + } + } + } + + // Verify that calling setCustomOption() with a nonexistent option + // returns an ErrorCodes::InvalidOptions status. + TEST( RecordStoreTestHarness, SetNonExistentOption ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + BSONObjBuilder info; + BSONObj option = BSON( "aabacadbbcbdccdd" << false ); + ASSERT_EQUALS( ErrorCodes::InvalidOptions, + rs->setCustomOption( opCtx.get(), option.firstElement(), &info ) ); + } + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_datafor.cpp b/src/mongo/db/storage/record_store_test_datafor.cpp new file mode 100644 index 00000000000..741d331007d --- /dev/null +++ b/src/mongo/db/storage/record_store_test_datafor.cpp @@ -0,0 +1,135 @@ +// record_store_test_datafor.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Insert a record and verify its contents by calling dataFor() + // on the returned DiskLoc. + TEST( RecordStoreTestHarness, DataFor ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "my record"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + RecordData record = rs->dataFor( opCtx.get(), loc ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + + // Insert multiple records and verify their contents by calling dataFor() + // on each of the returned DiskLocs. + TEST( RecordStoreTestHarness, DataForMultiple ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + RecordData record = rs->dataFor( opCtx.get(), locs[i] ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_datasize.cpp b/src/mongo/db/storage/record_store_test_datasize.cpp new file mode 100644 index 00000000000..4bcb67f8477 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_datasize.cpp @@ -0,0 +1,96 @@ +// record_store_test_datasize.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Verify that an empty collection takes up no space. + TEST( RecordStoreTestHarness, DataSizeEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT( rs->dataSize( opCtx.get() ) == 0 ); + } + } + + // Verify that a nonempty collection takes up some space. + TEST( RecordStoreTestHarness, DataSizeNonEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + int nToInsert = 10; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT( rs->dataSize( opCtx.get() ) > 0 ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_deleterecord.cpp b/src/mongo/db/storage/record_store_test_deleterecord.cpp new file mode 100644 index 00000000000..6d589b418fa --- /dev/null +++ b/src/mongo/db/storage/record_store_test_deleterecord.cpp @@ -0,0 +1,139 @@ +// record_store_test_deleterecord.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Insert a record and try to delete it. + TEST( RecordStoreTestHarness, DeleteRecord ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "my record"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + rs->deleteRecord( opCtx.get(), loc ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + } + + // Insert multiple records and try to delete them. + TEST( RecordStoreTestHarness, DeleteMultipleRecords ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + rs->deleteRecord( opCtx.get(), locs[i] ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_docwriter.h b/src/mongo/db/storage/record_store_test_docwriter.h new file mode 100644 index 00000000000..14e741c48d8 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_docwriter.h @@ -0,0 +1,62 @@ +// record_store_test_docwriter.h + +/** + * 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. + */ + +#pragma once + +#include "mongo/db/storage/record_store.h" + +using std::string; + +namespace mongo { +namespace { + + class StringDocWriter : public DocWriter { + public: + StringDocWriter( const string &data, bool padding ) + : _data( data ), _padding( padding ) { + } + + ~StringDocWriter() { } + + void writeDocument( char *buf ) const { + memcpy( buf, _data.c_str(), documentSize() ); + } + + size_t documentSize() const { return _data.size() + 1; } + + bool addPadding() const { return _padding; } + + private: + string _data; + bool _padding; + }; + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_insertrecord.cpp b/src/mongo/db/storage/record_store_test_insertrecord.cpp new file mode 100644 index 00000000000..5115d447729 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_insertrecord.cpp @@ -0,0 +1,182 @@ +// record_store_test_insertrecord.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/db/storage/record_store_test_docwriter.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Insert a record and verify the number of entries in the collection is 1. + TEST( RecordStoreTestHarness, InsertRecord ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "my record"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + } + + // Insert multiple records and verify the number of entries in the collection + // equals the number that were inserted. + TEST( RecordStoreTestHarness, InsertMultipleRecords ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + } + + // Insert a record using a DocWriter and verify the number of entries + // in the collection is 1. + TEST( RecordStoreTestHarness, InsertRecordUsingDocWriter ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + StringDocWriter docWriter( "my record", false ); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + &docWriter, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + } + + // Insert multiple records using a DocWriter and verify the number of entries + // in the collection equals the number that were inserted. + TEST( RecordStoreTestHarness, InsertMultipleRecordsUsingDocWriter ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + StringDocWriter docWriter( ss.str(), false ); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + &docWriter, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_manyiter.cpp b/src/mongo/db/storage/record_store_test_manyiter.cpp new file mode 100644 index 00000000000..1391637ecf7 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_manyiter.cpp @@ -0,0 +1,131 @@ +// record_store_test_manyiter.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::set; +using std::string; +using std::stringstream; +using std::vector; + +namespace mongo { + + // Create multiple iterators over an empty record store. + TEST( RecordStoreTestHarness, GetManyIteratorsEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + vector<RecordIterator*> v = rs->getManyIterators( opCtx.get() ); + + for (vector<RecordIterator*>::iterator vIter = v.begin(); + vIter != v.end(); vIter++) { + + RecordIterator *rIter = *vIter; + ASSERT( rIter->isEOF() ); + ASSERT_EQUALS( DiskLoc(), rIter->curr() ); + ASSERT_EQUALS( DiskLoc(), rIter->getNext() ); + ASSERT( rIter->isEOF() ); + ASSERT_EQUALS( DiskLoc(), rIter->curr() ); + } + } + } + + // Create multiple iterators over a nonempty record store. + TEST( RecordStoreTestHarness, GetManyIteratorsNonEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + set<DiskLoc> remain( locs, locs + nToInsert ); + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + vector<RecordIterator*> v = rs->getManyIterators( opCtx.get() ); + + for (vector<RecordIterator*>::iterator vIter = v.begin(); + vIter != v.end(); vIter++) { + + RecordIterator *rIter = *vIter; + while ( !rIter->isEOF() ) { + DiskLoc loc = rIter->curr(); + ASSERT( 1 == remain.erase( loc ) ); + ASSERT_EQUALS( loc, rIter->getNext() ); + } + + ASSERT_EQUALS( DiskLoc(), rIter->curr() ); + ASSERT_EQUALS( DiskLoc(), rIter->getNext() ); + ASSERT( rIter->isEOF() ); + ASSERT_EQUALS( DiskLoc(), rIter->curr() ); + } + ASSERT( remain.empty() ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_recorditer.cpp b/src/mongo/db/storage/record_store_test_recorditer.cpp new file mode 100644 index 00000000000..81e92be431a --- /dev/null +++ b/src/mongo/db/storage/record_store_test_recorditer.cpp @@ -0,0 +1,285 @@ +// record_store_test_recorditer.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/db/storage/record_store_test_harness.h" + +#include <algorithm> + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Insert multiple records and iterate through them in the forward direction. + // When curr() or getNext() is called on an iterator positioned at EOF, + // the iterator returns DiskLoc() and stays at EOF. + TEST( RecordStoreTestHarness, IterateOverMultipleRecords ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + std::sort( locs, locs + nToInsert ); // inserted records may not be in DiskLoc order + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + + RecordIterator *it = rs->getIterator( opCtx.get(), + DiskLoc(), + false, + CollectionScanParams::FORWARD ); + + for ( int i = 0; i < nToInsert; i++ ) { + ASSERT( !it->isEOF() ); + ASSERT_EQUALS( locs[i], it->curr() ); + ASSERT_EQUALS( locs[i], it->getNext() ); + } + ASSERT( it->isEOF() ); + + ASSERT_EQUALS( DiskLoc(), it->curr() ); + ASSERT_EQUALS( DiskLoc(), it->getNext() ); + ASSERT( it->isEOF() ); + ASSERT_EQUALS( DiskLoc(), it->curr() ); + } + } + + // Insert multiple records and iterate through them in the reverse direction. + // When curr() or getNext() is called on an iterator positioned at EOF, + // the iterator returns DiskLoc() and stays at EOF. + TEST( RecordStoreTestHarness, IterateOverMultipleRecordsReversed ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + std::sort( locs, locs + nToInsert ); // inserted records may not be in DiskLoc order + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + + RecordIterator *it = rs->getIterator( opCtx.get(), + DiskLoc(), + false, + CollectionScanParams::BACKWARD ); + + for ( int i = nToInsert - 1; i >= 0; i-- ) { + ASSERT( !it->isEOF() ); + ASSERT_EQUALS( locs[i], it->curr() ); + ASSERT_EQUALS( locs[i], it->getNext() ); + } + ASSERT( it->isEOF() ); + + ASSERT_EQUALS( DiskLoc(), it->curr() ); + ASSERT_EQUALS( DiskLoc(), it->getNext() ); + ASSERT( it->isEOF() ); + ASSERT_EQUALS( DiskLoc(), it->curr() ); + } + } + + // Insert multiple records and try to create a forward iterator + // starting at an interior position. + TEST( RecordStoreTestHarness, IterateStartFromMiddle ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + std::sort( locs, locs + nToInsert ); // inserted records may not be in DiskLoc order + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + + int start = nToInsert / 2; + RecordIterator *it = rs->getIterator( opCtx.get(), + locs[start], + false, + CollectionScanParams::FORWARD ); + + for ( int i = start; i < nToInsert; i++ ) { + ASSERT( !it->isEOF() ); + ASSERT_EQUALS( locs[i], it->curr() ); + ASSERT_EQUALS( locs[i], it->getNext() ); + } + ASSERT( it->isEOF() ); + + ASSERT_EQUALS( DiskLoc(), it->curr() ); + ASSERT_EQUALS( DiskLoc(), it->getNext() ); + ASSERT( it->isEOF() ); + ASSERT_EQUALS( DiskLoc(), it->curr() ); + } + } + + // Insert multiple records and try to create a reverse iterator + // starting at an interior position. + TEST( RecordStoreTestHarness, IterateStartFromMiddleReversed ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + std::sort( locs, locs + nToInsert ); // inserted records may not be in DiskLoc order + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + + int start = nToInsert / 2; + RecordIterator *it = rs->getIterator( opCtx.get(), + locs[start], + false, + CollectionScanParams::BACKWARD ); + + for ( int i = start; i >= 0; i-- ) { + ASSERT( !it->isEOF() ); + ASSERT_EQUALS( locs[i], it->curr() ); + ASSERT_EQUALS( locs[i], it->getNext() ); + } + ASSERT( it->isEOF() ); + + ASSERT_EQUALS( DiskLoc(), it->curr() ); + ASSERT_EQUALS( DiskLoc(), it->getNext() ); + ASSERT( it->isEOF() ); + ASSERT_EQUALS( DiskLoc(), it->curr() ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_recordstore.cpp b/src/mongo/db/storage/record_store_test_recordstore.cpp new file mode 100644 index 00000000000..8cca19a5426 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_recordstore.cpp @@ -0,0 +1,69 @@ +// record_store_test_recordstore.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; + +namespace mongo { + + // Verify that the name of the record store is not NULL and nonempty. + TEST ( RecordStoreTestHarness, RecordStoreName ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + const char *name = rs->name(); + ASSERT( name != NULL && name[0] != '\0' ); + } + } + + // Verify that the namespace of the record store is nonempty. + TEST( RecordStoreTestHarness, Namespace ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + string ns = rs->ns(); + ASSERT( ns[0] != '\0' ); + } + } + + // Call isCapped() on a non-capped collection and verify the result is false. + TEST( RecordStoreTestHarness, IsNotCapped ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + ASSERT( !rs->isCapped() ); + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_repairiter.cpp b/src/mongo/db/storage/record_store_test_repairiter.cpp new file mode 100644 index 00000000000..f58ebbab64e --- /dev/null +++ b/src/mongo/db/storage/record_store_test_repairiter.cpp @@ -0,0 +1,121 @@ +// record_store_test_repairiter.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::set; +using std::string; +using std::stringstream; + +namespace mongo { + + // Create an iterator for repairing an empty record store. + TEST( RecordStoreTestHarness, GetIteratorForRepairEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + RecordIterator *it = rs->getIteratorForRepair( opCtx.get() ); + + ASSERT( it->isEOF() ); + ASSERT_EQUALS( DiskLoc(), it->curr() ); + ASSERT_EQUALS( DiskLoc(), it->getNext() ); + ASSERT( it->isEOF() ); + ASSERT_EQUALS( DiskLoc(), it->curr() ); + } + } + + // Insert multiple records and create an iterator for repairing the record store, + // even though the it has not been corrupted. + TEST( RecordStoreTestHarness, GetIteratorForRepairNonEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + set<DiskLoc> remain( locs, locs + nToInsert ); + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + RecordIterator *it = rs->getIteratorForRepair( opCtx.get() ); + + while ( !it->isEOF() ) { + DiskLoc loc = it->curr(); + ASSERT( 1 == remain.erase( loc ) ); + ASSERT_EQUALS( loc, it->getNext() ); + } + ASSERT( remain.empty() ); + + ASSERT_EQUALS( DiskLoc(), it->curr() ); + ASSERT_EQUALS( DiskLoc(), it->getNext() ); + ASSERT( it->isEOF() ); + ASSERT_EQUALS( DiskLoc(), it->curr() ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_storagesize.cpp b/src/mongo/db/storage/record_store_test_storagesize.cpp new file mode 100644 index 00000000000..d9382d59f6b --- /dev/null +++ b/src/mongo/db/storage/record_store_test_storagesize.cpp @@ -0,0 +1,96 @@ +// record_store_test_storagesize.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Verify that an empty collection takes up no space on disk. + TEST( RecordStoreTestHarness, StorageSizeEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT( rs->storageSize( opCtx.get(), NULL ) == 0 ); + } + } + + // Verify that a nonempty collection maybe takes up some space on disk. + TEST( RecordStoreTestHarness, StorageSizeNonEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + int nToInsert = 10; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT( rs->storageSize( opCtx.get(), NULL ) >= 0 ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_touch.cpp b/src/mongo/db/storage/record_store_test_touch.cpp new file mode 100644 index 00000000000..cef55c82b11 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_touch.cpp @@ -0,0 +1,164 @@ +// record_store_test_touch.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Verify that calling touch() on an empty collection returns an OK status. + TEST( RecordStoreTestHarness, TouchEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + BSONObjBuilder stats; + ASSERT_OK( rs->touch( opCtx.get(), &stats ) ); + } + } + } + + // Insert multiple records, and verify that calling touch() on a nonempty collection + // returns an OK status. + TEST( RecordStoreTestHarness, TouchNonEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + int nToInsert = 10; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + BSONObjBuilder stats; + // XXX does not verify the collection was loaded into cache + // (even if supported by storage engine) + ASSERT_OK( rs->touch( opCtx.get(), &stats ) ); + } + } + } + + // Verify that calling touch() on an empty collection returns an OK status, + // even when NULL is passed in for the stats output. + TEST( RecordStoreTestHarness, TouchEmptyWithNullStats ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_OK( rs->touch( opCtx.get(), NULL /* stats output */ ) ); + } + } + + // Insert multiple records, and verify that calling touch() on a nonempty collection + // returns an OK status, even when NULL is passed in for the stats output. + TEST( RecordStoreTestHarness, TouchNonEmptyWithNullStats ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + int nToInsert = 10; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + // XXX does not verify the collection was loaded into cache + // (even if supported by storage engine) + ASSERT_OK( rs->touch( opCtx.get(), NULL /* stats output */ ) ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_truncate.cpp b/src/mongo/db/storage/record_store_test_truncate.cpp new file mode 100644 index 00000000000..538ddab3c54 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_truncate.cpp @@ -0,0 +1,115 @@ +// record_store_test_truncate.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Verify that calling truncate() on an already empty collection returns an OK status. + TEST( RecordStoreTestHarness, TruncateEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + ASSERT_OK( rs->truncate( opCtx.get() ) ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + } + + // Insert multiple records, and verify that calling truncate() on a nonempty collection + // removes all of them and returns an OK status. + TEST( RecordStoreTestHarness, TruncateNonEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + int nToInsert = 10; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + ASSERT_OK( rs->truncate( opCtx.get() ) ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_updaterecord.cpp b/src/mongo/db/storage/record_store_test_updaterecord.cpp new file mode 100644 index 00000000000..c4f744f98a2 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_updaterecord.cpp @@ -0,0 +1,238 @@ +// record_store_test_updaterecord.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/db/storage/record_store_test_updaterecord.h" + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/db/storage/record_store_test_harness.h" +#include "mongo/unittest/unittest.h" + +using std::string; +using std::stringstream; + +namespace mongo { + + // Insert a record and try to update it. + TEST( RecordStoreTestHarness, UpdateRecord ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "my record"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + data = "my updated record"; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->updateRecord( opCtx.get(), + loc, + data.c_str(), + data.size() + 1, + false, + NULL ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + RecordData record = rs->dataFor( opCtx.get(), loc ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + + // Insert multiple records and try to update them. + TEST( RecordStoreTestHarness, UpdateMultipleRecords ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + const int nToInsert = 10; + DiskLoc locs[nToInsert]; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( nToInsert, rs->numRecords( opCtx.get() ) ); + } + + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "updated record " << i; + string data = ss.str(); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->updateRecord( opCtx.get(), + locs[i], + data.c_str(), + data.size() + 1, + false, + NULL ); + ASSERT_OK( res.getStatus() ); + locs[i] = res.getValue(); + uow.commit(); + } + } + + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + stringstream ss; + ss << "updated record " << i; + string data = ss.str(); + + RecordData record = rs->dataFor( opCtx.get(), locs[i] ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + + // Insert a record, try to update it, and examine how the UpdateMoveNotifier is called. + TEST( RecordStoreTestHarness, UpdateRecordWithMoveNotifier ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string oldData = "my record"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + oldData.c_str(), + oldData.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + string newData = "my updated record"; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + UpdateMoveNotifierSpy umn( opCtx.get(), loc, oldData.c_str(), oldData.size() ); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->updateRecord( opCtx.get(), + loc, + newData.c_str(), + newData.size() + 1, + false, + &umn ); + ASSERT_OK( res.getStatus() ); + // UpdateMoveNotifier::recordStoreGoingToMove() called only if + // the DiskLoc for the record changes + if ( loc == res.getValue() ) { + ASSERT_EQUALS( 0, umn.getNumCalls() ); + } else { + ASSERT_EQUALS( 1, umn.getNumCalls() ); + } + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + RecordData record = rs->dataFor( opCtx.get(), loc ); + ASSERT_EQUALS( newData.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( newData, record.data() ); + } + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_updaterecord.h b/src/mongo/db/storage/record_store_test_updaterecord.h new file mode 100644 index 00000000000..57791337b63 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_updaterecord.h @@ -0,0 +1,75 @@ +// record_store_test_updaterecord.h + +/** + * 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. + */ + +#pragma once + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; + +namespace mongo { +namespace { + + class UpdateMoveNotifierSpy : public UpdateMoveNotifier { + public: + UpdateMoveNotifierSpy( OperationContext* txn, const DiskLoc &loc, + const char *buf, size_t size ) + : _txn( txn ), _loc( loc ), _data( buf, size ), nCalls( 0 ) { + } + + ~UpdateMoveNotifierSpy() { } + + Status recordStoreGoingToMove( OperationContext *txn, + const DiskLoc &oldLocation, + const char *oldBuffer, + size_t oldSize ) { + nCalls++; + ASSERT_EQUALS( _txn, txn ); + ASSERT_EQUALS( _loc, oldLocation ); + ASSERT_EQUALS( _data.size() + 1, oldSize ); + ASSERT_EQUALS( _data, oldBuffer ); + return Status::OK(); + } + + int getNumCalls() const { return nCalls; } + + private: + OperationContext *_txn; + DiskLoc _loc; + string _data; + + int nCalls; // to verify that recordStoreGoingToMove() gets called once + }; + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_updatewithdamages.cpp b/src/mongo/db/storage/record_store_test_updatewithdamages.cpp new file mode 100644 index 00000000000..7a7b7916f1f --- /dev/null +++ b/src/mongo/db/storage/record_store_test_updatewithdamages.cpp @@ -0,0 +1,277 @@ +// record_store_test_updatewithdamages.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/db/storage/record_store_test_harness.h" + +#include "mongo/db/diskloc.h" +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/unittest/unittest.h" + +using std::string; + +namespace mongo { + + // Insert a record and try to perform an in-place update on it. + TEST( RecordStoreTestHarness, UpdateWithDamages ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "00010111"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + mutablebson::DamageVector dv( 3 ); + dv[0].sourceOffset = 5; + dv[0].targetOffset = 0; + dv[0].size = 2; + dv[1].sourceOffset = 3; + dv[1].targetOffset = 2; + dv[1].size = 3; + dv[2].sourceOffset = 0; + dv[2].targetOffset = 5; + dv[2].size = 3; + + WriteUnitOfWork uow( opCtx.get() ); + ASSERT_OK( rs->updateWithDamages( opCtx.get(), loc, data.c_str(), dv ) ); + uow.commit(); + } + } + + data = "11101000"; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + RecordData record = rs->dataFor( opCtx.get(), loc ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + + // Insert a record and try to perform an in-place update on it with a DamageVector + // containing overlapping DamageEvents. + TEST( RecordStoreTestHarness, UpdateWithOverlappingDamageEvents ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "00010111"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + mutablebson::DamageVector dv( 2 ); + dv[0].sourceOffset = 3; + dv[0].targetOffset = 0; + dv[0].size = 5; + dv[1].sourceOffset = 0; + dv[1].targetOffset = 3; + dv[1].size = 5; + + WriteUnitOfWork uow( opCtx.get() ); + ASSERT_OK( rs->updateWithDamages( opCtx.get(), loc, data.c_str(), dv ) ); + uow.commit(); + } + } + + data = "10100010"; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + RecordData record = rs->dataFor( opCtx.get(), loc ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + + // Insert a record and try to perform an in-place update on it with a DamageVector + // containing overlapping DamageEvents. The changes should be applied in the order + // specified by the DamageVector, and not -- for instance -- by the targetOffset. + TEST( RecordStoreTestHarness, UpdateWithOverlappingDamageEventsReversed ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "00010111"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + mutablebson::DamageVector dv( 2 ); + dv[0].sourceOffset = 0; + dv[0].targetOffset = 3; + dv[0].size = 5; + dv[1].sourceOffset = 3; + dv[1].targetOffset = 0; + dv[1].size = 5; + + WriteUnitOfWork uow( opCtx.get() ); + ASSERT_OK( rs->updateWithDamages( opCtx.get(), loc, data.c_str(), dv ) ); + uow.commit(); + } + } + + data = "10111010"; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + RecordData record = rs->dataFor( opCtx.get(), loc ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + + // Insert a record and try to call updateWithDamages() with an empty DamageVector. + TEST( RecordStoreTestHarness, UpdateWithNoDamages ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + string data = "my record"; + DiskLoc loc; + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + loc = res.getValue(); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 1, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + mutablebson::DamageVector dv; + + WriteUnitOfWork uow( opCtx.get() ); + ASSERT_OK( rs->updateWithDamages( opCtx.get(), loc, "", dv ) ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + RecordData record = rs->dataFor( opCtx.get(), loc ); + ASSERT_EQUALS( data.size() + 1, static_cast<size_t>( record.size() ) ); + ASSERT_EQUALS( data, record.data() ); + } + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_validate.cpp b/src/mongo/db/storage/record_store_test_validate.cpp new file mode 100644 index 00000000000..1d836289c23 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_validate.cpp @@ -0,0 +1,247 @@ +// record_store_test_validate.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/db/storage/record_store_test_validate.h" + +#include "mongo/db/storage/record_store.h" +#include "mongo/db/storage/record_store_test_harness.h" +#include "mongo/unittest/unittest.h" + +using std::string; + +namespace mongo { + + // Verify that calling validate() on an empty collection returns an OK status. + // When either of `full` or `scanData` are false, the ValidateAdaptor + // should not be used. + TEST( RecordStoreTestHarness, ValidateEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + ValidateAdaptorSpy adaptor; + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( rs->validate( opCtx.get(), + false, // full validate + false, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + + // Verify that calling validate() on an empty collection returns an OK status. + // When either of `full` or `scanData` are false, the ValidateAdaptor + // should not be used. + TEST( RecordStoreTestHarness, ValidateEmptyAndScanData ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + ValidateAdaptorSpy adaptor; + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( rs->validate( opCtx.get(), + false, // full validate + true, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + + // Verify that calling validate() on an empty collection returns an OK status. + // When either of `full` or `scanData` are false, the ValidateAdaptor + // should not be used. + TEST( RecordStoreTestHarness, FullValidateEmpty ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + ValidateAdaptorSpy adaptor; + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( rs->validate( opCtx.get(), + true, // full validate + false, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + + // Verify that calling validate() on an empty collection returns an OK status. + TEST( RecordStoreTestHarness, FullValidateEmptyAndScanData ) { + scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() ); + scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() ); + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) ); + } + + { + scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() ); + { + ValidateAdaptorSpy adaptor; + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( rs->validate( opCtx.get(), + true, // full validate + true, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + + // Insert multiple records, and verify that calling validate() on a nonempty collection + // returns an OK status. When either of `full` or `scanData` are false, the ValidateAdaptor + // should not be used. + TEST_F( ValidateTest, ValidateNonEmpty ) { + { + scoped_ptr<OperationContext> opCtx( newOperationContext() ); + { + ValidateAdaptorSpy adaptor; + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( getRecordStore().validate( opCtx.get(), + false, // full validate + false, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + + // Insert multiple records, and verify that calling validate() on a nonempty collection + // returns an OK status. When either of `full` or `scanData` are false, the ValidateAdaptor + // should not be used. + TEST_F( ValidateTest, ValidateAndScanDataNonEmpty ) { + { + scoped_ptr<OperationContext> opCtx( newOperationContext() ); + { + ValidateAdaptorSpy adaptor; + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( getRecordStore().validate( opCtx.get(), + false, // full validate + true, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + + // Insert multiple records, and verify that calling validate() on a nonempty collection + // returns an OK status. When either of `full` or `scanData` are false, the ValidateAdaptor + // should not be used. + TEST_F( ValidateTest, FullValidateNonEmpty ) { + { + scoped_ptr<OperationContext> opCtx( newOperationContext() ); + { + ValidateAdaptorSpy adaptor; + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( getRecordStore().validate( opCtx.get(), + true, // full validate + false, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + + // Insert multiple records, and verify that calling validate() on a nonempty collection + // returns an OK status. + TEST_F( ValidateTest, FullValidateNonEmptyAndScanData ) { + { + scoped_ptr<OperationContext> opCtx( newOperationContext() ); + { + ValidateAdaptorSpy adaptor( getInsertedRecords() ); + ValidateResults results; + BSONObjBuilder stats; + ASSERT_OK( getRecordStore().validate( opCtx.get(), + true, // full validate + true, // scan data + &adaptor, + &results, + &stats ) ); + ASSERT( adaptor.allValidated() ); + ASSERT( results.valid ); + ASSERT( results.errors.empty() ); + } + } + } + +} // namespace mongo diff --git a/src/mongo/db/storage/record_store_test_validate.h b/src/mongo/db/storage/record_store_test_validate.h new file mode 100644 index 00000000000..5155fabb129 --- /dev/null +++ b/src/mongo/db/storage/record_store_test_validate.h @@ -0,0 +1,122 @@ +// record_store_test_validate.h + +/** + * 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. + */ + +#pragma once + +#include "mongo/db/storage/record_data.h" +#include "mongo/db/storage/record_store.h" +#include "mongo/db/storage/record_store_test_harness.h" +#include "mongo/unittest/unittest.h" + +using std::set; +using std::string; +using std::stringstream; + +namespace mongo { +namespace { + + class ValidateAdaptorSpy : public ValidateAdaptor { + public: + ValidateAdaptorSpy() { } + + ValidateAdaptorSpy( const set<string> &remain ) + : _remain( remain ) { + } + + ~ValidateAdaptorSpy() { } + + Status validate( const RecordData &recordData, size_t *dataSize ) { + string s( recordData.data(), recordData.size() - 1 ); + ASSERT( 1 == _remain.erase( s ) ); + + *dataSize = recordData.size(); + return Status::OK(); + } + + bool allValidated() { return _remain.empty(); } + + private: + set<string> _remain; // initially contains all inserted records + }; + + class ValidateTest : public mongo::unittest::Test { + public: + ValidateTest() + : _harnessHelper( newHarnessHelper() ), + _rs( _harnessHelper->newNonCappedRecordStore() ) { + } + + OperationContext* newOperationContext() { + return _harnessHelper->newOperationContext(); + } + + const RecordStore& getRecordStore() { return *_rs; } + + const set<string>& getInsertedRecords() { return _remain; } + + void setUp() { + { + scoped_ptr<OperationContext> opCtx( newOperationContext() ); + ASSERT_EQUALS( 0, _rs->numRecords( opCtx.get() ) ); + } + + int nToInsert = 10; + for ( int i = 0; i < nToInsert; i++ ) { + scoped_ptr<OperationContext> opCtx( newOperationContext() ); + { + stringstream ss; + ss << "record " << i; + string data = ss.str(); + ASSERT( _remain.insert( data ).second ); + + WriteUnitOfWork uow( opCtx.get() ); + StatusWith<DiskLoc> res = _rs->insertRecord( opCtx.get(), + data.c_str(), + data.size() + 1, + false ); + ASSERT_OK( res.getStatus() ); + uow.commit(); + } + } + + { + scoped_ptr<OperationContext> opCtx( newOperationContext() ); + ASSERT_EQUALS( nToInsert, _rs->numRecords( opCtx.get() ) ); + } + } + + private: + scoped_ptr<HarnessHelper> _harnessHelper; + scoped_ptr<RecordStore> _rs; + set<string> _remain; + }; + +} // namespace +} // namespace mongo |