1/*
2 * Copyright 2002-2008, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6/*!	Tests if the read functions of a file system work correctly by reading
7	arbitrary bytes at arbitrary positions of a file previously created.
8
9	The idea is to be able to calculate the contents on each position
10	of the file. It currently only counts numbers, beginning from zero,
11	which is probably not a really good test.
12	But if there is a bug, it would be very likely to show up after some
13	thousand (or even million) runs.
14
15	Works only on little-endian processors, such as x86 (or else the partial
16	read numbers wouldn't be correctly compared).
17
18	Use the --help option to see how it's used.
19*/
20
21#include <File.h>
22#include <StorageDefs.h>
23
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <ctype.h>
28
29#define FILE_NAME		"RANDOM_READ_TEST_FILE"
30#define FILE_SIZE		(1024 * 1024)
31#define BUFFER_SIZE		(64 * 1024)
32#define NUMBER_OF_LOOPS	1000
33#define MAX_FAULTS		10
34
35// currently only works with 4 byte values!
36typedef uint32 test_t;
37
38
39void
40createFile(const char *name, size_t size)
41{
42	BFile file;
43	status_t status = file.SetTo(name,
44		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
45	if (status < B_OK) {
46		fprintf(stderr, "Could not create test file: %s!\n", strerror(status));
47		return;
48	}
49
50	test_t max = size / sizeof(test_t);
51	for (uint32 i = 0; i < max; i++) {
52		if (file.Write(&i, sizeof(test_t)) != sizeof(test_t)) {
53			fprintf(stderr,"Could not create the whole test file!\n");
54			break;
55		}
56	}
57}
58
59
60void
61readTest(const char *name, int32 loops)
62{
63	BFile file;
64	status_t status = file.SetTo(name, B_READ_ONLY);
65	if (status < B_OK) {
66		fprintf(stderr, "Could not open test file! Run \"randomread create "
67			"[size]\" first.\n"
68			"This will create a file named \"%s\" in the current directory.\n",
69			name);
70		return;
71	}
72
73	off_t size;
74	if ((status = file.GetSize(&size)) < B_OK) {
75		fprintf(stderr, "Could not get file size: %s!\n", strerror(status));
76		return;
77	}
78
79	char *buffer = (char *)malloc(BUFFER_SIZE);
80	if (buffer == NULL) {
81		fprintf(stderr, "no memory to create read buffer.\n");
82		return;
83	}
84	srand(time(NULL));
85	int32 faults = 0;
86
87	for (int32 i = 0; i < loops; i++) {
88		off_t pos = rand() % size;
89		// we are lazy tester, minimum read size is 4 bytes
90		int32 bytes = (rand() % BUFFER_SIZE) + 4;
91		off_t max = size - pos;
92		if (max > bytes)
93			max = bytes;
94		else
95			bytes = max;
96
97		ssize_t bytesRead = file.ReadAt(pos, buffer, bytes);
98		if (bytesRead < B_OK) {
99			printf("  Could not read %ld bytes at offset %lld: %s\n",
100				bytes, pos, strerror(bytesRead));
101		} else if (bytesRead != max) {
102			printf("  Could only read %ld bytes instead of %ld at offset %lld\n",
103				bytesRead, bytes, pos);
104		}
105
106		// test contents
107
108		off_t bufferPos = pos;
109		test_t num = bufferPos / sizeof(test_t);
110
111		// check leading partial number
112		int32 partial = bufferPos % sizeof(test_t);
113		if (partial) {
114			test_t read = *(test_t *)(buffer - partial);
115			bool correct;
116			switch (partial) {
117				// byteorder little-endian
118				case 1:
119					correct = (num & 0xffffff00) == (read & 0xffffff00);
120					break;
121				case 2:
122					correct = (num & 0xffff0000) == (read & 0xffff0000);
123					break;
124				case 3:
125					correct = (num & 0xff000000) == (read & 0xff000000);
126					break;
127			}
128			if (!correct) {
129				printf("[%lld,%ld] Bytes at %lld don't match (partial begin = "
130					"%ld, should be %08lx, is %08lx)!\n", pos, bytes,
131					bufferPos, partial, num, read);
132				faults++;
133			}
134			bufferPos += sizeof(test_t) - partial;
135		}
136		num++;
137		test_t *numBuffer = (test_t *)(buffer + sizeof(test_t) - partial);
138
139		// test full numbers
140		for (; bufferPos < bytesRead - sizeof(test_t);
141				bufferPos += sizeof(test_t)) {
142			if (faults > MAX_FAULTS) {
143				printf("maximum number of faults reached, bail out.\n");
144				return;
145			}
146			if (num != *numBuffer) {
147				printf("[%lld,%ld] Bytes at %lld don't match (should be %08lx, "
148					"is %08lx)!\n", pos, bytes, bufferPos, num, *numBuffer);
149				faults++;
150			}
151			num++;
152			numBuffer++;
153		}
154
155		// test last partial number
156		partial = bytesRead - bufferPos;
157		if (partial > 0) {
158			uint32 read = *numBuffer;
159			bool correct;
160			switch (partial) {
161				// byteorder little-endian
162				case 1:
163					correct = (num & 0x00ffffff) == (read & 0x00ffffff);
164					break;
165				case 2:
166					correct = (num & 0x0000ffff) == (read & 0x0000ffff);
167					break;
168				case 3:
169					correct = (num & 0x000000ff) == (read & 0x000000ff);
170					break;
171			}
172			if (!correct) {
173				printf("[%lld,%ld] Bytes at %lld don't match (partial end = "
174					"%ld, should be %08lx, is %08lx)!\n", pos, bytes,
175					bufferPos, partial, num, read);
176				faults++;
177			}
178		}
179	}
180}
181
182
183int
184main(int argc, char **argv)
185{
186	size_t size = FILE_SIZE;
187	int32 loops = NUMBER_OF_LOOPS;
188	bool create = false;
189
190	if (argv[1]) {
191		if (!strcmp(argv[1], "create")) {
192			create = true;
193			if (argv[2])
194				size = atol(argv[2]);
195		}
196		else if (isdigit(*argv[1]))
197			loops = atol(argv[1]);
198		else {
199			// get a nice filename of the program
200			char *filename = strrchr(argv[0], '/')
201				? strrchr(argv[0] , '/') + 1 : argv[0];
202
203			printf("You can either create a test file or perform the test.\n"
204				"  Create:\t%s create [filesize]\n"
205				"  Test:  \t%s [loops]\n\n"
206				"Default size = %d, loops = %d\n",
207				filename, filename, FILE_SIZE, NUMBER_OF_LOOPS);
208
209			return 0;
210		}
211	}
212
213	if (size == 0) {
214		fprintf(stderr, "%s: given file size too small, set to 1 MB.\n",
215			argv[0]);
216		size = FILE_SIZE;
217	}
218
219	if (create)
220		createFile(FILE_NAME, size);
221	else
222		readTest(FILE_NAME, loops);
223
224	return 0;
225}
226