1 /* vim: set ts=8 sts=4 sw=4 tw=80 noet: */
2 /*======================================================================
3 Copyright (C) 2004,2005,2009 Walter Doekes <walter+tthsum@wjd.nu>
4 This file is part of tthsum.
5 
6 tthsum is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 tthsum is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with tthsum.  If not, see <http://www.gnu.org/licenses/>.
18 ======================================================================*/
19 #include "read.h"
20 
21 #include "test.h"
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #ifndef _WIN32
29 #   include <unistd.h>
30 #   define O_BINARY 0
31 #endif /* !_WIN32 */
32 
33 
34 static const char test_data_src[] =
35 "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in erat. "
36 "Vivamus lorem felis, tempor a, aliquet a, pretium id, lectus. Maecenas semper"
37 " blandit sem. Aenean faucibus interdum metus. Curabitur quis nulla. Curabitur"
38 " risus massa, commodo sit amet, adipiscing quis, aliquam eu, dolor. Donec nec"
39 " libero. Cras ipsum ante, ultricies in, euismod quis, lobortis ut, turpis. "
40 "Aliquam purus quam, hendrerit et, luctus et, lobortis porttitor, ipsum. "
41 "Mauris ut urna sit amet tellus pretium ullamcorper. Praesent non ante. "
42 "\xff\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
43 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
44 "\xff\xfe\xfd\xfc\xfb\x84\x83\x82\x81\x80\x7f\x7e\x7d\x7c\x7b\x7a"
45 "123\r\n456\r789\n101";
46 static char test_data[2048] = {0};
47 static unsigned test_data_len = 0;
48 static char test_filename[512] = {0};
49 
is_multiple_of_two(unsigned len)50 static int is_multiple_of_two(unsigned len) {
51     unsigned previous = len << 1;
52     while (previous == len << 1) {
53 	previous = len;
54 	len >>= 1;
55     }
56     return len == 0;
57 }
58 
init_test_data(const char * src,unsigned length)59 static void init_test_data(const char* src, unsigned length) {
60     test_data[0] = test_data[sizeof(test_data)-1] = '\0';
61     test_data_len = length;
62     if (test_data_len >= sizeof(test_data)) {
63 	fprintf(stderr, "read_test: please increase test_data size\n");
64 	return;
65     }
66     memcpy(test_data, test_data_src, test_data_len);
67 }
68 
init_test_filename()69 static void init_test_filename() {
70 #if defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || _XOPEN_SOURCE >= 500
71     int fd;
72     strcpy(test_filename, "test_read.XXXXXX");
73     if ((fd = mkstemp(test_filename)) < 0) {
74 	perror("read_test: error running mkstemp");
75 	test_filename[0] = '\0';
76 	return;
77     }
78     close(fd);
79 #else
80     strcpy(test_filename, "tempfile.bin"); /* XXX not a proper name, no? */
81 #endif
82 }
83 
init_test_file(const char * data,unsigned data_len)84 static void init_test_file(const char* data, unsigned data_len) {
85     FILE* fp;
86     if ((fp = fopen(test_filename, "wb")) == NULL) {
87 	perror("read_test: error fopen'ing test_filename for writing");
88 	return;
89     }
90     if (fwrite(data, sizeof(char), data_len, fp) != data_len) {
91 	perror("read_test: error fwrite'ing to test_filename");
92 	fclose(fp);
93 	return;
94     }
95     if (fflush(fp) != 0) {
96 	perror("read_test: error fflush'ing test_filename");
97 	fclose(fp);
98 	return;
99     }
100     if (fclose(fp) != 0)
101 	perror("read_test: error fclose'ing test_filename");
102 }
103 
set_up_file(const char * data,unsigned length)104 static void set_up_file(const char* data, unsigned length) {
105     init_test_filename();
106     init_test_file(data, length);
107 }
108 
set_up_file_lorem()109 static void set_up_file_lorem() {
110     init_test_data(test_data_src, strlen(test_data_src));
111     /* Cheat a bit: we replace the first \xff in test_data with \x00 */
112     {
113 	char* p = strchr(test_data, '\xff');
114 	if (p == NULL) {
115 	    fprintf(stderr, "read_test: expected to find \\xff to replace\n");
116 	    return;
117 	}
118 	*p = '\0';
119     }
120     init_test_filename();
121     init_test_file(test_data, test_data_len);
122 }
123 
tear_down_file()124 static void tear_down_file() {
125     if (test_filename[0] != '\0') {
126 	unlink(test_filename);
127 	test_filename[0] = '\0';
128     }
129 }
130 
test_read(const char * type,struct rofile * rf,const char * compare,unsigned compare_len)131 static int test_read(const char* type, struct rofile* rf,
132 	const char* compare, unsigned compare_len) {
133     int ret, last_read;
134     unsigned blocksize, getsize;
135     uint64_t filesize;
136     const char *data_p;
137     if (rf == NULL)
138 	FAIL1("%s open failed!", type);
139     rofinfo(&blocksize, &filesize, rf);
140     TEST_PASS2(filesize == (uint64_t)compare_len, "File size mismatch: "
141 	    "expected %u, got %" PRIu64 "\n", compare_len, filesize);
142     TEST_PASS1(is_multiple_of_two(blocksize), "Expected multiple of two for "
143 	    "block size, got %u", blocksize);
144 
145     last_read = 0;
146     do {
147 	if ((ret = rofread(&data_p, &getsize, rf)) < 0)
148 	    FAIL1("%s's read returned -1", type);
149 	if (ret == 0)
150 	    break;
151 	/* We test that all reads are exactly blocksize except the last read. */
152 	if (getsize < blocksize) {
153 	    TEST_PASS(!last_read,
154 		    "Previous block returned was too small while not at EOF");
155 	    last_read = 1;
156 	}
157 	TEST_PASS(memcmp(compare, data_p, getsize) == 0, "Data mismatch!");
158 	compare += getsize;
159     } while (ret);
160 
161     return 0;
162 }
163 
test_readall(const char * type,struct rofile * rf,const char * compare,unsigned compare_len)164 static int test_readall(const char* type, struct rofile* rf,
165 	const char* compare, unsigned compare_len) {
166     unsigned length;
167     char* all = rof_readall(rf, &length);
168     TEST_PASS2(all != NULL, "%s readall returned NULL, rf is %p", type,
169 	    (const void*)rf);
170     if (rf == NULL) {
171 	free(all);
172 	FAIL1("%s open returned NULL", type);
173     }
174     if (length != compare_len) {
175 	free(all);
176 	FAIL3("%s file length %u != %u", type, length, compare_len);
177     }
178     if (memcmp(all, compare, length) != 0) {
179 	free(all);
180 	FAIL1("%s data mismatch", type);
181     }
182     free(all);
183     return 0;
184 }
185 
test_read_file(const char * filename,const char * data,unsigned length)186 static int test_read_file(const char* filename, const char* data,
187 	unsigned length) {
188     struct rofile* rf = NULL;
189     int i, ret = 0;
190     for (i = 0; i < 6; ++i) {
191 	switch (i) {
192 	case 0: ret += test_read("rofopen_mem",
193 		(rf = rofopen_mem(data, length)), data, length); break;
194 	case 1: ret += test_readall("rofopen_mem",
195 		(rf = rofopen_mem(data, length)), data, length); break;
196 	case 2: ret += test_read("rofopen_mmap",
197 		(rf = rofopen_mmap(filename)), data, length); break;
198 	case 3: ret += test_readall("rofopen_mmap",
199 		(rf = rofopen_mmap(filename)), data, length); break;
200 	case 4: ret += test_read("rofopen_sysfile",
201 		(rf = rofopen_sysfile(filename)), data, length); break;
202 	case 5: ret += test_readall("rofopen_sysfile",
203 		(rf = rofopen_sysfile(filename)), data, length); break;
204 	}
205 	if (rf != NULL)
206 	    rofclose(rf);
207     }
208     return ret;
209 }
210 
test_read_file_lorem()211 static int test_read_file_lorem() {
212     int ret;
213     set_up_file_lorem();
214     ret = test_read_file(test_filename, test_data, test_data_len);
215     tear_down_file();
216     return ret;
217 }
218 
test_read_file_85s()219 static int test_read_file_85s() {
220     unsigned len = 3 * 16 * 1024 * 1024 - 123;
221     char* data;
222     int ret;
223 
224     if ((data = (char*)malloc(len)) == NULL)
225 	FAIL("not enough memory");
226     memset(data, 85, len);
227     data[0] = '\0';
228     data[len - 1] = '\xff';
229 
230     set_up_file(data, len);
231     ret = test_read_file(test_filename, data, len);
232     tear_down_file();
233 
234     free(data);
235     return ret;
236 }
237 
test_readall_properties()238 static int test_readall_properties() {
239     unsigned unused;
240     TEST_PASS(rof_readall(NULL, &unused) == NULL,
241 	    "rof_readall should return NULL when rf is NULL");
242     return 0;
243 }
244 
245 
246 TESTS(read_test)
247     TEST(test_read_file_lorem);
248     TEST(test_read_file_85s);
249     TEST(test_readall_properties);
250 ENDTESTS
251