Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

865 lines (785 sloc) 30.001 kB
// namespacetests.cpp : namespace.{h,cpp} unit tests.
//
/**
* Copyright (C) 2008 10gen 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/>.
*/
// Where IndexDetails defined.
#include "pch.h"
#include "../db/namespace.h"
#include "../db/db.h"
#include "../db/json.h"
#include "dbtests.h"
namespace NamespaceTests {
namespace IndexDetailsTests {
class Base {
dblock lk;
Client::Context _context;
public:
Base() : _context(ns()) {
}
virtual ~Base() {
if ( id_.info.isNull() )
return;
theDataFileMgr.deleteRecord( ns(), id_.info.rec(), id_.info );
ASSERT( theDataFileMgr.findAll( ns() )->eof() );
}
protected:
void create() {
NamespaceDetailsTransient::get_w( ns() ).deletedIndex();
BSONObjBuilder builder;
builder.append( "ns", ns() );
builder.append( "name", "testIndex" );
builder.append( "key", key() );
BSONObj bobj = builder.done();
id_.info = theDataFileMgr.insert( ns(), bobj.objdata(), bobj.objsize() );
// head not needed for current tests
// idx_.head = BtreeBucket::addHead( id_ );
}
static const char* ns() {
return "unittests.indexdetailstests";
}
IndexDetails& id() {
return id_;
}
virtual BSONObj key() const {
BSONObjBuilder k;
k.append( "a", 1 );
return k.obj();
}
BSONObj aDotB() const {
BSONObjBuilder k;
k.append( "a.b", 1 );
return k.obj();
}
BSONObj aAndB() const {
BSONObjBuilder k;
k.append( "a", 1 );
k.append( "b", 1 );
return k.obj();
}
static vector< int > shortArray() {
vector< int > a;
a.push_back( 1 );
a.push_back( 2 );
a.push_back( 3 );
return a;
}
static BSONObj simpleBC( int i ) {
BSONObjBuilder b;
b.append( "b", i );
b.append( "c", 4 );
return b.obj();
}
static void checkSize( int expected, const BSONObjSetDefaultOrder &objs ) {
ASSERT_EQUALS( BSONObjSetDefaultOrder::size_type( expected ), objs.size() );
}
static void assertEquals( const BSONObj &a, const BSONObj &b ) {
if ( a.woCompare( b ) != 0 ) {
out() << "expected: " << a.toString()
<< ", got: " << b.toString() << endl;
}
ASSERT( a.woCompare( b ) == 0 );
}
BSONObj nullObj() const {
BSONObjBuilder b;
b.appendNull( "" );
return b.obj();
}
private:
dblock lk_;
IndexDetails id_;
};
class Create : public Base {
public:
void run() {
create();
ASSERT_EQUALS( "testIndex", id().indexName() );
ASSERT_EQUALS( ns(), id().parentNS() );
assertEquals( key(), id().keyPattern() );
}
};
class GetKeysFromObjectSimple : public Base {
public:
void run() {
create();
BSONObjBuilder b, e;
b.append( "b", 4 );
b.append( "a", 5 );
e.append( "", 5 );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 1, keys );
assertEquals( e.obj(), *keys.begin() );
}
};
class GetKeysFromObjectDotted : public Base {
public:
void run() {
create();
BSONObjBuilder a, e, b;
b.append( "b", 4 );
a.append( "a", b.done() );
a.append( "c", "foo" );
e.append( "", 4 );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( a.done(), keys );
checkSize( 1, keys );
ASSERT_EQUALS( e.obj(), *keys.begin() );
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class GetKeysFromArraySimple : public Base {
public:
void run() {
create();
BSONObjBuilder b;
b.append( "a", shortArray()) ;
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 3, keys );
int j = 1;
for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
BSONObjBuilder b;
b.append( "", j );
assertEquals( b.obj(), *i );
}
}
};
class GetKeysFromArrayFirstElement : public Base {
public:
void run() {
create();
BSONObjBuilder b;
b.append( "a", shortArray() );
b.append( "b", 2 );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 3, keys );
int j = 1;
for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
BSONObjBuilder b;
b.append( "", j );
b.append( "", 2 );
assertEquals( b.obj(), *i );
}
}
private:
virtual BSONObj key() const {
return aAndB();
}
};
class GetKeysFromArraySecondElement : public Base {
public:
void run() {
create();
BSONObjBuilder b;
b.append( "first", 5 );
b.append( "a", shortArray()) ;
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 3, keys );
int j = 1;
for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
BSONObjBuilder b;
b.append( "", 5 );
b.append( "", j );
assertEquals( b.obj(), *i );
}
}
private:
virtual BSONObj key() const {
BSONObjBuilder k;
k.append( "first", 1 );
k.append( "a", 1 );
return k.obj();
}
};
class GetKeysFromSecondLevelArray : public Base {
public:
void run() {
create();
BSONObjBuilder b;
b.append( "b", shortArray() );
BSONObjBuilder a;
a.append( "a", b.done() );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( a.done(), keys );
checkSize( 3, keys );
int j = 1;
for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
BSONObjBuilder b;
b.append( "", j );
assertEquals( b.obj(), *i );
}
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class ParallelArraysBasic : public Base {
public:
void run() {
create();
BSONObjBuilder b;
b.append( "a", shortArray() );
b.append( "b", shortArray() );
BSONObjSetDefaultOrder keys;
ASSERT_EXCEPTION( id().getKeysFromObject( b.done(), keys ),
UserException );
}
private:
virtual BSONObj key() const {
return aAndB();
}
};
class ArraySubobjectBasic : public Base {
public:
void run() {
create();
vector< BSONObj > elts;
for ( int i = 1; i < 4; ++i )
elts.push_back( simpleBC( i ) );
BSONObjBuilder b;
b.append( "a", elts );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 3, keys );
int j = 1;
for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
BSONObjBuilder b;
b.append( "", j );
assertEquals( b.obj(), *i );
}
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class ArraySubobjectMultiFieldIndex : public Base {
public:
void run() {
create();
vector< BSONObj > elts;
for ( int i = 1; i < 4; ++i )
elts.push_back( simpleBC( i ) );
BSONObjBuilder b;
b.append( "a", elts );
b.append( "d", 99 );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 3, keys );
int j = 1;
for ( BSONObjSetDefaultOrder::iterator i = keys.begin(); i != keys.end(); ++i, ++j ) {
BSONObjBuilder c;
c.append( "", j );
c.append( "", 99 );
assertEquals( c.obj(), *i );
}
}
private:
virtual BSONObj key() const {
BSONObjBuilder k;
k.append( "a.b", 1 );
k.append( "d", 1 );
return k.obj();
}
};
class ArraySubobjectSingleMissing : public Base {
public:
void run() {
create();
vector< BSONObj > elts;
BSONObjBuilder s;
s.append( "foo", 41 );
elts.push_back( s.obj() );
for ( int i = 1; i < 4; ++i )
elts.push_back( simpleBC( i ) );
BSONObjBuilder b;
b.append( "a", elts );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 4, keys );
BSONObjSetDefaultOrder::iterator i = keys.begin();
assertEquals( nullObj(), *i++ );
for ( int j = 1; j < 4; ++i, ++j ) {
BSONObjBuilder b;
b.append( "", j );
assertEquals( b.obj(), *i );
}
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class ArraySubobjectMissing : public Base {
public:
void run() {
create();
vector< BSONObj > elts;
BSONObjBuilder s;
s.append( "foo", 41 );
for ( int i = 1; i < 4; ++i )
elts.push_back( s.done() );
BSONObjBuilder b;
b.append( "a", elts );
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( b.done(), keys );
checkSize( 1, keys );
assertEquals( nullObj(), *keys.begin() );
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class MissingField : public Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( BSON( "b" << 1 ), keys );
checkSize( 1, keys );
assertEquals( nullObj(), *keys.begin() );
}
private:
virtual BSONObj key() const {
return BSON( "a" << 1 );
}
};
class SubobjectMissing : public Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{a:[1,2]}" ), keys );
checkSize( 1, keys );
assertEquals( nullObj(), *keys.begin() );
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class CompoundMissing : public Base {
public:
void run() {
create();
{
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{x:'a',y:'b'}" ) , keys );
checkSize( 1 , keys );
assertEquals( BSON( "" << "a" << "" << "b" ) , *keys.begin() );
}
{
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{x:'a'}" ) , keys );
checkSize( 1 , keys );
BSONObjBuilder b;
b.append( "" , "a" );
b.appendNull( "" );
assertEquals( b.obj() , *keys.begin() );
}
}
private:
virtual BSONObj key() const {
return BSON( "x" << 1 << "y" << 1 );
}
};
class ArraySubelementComplex : public Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{a:[{b:[2]}]}" ), keys );
checkSize( 1, keys );
assertEquals( BSON( "" << 2 ), *keys.begin() );
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class ParallelArraysComplex : public Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
ASSERT_EXCEPTION( id().getKeysFromObject( fromjson( "{a:[{b:[1],c:[2]}]}" ), keys ),
UserException );
}
private:
virtual BSONObj key() const {
return fromjson( "{'a.b':1,'a.c':1}" );
}
};
class AlternateMissing : public Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{a:[{b:1},{c:2}]}" ), keys );
checkSize( 2, keys );
BSONObjSetDefaultOrder::iterator i = keys.begin();
{
BSONObjBuilder e;
e.appendNull( "" );
e.append( "", 2 );
assertEquals( e.obj(), *i++ );
}
{
BSONObjBuilder e;
e.append( "", 1 );
e.appendNull( "" );
assertEquals( e.obj(), *i++ );
}
}
private:
virtual BSONObj key() const {
return fromjson( "{'a.b':1,'a.c':1}" );
}
};
class MultiComplex : public Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{a:[{b:1},{b:[1,2,3]}]}" ), keys );
checkSize( 3, keys );
}
private:
virtual BSONObj key() const {
return aDotB();
}
};
class EmptyArray : Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{a:[1,2]}" ), keys );
checkSize(2, keys );
keys.clear();
id().getKeysFromObject( fromjson( "{a:[1]}" ), keys );
checkSize(1, keys );
keys.clear();
id().getKeysFromObject( fromjson( "{a:null}" ), keys );
checkSize(1, keys );
keys.clear();
id().getKeysFromObject( fromjson( "{a:[]}" ), keys );
checkSize(1, keys );
keys.clear();
}
};
class MultiEmptyArray : Base {
public:
void run() {
create();
BSONObjSetDefaultOrder keys;
id().getKeysFromObject( fromjson( "{a:1,b:[1,2]}" ), keys );
checkSize(2, keys );
keys.clear();
id().getKeysFromObject( fromjson( "{a:1,b:[1]}" ), keys );
checkSize(1, keys );
keys.clear();
id().getKeysFromObject( fromjson( "{a:1,b:null}" ), keys );
//cout << "YO : " << *(keys.begin()) << endl;
checkSize(1, keys );
keys.clear();
id().getKeysFromObject( fromjson( "{a:1,b:[]}" ), keys );
checkSize(1, keys );
//cout << "YO : " << *(keys.begin()) << endl;
ASSERT_EQUALS( NumberInt , keys.begin()->firstElement().type() );
keys.clear();
}
protected:
BSONObj key() const {
return aAndB();
}
};
} // namespace IndexDetailsTests
namespace NamespaceDetailsTests {
class Base {
const char *ns_;
dblock lk;
Client::Context _context;
public:
Base( const char *ns = "unittests.NamespaceDetailsTests" ) : ns_( ns ) , _context( ns ) {}
virtual ~Base() {
if ( !nsd() )
return;
string s( ns() );
string errmsg;
BSONObjBuilder result;
dropCollection( s, errmsg, result );
}
protected:
void create() {
dblock lk;
string err;
ASSERT( userCreateNS( ns(), fromjson( spec() ), err, false ) );
}
virtual string spec() const {
return "{\"capped\":true,\"size\":512,\"$nExtents\":1}";
}
int nRecords() const {
int count = 0;
for ( DiskLoc i = nsd()->firstExtent; !i.isNull(); i = i.ext()->xnext ) {
int fileNo = i.ext()->firstRecord.a();
if ( fileNo == -1 )
continue;
for ( int j = i.ext()->firstRecord.getOfs(); j != DiskLoc::NullOfs;
j = DiskLoc( fileNo, j ).rec()->nextOfs ) {
++count;
}
}
ASSERT_EQUALS( count, nsd()->stats.nrecords );
return count;
}
int nExtents() const {
int count = 0;
for ( DiskLoc i = nsd()->firstExtent; !i.isNull(); i = i.ext()->xnext )
++count;
return count;
}
static int min( int a, int b ) {
return a < b ? a : b;
}
const char *ns() const {
return ns_;
}
NamespaceDetails *nsd() const {
return nsdetails( ns() )->writingWithExtra();
}
static BSONObj bigObj() {
string as( 187, 'a' );
BSONObjBuilder b;
b.append( "a", as );
return b.obj();
}
};
class Create : public Base {
public:
void run() {
create();
ASSERT( nsd() );
ASSERT_EQUALS( 0, nRecords() );
ASSERT( nsd()->firstExtent == nsd()->capExtent );
DiskLoc initial = DiskLoc();
initial.setInvalid();
ASSERT( initial == nsd()->capFirstNewRecord );
}
};
class SingleAlloc : public Base {
public:
void run() {
create();
BSONObj b = bigObj();
ASSERT( !theDataFileMgr.insert( ns(), b.objdata(), b.objsize() ).isNull() );
ASSERT_EQUALS( 1, nRecords() );
}
};
class Realloc : public Base {
public:
void run() {
create();
BSONObj b = bigObj();
DiskLoc l[ 6 ];
for ( int i = 0; i < 6; ++i ) {
l[ i ] = theDataFileMgr.insert( ns(), b.objdata(), b.objsize() );
ASSERT( !l[ i ].isNull() );
ASSERT_EQUALS( 1 + i % 2, nRecords() );
if ( i > 1 )
ASSERT( l[ i ] == l[ i - 2 ] );
}
}
};
class TwoExtent : public Base {
public:
void run() {
create();
ASSERT_EQUALS( 2, nExtents() );
BSONObj b = bigObj();
DiskLoc l[ 8 ];
for ( int i = 0; i < 8; ++i ) {
l[ i ] = theDataFileMgr.insert( ns(), b.objdata(), b.objsize() );
ASSERT( !l[ i ].isNull() );
ASSERT_EQUALS( i < 2 ? i + 1 : 3 + i % 2, nRecords() );
if ( i > 3 )
ASSERT( l[ i ] == l[ i - 4 ] );
}
// Too big
BSONObjBuilder bob;
bob.append( "a", string( 787, 'a' ) );
BSONObj bigger = bob.done();
ASSERT( theDataFileMgr.insert( ns(), bigger.objdata(), bigger.objsize() ).isNull() );
ASSERT_EQUALS( 0, nRecords() );
}
private:
virtual string spec() const {
return "{\"capped\":true,\"size\":512,\"$nExtents\":2}";
}
};
/* test NamespaceDetails::cappedTruncateAfter(const char *ns, DiskLoc loc)
*/
class TruncateCapped : public Base {
virtual string spec() const {
return "{\"capped\":true,\"size\":512,\"$nExtents\":2}";
}
void pass(int p) {
create();
ASSERT_EQUALS( 2, nExtents() );
BSONObj b = bigObj();
DiskLoc l[ 8 ];
for ( int i = 0; i < 8; ++i ) {
l[ i ] = theDataFileMgr.insert( ns(), b.objdata(), b.objsize() );
ASSERT( !l[ i ].isNull() );
ASSERT_EQUALS( i < 2 ? i + 1 : 3 + i % 2, nRecords() );
if ( i > 3 )
ASSERT( l[ i ] == l[ i - 4 ] );
}
NamespaceDetails *nsd = nsdetails(ns());
DiskLoc last, first;
{
ReverseCappedCursor c(nsd);
last = c.currLoc();
ASSERT( !last.isNull() );
}
{
ForwardCappedCursor c(nsd);
first = c.currLoc();
ASSERT( !first.isNull() );
ASSERT( first != last ) ;
}
DiskLoc d = l[6];
long long n = nsd->stats.nrecords;
nsd->cappedTruncateAfter(ns(), d, false);
ASSERT_EQUALS( nsd->stats.nrecords , n-1 );
{
ForwardCappedCursor c(nsd);
ASSERT( first == c.currLoc() );
}
{
ReverseCappedCursor c(nsd);
ASSERT( last != c.currLoc() ); // old last should be deleted
ASSERT( !last.isNull() );
}
// Too big
BSONObjBuilder bob;
bob.append( "a", string( 787, 'a' ) );
BSONObj bigger = bob.done();
ASSERT( theDataFileMgr.insert( ns(), bigger.objdata(), bigger.objsize() ).isNull() );
ASSERT_EQUALS( 0, nRecords() );
}
public:
void run() {
// log() << "******** NOT RUNNING TruncateCapped test yet ************" << endl;
pass(0);
}
};
class Migrate : public Base {
public:
void run() {
create();
nsd()->deletedList[ 2 ] = nsd()->cappedListOfAllDeletedRecords().drec()->nextDeleted.drec()->nextDeleted;
nsd()->cappedListOfAllDeletedRecords().drec()->nextDeleted.drec()->nextDeleted.writing() = DiskLoc();
nsd()->cappedLastDelRecLastExtent().Null();
NamespaceDetails *d = nsd();
zero( &d->capExtent );
zero( &d->capFirstNewRecord );
nsd();
ASSERT( nsd()->firstExtent == nsd()->capExtent );
ASSERT( nsd()->capExtent.getOfs() != 0 );
ASSERT( !nsd()->capFirstNewRecord.isValid() );
int nDeleted = 0;
for ( DiskLoc i = nsd()->cappedListOfAllDeletedRecords(); !i.isNull(); i = i.drec()->nextDeleted, ++nDeleted );
ASSERT_EQUALS( 10, nDeleted );
ASSERT( nsd()->cappedLastDelRecLastExtent().isNull() );
}
private:
static void zero( DiskLoc *d ) {
memset( d, 0, sizeof( DiskLoc ) );
}
virtual string spec() const {
return "{\"capped\":true,\"size\":512,\"$nExtents\":10}";
}
};
// This isn't a particularly useful test, and because it doesn't clean up
// after itself, /tmp/unittest needs to be cleared after running.
// class BigCollection : public Base {
// public:
// BigCollection() : Base( "NamespaceDetailsTests_BigCollection" ) {}
// void run() {
// create();
// ASSERT_EQUALS( 2, nExtents() );
// }
// private:
// virtual string spec() const {
// // NOTE 256 added to size in _userCreateNS()
// long long big = MongoDataFile::maxSize() - DataFileHeader::HeaderSize;
// stringstream ss;
// ss << "{\"capped\":true,\"size\":" << big << "}";
// return ss.str();
// }
// };
class Size {
public:
void run() {
ASSERT_EQUALS( 496U, sizeof( NamespaceDetails ) );
}
};
} // namespace NamespaceDetailsTests
class All : public Suite {
public:
All() : Suite( "namespace" ) {
}
void setupTests() {
add< IndexDetailsTests::Create >();
add< IndexDetailsTests::GetKeysFromObjectSimple >();
add< IndexDetailsTests::GetKeysFromObjectDotted >();
add< IndexDetailsTests::GetKeysFromArraySimple >();
add< IndexDetailsTests::GetKeysFromArrayFirstElement >();
add< IndexDetailsTests::GetKeysFromArraySecondElement >();
add< IndexDetailsTests::GetKeysFromSecondLevelArray >();
add< IndexDetailsTests::ParallelArraysBasic >();
add< IndexDetailsTests::ArraySubobjectBasic >();
add< IndexDetailsTests::ArraySubobjectMultiFieldIndex >();
add< IndexDetailsTests::ArraySubobjectSingleMissing >();
add< IndexDetailsTests::ArraySubobjectMissing >();
add< IndexDetailsTests::ArraySubelementComplex >();
add< IndexDetailsTests::ParallelArraysComplex >();
add< IndexDetailsTests::AlternateMissing >();
add< IndexDetailsTests::MultiComplex >();
add< IndexDetailsTests::EmptyArray >();
add< IndexDetailsTests::MultiEmptyArray >();
add< IndexDetailsTests::MissingField >();
add< IndexDetailsTests::SubobjectMissing >();
add< IndexDetailsTests::CompoundMissing >();
add< NamespaceDetailsTests::Create >();
add< NamespaceDetailsTests::SingleAlloc >();
add< NamespaceDetailsTests::Realloc >();
add< NamespaceDetailsTests::TwoExtent >();
add< NamespaceDetailsTests::TruncateCapped >();
add< NamespaceDetailsTests::Migrate >();
// add< NamespaceDetailsTests::BigCollection >();
add< NamespaceDetailsTests::Size >();
}
} myall;
} // namespace NamespaceTests
Jump to Line
Something went wrong with that request. Please try again.