diff options
author | Ben Summers <ben@fluffy.co.uk> | 2005-10-14 08:50:54 +0000 |
---|---|---|
committer | Ben Summers <ben@fluffy.co.uk> | 2005-10-14 08:50:54 +0000 |
commit | 99f8ce096bc5569adbfea1911dbcda24c28d8d8b (patch) | |
tree | 049c302161fea1f2f6223e1e8f3c40d9e8aadc8b /test/raidfile |
Box Backup 0.09 with a few tweeks
Diffstat (limited to 'test/raidfile')
-rwxr-xr-x | test/raidfile/Darwin-SYS.h | 167 | ||||
-rwxr-xr-x | test/raidfile/Makefile.extra.Darwin | 6 | ||||
-rwxr-xr-x | test/raidfile/intercept.cpp | 252 | ||||
-rwxr-xr-x | test/raidfile/make-darwin-intercepts.pl | 46 | ||||
-rwxr-xr-x | test/raidfile/testextra | 7 | ||||
-rwxr-xr-x | test/raidfile/testfiles/raidfile.conf | 30 | ||||
-rwxr-xr-x | test/raidfile/testraidfile.cpp | 907 |
7 files changed, 1415 insertions, 0 deletions
diff --git a/test/raidfile/Darwin-SYS.h b/test/raidfile/Darwin-SYS.h new file mode 100755 index 00000000..da95c347 --- /dev/null +++ b/test/raidfile/Darwin-SYS.h @@ -0,0 +1,167 @@ +
+/* Taken from the Darwin Libc source
+*/
+
+/*
+ * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License"). You may not use this file except in compliance with the
+ * License. Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/* Copyright (c) 1992 NeXT Computer, Inc. All rights reserved.
+ *
+ * File: SYS.h
+ *
+ * Definition of the user side of the UNIX system call interface
+ * for M98K.
+ *
+ * Errors are flagged by the location of the trap return (ie., which
+ * instruction is executed upon rfi):
+ *
+ * SC PC + 4: Error (typically branch to cerror())
+ * SC PC + 8: Success
+ *
+ * HISTORY
+ * 18-Nov-92 Ben Fathi (benf@next.com)
+ * Ported to m98k.
+ *
+ * 9-Jan-92 Peter King (king@next.com)
+ * Created.
+ */
+
+#define KERNEL_PRIVATE 1
+/*
+ * Header files.
+ */
+#import <architecture/ppc/asm_help.h>
+#import <architecture/ppc/pseudo_inst.h>
+#import <mach/ppc/exception.h>
+#import <sys/syscall.h>
+
+/* From rhapsody kernel mach/ppc/syscall_sw.h */
+#define kernel_trap_args_0
+#define kernel_trap_args_1
+#define kernel_trap_args_2
+#define kernel_trap_args_3
+#define kernel_trap_args_4
+#define kernel_trap_args_5
+#define kernel_trap_args_6
+#define kernel_trap_args_7
+
+/*
+ * simple_kernel_trap -- Mach system calls with 8 or less args
+ * Args are passed in a0 - a7, system call number in r0.
+ * Do a "sc" instruction to enter kernel.
+ */
+#define simple_kernel_trap(trap_name, trap_number) \
+ .globl _##trap_name @\
+_##trap_name: @\
+ li r0,trap_number @\
+ sc @\
+ blr @\
+ END(trap_name)
+
+#define kernel_trap_0(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_1(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_2(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_3(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_4(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_5(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_6(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_7(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_8(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+#define kernel_trap_9(trap_name,trap_number) \
+ simple_kernel_trap(trap_name,trap_number)
+
+/* End of rhapsody kernel mach/ppc/syscall_sw.h */
+
+/*
+ * Macros.
+ */
+
+#define SYSCALL(name, nargs) \
+ .globl cerror @\
+LEAF(_##name) @\
+ kernel_trap_args_##nargs @\
+ li r0,SYS_##name @\
+ sc @\
+ b 1f @\
+ b 2f @\
+1: BRANCH_EXTERN(cerror) @\
+.text \
+2: nop
+
+#define SYSCALL_NONAME(name, nargs) \
+ .globl cerror @\
+ kernel_trap_args_##nargs @\
+ li r0,SYS_##name @\
+ sc @\
+ b 1f @\
+ b 2f @\
+1: BRANCH_EXTERN(cerror) @\
+.text \
+2: nop
+
+#define PSEUDO(pseudo, name, nargs) \
+LEAF(_##pseudo) @\
+ SYSCALL_NONAME(name, nargs)
+
+
+#undef END
+#import <mach/ppc/syscall_sw.h>
+
+#if !defined(SYS_getdirentriesattr)
+#define SYS_getdirentriesattr 222
+#endif
+
+#if !defined(SYS_semsys)
+#define SYS_semsys 251
+#define SYS_msgsys 252
+#define SYS_shmsys 253
+#define SYS_semctl 254
+#define SYS_semget 255
+#define SYS_semop 256
+#define SYS_semconfig 257
+#define SYS_msgctl 258
+#define SYS_msgget 259
+#define SYS_msgsnd 260
+#define SYS_msgrcv 261
+#define SYS_shmat 262
+#define SYS_shmctl 263
+#define SYS_shmdt 264
+#define SYS_shmget 265
+#endif
+
diff --git a/test/raidfile/Makefile.extra.Darwin b/test/raidfile/Makefile.extra.Darwin new file mode 100755 index 00000000..dc3bad81 --- /dev/null +++ b/test/raidfile/Makefile.extra.Darwin @@ -0,0 +1,6 @@ + +# link-extra: intercept-lseek.o intercept-close.o intercept-open.o intercept-read.o intercept-readv.o intercept-write.o + +$(OUTDIR)/intercept-lseek.o: make-darwin-intercepts.pl $(OUTDIR) + ./make-darwin-intercepts.pl $(OUTDIR) + diff --git a/test/raidfile/intercept.cpp b/test/raidfile/intercept.cpp new file mode 100755 index 00000000..4f20ba0a --- /dev/null +++ b/test/raidfile/intercept.cpp @@ -0,0 +1,252 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: intercept.cpp +// Purpose: Syscall interception code for the raidfile test +// Created: 2003/07/22 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include <sys/syscall.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/uio.h> +#include <errno.h> + +#ifndef PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE + +#ifdef PLATFORM_DARWIN + // For some reason, __syscall just doesn't work on Darwin + // so instead, we build functions using assembler in a varient + // of the technique used in the Darwin Libc + extern "C" int + TEST_open(const char *path, int flags, mode_t mode); + extern "C" int + TEST_close(int d); + extern "C" ssize_t + TEST_write(int d, const void *buf, size_t nbytes); + extern "C" ssize_t + TEST_read(int d, void *buf, size_t nbytes); + extern "C" ssize_t + TEST_readv(int d, const struct iovec *iov, int iovcnt); + extern "C" off_t + TEST_lseek(int fildes, off_t offset, int whence); +#else + #ifdef PLATFORM_LINUX + #undef __syscall + #define __syscall syscall + #else + // Need this, not declared in syscall.h + extern "C" off_t __syscall(quad_t number, ...); + #endif +#endif + +#include <string.h> +#include <stdio.h> + +#include "MemLeakFindOn.h" + +bool intercept_enabled = false; +const char *intercept_filename = 0; +int intercept_filedes = -1; +unsigned int intercept_errorafter = 0; +int intercept_errno = 0; +int intercept_syscall = 0; +off_t intercept_filepos = 0; + +#define SIZE_ALWAYS_ERROR -773 + +void intercept_clear_setup() +{ + intercept_enabled = false; + intercept_filename = 0; + intercept_filedes = -1; + intercept_errorafter = 0; + intercept_syscall = 0; + intercept_filepos = 0; +} + +bool intercept_triggered() +{ + return !intercept_enabled; +} + +void intercept_setup_error(const char *filename, unsigned int errorafter, int errortoreturn, int syscalltoerror) +{ + TRACE4("Setup for error: %s, after %d, err %d, syscall %d\n", filename, errorafter, errortoreturn, syscalltoerror); + intercept_enabled = true; + intercept_filename = filename; + intercept_filedes = -1; + intercept_errorafter = errorafter; + intercept_syscall = syscalltoerror; + intercept_errno = errortoreturn; + intercept_filepos = 0; +} + +bool intercept_errornow(int d, int size, int syscallnum) +{ + if(intercept_filedes != -1 && d == intercept_filedes && syscallnum == intercept_syscall) + { + //printf("Checking for err, %d, %d, %d\n", d, size, syscallnum); + if(size == SIZE_ALWAYS_ERROR) + { + // Looks good for an error! + TRACE2("Returning error %d for syscall %d\n", intercept_errno, syscallnum); + return true; + } + // where are we in the file? + if(intercept_filepos >= intercept_errorafter || intercept_filepos >= ((int)intercept_errorafter - size)) + { + TRACE3("Returning error %d for syscall %d, file pos %d\n", intercept_errno, syscallnum, (int)intercept_filepos); + return true; + } + } + return false; // no error please! +} + +int intercept_reterr() +{ + intercept_enabled = false; + intercept_filename = 0; + intercept_filedes = -1; + intercept_errorafter = 0; + intercept_syscall = 0; + return intercept_errno; +} + +#define CHECK_FOR_FAKE_ERROR_COND(D, S, CALL, FAILRES) \ + if(intercept_enabled) \ + { \ + if(intercept_errornow(D, S, CALL)) \ + { \ + errno = intercept_reterr(); \ + return FAILRES; \ + } \ + } + +extern "C" int +open(const char *path, int flags, mode_t mode) +{ + if(intercept_enabled) + { + if(intercept_syscall == SYS_open && strcmp(path, intercept_filename) == 0) + { + errno = intercept_reterr(); + return -1; + } + } +#ifdef PLATFORM_DARWIN + int r = TEST_open(path, flags, mode); +#else + int r = __syscall(SYS_open, path, flags, mode); +#endif + if(intercept_enabled && intercept_filedes == -1) + { + // Right file? + if(strcmp(intercept_filename, path) == 0) + { + intercept_filedes = r; + //printf("Found file to intercept, h = %d\n", r); + } + } + return r; +} + +extern "C" int +close(int d) +{ + CHECK_FOR_FAKE_ERROR_COND(d, SIZE_ALWAYS_ERROR, SYS_close, -1); +#ifdef PLATFORM_DARWIN + int r = TEST_close(d); +#else + int r = __syscall(SYS_close, d); +#endif + if(r == 0) + { + if(d == intercept_filedes) + { + intercept_filedes = -1; + } + } + return r; +} + +extern "C" ssize_t +write(int d, const void *buf, size_t nbytes) +{ + CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_write, -1); +#ifdef PLATFORM_DARWIN + int r = TEST_write(d, buf, nbytes); +#else + int r = __syscall(SYS_write, d, buf, nbytes); +#endif + if(r != -1) + { + intercept_filepos += r; + } + return r; +} + +extern "C" ssize_t +read(int d, void *buf, size_t nbytes) +{ + CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_read, -1); +#ifdef PLATFORM_DARWIN + int r = TEST_read(d, buf, nbytes); +#else + int r = __syscall(SYS_read, d, buf, nbytes); +#endif + if(r != -1) + { + intercept_filepos += r; + } + return r; +} + +extern "C" ssize_t +readv(int d, const struct iovec *iov, int iovcnt) +{ + // how many bytes? + int nbytes = 0; + for(int b = 0; b < iovcnt; ++b) + { + nbytes += iov[b].iov_len; + } + + CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_readv, -1); +#ifdef PLATFORM_DARWIN + int r = TEST_readv(d, iov, iovcnt); +#else + int r = __syscall(SYS_readv, d, iov, iovcnt); +#endif + if(r != -1) + { + intercept_filepos += r; + } + return r; +} + +extern "C" off_t +lseek(int fildes, off_t offset, int whence) +{ + // random magic for lseek syscall, see /usr/src/lib/libc/sys/lseek.c + CHECK_FOR_FAKE_ERROR_COND(fildes, 0, SYS_lseek, -1); +#ifdef PLATFORM_DARWIN + int r = TEST_lseek(fildes, offset, whence); +#else + #ifdef PLATFORM_LINUX + off_t r = __syscall(SYS_lseek, fildes, offset, whence); + #else + off_t r = __syscall(SYS_lseek, fildes, 0 /* extra 0 required here! */, offset, whence); + #endif +#endif + if(r != -1) + { + intercept_filepos = r; + } + return r; +} + +#endif // n PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE diff --git a/test/raidfile/make-darwin-intercepts.pl b/test/raidfile/make-darwin-intercepts.pl new file mode 100755 index 00000000..2f19e090 --- /dev/null +++ b/test/raidfile/make-darwin-intercepts.pl @@ -0,0 +1,46 @@ +#!/usr/bin/perl +use strict; + +my $out = $ARGV[0]; +die "No out directory specified" unless $out ne ''; + +my @calls = split /[\r\n]+/,<<__E; +lseek SYSCALL_TEST(lseek, 3) +open SYSCALL_TEST(open, 3) +close SYSCALL_TEST(close, 1) +write SYSCALL_TEST(write, 3) +read SYSCALL_TEST(read, 3) +readv SYSCALL_TEST(readv, 3) +__E + +for(@calls) +{ + my ($name,$line) = split / /,$_,2; + + open FL,">$out/intercept-$name.s" or die "Can't open out file"; + print FL <<'__S'; +#include "../../../test/raidfile/Darwin-SYS.h" + +#define SYSCALL_TEST(name, nargs) \ + .globl cerror @\ +LEAF(_TEST_##name) @\ + kernel_trap_args_##nargs @\ + li r0,SYS_##name @\ + sc @\ + b 1f @\ + b 2f @\ +1: BRANCH_EXTERN(cerror) @\ +.text \ +2: nop + +__S + print FL $line,"\n\tblr\n\n"; + + close FL; + + if(system("gcc -c $out/intercept-$name.s -o $out/intercept-$name.o") != 0) + { + die "Assembly failed\n"; + } +} + diff --git a/test/raidfile/testextra b/test/raidfile/testextra new file mode 100755 index 00000000..1f4fbcb2 --- /dev/null +++ b/test/raidfile/testextra @@ -0,0 +1,7 @@ +mkdir testfiles/0_0 +mkdir testfiles/0_1 +mkdir testfiles/0_2 +mkdir testfiles/1_0 +mkdir testfiles/1_1 +mkdir testfiles/1_2 +mkdir testfiles/2 diff --git a/test/raidfile/testfiles/raidfile.conf b/test/raidfile/testfiles/raidfile.conf new file mode 100755 index 00000000..6c6d02f9 --- /dev/null +++ b/test/raidfile/testfiles/raidfile.conf @@ -0,0 +1,30 @@ + +disc0 +{ + SetNumber = 0 + BlockSize = 2048 + Dir0 = testfiles/0_0 + Dir1 = testfiles/0_1 + Dir2 = testfiles/0_2 +} + +disc1 +{ + SetNumber = 1 + BlockSize = 2048 + Dir0 = testfiles/1_0 + Dir1 = testfiles/1_1 + Dir2 = testfiles/1_2 +} + +disc2 +{ + SetNumber = 2 + BlockSize = 2048 + Dir0 = testfiles/2 + Dir1 = testfiles/2 + Dir2 = testfiles/2 +} + + + diff --git a/test/raidfile/testraidfile.cpp b/test/raidfile/testraidfile.cpp new file mode 100755 index 00000000..13ae9083 --- /dev/null +++ b/test/raidfile/testraidfile.cpp @@ -0,0 +1,907 @@ +// -------------------------------------------------------------------------- +// +// 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); + + { + RaidFileWrite writeA2(0, "overwrite_A"); + TEST_CHECK_THROWS(writeA2.Open(), RaidFileException, FileIsCurrentlyOpenForWriting); + } + } + + // 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; +} |