1 /* test/hfile.c -- Test cases for low-level input/output streams.
2
3 Copyright (C) 2013-2014, 2016, 2018 Genome Research Ltd.
4
5 Author: John Marshall <jm18@sanger.ac.uk>
6
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 DEALINGS IN THE SOFTWARE. */
24
25 #include <config.h>
26
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31
32 #include <sys/stat.h>
33
34 #include "../htslib/hfile.h"
35 #include "../htslib/hts_defs.h"
36 #include "../htslib/kstring.h"
37
fail(const char * format,...)38 void HTS_NORETURN fail(const char *format, ...)
39 {
40 int err = errno;
41 va_list args;
42 va_start(args, format);
43 vfprintf(stderr, format, args);
44 va_end(args);
45 if (err != 0) fprintf(stderr, ": %s", strerror(err));
46 fprintf(stderr, "\n");
47 exit(EXIT_FAILURE);
48 }
49
check_offset(hFILE * f,off_t off,const char * message)50 void check_offset(hFILE *f, off_t off, const char *message)
51 {
52 off_t ret = htell(f);
53 if (ret < 0) fail("htell(%s)", message);
54 if (ret == off) return;
55
56 fprintf(stderr, "%s offset incorrect: expected %ld but got %ld\n",
57 message, (long)off, (long)ret);
58 exit(EXIT_FAILURE);
59 }
60
slurp(const char * filename)61 char *slurp(const char *filename)
62 {
63 char *text;
64 struct stat sbuf;
65 size_t filesize;
66 FILE *f = fopen(filename, "rb");
67 if (f == NULL) fail("fopen(\"%s\", \"rb\")", filename);
68 if (fstat(fileno(f), &sbuf) != 0) fail("fstat(\"%s\")", filename);
69 filesize = sbuf.st_size;
70
71 text = (char *) malloc(filesize + 1);
72 if (text == NULL) fail("malloc(text)");
73
74 if (fread(text, 1, filesize, f) != filesize) fail("fread");
75 fclose(f);
76
77 text[filesize] = '\0';
78 return text;
79 }
80
81 hFILE *fin = NULL;
82 hFILE *fout = NULL;
83
reopen(const char * infname,const char * outfname)84 void reopen(const char *infname, const char *outfname)
85 {
86 if (fin) { if (hclose(fin) != 0) fail("hclose(input)"); }
87 if (fout) { if (hclose(fout) != 0) fail("hclose(output)"); }
88
89 fin = hopen(infname, "r");
90 if (fin == NULL) fail("hopen(\"%s\")", infname);
91
92 fout = hopen(outfname, "w");
93 if (fout == NULL) fail("hopen(\"%s\")", outfname);
94 }
95
main(void)96 int main(void)
97 {
98 static const int size[] = { 1, 13, 403, 999, 30000 };
99
100 char buffer[40000];
101 char *original;
102 int c, i;
103 ssize_t n;
104 off_t off;
105
106 reopen("vcf.c", "test/hfile1.tmp");
107 while ((c = hgetc(fin)) != EOF) {
108 if (hputc(c, fout) == EOF) fail("hputc");
109 }
110 if (herrno(fin)) { errno = herrno(fin); fail("hgetc"); }
111
112 reopen("test/hfile1.tmp", "test/hfile2.tmp");
113 if (hpeek(fin, buffer, 50) < 0) fail("hpeek");
114 while ((n = hread(fin, buffer, 17)) > 0) {
115 if (hwrite(fout, buffer, n) != n) fail("hwrite");
116 }
117 if (n < 0) fail("hread");
118
119 reopen("test/hfile2.tmp", "test/hfile3.tmp");
120 while ((n = hread(fin, buffer, sizeof buffer)) > 0) {
121 if (hwrite(fout, buffer, n) != n) fail("hwrite");
122 if (hpeek(fin, buffer, 700) < 0) fail("hpeek");
123 }
124 if (n < 0) fail("hread");
125
126 reopen("test/hfile3.tmp", "test/hfile4.tmp");
127 i = 0;
128 off = 0;
129 while ((n = hread(fin, buffer, size[i++ % 5])) > 0) {
130 off += n;
131 buffer[n] = '\0';
132 check_offset(fin, off, "pre-peek");
133 if (hputs(buffer, fout) == EOF) fail("hputs");
134 if ((n = hpeek(fin, buffer, size[(i+3) % 5])) < 0) fail("hpeek");
135 check_offset(fin, off, "post-peek");
136 }
137 if (n < 0) fail("hread");
138
139 reopen("test/hfile4.tmp", "test/hfile5.tmp");
140 while (hgets(buffer, 80, fin) != NULL) {
141 size_t l = strlen(buffer);
142 if (l > 79) fail("hgets read %zu bytes, should be < 80", l);
143 if (hwrite(fout, buffer, l) != l) fail("hwrite");
144 }
145 if (herrno(fin)) fail("hgets");
146
147 reopen("test/hfile5.tmp", "test/hfile6.tmp");
148 n = hread(fin, buffer, 200);
149 if (n < 0) fail("hread");
150 else if (n != 200) fail("hread only got %d", (int)n);
151 if (hwrite(fout, buffer, 1000) != 1000) fail("hwrite");
152 check_offset(fin, 200, "input/first200");
153 check_offset(fout, 1000, "output/first200");
154
155 if (hseek(fin, 800, SEEK_CUR) < 0) fail("hseek/cur");
156 check_offset(fin, 1000, "input/seek");
157 for (off = 1000; (n = hread(fin, buffer, sizeof buffer)) > 0; off += n)
158 if (hwrite(fout, buffer, n) != n) fail("hwrite");
159 if (n < 0) fail("hread");
160 check_offset(fin, off, "input/eof");
161 check_offset(fout, off, "output/eof");
162
163 if (hseek(fin, 200, SEEK_SET) < 0) fail("hseek/set");
164 if (hseek(fout, 200, SEEK_SET) < 0) fail("hseek(output)");
165 check_offset(fin, 200, "input/backto200");
166 check_offset(fout, 200, "output/backto200");
167 n = hread(fin, buffer, 800);
168 if (n < 0) fail("hread");
169 else if (n != 800) fail("hread only got %d", (int)n);
170 if (hwrite(fout, buffer, 800) != 800) fail("hwrite");
171 check_offset(fin, 1000, "input/wrote800");
172 check_offset(fout, 1000, "output/wrote800");
173
174 if (hflush(fout) == EOF) fail("hflush");
175
176 original = slurp("vcf.c");
177 for (i = 1; i <= 6; i++) {
178 char *text;
179 sprintf(buffer, "test/hfile%d.tmp", i);
180 text = slurp(buffer);
181 if (strcmp(original, text) != 0) {
182 fprintf(stderr, "%s differs from vcf.c\n", buffer);
183 free(text);
184 free(original);
185 return EXIT_FAILURE;
186 }
187 free(text);
188 }
189 free(original);
190
191 if (hclose(fin) != 0) fail("hclose(input)");
192 if (hclose(fout) != 0) fail("hclose(output)");
193
194 fout = hopen("test/hfile_chars.tmp", "w");
195 if (fout == NULL) fail("hopen(\"test/hfile_chars.tmp\")");
196 for (i = 0; i < 256; i++)
197 if (hputc(i, fout) != i) fail("chars: hputc (%d)", i);
198 if (hclose(fout) != 0) fail("hclose(test/hfile_chars.tmp)");
199
200 fin = hopen("test/hfile_chars.tmp", "r");
201 if (fin == NULL) fail("hopen(\"test/hfile_chars.tmp\") for reading");
202 for (i = 0; i < 256; i++)
203 if ((c = hgetc(fin)) != i)
204 fail("chars: hgetc (%d = 0x%x) returned %d = 0x%x", i, i, c, c);
205 if ((c = hgetc(fin)) != EOF) fail("chars: hgetc (EOF) returned %d", c);
206 if (hclose(fin) != 0) fail("hclose(test/hfile_chars.tmp) for reading");
207
208 fin = hopen("preload:test/hfile_chars.tmp", "r");
209 if (fin == NULL) fail("preloading \"test/hfile_chars.tmp\" for reading");
210 for (i = 0; i < 256; i++)
211 if ((c = hgetc(fin)) != i)
212 fail("preloading chars: hgetc (%d = 0x%x) returned %d = 0x%x", i, i, c, c);
213 if ((c = hgetc(fin)) != EOF) fail("preloading chars: hgetc (EOF) returned %d", c);
214 if (hclose(fin) != 0) fail("preloading hclose(test/hfile_chars.tmp) for reading");
215
216 char* test_string = strdup("Test string");
217 fin = hopen("mem:", "r:", test_string, 12);
218 if (fin == NULL) fail("hopen(\"mem:\", \"r:\", ...)");
219 if (hread(fin, buffer, 12) != 12)
220 fail("hopen('mem:', 'r') failed read");
221 if(strcmp(buffer, test_string) != 0)
222 fail("hopen('mem:', 'r') missread '%s' != '%s'", buffer, test_string);
223 char* internal_buf;
224 size_t interval_buf_len;
225 if((internal_buf = hfile_mem_get_buffer(fin, &interval_buf_len)) == NULL){
226 fail("hopen('mem:', 'r') failed to get internal buffer");
227 }
228 if (hclose(fin) != 0) fail("hclose mem for reading");
229
230 test_string = strdup("Test string");
231 fin = hopen("mem:", "wr:", test_string, 12);
232 if (fin == NULL) fail("hopen(\"mem:\", \"w:\", ...)");
233 if (hseek(fin, -1, SEEK_END) < 0)
234 fail("hopen('mem:', 'wr') failed seek");
235 if (hwrite(fin, " extra", 7) != 7)
236 fail("hopen('mem:', 'wr') failed write");
237 if (hseek(fin, 0, SEEK_SET) < 0)
238 fail("hopen('mem:', 'wr') failed seek");
239 if (hread(fin, buffer, 18) != 18)
240 fail("hopen('mem:', 'wr') failed read");
241 if (strcmp(buffer, "Test string extra") != 0)
242 fail("hopen('mem:', 'wr') misswrote '%s' != '%s'", buffer, "Test string extra");
243 if((internal_buf = hfile_mem_steal_buffer(fin, &interval_buf_len)) == NULL){
244 fail("hopen('mem:', 'wr') failed to get internal buffer");
245 }
246 free(internal_buf);
247 if (hclose(fin) != 0) fail("hclose mem for writing");
248
249 fin = hopen("data:,hello, world!%0A", "r");
250 if (fin == NULL) fail("hopen(\"data:...\")");
251 n = hread(fin, buffer, 300);
252 if (n < 0) fail("hread");
253 buffer[n] = '\0';
254 if (strcmp(buffer, "hello, world!\x0A") != 0) fail("hread result");
255 if (hclose(fin) != 0) fail("hclose(\"data:...\")");
256
257 fin = hopen("test/emptyfile", "r");
258 if (fin == NULL) fail("hopen(\"test/emptyfile\") for reading");
259 if (hread(fin, buffer, 100) != 0) fail("test/emptyfile is non-empty");
260 if (hclose(fin) != 0) fail("hclose(\"test/emptyfile\") for reading");
261
262 fin = hopen("data:,", "r");
263 if (fin == NULL) fail("hopen(\"data:\") for reading");
264 if (hread(fin, buffer, 100) != 0) fail("empty data: URL is non-empty");
265 if (hclose(fin) != 0) fail("hclose(\"data:\") for reading");
266
267 fin = hopen("data:;base64,"
268 // Wikipedia's example quote from Thomas Hobbes' Leviathan
269 "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
270 "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
271 "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
272 "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
273 "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=", "r");
274 if (fin == NULL) fail("hopen(\"data:;base64,...\")");
275 n = hread(fin, buffer, 300);
276 if (n < 0) fail("hread for base64");
277 buffer[n] = '\0';
278 if (strcmp(buffer, "Man is distinguished, not only by his reason, but by "
279 "this singular passion from other animals, which is a lust of the mind, that "
280 "by a perseverance of delight in the continued and indefatigable generation "
281 "of knowledge, exceeds the short vehemence of any carnal pleasure.") != 0)
282 fail("hread result for base64");
283 if (hclose(fin) != 0) fail("hclose(\"data:;base64,...\")");
284
285 kstring_t kstr = { 0, 0, NULL };
286
287 if (strcmp(haddextension(&kstr, "foo/bar.bam", 0, ".bai"),
288 "foo/bar.bam.bai") != 0) fail("haddextension foo/bar.bam[.bai]");
289 if (strcmp(haddextension(&kstr, "foo/bar.bam", 1, ".bai"),
290 "foo/bar.bai") != 0) fail("haddextension foo/bar[.bai]");
291 if (strcmp(haddextension(&kstr, "foo.bar/baz", 1, ".bai"),
292 "foo.bar/baz.bai") != 0) fail("haddextension foo.bar/baz[.bai]");
293 if (strcmp(haddextension(&kstr, "foo#bar.bam", 0, ".bai"),
294 "foo#bar.bam.bai") != 0) fail("haddextension foo#bar.bam[.bai]");
295 if (strcmp(haddextension(&kstr, ".bam", 1, ".bai"),
296 ".bai") != 0) fail("haddextension [.bai]");
297 if (strcmp(haddextension(&kstr, "foo", 1, ".csi"),
298 "foo.csi") != 0) fail("haddextension foo[.csi]");
299
300 if (strcmp(haddextension(&kstr, "http://host/bar.cram?a&b&c", 0, ".crai"),
301 "http://host/bar.cram.crai?a&b&c") != 0)
302 fail("haddextension http://host/bar.cram[.crai]?a&b&c");
303
304 if (strcmp(haddextension(&kstr, "http://host/bar.cram#frag", 1, ".crai"),
305 "http://host/bar.crai#frag") != 0)
306 fail("haddextension http://host/bar[.crai]#frag");
307
308 free(ks_release(&kstr));
309
310 return EXIT_SUCCESS;
311 }
312