/* * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ /*! Tests if the read functions of a file system work correctly by reading arbitrary bytes at arbitrary positions of a file previously created. The idea is to be able to calculate the contents on each position of the file. It currently only counts numbers, beginning from zero, which is probably not a really good test. But if there is a bug, it would be very likely to show up after some thousand (or even million) runs. Works only on little-endian processors, such as x86 (or else the partial read numbers wouldn't be correctly compared). Use the --help option to see how it's used. */ #include #include #include #include #include #include #define FILE_NAME "RANDOM_READ_TEST_FILE" #define FILE_SIZE (1024 * 1024) #define BUFFER_SIZE (64 * 1024) #define NUMBER_OF_LOOPS 1000 #define MAX_FAULTS 10 // currently only works with 4 byte values! typedef uint32 test_t; void createFile(const char *name, size_t size) { BFile file; status_t status = file.SetTo(name, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); if (status < B_OK) { fprintf(stderr, "Could not create test file: %s!\n", strerror(status)); return; } test_t max = size / sizeof(test_t); for (uint32 i = 0; i < max; i++) { if (file.Write(&i, sizeof(test_t)) != sizeof(test_t)) { fprintf(stderr,"Could not create the whole test file!\n"); break; } } } void readTest(const char *name, int32 loops) { BFile file; status_t status = file.SetTo(name, B_READ_ONLY); if (status < B_OK) { fprintf(stderr, "Could not open test file! Run \"randomread create " "[size]\" first.\n" "This will create a file named \"%s\" in the current directory.\n", name); return; } off_t size; if ((status = file.GetSize(&size)) < B_OK) { fprintf(stderr, "Could not get file size: %s!\n", strerror(status)); return; } char *buffer = (char *)malloc(BUFFER_SIZE); if (buffer == NULL) { fprintf(stderr, "no memory to create read buffer.\n"); return; } srand(time(NULL)); int32 faults = 0; for (int32 i = 0; i < loops; i++) { off_t pos = rand() % size; // we are lazy tester, minimum read size is 4 bytes int32 bytes = (rand() % BUFFER_SIZE) + 4; off_t max = size - pos; if (max > bytes) max = bytes; else bytes = max; ssize_t bytesRead = file.ReadAt(pos, buffer, bytes); if (bytesRead < B_OK) { printf(" Could not read %ld bytes at offset %lld: %s\n", bytes, pos, strerror(bytesRead)); } else if (bytesRead != max) { printf(" Could only read %ld bytes instead of %ld at offset %lld\n", bytesRead, bytes, pos); } // test contents off_t bufferPos = pos; test_t num = bufferPos / sizeof(test_t); // check leading partial number int32 partial = bufferPos % sizeof(test_t); if (partial) { test_t read = *(test_t *)(buffer - partial); bool correct; switch (partial) { // byteorder little-endian case 1: correct = (num & 0xffffff00) == (read & 0xffffff00); break; case 2: correct = (num & 0xffff0000) == (read & 0xffff0000); break; case 3: correct = (num & 0xff000000) == (read & 0xff000000); break; } if (!correct) { printf("[%lld,%ld] Bytes at %lld don't match (partial begin = " "%ld, should be %08lx, is %08lx)!\n", pos, bytes, bufferPos, partial, num, read); faults++; } bufferPos += sizeof(test_t) - partial; } num++; test_t *numBuffer = (test_t *)(buffer + sizeof(test_t) - partial); // test full numbers for (; bufferPos < bytesRead - sizeof(test_t); bufferPos += sizeof(test_t)) { if (faults > MAX_FAULTS) { printf("maximum number of faults reached, bail out.\n"); return; } if (num != *numBuffer) { printf("[%lld,%ld] Bytes at %lld don't match (should be %08lx, " "is %08lx)!\n", pos, bytes, bufferPos, num, *numBuffer); faults++; } num++; numBuffer++; } // test last partial number partial = bytesRead - bufferPos; if (partial > 0) { uint32 read = *numBuffer; bool correct; switch (partial) { // byteorder little-endian case 1: correct = (num & 0x00ffffff) == (read & 0x00ffffff); break; case 2: correct = (num & 0x0000ffff) == (read & 0x0000ffff); break; case 3: correct = (num & 0x000000ff) == (read & 0x000000ff); break; } if (!correct) { printf("[%lld,%ld] Bytes at %lld don't match (partial end = " "%ld, should be %08lx, is %08lx)!\n", pos, bytes, bufferPos, partial, num, read); faults++; } } } } int main(int argc, char **argv) { size_t size = FILE_SIZE; int32 loops = NUMBER_OF_LOOPS; bool create = false; if (argv[1]) { if (!strcmp(argv[1], "create")) { create = true; if (argv[2]) size = atol(argv[2]); } else if (isdigit(*argv[1])) loops = atol(argv[1]); else { // get a nice filename of the program char *filename = strrchr(argv[0], '/') ? strrchr(argv[0] , '/') + 1 : argv[0]; printf("You can either create a test file or perform the test.\n" " Create:\t%s create [filesize]\n" " Test: \t%s [loops]\n\n" "Default size = %d, loops = %d\n", filename, filename, FILE_SIZE, NUMBER_OF_LOOPS); return 0; } } if (size == 0) { fprintf(stderr, "%s: given file size too small, set to 1 MB.\n", argv[0]); size = FILE_SIZE; } if (create) createFile(FILE_NAME, size); else readTest(FILE_NAME, loops); return 0; }