summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorEliot Horowitz <eliot@10gen.com>2014-09-16 13:01:46 -0400
committerEliot Horowitz <eliot@10gen.com>2014-09-17 10:49:29 -0400
commitbb0a34d80b4b1e555fab5dda4ddac8ae48e99152 (patch)
treea3458d9a4fb63f6b8c0723be95f54aa295c9d5be /src/mongo/db
parentcce6a04bcb073699a9e16a6ce9eca587060f8c3f (diff)
downloadmongo-bb0a34d80b4b1e555fab5dda4ddac8ae48e99152.tar.gz
SERVER-13635: make generic RecordStoreTestHarness
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/storage/SConscript8
-rw-r--r--src/mongo/db/storage/heap1/SConscript10
-rw-r--r--src/mongo/db/storage/heap1/heap1_record_store_test.cpp56
-rw-r--r--src/mongo/db/storage/record_store_test_harness.cpp373
-rw-r--r--src/mongo/db/storage/record_store_test_harness.h54
5 files changed, 501 insertions, 0 deletions
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index 9da4736e018..fbb4cbb8516 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -23,3 +23,11 @@ env.Library(
],
LIBDEPS=[]
)
+
+env.Library(
+ target='record_store_test_harness',
+ source=[
+ 'record_store_test_harness.cpp',
+ ],
+ LIBDEPS=[]
+ )
diff --git a/src/mongo/db/storage/heap1/SConscript b/src/mongo/db/storage/heap1/SConscript
index bd6784c6a5d..9ce15f15997 100644
--- a/src/mongo/db/storage/heap1/SConscript
+++ b/src/mongo/db/storage/heap1/SConscript
@@ -71,3 +71,13 @@ env.CppUnitTest(
]
)
+env.CppUnitTest(
+ target='storage_heap1_record_storetest',
+ source=['heap1_record_store_test.cpp'
+ ],
+ LIBDEPS=[
+ 'storage_heap1_fake',
+ '$BUILD_DIR/mongo/db/storage/record_store_test_harness'
+ ]
+ )
+
diff --git a/src/mongo/db/storage/heap1/heap1_record_store_test.cpp b/src/mongo/db/storage/heap1/heap1_record_store_test.cpp
new file mode 100644
index 00000000000..cac88f3646d
--- /dev/null
+++ b/src/mongo/db/storage/heap1/heap1_record_store_test.cpp
@@ -0,0 +1,56 @@
+// heap1_record_store_test.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/heap1/record_store_heap.h"
+#include "mongo/db/storage/heap1/heap1_recovery_unit.h"
+#include "mongo/db/storage/record_store_test_harness.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+
+ class MyHarnessHelper : public HarnessHelper {
+ public:
+ MyHarnessHelper() {
+ }
+
+ virtual RecordStore* newNonCappedRecordStore() {
+ return new HeapRecordStore( "a.b" );
+ }
+
+ virtual RecoveryUnit* newRecoveryUnit() {
+ return new Heap1RecoveryUnit();
+ }
+ };
+
+ HarnessHelper* newHarnessHelper() {
+ return new MyHarnessHelper();
+ }
+
+}
diff --git a/src/mongo/db/storage/record_store_test_harness.cpp b/src/mongo/db/storage/record_store_test_harness.cpp
new file mode 100644
index 00000000000..889ee900f1a
--- /dev/null
+++ b/src/mongo/db/storage/record_store_test_harness.cpp
@@ -0,0 +1,373 @@
+// record_store_test_harness.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"
+
+namespace mongo {
+ TEST( RecordStoreTestHarness, Simple1 ) {
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ ASSERT_EQUALS( 0, rs->numRecords( NULL ) );
+
+ string s = "eliot was here";
+
+ DiskLoc loc1;
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), s.c_str(), s.size() + 1, false );
+ ASSERT_OK( res.getStatus() );
+ loc1 = res.getValue();
+ uow.commit();
+ }
+
+ ASSERT_EQUALS( s, rs->dataFor( opCtx.get(), loc1 ).data() );
+ }
+
+ ASSERT_EQUALS( s, rs->dataFor( NULL, loc1 ).data() );
+ ASSERT_EQUALS( 1, rs->numRecords( NULL ) );
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), s.c_str(), s.size() + 1, false );
+ ASSERT_OK( res.getStatus() );
+ uow.commit();
+ }
+
+ }
+
+ ASSERT_EQUALS( 2, rs->numRecords( NULL ) );
+ }
+
+ namespace {
+ class DummyDocWriter : public DocWriter {
+ public:
+ virtual ~DummyDocWriter(){}
+ virtual void writeDocument( char* buf ) const {
+ memcpy( buf, "eliot", 6 );
+ }
+ virtual size_t documentSize() const { return 6; }
+ virtual bool addPadding() const { return false; }
+
+ };
+ }
+
+
+ TEST( RecordStoreTestHarness, Simple1InsertDocWroter ) {
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ DiskLoc loc1;
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ DummyDocWriter dw;
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), &dw, false );
+ ASSERT_OK( res.getStatus() );
+ loc1 = res.getValue();
+ uow.commit();
+ }
+
+ ASSERT_EQUALS( string("eliot"), rs->dataFor( opCtx.get(), loc1 ).data() );
+ }
+ }
+
+ TEST( RecordStoreTestHarness, Delete1 ) {
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ ASSERT_EQUALS( 0, rs->numRecords( NULL ) );
+
+ string s = "eliot was here";
+
+ DiskLoc loc;
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), s.c_str(), s.size() + 1, false );
+ ASSERT_OK( res.getStatus() );
+ loc = res.getValue();
+ uow.commit();
+ }
+
+ ASSERT_EQUALS( s, rs->dataFor( opCtx.get(), loc ).data() );
+
+ }
+
+ ASSERT_EQUALS( 1, rs->numRecords( NULL ) );
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ rs->deleteRecord( opCtx.get(), loc );
+ uow.commit();
+ }
+
+ ASSERT_EQUALS( 0, rs->numRecords( opCtx.get() ) );
+ }
+
+ }
+
+ TEST( RecordStoreTestHarness, Delete2 ) {
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ ASSERT_EQUALS( 0, rs->numRecords( NULL ) );
+
+ string s = "eliot was here";
+
+ DiskLoc loc;
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), s.c_str(), s.size() + 1, false );
+ ASSERT_OK( res.getStatus() );
+ res = rs->insertRecord( opCtx.get(), s.c_str(), s.size() + 1, false );
+ ASSERT_OK( res.getStatus() );
+ loc = res.getValue();
+ uow.commit();
+ }
+
+ }
+ ASSERT_EQUALS( s, rs->dataFor( NULL, loc ).data() );
+ ASSERT_EQUALS( 2, rs->numRecords( NULL ) );
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ rs->deleteRecord( opCtx.get(), loc );
+ uow.commit();
+ }
+ }
+ }
+
+ TEST( RecordStoreTestHarness, Update1 ) {
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ ASSERT_EQUALS( 0, rs->numRecords( NULL ) );
+
+ string s1 = "eliot was here";
+ string s2 = "eliot was here again";
+
+ DiskLoc loc;
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(),
+ s1.c_str(), s1.size() + 1,
+ false );
+ ASSERT_OK( res.getStatus() );
+ loc = res.getValue();
+ uow.commit();
+ }
+
+ }
+
+ ASSERT_EQUALS( s1, rs->dataFor( NULL, loc ).data() );
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->updateRecord( opCtx.get(), loc,
+ s2.c_str(), s2.size() + 1,
+ false, NULL );
+ ASSERT_OK( res.getStatus() );
+ ASSERT_EQUALS( loc, res.getValue() );
+ uow.commit();
+ }
+
+ }
+
+ ASSERT_EQUALS( 1, rs->numRecords( NULL ) );
+ ASSERT_EQUALS( s2, rs->dataFor( NULL, loc ).data() );
+
+ }
+
+ TEST( RecordStoreTestHarness, UpdateInPlace1 ) {
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ string s1 = "aaa111bbb";
+ string s2 = "aaa222bbb";
+
+ DiskLoc loc;
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(),
+ s1.c_str(),
+ s1.size() + 1,
+ -1 );
+ ASSERT_OK( res.getStatus() );
+ loc = res.getValue();
+ uow.commit();
+ }
+
+ }
+
+ ASSERT_EQUALS( s1, rs->dataFor( NULL, loc ).data() );
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ const char* damageSource = "222";
+ mutablebson::DamageVector dv;
+ dv.push_back( mutablebson::DamageEvent() );
+ dv[0].sourceOffset = 0;
+ dv[0].targetOffset = 3;
+ dv[0].size = 3;
+ Status res = rs->updateWithDamages( opCtx.get(),
+ loc,
+ damageSource,
+ dv );
+ ASSERT_OK( res );
+ uow.commit();
+ }
+ }
+ ASSERT_EQUALS( s2, rs->dataFor( NULL, loc ).data() );
+ }
+
+
+ TEST( RecordStoreTestHarness, Truncate1 ) {
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ ASSERT_EQUALS( 0, rs->numRecords( NULL ) );
+
+ string s = "eliot was here";
+
+ DiskLoc loc;
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ StatusWith<DiskLoc> res = rs->insertRecord( opCtx.get(), s.c_str(), s.size() + 1, false );
+ ASSERT_OK( res.getStatus() );
+ loc = res.getValue();
+ uow.commit();
+ }
+
+ }
+
+ ASSERT_EQUALS( s, rs->dataFor( NULL, loc ).data() );
+
+ ASSERT_EQUALS( 1, rs->numRecords( NULL ) );
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ rs->truncate( opCtx.get() );
+ uow.commit();
+ }
+
+ }
+
+ ASSERT_EQUALS( 0, rs->numRecords( NULL ) );
+
+ }
+
+ TEST( RecordStoreTestHarness, Cursor1 ) {
+ const int N = 10;
+
+ scoped_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
+ scoped_ptr<RecordStore> rs( harnessHelper->newNonCappedRecordStore() );
+
+ ASSERT_EQUALS( 0, rs->numRecords( NULL ) );
+
+ {
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ {
+ WriteUnitOfWork uow( opCtx.get() );
+ for ( int i = 0; i < N; i++ ) {
+ string s = str::stream() << "eliot" << i;
+ ASSERT_OK( rs->insertRecord( opCtx.get(), s.c_str(), s.size() + 1, false ).getStatus() );
+ }
+ uow.commit();
+ }
+ }
+
+ ASSERT_EQUALS( N, rs->numRecords( NULL ) );
+
+ {
+ int x = 0;
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ scoped_ptr<RecordIterator> it( rs->getIterator( opCtx.get() ) );
+ while ( !it->isEOF() ) {
+ DiskLoc loc = it->getNext();
+ RecordData data = it->dataFor( loc );
+ string s = str::stream() << "eliot" << x++;
+ ASSERT_EQUALS( s, data.data() );
+ }
+ ASSERT_EQUALS( N, x );
+ }
+
+ {
+ int x = N;
+ scoped_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
+ scoped_ptr<RecordIterator> it( rs->getIterator( opCtx.get(),
+ DiskLoc(),
+ false,
+ CollectionScanParams::BACKWARD ) );
+ while ( !it->isEOF() ) {
+ DiskLoc loc = it->getNext();
+ RecordData data = it->dataFor( loc );
+ string s = str::stream() << "eliot" << --x;
+ ASSERT_EQUALS( s, data.data() );
+ }
+ ASSERT_EQUALS( 0, x );
+ }
+
+ }
+
+}
diff --git a/src/mongo/db/storage/record_store_test_harness.h b/src/mongo/db/storage/record_store_test_harness.h
new file mode 100644
index 00000000000..cecaea06c55
--- /dev/null
+++ b/src/mongo/db/storage/record_store_test_harness.h
@@ -0,0 +1,54 @@
+// record_store_test_harness.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/operation_context_noop.h"
+
+namespace mongo {
+
+ class RecordStore;
+ class RecoveryUnit;
+
+ class HarnessHelper {
+ public:
+ HarnessHelper(){}
+ virtual ~HarnessHelper(){}
+
+ virtual RecordStore* newNonCappedRecordStore() = 0;
+ virtual RecoveryUnit* newRecoveryUnit() = 0;
+
+ virtual OperationContext* newOperationContext() {
+ return new OperationContextNoop( newRecoveryUnit() );
+ }
+ };
+
+ HarnessHelper* newHarnessHelper();
+}