summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Hirschhorn <max.hirschhorn@mongodb.com>2014-10-07 22:18:50 -0400
committerMax Hirschhorn <max.hirschhorn@mongodb.com>2014-10-07 22:18:50 -0400
commit6282f86e966b58cce4eab9403ff428118ae0ad0a (patch)
tree9371f8ba61bd574c2662b524c251d731cd6d2079 /src
parent5e46ce21ff9f5b8034f6f7828c2b817cdaee9bad (diff)
downloadmongo-6282f86e966b58cce4eab9403ff428118ae0ad0a.tar.gz
SERVER-13635 Expand set of generic RecordStore API tests.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/storage/SConscript15
-rw-r--r--src/mongo/db/storage/heap1/record_store_heap.cpp7
-rw-r--r--src/mongo/db/storage/record_data.h4
-rw-r--r--src/mongo/db/storage/record_store_test_customoption.cpp89
-rw-r--r--src/mongo/db/storage/record_store_test_datafor.cpp135
-rw-r--r--src/mongo/db/storage/record_store_test_datasize.cpp96
-rw-r--r--src/mongo/db/storage/record_store_test_deleterecord.cpp139
-rw-r--r--src/mongo/db/storage/record_store_test_docwriter.h62
-rw-r--r--src/mongo/db/storage/record_store_test_insertrecord.cpp182
-rw-r--r--src/mongo/db/storage/record_store_test_manyiter.cpp131
-rw-r--r--src/mongo/db/storage/record_store_test_recorditer.cpp285
-rw-r--r--src/mongo/db/storage/record_store_test_recordstore.cpp69
-rw-r--r--src/mongo/db/storage/record_store_test_repairiter.cpp121
-rw-r--r--src/mongo/db/storage/record_store_test_storagesize.cpp96
-rw-r--r--src/mongo/db/storage/record_store_test_touch.cpp164
-rw-r--r--src/mongo/db/storage/record_store_test_truncate.cpp115
-rw-r--r--src/mongo/db/storage/record_store_test_updaterecord.cpp238
-rw-r--r--src/mongo/db/storage/record_store_test_updaterecord.h75
-rw-r--r--src/mongo/db/storage/record_store_test_updatewithdamages.cpp277
-rw-r--r--src/mongo/db/storage/record_store_test_validate.cpp247
-rw-r--r--src/mongo/db/storage/record_store_test_validate.h122
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