1 /*  test/hfile.c -- Test cases for low-level input/output streams.
2 
3     Copyright (C) 2013-2014, 2016 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 
fail(const char * format,...)37 void HTS_NORETURN fail(const char *format, ...)
38 {
39     int err = errno;
40     va_list args;
41     va_start(args, format);
42     vfprintf(stderr, format, args);
43     va_end(args);
44     if (err != 0) fprintf(stderr, ": %s", strerror(err));
45     fprintf(stderr, "\n");
46     exit(EXIT_FAILURE);
47 }
48 
check_offset(hFILE * f,off_t off,const char * message)49 void check_offset(hFILE *f, off_t off, const char *message)
50 {
51     off_t ret = htell(f);
52     if (ret < 0) fail("htell(%s)", message);
53     if (ret == off) return;
54 
55     fprintf(stderr, "%s offset incorrect: expected %ld but got %ld\n",
56             message, (long)off, (long)ret);
57     exit(EXIT_FAILURE);
58 }
59 
slurp(const char * filename)60 char *slurp(const char *filename)
61 {
62     char *text;
63     struct stat sbuf;
64     size_t filesize;
65     FILE *f = fopen(filename, "rb");
66     if (f == NULL) fail("fopen(\"%s\", \"rb\")", filename);
67     if (fstat(fileno(f), &sbuf) != 0) fail("fstat(\"%s\")", filename);
68     filesize = sbuf.st_size;
69 
70     text = (char *) malloc(filesize + 1);
71     if (text == NULL) fail("malloc(text)");
72 
73     if (fread(text, 1, filesize, f) != filesize) fail("fread");
74     fclose(f);
75 
76     text[filesize] = '\0';
77     return text;
78 }
79 
80 hFILE *fin = NULL;
81 hFILE *fout = NULL;
82 
reopen(const char * infname,const char * outfname)83 void reopen(const char *infname, const char *outfname)
84 {
85     if (fin) { if (hclose(fin) != 0) fail("hclose(input)"); }
86     if (fout) { if (hclose(fout) != 0) fail("hclose(output)"); }
87 
88     fin = hopen(infname, "r");
89     if (fin == NULL) fail("hopen(\"%s\")", infname);
90 
91     fout = hopen(outfname, "w");
92     if (fout == NULL) fail("hopen(\"%s\")", outfname);
93 }
94 
main(void)95 int main(void)
96 {
97     static const int size[] = { 1, 13, 403, 999, 30000 };
98 
99     char buffer[40000];
100     char *original;
101     int c, i;
102     ssize_t n;
103     off_t off;
104 
105     reopen("vcf.c", "test/hfile1.tmp");
106     while ((c = hgetc(fin)) != EOF) {
107         if (hputc(c, fout) == EOF) fail("hputc");
108     }
109     if (herrno(fin)) { errno = herrno(fin); fail("hgetc"); }
110 
111     reopen("test/hfile1.tmp", "test/hfile2.tmp");
112     if (hpeek(fin, buffer, 50) < 0) fail("hpeek");
113     while ((n = hread(fin, buffer, 17)) > 0) {
114         if (hwrite(fout, buffer, n) != n) fail("hwrite");
115     }
116     if (n < 0) fail("hread");
117 
118     reopen("test/hfile2.tmp", "test/hfile3.tmp");
119     while ((n = hread(fin, buffer, sizeof buffer)) > 0) {
120         if (hwrite(fout, buffer, n) != n) fail("hwrite");
121         if (hpeek(fin, buffer, 700) < 0) fail("hpeek");
122     }
123     if (n < 0) fail("hread");
124 
125     reopen("test/hfile3.tmp", "test/hfile4.tmp");
126     i = 0;
127     off = 0;
128     while ((n = hread(fin, buffer, size[i++ % 5])) > 0) {
129         off += n;
130         buffer[n] = '\0';
131         check_offset(fin, off, "pre-peek");
132         if (hputs(buffer, fout) == EOF) fail("hputs");
133         if ((n = hpeek(fin, buffer, size[(i+3) % 5])) < 0) fail("hpeek");
134         check_offset(fin, off, "post-peek");
135     }
136     if (n < 0) fail("hread");
137 
138     reopen("test/hfile4.tmp", "test/hfile5.tmp");
139     while (hgets(buffer, 80, fin) != NULL) {
140         size_t l = strlen(buffer);
141         if (l > 79) fail("hgets read %zu bytes, should be < 80", l);
142         if (hwrite(fout, buffer, l) != l) fail("hwrite");
143     }
144     if (herrno(fin)) fail("hgets");
145 
146     reopen("test/hfile5.tmp", "test/hfile6.tmp");
147     n = hread(fin, buffer, 200);
148     if (n < 0) fail("hread");
149     else if (n != 200) fail("hread only got %d", (int)n);
150     if (hwrite(fout, buffer, 1000) != 1000) fail("hwrite");
151     check_offset(fin, 200, "input/first200");
152     check_offset(fout, 1000, "output/first200");
153 
154     if (hseek(fin, 800, SEEK_CUR) < 0) fail("hseek/cur");
155     check_offset(fin, 1000, "input/seek");
156     for (off = 1000; (n = hread(fin, buffer, sizeof buffer)) > 0; off += n)
157         if (hwrite(fout, buffer, n) != n) fail("hwrite");
158     if (n < 0) fail("hread");
159     check_offset(fin, off, "input/eof");
160     check_offset(fout, off, "output/eof");
161 
162     if (hseek(fin, 200, SEEK_SET) < 0) fail("hseek/set");
163     if (hseek(fout, 200, SEEK_SET) < 0) fail("hseek(output)");
164     check_offset(fin, 200, "input/backto200");
165     check_offset(fout, 200, "output/backto200");
166     n = hread(fin, buffer, 800);
167     if (n < 0) fail("hread");
168     else if (n != 800) fail("hread only got %d", (int)n);
169     if (hwrite(fout, buffer, 800) != 800) fail("hwrite");
170     check_offset(fin, 1000, "input/wrote800");
171     check_offset(fout, 1000, "output/wrote800");
172 
173     if (hflush(fout) == EOF) fail("hflush");
174 
175     original = slurp("vcf.c");
176     for (i = 1; i <= 6; i++) {
177         char *text;
178         sprintf(buffer, "test/hfile%d.tmp", i);
179         text = slurp(buffer);
180         if (strcmp(original, text) != 0) {
181             fprintf(stderr, "%s differs from vcf.c\n", buffer);
182             return EXIT_FAILURE;
183         }
184         free(text);
185     }
186     free(original);
187 
188     if (hclose(fin) != 0) fail("hclose(input)");
189     if (hclose(fout) != 0) fail("hclose(output)");
190 
191     fout = hopen("test/hfile_chars.tmp", "w");
192     if (fout == NULL) fail("hopen(\"test/hfile_chars.tmp\")");
193     for (i = 0; i < 256; i++)
194         if (hputc(i, fout) != i) fail("chars: hputc (%d)", i);
195     if (hclose(fout) != 0) fail("hclose(test/hfile_chars.tmp)");
196 
197     fin = hopen("test/hfile_chars.tmp", "r");
198     if (fin == NULL) fail("hopen(\"test/hfile_chars.tmp\") for reading");
199     for (i = 0; i < 256; i++)
200         if ((c = hgetc(fin)) != i)
201             fail("chars: hgetc (%d = 0x%x) returned %d = 0x%x", i, i, c, c);
202     if ((c = hgetc(fin)) != EOF) fail("chars: hgetc (EOF) returned %d", c);
203     if (hclose(fin) != 0) fail("hclose(test/hfile_chars.tmp) for reading");
204 
205     fin = hopen("preload:test/hfile_chars.tmp", "r");
206     if (fin == NULL) fail("preloading \"test/hfile_chars.tmp\" for reading");
207     for (i = 0; i < 256; i++)
208         if ((c = hgetc(fin)) != i)
209             fail("preloading chars: hgetc (%d = 0x%x) returned %d = 0x%x", i, i, c, c);
210     if ((c = hgetc(fin)) != EOF) fail("preloading chars: hgetc (EOF) returned %d", c);
211     if (hclose(fin) != 0) fail("preloading hclose(test/hfile_chars.tmp) for reading");
212 
213     char* test_string = strdup("Test string");
214     fin = hopen("mem:", "r:", test_string, 12);
215     if (fin == NULL) fail("hopen(\"mem:\", \"r:\", ...)");
216     if (hread(fin, buffer, 12) != 12)
217         fail("hopen('mem:', 'r') failed read");
218     if(strcmp(buffer, test_string) != 0)
219         fail("hopen('mem:', 'r') missread '%s' != '%s'", buffer, test_string);
220     char* internal_buf;
221     size_t interval_buf_len;
222     if((internal_buf = hfile_mem_get_buffer(fin, &interval_buf_len)) == NULL){
223         fail("hopen('mem:', 'r') failed to get internal buffer");
224     }
225     if (hclose(fin) != 0) fail("hclose mem for reading");
226 
227     test_string = strdup("Test string");
228     fin = hopen("mem:", "wr:", test_string, 12);
229     if (fin == NULL) fail("hopen(\"mem:\", \"w:\", ...)");
230     if (hseek(fin, -1, SEEK_END) < 0)
231         fail("hopen('mem:', 'wr') failed seek");
232     if (hwrite(fin, " extra", 7) != 7)
233         fail("hopen('mem:', 'wr') failed write");
234     if (hseek(fin, 0, SEEK_SET) < 0)
235         fail("hopen('mem:', 'wr') failed seek");
236     if (hread(fin, buffer, 18) != 18)
237         fail("hopen('mem:', 'wr') failed read");
238     if (strcmp(buffer, "Test string extra") != 0)
239         fail("hopen('mem:', 'wr') misswrote '%s' != '%s'", buffer, "Test string extra");
240     if((internal_buf = hfile_mem_steal_buffer(fin, &interval_buf_len)) == NULL){
241         fail("hopen('mem:', 'wr') failed to get internal buffer");
242     }
243     free(internal_buf);
244     if (hclose(fin) != 0) fail("hclose mem for writing");
245 
246     fin = hopen("data:,hello, world!%0A", "r");
247     if (fin == NULL) fail("hopen(\"data:...\")");
248     n = hread(fin, buffer, 300);
249     if (n < 0) fail("hread");
250     buffer[n] = '\0';
251     if (strcmp(buffer, "hello, world!\x0A") != 0) fail("hread result");
252     if (hclose(fin) != 0) fail("hclose(\"data:...\")");
253 
254     fin = hopen("test/xx#blank.sam", "r");
255     if (fin == NULL) fail("hopen(\"test/xx#blank.sam\") for reading");
256     if (hread(fin, buffer, 100) != 0) fail("test/xx#blank.sam is non-empty");
257     if (hclose(fin) != 0) fail("hclose(\"test/xx#blank.sam\") for reading");
258 
259     fin = hopen("data:,", "r");
260     if (fin == NULL) fail("hopen(\"data:\") for reading");
261     if (hread(fin, buffer, 100) != 0) fail("empty data: URL is non-empty");
262     if (hclose(fin) != 0) fail("hclose(\"data:\") for reading");
263 
264     fin = hopen("data:;base64,"
265 // Wikipedia's example quote from Thomas Hobbes' Leviathan
266 "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
267 "IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
268 "dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
269 "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
270 "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=", "r");
271     if (fin == NULL) fail("hopen(\"data:;base64,...\")");
272     n = hread(fin, buffer, 300);
273     if (n < 0) fail("hread for base64");
274     buffer[n] = '\0';
275     if (strcmp(buffer, "Man is distinguished, not only by his reason, but by "
276 "this singular passion from other animals, which is a lust of the mind, that "
277 "by a perseverance of delight in the continued and indefatigable generation "
278 "of knowledge, exceeds the short vehemence of any carnal pleasure.") != 0)
279         fail("hread result for base64");
280     if (hclose(fin) != 0) fail("hclose(\"data:;base64,...\")");
281 
282     return EXIT_SUCCESS;
283 }
284