// pdfiletests.cpp : pdfile 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 .
*/
#include "pch.h"
#include "../db/pdfile.h"
#include "../db/db.h"
#include "../db/json.h"
#include "dbtests.h"
namespace PdfileTests {
namespace ScanCapped {
class Base {
public:
Base() : _context( ns() ) {
}
virtual ~Base() {
if ( !nsd() )
return;
string n( ns() );
dropNS( n );
}
void run() {
stringstream spec;
spec << "{\"capped\":true,\"size\":2000,\"$nExtents\":" << nExtents() << "}";
string err;
ASSERT( userCreateNS( ns(), fromjson( spec.str() ), err, false ) );
prepare();
int j = 0;
for ( boost::shared_ptr i = theDataFileMgr.findAll( ns() );
i->ok(); i->advance(), ++j )
ASSERT_EQUALS( j, i->current().firstElement().number() );
ASSERT_EQUALS( count(), j );
j = count() - 1;
for ( boost::shared_ptr i =
findTableScan( ns(), fromjson( "{\"$natural\":-1}" ) );
i->ok(); i->advance(), --j )
ASSERT_EQUALS( j, i->current().firstElement().number() );
ASSERT_EQUALS( -1, j );
}
protected:
virtual void prepare() = 0;
virtual int count() const = 0;
virtual int nExtents() const {
return 0;
}
// bypass standard alloc/insert routines to use the extent we want.
static DiskLoc insert( DiskLoc ext, int i ) {
BSONObjBuilder b;
b.append( "a", i );
BSONObj o = b.done();
int len = o.objsize();
Extent *e = ext.ext();
e = getDur().writing(e);
int ofs;
if ( e->lastRecord.isNull() )
ofs = ext.getOfs() + ( e->_extentData - (char *)e );
else
ofs = e->lastRecord.getOfs() + e->lastRecord.rec()->lengthWithHeaders;
DiskLoc dl( ext.a(), ofs );
Record *r = dl.rec();
r = (Record*) getDur().writingPtr(r, Record::HeaderSize + len);
r->lengthWithHeaders = Record::HeaderSize + len;
r->extentOfs = e->myLoc.getOfs();
r->nextOfs = DiskLoc::NullOfs;
r->prevOfs = e->lastRecord.isNull() ? DiskLoc::NullOfs : e->lastRecord.getOfs();
memcpy( r->data, o.objdata(), len );
if ( e->firstRecord.isNull() )
e->firstRecord = dl;
else
getDur().writingInt(e->lastRecord.rec()->nextOfs) = ofs;
e->lastRecord = dl;
return dl;
}
static const char *ns() {
return "unittests.ScanCapped";
}
static NamespaceDetails *nsd() {
return nsdetails( ns() );
}
private:
dblock lk_;
Client::Context _context;
};
class Empty : public Base {
virtual void prepare() {}
virtual int count() const {
return 0;
}
};
class EmptyLooped : public Base {
virtual void prepare() {
nsd()->writingWithExtra()->capFirstNewRecord = DiskLoc();
}
virtual int count() const {
return 0;
}
};
class EmptyMultiExtentLooped : public Base {
virtual void prepare() {
nsd()->writingWithExtra()->capFirstNewRecord = DiskLoc();
}
virtual int count() const {
return 0;
}
virtual int nExtents() const {
return 3;
}
};
class Single : public Base {
virtual void prepare() {
nsd()->writingWithExtra()->capFirstNewRecord = insert( nsd()->capExtent, 0 );
}
virtual int count() const {
return 1;
}
};
class NewCapFirst : public Base {
virtual void prepare() {
DiskLoc x = insert( nsd()->capExtent, 0 );
nsd()->writingWithExtra()->capFirstNewRecord = x;
insert( nsd()->capExtent, 1 );
}
virtual int count() const {
return 2;
}
};
class NewCapLast : public Base {
virtual void prepare() {
insert( nsd()->capExtent, 0 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 1 );
}
virtual int count() const {
return 2;
}
};
class NewCapMiddle : public Base {
virtual void prepare() {
insert( nsd()->capExtent, 0 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 1 );
insert( nsd()->capExtent, 2 );
}
virtual int count() const {
return 3;
}
};
class FirstExtent : public Base {
virtual void prepare() {
insert( nsd()->capExtent, 0 );
insert( nsd()->lastExtent, 1 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 2 );
insert( nsd()->capExtent, 3 );
}
virtual int count() const {
return 4;
}
virtual int nExtents() const {
return 2;
}
};
class LastExtent : public Base {
virtual void prepare() {
nsd()->capExtent.writing() = nsd()->lastExtent;
insert( nsd()->capExtent, 0 );
insert( nsd()->firstExtent, 1 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 2 );
insert( nsd()->capExtent, 3 );
}
virtual int count() const {
return 4;
}
virtual int nExtents() const {
return 2;
}
};
class MidExtent : public Base {
virtual void prepare() {
nsd()->capExtent.writing() = nsd()->firstExtent.ext()->xnext;
insert( nsd()->capExtent, 0 );
insert( nsd()->lastExtent, 1 );
insert( nsd()->firstExtent, 2 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 3 );
insert( nsd()->capExtent, 4 );
}
virtual int count() const {
return 5;
}
virtual int nExtents() const {
return 3;
}
};
class AloneInExtent : public Base {
virtual void prepare() {
nsd()->capExtent.writing() = nsd()->firstExtent.ext()->xnext;
insert( nsd()->lastExtent, 0 );
insert( nsd()->firstExtent, 1 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 2 );
}
virtual int count() const {
return 3;
}
virtual int nExtents() const {
return 3;
}
};
class FirstInExtent : public Base {
virtual void prepare() {
nsd()->capExtent.writing() = nsd()->firstExtent.ext()->xnext;
insert( nsd()->lastExtent, 0 );
insert( nsd()->firstExtent, 1 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 2 );
insert( nsd()->capExtent, 3 );
}
virtual int count() const {
return 4;
}
virtual int nExtents() const {
return 3;
}
};
class LastInExtent : public Base {
virtual void prepare() {
nsd()->capExtent.writing() = nsd()->firstExtent.ext()->xnext;
insert( nsd()->capExtent, 0 );
insert( nsd()->lastExtent, 1 );
insert( nsd()->firstExtent, 2 );
nsd()->capFirstNewRecord.writing() = insert( nsd()->capExtent, 3 );
}
virtual int count() const {
return 4;
}
virtual int nExtents() const {
return 3;
}
};
} // namespace ScanCapped
namespace Insert {
class Base {
public:
Base() : _context( ns() ) {
}
virtual ~Base() {
if ( !nsd() )
return;
string n( ns() );
dropNS( n );
}
protected:
static const char *ns() {
return "unittests.pdfiletests.Insert";
}
static NamespaceDetails *nsd() {
return nsdetails( ns() );
}
private:
dblock lk_;
Client::Context _context;
};
class UpdateDate : public Base {
public:
void run() {
BSONObjBuilder b;
b.appendTimestamp( "a" );
BSONObj o = b.done();
ASSERT( 0 == o.getField( "a" ).date() );
theDataFileMgr.insertWithObjMod( ns(), o );
ASSERT( 0 != o.getField( "a" ).date() );
}
};
} // namespace Insert
class ExtentSizing {
public:
struct SmallFilesControl {
SmallFilesControl() {
old = cmdLine.smallfiles;
cmdLine.smallfiles = false;
}
~SmallFilesControl() {
cmdLine.smallfiles = old;
}
bool old;
};
void run() {
SmallFilesControl c;
// test that no matter what we start with, we always get to max extent size
for ( int obj=16; objaddAFile( big , false );
cout << f->length() << ' ' << n << endl;
if ( f->length() == l )
break;
l = f->length();
}
int start = d->numFiles();
for ( int i=0; iallocExtent( c1.c_str() , d->getFile( i )->getHeader()->unusedLength , false );
ASSERT_EQUALS( start , d->numFiles() );
{
DBDirectClient db;
db.dropDatabase( dbname );
}
}
};
class All : public Suite {
public:
All() : Suite( "pdfile" ) {}
void setupTests() {
add< ScanCapped::Empty >();
add< ScanCapped::EmptyLooped >();
add< ScanCapped::EmptyMultiExtentLooped >();
add< ScanCapped::Single >();
add< ScanCapped::NewCapFirst >();
add< ScanCapped::NewCapLast >();
add< ScanCapped::NewCapMiddle >();
add< ScanCapped::FirstExtent >();
add< ScanCapped::LastExtent >();
add< ScanCapped::MidExtent >();
add< ScanCapped::AloneInExtent >();
add< ScanCapped::FirstInExtent >();
add< ScanCapped::LastInExtent >();
add< Insert::UpdateDate >();
add< ExtentSizing >();
add< ExtentAllocOrder >();
}
} myall;
} // namespace PdfileTests