// distribution boxbackup-0.10 (svn version: 494)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must
// display the following acknowledgment:
// This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// [Where legally impermissible the Authors do not disclaim liability for
// direct physical injury or death caused solely by defects in the software
// unless it is modified by a third party.]
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//
//
// --------------------------------------------------------------------------
//
// File
// Name: test/raidfile/test.cpp
// Purpose: Test RaidFile system
// Created: 2003/07/08
//
// --------------------------------------------------------------------------
#include "Box.h"
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/syscall.h>
#include <string.h>
#include "Test.h"
#include "RaidFileController.h"
#include "RaidFileWrite.h"
#include "RaidFileException.h"
#include "RaidFileRead.h"
#include "Guards.h"
#include "MemLeakFindOn.h"
#define RAID_BLOCK_SIZE 2048
#define RAID_NUMBER_DISCS 3
#define TEST_DATA_SIZE (8*1024 + 173)
#ifndef PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE
#define TRF_CAN_INTERCEPT
#endif
#ifdef TRF_CAN_INTERCEPT
// function in intercept.cpp for setting up errors
void intercept_setup_error(const char *filename, unsigned int errorafter, int errortoreturn, int syscalltoerror);
bool intercept_triggered();
void intercept_clear_setup();
#endif
// Nice random data for testing written files
class R250 {
public:
// Set up internal state table with 32-bit random numbers.
// The bizarre bit-twiddling is because rand() returns 16 bits of which
// the bottom bit is always zero! Hence, I use only some of the bits.
// You might want to do something better than this....
R250(int seed) : posn1(0), posn2(103)
{
// populate the state and incr tables
srand(seed);
for (int i = 0; i != stateLen; ++i) {
state[i] = ((rand() >> 2) << 19) ^ ((rand() >> 2) << 11) ^ (rand() >> 2);
incrTable[i] = i == stateLen - 1 ? 0 : i + 1;
}
// stir up the numbers to ensure they're random
for (int j = 0; j != stateLen * 4; ++j)
(void) next();
}
// Returns the next random number. Xor together two elements separated
// by 103 mod 250, replacing the first element with the result. Then
// increment the two indices mod 250.
inline int next()
{
int ret = (state[posn1] ^= state[posn2]); // xor and replace element
posn1 = incrTable[posn1]; // increment indices using lookup table
posn2 = incrTable[posn2];
return ret;
}
private:
enum { stateLen = 250 }; // length of the state table
int state[stateLen]; // holds the random number state
int incrTable[stateLen]; // lookup table: maps i to (i+1) % stateLen
int posn1, posn2; // indices into the state table
};
void testReadingFileContents(int set, const char *filename, void *data, int datasize, bool TestRAIDProperties, int UsageInBlocks = -1)
{
// Work out which disc is the "start" disc.
int h = 0;
int n = 0;
while(filename[n] != 0)
{
h += filename[n];
n++;
}
int startDisc = h % RAID_NUMBER_DISCS;
//printf("UsageInBlocks = %d\n", UsageInBlocks);
// sizes of data to read
static int readsizes[] = {2047, 1, 1, 2047, 12, 1, 1, RAID_BLOCK_SIZE - (12+1+1), RAID_BLOCK_SIZE, RAID_BLOCK_SIZE + 246, (RAID_BLOCK_SIZE * 3) + 3, 243};
// read the data in to test it
char testbuff[(RAID_BLOCK_SIZE * 3) + 128]; // bigger than the max request above!
std::auto_ptr<RaidFileRead> pread = RaidFileRead::Open(set, filename);
if(UsageInBlocks != -1)
{
TEST_THAT(UsageInBlocks == pread->GetDiscUsageInBlocks());
}
//printf("%d, %d\n", pread->GetFileSize(), datasize);
TEST_THAT(pread->GetFileSize() == datasize);
IOStream &readstream1 = *(pread.get());
int dataread = 0;
int r;
int readsize = readsizes[0];
int bsc = 1;
while((r = readstream1.Read(testbuff, readsize)) > 0)
{
//printf("== read, asked: %d actual: %d\n", readsize, r);
TEST_THAT(((dataread+r) == datasize) || r == readsize);
TEST_THAT(r > 0);
TEST_THAT(readstream1.StreamDataLeft()); // check IOStream interface is correct
for(int z = 0; z < r; ++z)
{
TEST_THAT(((char*)data)[dataread+z] == testbuff[z]);
/*if(((char*)data)[dataread+z] != testbuff[z])
{
printf("z = %d\n", z);
}*/
}
// Next size...
if(bsc <= (int)((sizeof(readsizes) / sizeof(readsizes[0])) - 1))
{
readsize = readsizes[bsc++];
}
dataread += r;
}
TEST_THAT(dataread == datasize);
pread->Close();
// open and close it...
pread.reset(RaidFileRead::Open(set, filename).release());
if(UsageInBlocks != -1)
{
TEST_THAT(UsageInBlocks == pread->GetDiscUsageInBlocks());
}
IOStream &readstream2 = *(pread.get());
// positions to try seeking too..
static int seekpos[] = {0, 1, 2, 887, 887+256 /* no seek required */, RAID_BLOCK_SIZE, RAID_BLOCK_SIZE + 1, RAID_BLOCK_SIZE - 1, RAID_BLOCK_SIZE*3, RAID_BLOCK_SIZE + 23, RAID_BLOCK_SIZE * 4, RAID_BLOCK_SIZE * 4 + 1};
for(unsigned int p = 0; p < (sizeof(seekpos) / sizeof(seekpos[0])); ++p)
{
//printf("== seekpos = %d\n", seekpos[p]);
// only try if test file size is big enough
if(seekpos[p]+256 > datasize) continue;
readstream2.Seek(seekpos[p], IOStream::SeekType_Absolute);
TEST_THAT(readstream2.Read(testbuff, 256) == 256);
TEST_THAT(readstream2.GetPosition() == seekpos[p] + 256);
TEST_THAT(::memcmp(((char*)data) + seekpos[p], testbuff, 256) == 0);
}
// open and close it...
pread.reset(RaidFileRead::Open(set, filename).release());
if(UsageInBlocks != -1)
{
TEST_THAT(UsageInBlocks == pread->GetDiscUsageInBlocks());
}
IOStream &readstream3 = *(pread.get());
int pos = 0;
for(unsigned int p = 0; p < (sizeof(seekpos) / sizeof(seekpos[0])); ++p)
{
// only try if test file size is big enough
if(seekpos[p]+256 > datasize) continue;
//printf("pos %d, seekpos %d, p %d\n", pos, seekpos[p], p);
readstream3.Seek(seekpos[p] - pos, IOStream::SeekType_Relative);
TEST_THAT(readstream3.Read(testbuff, 256) == 256);
pos = seekpos[p] + 256;
TEST_THAT(readstream3.GetPosition() == pos);
TEST_THAT(::memcmp(((char*)data) + seekpos[p], testbuff, 256) == 0);
}
// Straight read of file
pread.reset(RaidFileRead::Open(set, filename).release());
if(UsageInBlocks != -1)
{
TEST_THAT(UsageInBlocks == pread->GetDiscUsageInBlocks());
}
IOStream &readstream4 = *(pread.get());
pos = 0;
int bytesread = 0;
while((r = readstream4.Read(testbuff, 988)) != 0)
{
TEST_THAT(readstream4.StreamDataLeft()); // check IOStream interface is behaving as expected
// check contents
TEST_THAT(::memcmp(((char*)data) + pos, testbuff, r) == 0);
// move on
pos += r;
bytesread += r;
}
TEST_THAT(!readstream4.StreamDataLeft()); // check IOStream interface is correct
// Be nasty, and create some errors for the RAID stuff to recover from...
if(TestRAIDProperties)
{
char stripe1fn[256], stripe1fnRename[256];
sprintf(stripe1fn, "testfiles/%d_%d/%s.rf", set, startDisc, filename);
sprintf(stripe1fnRename, "testfiles/%d_%d/%s.rf-REMOVED", set, startDisc, filename);
char stripe2fn[256], stripe2fnRename[256];
sprintf(stripe2fn, "testfiles/%d_%d/%s.rf", set, (startDisc + 1) % RAID_NUMBER_DISCS, filename);
sprintf(stripe2fnRename, "testfiles/%d_%d/%s.rf-REMOVED", set, (startDisc + 1) % RAID_NUMBER_DISCS, filename);
// Read with stripe1 + parity
TEST_THAT(::rename(stripe2fn, stripe2fnRename) == 0);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(::rename(stripe2fnRename, stripe2fn) == 0);
// Read with stripe2 + parity
TEST_THAT(::rename(stripe1fn, stripe1fnRename) == 0);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(::rename(stripe1fnRename, stripe1fn) == 0);
// Munged filename for avoidance
char mungefilename[256];
char filenamepart[256];
sprintf(filenamepart, "%s.rf", filename);
int m = 0, s = 0;
while(filenamepart[s] != '\0')
{
if(filenamepart[s] == '/')
{
mungefilename[m++] = '_';
}
else if(filenamepart[s] == '_')
{
mungefilename[m++] = '_';
mungefilename[m++] = '_';
}
else
{
mungefilename[m++] = filenamepart[s];
}
s++;
}
mungefilename[m++] = '\0';
char stripe1munge[256];
sprintf(stripe1munge, "testfiles/%d_%d/.raidfile-unreadable/%s", set, startDisc, mungefilename);
char stripe2munge[256];
sprintf(stripe2munge, "testfiles/%d_%d/.raidfile-unreadable/%s", set, (startDisc + 1) % RAID_NUMBER_DISCS, mungefilename);
#ifdef TRF_CAN_INTERCEPT
// Test I/O errors on opening
// stripe 1
intercept_setup_error(stripe1fn, 0, EIO, SYS_open);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(intercept_triggered());
intercept_clear_setup();
// Check that the file was moved correctly.
TEST_THAT(TestFileExists(stripe1munge));
TEST_THAT(::rename(stripe1munge, stripe1fn) == 0);
// Test error in reading stripe 2
intercept_setup_error(stripe2fn, 0, EIO, SYS_open);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(intercept_triggered());
intercept_clear_setup();
// Check that the file was moved correctly.
TEST_THAT(TestFileExists(stripe2munge));
TEST_THAT(::rename(stripe2munge, stripe2fn) == 0);
// Test I/O errors on seeking
// stripe 1, if the file is bigger than the minimum thing that it'll get seeked for
if(datasize > 257)
{
intercept_setup_error(stripe1fn, 1, EIO, SYS_lseek);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(intercept_triggered());
intercept_clear_setup();
// Check that the file was moved correctly.
TEST_THAT(TestFileExists(stripe1munge));
TEST_THAT(::rename(stripe1munge, stripe1fn) == 0);
}
// Stripe 2, only if the file is big enough to merit this
if(datasize > (RAID_BLOCK_SIZE + 4))
{
intercept_setup_error(stripe2fn, 1, EIO, SYS_lseek);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(intercept_triggered());
intercept_clear_setup();
// Check that the file was moved correctly.
TEST_THAT(TestFileExists(stripe2munge));
TEST_THAT(::rename(stripe2munge, stripe2fn) == 0);
}
// Test I/O errors on read, but only if the file is size greater than 0
if(datasize > 0)
{
// Where shall we error after?
int errafter = datasize / 4;
// Test error in reading stripe 1
intercept_setup_error(stripe1fn, errafter, EIO, SYS_readv);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(intercept_triggered());
intercept_clear_setup();
// Check that the file was moved correctly.
TEST_THAT(TestFileExists(stripe1munge));
TEST_THAT(::rename(stripe1munge, stripe1fn) == 0);
// Can only test error if file size > RAID_BLOCK_SIZE, as otherwise stripe2 has nothing in it
if(datasize > RAID_BLOCK_SIZE)
{
// Test error in reading stripe 2
intercept_setup_error(stripe2fn, errafter, EIO, SYS_readv);
testReadingFileContents(set, filename, data, datasize, false /* avoid recursion! */, UsageInBlocks);
TEST_THAT(intercept_triggered());
intercept_clear_setup();
// Check that the file was moved correctly.
TEST_THAT(TestFileExists(stripe2munge));
TEST_THAT(::rename(stripe2munge, stripe2fn) == 0);
}
}
#endif // TRF_CAN_INTERCEPT
}
}
void testReadWriteFileDo(int set, const char *filename, void *data, int datasize, bool DoTransform)
{
// Work out which disc is the "start" disc.
int h = 0;
int n = 0;
while(filename[n] != 0)
{
h += filename[n];
n++;
}
int startDisc = h % RAID_NUMBER_DISCS;
// Another to test the transform works OK...
RaidFileWrite write4(set, filename);
write4.Open();
write4.Write(data, datasize);
// This time, don't discard and transform it to a RAID File
char writefnPre[256];
sprintf(writefnPre, "testfiles/%d_%d/%s.rfwX", set, startDisc, filename);
TEST_THAT(TestFileExists(writefnPre));
char writefn[256];
sprintf(writefn, "testfiles/%d_%d/%s.rfw", set, startDisc, filename);
int usageInBlocks = write4.GetDiscUsageInBlocks();
write4.Commit(DoTransform);
// Check that files are nicely done...
if(!DoTransform)
{
TEST_THAT(TestFileExists(writefn));
TEST_THAT(!TestFileExists(writefnPre));
}
else
{
TEST_THAT(!TestFileExists(writefn));
TEST_THAT(!TestFileExists(writefnPre));
// Stripe file sizes
int fullblocks = datasize / RAID_BLOCK_SIZE;
int leftover = datasize - (fullblocks * RAID_BLOCK_SIZE);
int fs1 = -2;
if((fullblocks & 1) == 0)
{
// last block of data will be on the first stripe
fs1 = ((fullblocks / 2) * RAID_BLOCK_SIZE) + leftover;
}
else
{
// last block is on second stripe
fs1 = ((fullblocks / 2)+1) * RAID_BLOCK_SIZE;
}
char stripe1fn[256];
sprintf(stripe1fn, "testfiles/%d_%d/%s.rf", set, startDisc, filename);
TEST_THAT(TestGetFileSize(stripe1fn) == fs1);
char stripe2fn[256];
sprintf(stripe2fn, "testfiles/%d_%d/%s.rf", set, (startDisc + 1) % RAID_NUMBER_DISCS, filename);
TEST_THAT(TestGetFileSize(stripe2fn) == (int)(datasize - fs1));
// Parity file size
char parityfn[256];
sprintf(parityfn, "testfiles/%d_%d/%s.rf", set, (startDisc + 2) % RAID_NUMBER_DISCS, filename);
// Mildly complex calculation
unsigned int blocks = datasize / RAID_BLOCK_SIZE;
unsigned int bytesOver = datasize % RAID_BLOCK_SIZE;
int paritysize = (blocks / 2) * RAID_BLOCK_SIZE;
// Then add in stuff for the last couple of blocks
if((blocks & 1) == 0)
{
if(bytesOver == 0)
{
paritysize += sizeof(RaidFileRead::FileSizeType);
}
else
{
paritysize += (bytesOver == sizeof(RaidFileRead::FileSizeType))?(RAID_BLOCK_SIZE+sizeof(RaidFileRead::FileSizeType)):bytesOver;
}
}
else
{
paritysize += RAID_BLOCK_SIZE;
if(bytesOver == 0 || bytesOver >= (RAID_BLOCK_SIZE-sizeof(RaidFileRead::FileSizeType)))
{
paritysize += sizeof(RaidFileRead::FileSizeType);
}
}
//printf("datasize = %d, calc paritysize = %d, actual size of file = %d\n", datasize, paritysize, TestGetFileSize(parityfn));
TEST_THAT(TestGetFileSize(parityfn) == paritysize);
//printf("stripe1 size = %d, stripe2 size = %d, parity size = %d\n", TestGetFileSize(stripe1fn), TestGetFileSize(stripe2fn), TestGetFileSize(parityfn));
// Check that block calculation is correct
//printf("filesize = %d\n", datasize);
#define TO_BLOCKS_ROUND_UP(x) (((x) + (RAID_BLOCK_SIZE-1)) / RAID_BLOCK_SIZE)
TEST_THAT(usageInBlocks == (TO_BLOCKS_ROUND_UP(paritysize) + TO_BLOCKS_ROUND_UP(fs1) + TO_BLOCKS_ROUND_UP(datasize - fs1)));
// See about whether or not the files look correct
char testblock[1024]; // compiler bug? This can't go in the block below without corrupting stripe2fn...
if(datasize > (3*1024))
{
int f;
TEST_THAT((f = ::open(stripe1fn, O_RDONLY, 0)) != -1);
TEST_THAT(sizeof(testblock) == ::read(f, testblock, sizeof(testblock)));
for(unsigned int q = 0; q < sizeof(testblock); ++q)
{
TEST_THAT(testblock[q] == ((char*)data)[q]);
}
::close(f);
TEST_THAT((f = ::open(stripe2fn, O_RDONLY, 0)) != -1);
TEST_THAT(sizeof(testblock) == ::read(f, testblock, sizeof(testblock)));
for(unsigned int q = 0; q < sizeof(testblock); ++q)
{
TEST_THAT(testblock[q] == ((char*)data)[q+RAID_BLOCK_SIZE]);
}
::close(f);
}
}
// See if the contents look right
testReadingFileContents(set, filename, data, datasize, DoTransform /* only test RAID stuff if it has been transformed to RAID */, usageInBlocks);
}
void testReadWriteFile(int set, const char *filename, void *data, int datasize)
{
// Test once, transforming it...
testReadWriteFileDo(set, filename, data, datasize, true);
// And then again, not transforming it
std::string fn(filename);
fn += "NT";
testReadWriteFileDo(set, fn.c_str(), data, datasize, false);
}
bool list_matches(const std::vector<std::string> &rList, const char *compareto[])
{
// count in compare to
int count = 0;
while(compareto[count] != 0)
count++;
if((int)rList.size() != count)
{
return false;
}
// Space for bools
bool *found = new bool[count];
for(int c = 0; c < count; ++c)
{
found[c] = false;
}
for(int c = 0; c < count; ++c)
{
bool f = false;
for(int l = 0; l < (int)rList.size(); ++l)
{
if(rList[l] == compareto[c])
{
f = true;
break;
}
}
found[c] = f;
}
bool ret = true;
for(int c = 0; c < count; ++c)
{
if(found[c] == false)
{
ret = false;
}
}
delete [] found;
return ret;
}
void test_overwrites()
{
// Opening twice is bad
{
RaidFileWrite writeA(0, "overwrite_A");
writeA.Open();
writeA.Write("TESTTEST", 8);
{
#if defined(HAVE_FLOCK) || HAVE_DECL_O_EXLOCK
RaidFileWrite writeA2(0, "overwrite_A");
TEST_CHECK_THROWS(writeA2.Open(), RaidFileException, FileIsCurrentlyOpenForWriting);
#endif
}
}
// But opening a file which has previously been open, but isn't now, is OK.
// Generate a random pre-existing write file (and ensure that it doesn't exist already)
int f;
TEST_THAT((f = ::open("testfiles/0_2/overwrite_B.rfwX", O_WRONLY | O_CREAT | O_EXCL, 0755)) != -1);
TEST_THAT(::write(f, "TESTTEST", 8) == 8);
::close(f);
// Attempt to overwrite it, which should work nicely.
RaidFileWrite writeB(0, "overwrite_B");
writeB.Open();
writeB.Write("TEST", 4);
TEST_THAT(writeB.GetFileSize() == 4);
writeB.Commit();
}
int test(int argc, const char *argv[])
{
#ifndef TRF_CAN_INTERCEPT
printf("NOTE: Skipping intercept based tests on this platform.\n\n");
#endif
// Initialise the controller
RaidFileController &rcontroller = RaidFileController::GetController();
rcontroller.Initialise("testfiles/raidfile.conf");
// some data
char data[TEST_DATA_SIZE];
R250 random(619);
for(unsigned int l = 0; l < sizeof(data); ++l)
{
data[l] = random.next() & 0xff;
}
char data2[57];
for(unsigned int l = 0; l < sizeof(data2); ++l)
{
data2[l] = l;
}
// Try creating a directory
RaidFileWrite::CreateDirectory(0, "test-dir");
TEST_THAT(TestDirExists("testfiles/0_0/test-dir"));
TEST_THAT(TestDirExists("testfiles/0_1/test-dir"));
TEST_THAT(TestDirExists("testfiles/0_2/test-dir"));
TEST_THAT(RaidFileRead::DirectoryExists(0, "test-dir"));
TEST_THAT(!RaidFileRead::DirectoryExists(0, "test-dir-not"));
// Test converting to disc set names
{
std::string n1(RaidFileController::DiscSetPathToFileSystemPath(0, "testX", 0));
std::string n2(RaidFileController::DiscSetPathToFileSystemPath(0, "testX", 1));
std::string n3(RaidFileController::DiscSetPathToFileSystemPath(0, "testX", 2));
std::string n4(RaidFileController::DiscSetPathToFileSystemPath(0, "testX", 3));
TEST_THAT(n1 != n2);
TEST_THAT(n2 != n3);
TEST_THAT(n1 != n3);
TEST_THAT(n1 == n4); // ie wraps around
TRACE3("Gen paths= '%s','%s',%s'\n", n1.c_str(), n2.c_str(), n3.c_str());
}
// Create a RaidFile
RaidFileWrite write1(0, "test1");
IOStream &write1stream = write1; // use the stream interface where possible
write1.Open();
write1stream.Write(data, sizeof(data));
write1stream.Seek(1024, IOStream::SeekType_Absolute);
write1stream.Write(data2, sizeof(data2));
write1stream.Seek(1024, IOStream::SeekType_Relative);
write1stream.Write(data2, sizeof(data2));
write1stream.Seek(0, IOStream::SeekType_End);
write1stream.Write(data, sizeof(data));
// Before it's deleted, check to see the contents are as expected
int f;
TEST_THAT((f = ::open("testfiles/0_2/test1.rfwX", O_RDONLY, 0)) >= 0);
char buffer[sizeof(data)];
TEST_THAT(::read(f, buffer, sizeof(buffer)) == sizeof(buffer));
for(unsigned int l = 0; l < 1024; ++l)
{
TEST_THAT(buffer[l] == data[l]);
}
for(unsigned int l = 0; l < sizeof(data2); ++l)
{
TEST_THAT(buffer[l+1024] == data2[l]);
}
for(unsigned int l = 0; l < sizeof(data2); ++l)
{
TEST_THAT(buffer[l+2048+sizeof(data2)] == data2[l]);
}
TEST_THAT(::lseek(f, sizeof(data), SEEK_SET) == sizeof(buffer));
TEST_THAT(::read(f, buffer, sizeof(buffer)) == sizeof(buffer));
for(unsigned int l = 0; l < 1024; ++l)
{
TEST_THAT(buffer[l] == data[l]);
}
// make sure that's the end of the file
TEST_THAT(::read(f, buffer, sizeof(buffer)) == 0);
::close(f);
// Commit the data
write1.Commit();
TEST_THAT((f = ::open("testfiles/0_2/test1.rfw", O_RDONLY, 0)) >= 0);
::close(f);
// Now try and read it
{
std::auto_ptr<RaidFileRead> pread = RaidFileRead::Open(0, "test1");
TEST_THAT(pread->GetFileSize() == sizeof(buffer)*2);
char buffer[sizeof(data)];
TEST_THAT(pread->Read(buffer, sizeof(buffer)) == sizeof(buffer));
for(unsigned int l = 0; l < 1024; ++l)
{
TEST_THAT(buffer[l] == data[l]);
}
for(unsigned int l = 0; l < sizeof(data2); ++l)
{
TEST_THAT(buffer[l+1024] == data2[l]);
}
for(unsigned int l = 0; l < sizeof(data2); ++l)
{
TEST_THAT(buffer[l+2048+sizeof(data2)] == data2[l]);
}
pread->Seek(sizeof(data), IOStream::SeekType_Absolute);
TEST_THAT(pread->Read(buffer, sizeof(buffer)) == sizeof(buffer));
for(unsigned int l = 0; l < 1024; ++l)
{
TEST_THAT(buffer[l] == data[l]);
}
// make sure that's the end of the file
TEST_THAT(pread->Read(buffer, sizeof(buffer)) == 0);
// Seek backwards a bit
pread->Seek(-1024, IOStream::SeekType_Relative);
TEST_THAT(pread->Read(buffer, 1024) == 1024);
// make sure that's the end of the file
TEST_THAT(pread->Read(buffer, sizeof(buffer)) == 0);
// Test seeking to end works
pread->Seek(-1024, IOStream::SeekType_Relative);
TEST_THAT(pread->Read(buffer, 512) == 512);
pread->Seek(0, IOStream::SeekType_End);
TEST_THAT(pread->Read(buffer, sizeof(buffer)) == 0);
}
// Delete it
RaidFileWrite writeDel(0, "test1");
writeDel.Delete();
// And again...
RaidFileWrite write2(0, "test1");
write2.Open();
write2.Write(data, sizeof(data));
// This time, discard it
write2.Discard();
TEST_THAT((f = ::open("testfiles/0_2/test1.rfw", O_RDONLY, 0)) == -1);
// And leaving it there...
RaidFileWrite writeLeave(0, "test1");
writeLeave.Open();
writeLeave.Write(data, sizeof(data));
// This time, commit it
writeLeave.Commit();
TEST_THAT((f = ::open("testfiles/0_2/test1.rfw", O_RDONLY, 0)) != -1);
::close(f);
// Then check that the thing will refuse to open it again.
RaidFileWrite write3(0, "test1");
TEST_CHECK_THROWS(write3.Open(), RaidFileException, CannotOverwriteExistingFile);
// Test overwrite behaviour
test_overwrites();
// Then... open it again allowing overwrites
RaidFileWrite write3b(0, "test1");
write3b.Open(true);
// Write something
write3b.Write(data + 3, sizeof(data) - 3);
write3b.Commit();
// Test it
testReadingFileContents(0, "test1", data+3, sizeof(data) - 3, false /*TestRAIDProperties*/);
// And once again, but this time making it a raid file
RaidFileWrite write3c(0, "test1");
write3c.Open(true);
// Write something
write3c.Write(data + 7, sizeof(data) - 7);
write3c.Commit(true); // make RAID
// Test it
testReadingFileContents(0, "test1", data+7, sizeof(data) - 7, false /*TestRAIDProperties*/);
// Test opening a file which doesn't exist
TEST_CHECK_THROWS(
std::auto_ptr<RaidFileRead> preadnotexist = RaidFileRead::Open(1, "doesnt-exist"),
RaidFileException, RaidFileDoesntExist);
{
// Test unrecoverable damage
RaidFileWrite w(0, "damage");
w.Open();
w.Write(data, sizeof(data));
w.Commit(true);
// Try removing the parity file
TEST_THAT(::rename("testfiles/0_0/damage.rf", "testfiles/0_0/damage.rf-NT") == 0);
{
std::auto_ptr<RaidFileRead> pr0 = RaidFileRead::Open(0, "damage");
pr0->Read(buffer, sizeof(data));
}
TEST_THAT(::rename("testfiles/0_0/damage.rf-NT", "testfiles/0_0/damage.rf") == 0);
// Delete one of the files
TEST_THAT(::unlink("testfiles/0_1/damage.rf") == 0); // stripe 1
#ifdef TRF_CAN_INTERCEPT
// Open it and read...
{
intercept_setup_error("testfiles/0_2/damage.rf", 0, EIO, SYS_read); // stripe 2
std::auto_ptr<RaidFileRead> pr1 = RaidFileRead::Open(0, "damage");
TEST_CHECK_THROWS(
pr1->Read(buffer, sizeof(data)),
RaidFileException, OSError);
TEST_THAT(intercept_triggered());
intercept_clear_setup();
}
#endif //TRF_CAN_INTERCEPT
// Delete another
TEST_THAT(::unlink("testfiles/0_0/damage.rf") == 0); // parity
TEST_CHECK_THROWS(
std::auto_ptr<RaidFileRead> pread2 = RaidFileRead::Open(0, "damage"),
RaidFileException, FileIsDamagedNotRecoverable);
}
// Test reading a directory
{
RaidFileWrite::CreateDirectory(0, "dirread");
// Make some contents
RaidFileWrite::CreateDirectory(0, "dirread/dfsdf1");
RaidFileWrite::CreateDirectory(0, "dirread/ponwq2");
{
RaidFileWrite w(0, "dirread/sdf9873241");
w.Open();
w.Write(data, sizeof(data));
w.Commit(true);
}
{
RaidFileWrite w(0, "dirread/fsdcxjni3242");
w.Open();
w.Write(data, sizeof(data));
w.Commit(true);
}
{
RaidFileWrite w(0, "dirread/cskjnds3");
w.Open();
w.Write(data, sizeof(data));
w.Commit(false);
}
const static char *dir_list1[] = {"dfsdf1", "ponwq2", 0};
const static char *file_list1[] = {"sdf9873241", "fsdcxjni3242", "cskjnds3", 0};
const static char *file_list2[] = {"fsdcxjni3242", "cskjnds3", 0};
std::vector<std::string> names;
TEST_THAT(true == RaidFileRead::ReadDirectoryContents(0, std::string("dirread"), RaidFileRead::DirReadType_FilesOnly, names));
TEST_THAT(list_matches(names, file_list1));
TEST_THAT(true == RaidFileRead::ReadDirectoryContents(0, std::string("dirread"), RaidFileRead::DirReadType_DirsOnly, names));
TEST_THAT(list_matches(names, dir_list1));
// Delete things
TEST_THAT(::unlink("testfiles/0_0/dirread/sdf9873241.rf") == 0);
TEST_THAT(true == RaidFileRead::ReadDirectoryContents(0, std::string("dirread"), RaidFileRead::DirReadType_FilesOnly, names));
TEST_THAT(list_matches(names, file_list1));
// Delete something else so that it's not recoverable
TEST_THAT(::unlink("testfiles/0_1/dirread/sdf9873241.rf") == 0);
TEST_THAT(false == RaidFileRead::ReadDirectoryContents(0, std::string("dirread"), RaidFileRead::DirReadType_FilesOnly, names));
TEST_THAT(list_matches(names, file_list1));
// And finally...
TEST_THAT(::unlink("testfiles/0_2/dirread/sdf9873241.rf") == 0);
TEST_THAT(true == RaidFileRead::ReadDirectoryContents(0, std::string("dirread"), RaidFileRead::DirReadType_FilesOnly, names));
TEST_THAT(list_matches(names, file_list2));
}
// Check that sizes are reported correctly for non-raid discs
{
int sizeInBlocks = (sizeof(data) + RAID_BLOCK_SIZE - 1) / RAID_BLOCK_SIZE;
// for writing
{
RaidFileWrite write(2, "testS");
write.Open();
write.Write(data, sizeof(data));
TEST_THAT(write.GetDiscUsageInBlocks() == sizeInBlocks);
write.Commit();
}
// for reading
{
std::auto_ptr<RaidFileRead> pread(RaidFileRead::Open(2, "testS"));
TEST_THAT(pread->GetDiscUsageInBlocks() == sizeInBlocks);
}
}
//printf("SKIPPING tests ------------------\n");
//return 0;
// Test a load of transformed things
#define BIG_BLOCK_SIZE (25*1024 + 19)
MemoryBlockGuard<void*> bigblock(BIG_BLOCK_SIZE);
R250 randomX2(2165);
for(unsigned int l = 0; l < BIG_BLOCK_SIZE; ++l)
{
((char*)(void*)bigblock)[l] = randomX2.next() & 0xff;
}
// First on one size of data, on different discs
testReadWriteFile(0, "testdd", data, sizeof(data));
testReadWriteFile(0, "test2", bigblock, BIG_BLOCK_SIZE);
testReadWriteFile(1, "testThree", bigblock, BIG_BLOCK_SIZE - 2048);
testReadWriteFile(1, "testX", bigblock, BIG_BLOCK_SIZE - 2289);
testReadWriteFile(1, "testSmall0", data, 0);
testReadWriteFile(1, "testSmall1", data, 1);
testReadWriteFile(1, "testSmall2", data, 2);
testReadWriteFile(1, "testSmall3", data, 3);
testReadWriteFile(1, "testSmall4", data, 4);
testReadWriteFile(0, "testSmall5", data, 5);
testReadWriteFile(0, "testSmall6", data, 6);
testReadWriteFile(1, "testSmall7", data, 7);
testReadWriteFile(1, "testSmall8", data, 8);
testReadWriteFile(1, "testSmall9", data, 9);
testReadWriteFile(1, "testSmall10", data, 10);
// See about a file which is one block bigger than the previous tests
{
char dataonemoreblock[TEST_DATA_SIZE + RAID_BLOCK_SIZE];
R250 random(715);
for(unsigned int l = 0; l < sizeof(dataonemoreblock); ++l)
{
dataonemoreblock[l] = random.next() & 0xff;
}
testReadWriteFile(0, "testfour", dataonemoreblock, sizeof(dataonemoreblock));
}
// Some more nasty sizes
static int nastysize[] = {0, 1, 2, 7, 8, 9, (RAID_BLOCK_SIZE/2)+3,
RAID_BLOCK_SIZE-9, RAID_BLOCK_SIZE-8, RAID_BLOCK_SIZE-7, RAID_BLOCK_SIZE-6, RAID_BLOCK_SIZE-5,
RAID_BLOCK_SIZE-4, RAID_BLOCK_SIZE-3, RAID_BLOCK_SIZE-2, RAID_BLOCK_SIZE-1};
for(int o = 0; o <= 5; ++o)
{
for(unsigned int n = 0; n < (sizeof(nastysize)/sizeof(nastysize[0])); ++n)
{
int s = (o*RAID_BLOCK_SIZE)+nastysize[n];
char fn[64];
sprintf(fn, "testN%d", s);
testReadWriteFile(n&1, fn, bigblock, s);
}
}
// Finally, a mega test (not necessary for every run, I would have thought)
/* unsigned int megamax = (1024*128) + 9;
MemoryBlockGuard<void*> megablock(megamax);
R250 randomX3(183);
for(unsigned int l = 0; l < megamax; ++l)
{
((char*)(void*)megablock)[l] = randomX3.next() & 0xff;
}
for(unsigned int s = 0; s < megamax; ++s)
{
testReadWriteFile(s & 1, "mega", megablock, s);
RaidFileWrite deleter(s & 1, "mega");
deleter.Delete();
RaidFileWrite deleter2(s & 1, "megaNT");
deleter2.Delete();
}*/
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1