2008-12-02 18:53:05 +01:00
|
|
|
// btreetests.cpp : Btree unit tests
|
|
|
|
//
|
|
|
|
|
|
|
|
/**
|
2008-12-08 02:58:20 +01:00
|
|
|
* Copyright (C) 2008 10gen Inc.
|
2008-12-29 02:28:49 +01:00
|
|
|
*
|
2008-12-08 02:58:20 +01:00
|
|
|
* 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.
|
2008-12-29 02:28:49 +01:00
|
|
|
*
|
2008-12-08 02:58:20 +01:00
|
|
|
* 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.
|
2008-12-29 02:28:49 +01:00
|
|
|
*
|
2008-12-08 02:58:20 +01:00
|
|
|
* 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/>.
|
|
|
|
*/
|
2008-12-02 18:53:05 +01:00
|
|
|
|
|
|
|
#include "../db/btree.h"
|
2009-01-23 16:17:29 +01:00
|
|
|
|
2008-12-02 18:53:05 +01:00
|
|
|
#include "../db/db.h"
|
|
|
|
|
|
|
|
#include "dbtests.h"
|
|
|
|
|
|
|
|
namespace BtreeTests {
|
2008-12-29 02:28:49 +01:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
class Base {
|
|
|
|
public:
|
|
|
|
Base() {
|
|
|
|
{
|
|
|
|
dblock lk;
|
|
|
|
setClient( ns() );
|
|
|
|
}
|
|
|
|
BSONObjBuilder builder;
|
|
|
|
builder.append( "ns", ns() );
|
|
|
|
builder.append( "name", "testIndex" );
|
|
|
|
BSONObj bobj = builder.done();
|
|
|
|
idx_.info =
|
|
|
|
theDataFileMgr.insert( ns(), bobj.objdata(), bobj.objsize() );
|
|
|
|
idx_.head = BtreeBucket::addHead( idx_ );
|
|
|
|
}
|
|
|
|
~Base() {
|
|
|
|
// FIXME cleanup all btree buckets.
|
|
|
|
theDataFileMgr.deleteRecord( ns(), idx_.info.rec(), idx_.info );
|
|
|
|
ASSERT( theDataFileMgr.findAll( ns() )->eof() );
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
BtreeBucket* bt() const {
|
|
|
|
return idx_.head.btree();
|
|
|
|
}
|
|
|
|
DiskLoc dl() const {
|
|
|
|
return idx_.head;
|
|
|
|
}
|
|
|
|
IndexDetails& id() {
|
|
|
|
return idx_;
|
|
|
|
}
|
|
|
|
static const char* ns() {
|
|
|
|
return "sys.unittest.btreetests";
|
|
|
|
}
|
|
|
|
// dummy, valid record loc
|
|
|
|
static DiskLoc recordLoc() {
|
|
|
|
return DiskLoc( 0, 2 );
|
|
|
|
}
|
|
|
|
void checkValid( int nKeys ) const {
|
|
|
|
ASSERT( bt() );
|
|
|
|
ASSERT( bt()->isHead() );
|
|
|
|
bt()->assertValid( order(), true );
|
|
|
|
ASSERT_EQUALS( nKeys, bt()->fullValidate( dl(), order() ) );
|
|
|
|
}
|
|
|
|
void insert( BSONObj &key ) {
|
|
|
|
bt()->insert( dl(), recordLoc(), key, order(), true, id(), true );
|
|
|
|
}
|
|
|
|
void unindex( BSONObj &key ) {
|
|
|
|
bt()->unindex( dl(), id(), key, recordLoc() );
|
|
|
|
}
|
|
|
|
static BSONObj simpleKey( char c, int n = 1 ) {
|
|
|
|
BSONObjBuilder builder;
|
|
|
|
string val( n, c );
|
|
|
|
builder.append( "a", val );
|
|
|
|
return builder.doneAndDecouple();
|
|
|
|
}
|
|
|
|
void locate( BSONObj &key, int expectedPos,
|
|
|
|
bool expectedFound, const DiskLoc &expectedLocation,
|
|
|
|
int direction = 1 ) {
|
|
|
|
int pos;
|
|
|
|
bool found;
|
|
|
|
DiskLoc location =
|
|
|
|
bt()->locate( dl(), key, order(), pos, found, recordLoc(), direction );
|
|
|
|
ASSERT_EQUALS( expectedFound, found );
|
|
|
|
ASSERT( location == expectedLocation );
|
|
|
|
ASSERT_EQUALS( expectedPos, pos );
|
|
|
|
}
|
|
|
|
BSONObj order() const {
|
|
|
|
return idx_.keyPattern();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
IndexDetails idx_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Create : public Base {
|
|
|
|
public:
|
|
|
|
void run() {
|
|
|
|
checkValid( 0 );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class SimpleInsertDelete : public Base {
|
|
|
|
public:
|
|
|
|
void run() {
|
|
|
|
BSONObj key = simpleKey( 'z' );
|
|
|
|
insert( key );
|
|
|
|
|
|
|
|
checkValid( 1 );
|
|
|
|
locate( key, 0, true, dl() );
|
|
|
|
|
|
|
|
unindex( key );
|
|
|
|
|
|
|
|
checkValid( 0 );
|
|
|
|
locate( key, 0, false, DiskLoc() );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class SplitUnevenBucketBase : public Base {
|
|
|
|
public:
|
|
|
|
void run() {
|
|
|
|
for ( int i = 0; i < 10; ++i ) {
|
|
|
|
BSONObj shortKey = simpleKey( shortToken( i ), 1 );
|
|
|
|
insert( shortKey );
|
|
|
|
BSONObj longKey = simpleKey( longToken( i ), 800 );
|
|
|
|
insert( longKey );
|
|
|
|
}
|
|
|
|
checkValid( 20 );
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
virtual char shortToken( int i ) const = 0;
|
|
|
|
virtual char longToken( int i ) const = 0;
|
|
|
|
static char leftToken( int i ) {
|
|
|
|
return 'a' + i;
|
|
|
|
}
|
|
|
|
static char rightToken( int i ) {
|
|
|
|
return 'z' - i;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class SplitRightHeavyBucket : public SplitUnevenBucketBase {
|
|
|
|
private:
|
|
|
|
virtual char shortToken( int i ) const {
|
|
|
|
return leftToken( i );
|
|
|
|
}
|
|
|
|
virtual char longToken( int i ) const {
|
|
|
|
return rightToken( i );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class SplitLeftHeavyBucket : public SplitUnevenBucketBase {
|
|
|
|
private:
|
|
|
|
virtual char shortToken( int i ) const {
|
|
|
|
return rightToken( i );
|
|
|
|
}
|
|
|
|
virtual char longToken( int i ) const {
|
|
|
|
return leftToken( i );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class MissingLocate : public Base {
|
|
|
|
public:
|
|
|
|
void run() {
|
|
|
|
for ( int i = 0; i < 3; ++i ) {
|
|
|
|
BSONObj k = simpleKey( 'b' + 2 * i );
|
|
|
|
insert( k );
|
|
|
|
}
|
|
|
|
|
|
|
|
locate( 1, 'a', 'b', dl() );
|
|
|
|
locate( 1, 'c', 'd', dl() );
|
|
|
|
locate( 1, 'e', 'f', dl() );
|
|
|
|
locate( 1, 'g', 'g' + 1, DiskLoc() ); // of course, 'h' isn't in the index.
|
|
|
|
|
|
|
|
// old behavior
|
|
|
|
// locate( -1, 'a', 'b', dl() );
|
|
|
|
// locate( -1, 'c', 'd', dl() );
|
|
|
|
// locate( -1, 'e', 'f', dl() );
|
|
|
|
// locate( -1, 'g', 'f', dl() );
|
|
|
|
|
|
|
|
locate( -1, 'a', 'a' - 1, DiskLoc() ); // of course, 'a' - 1 isn't in the index
|
|
|
|
locate( -1, 'c', 'b', dl() );
|
|
|
|
locate( -1, 'e', 'd', dl() );
|
|
|
|
locate( -1, 'g', 'f', dl() );
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
void locate( int direction, char token, char expectedMatch,
|
|
|
|
DiskLoc expectedLocation ) {
|
|
|
|
BSONObj k = simpleKey( token );
|
|
|
|
int expectedPos = ( expectedMatch - 'b' ) / 2;
|
|
|
|
Base::locate( k, expectedPos, false, expectedLocation, direction );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class MissingLocateMultiBucket : public Base {
|
|
|
|
public:
|
|
|
|
void run() {
|
|
|
|
for ( int i = 0; i < 10; ++i ) {
|
|
|
|
BSONObj k = key( 'b' + 2 * i );
|
|
|
|
insert( k );
|
|
|
|
}
|
|
|
|
BSONObj straddle = key( 'i' );
|
|
|
|
locate( straddle, 0, false, dl(), 1 );
|
|
|
|
straddle = key( 'k' );
|
|
|
|
locate( straddle, 0, false, dl(), -1 );
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
BSONObj key( char c ) {
|
|
|
|
return simpleKey( c, 800 );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class All : public UnitTest::Suite {
|
|
|
|
public:
|
|
|
|
All() {
|
|
|
|
add< Create >();
|
|
|
|
add< SimpleInsertDelete >();
|
|
|
|
add< SplitRightHeavyBucket >();
|
|
|
|
add< SplitLeftHeavyBucket >();
|
|
|
|
add< MissingLocate >();
|
|
|
|
add< MissingLocateMultiBucket >();
|
|
|
|
}
|
|
|
|
};
|
2008-12-02 18:53:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
UnitTest::TestPtr btreeTests() {
|
2008-12-29 02:28:49 +01:00
|
|
|
return UnitTest::createSuite< BtreeTests::All >();
|
2008-12-02 18:53:05 +01:00
|
|
|
}
|