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