1 // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
2 // (C) Copyright 2004-2007 Jonathan Turkanis
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5 
6 // See http://www.boost.org/libs/iostreams for documentation.
7 
8 #include <fstream>
9 #include <fcntl.h>
10 #include <boost/iostreams/device/file_descriptor.hpp>
11 #include <boost/iostreams/stream.hpp>
12 #include <boost/test/test_tools.hpp>
13 #include <boost/test/unit_test.hpp>
14 #include "detail/temp_file.hpp"
15 #include "detail/verification.hpp"
16 #include "detail/file_handle.hpp"
17 
18 using namespace boost;
19 using namespace boost::iostreams;
20 using namespace boost::iostreams::test;
21 namespace boost_ios = boost::iostreams;
22 using std::ifstream;
23 using boost::unit_test::test_suite;
24 
file_descriptor_test()25 void file_descriptor_test()
26 {
27 
28     typedef stream<file_descriptor_source> fdistream;
29     typedef stream<file_descriptor_sink>   fdostream;
30     typedef stream<file_descriptor>        fdstream;
31 
32     test_file  test1;
33     test_file  test2;
34 
35     //--------------Test file_descriptor_source-------------------------------//
36 
37     {
38         fdistream  first(file_descriptor_source(test1.name()), 0);
39         ifstream   second(test2.name().c_str());
40         BOOST_CHECK(first->is_open());
41         BOOST_CHECK_MESSAGE(
42             compare_streams_in_chars(first, second),
43             "failed reading from file_descriptor_source in chars with no buffer"
44         );
45         first->close();
46         BOOST_CHECK(!first->is_open());
47     }
48 
49     {
50         fdistream  first(file_descriptor_source(test1.name()), 0);
51         ifstream   second(test2.name().c_str());
52         BOOST_CHECK(first->is_open());
53         BOOST_CHECK_MESSAGE(
54             compare_streams_in_chunks(first, second),
55             "failed reading from file_descriptor_source in chunks with no buffer"
56         );
57         first->close();
58         BOOST_CHECK(!first->is_open());
59     }
60 
61     {
62         file_descriptor_source  file(test1.name());
63         fdistream               first(file);
64         ifstream                second(test2.name().c_str());
65         BOOST_CHECK(first->is_open());
66         BOOST_CHECK_MESSAGE(
67             compare_streams_in_chars(first, second),
68             "failed reading from file_descriptor_source in chars with buffer"
69         );
70         first->close();
71         BOOST_CHECK(!first->is_open());
72     }
73 
74     {
75         file_descriptor_source  file(test1.name());
76         fdistream               first(file);
77         ifstream                second(test2.name().c_str());
78         BOOST_CHECK(first->is_open());
79         BOOST_CHECK_MESSAGE(
80             compare_streams_in_chunks(first, second),
81             "failed reading from file_descriptor_source in chunks with buffer"
82         );
83         first->close();
84         BOOST_CHECK(!first->is_open());
85     }
86 
87     // test illegal flag combinations
88     {
89         BOOST_CHECK_THROW(
90             file_descriptor_source(test1.name(),
91                 BOOST_IOS::app),
92             BOOST_IOSTREAMS_FAILURE);
93         BOOST_CHECK_THROW(
94             file_descriptor_source(test1.name(),
95                 BOOST_IOS::trunc),
96             BOOST_IOSTREAMS_FAILURE);
97         BOOST_CHECK_THROW(
98             file_descriptor_source(test1.name(),
99                 BOOST_IOS::app | BOOST_IOS::trunc),
100             BOOST_IOSTREAMS_FAILURE);
101         BOOST_CHECK_THROW(
102             file_descriptor_source(test1.name(),
103                 BOOST_IOS::out),
104             BOOST_IOSTREAMS_FAILURE);
105         BOOST_CHECK_THROW(
106             file_descriptor_source(test1.name(),
107                 BOOST_IOS::out | BOOST_IOS::app),
108             BOOST_IOSTREAMS_FAILURE);
109         BOOST_CHECK_THROW(
110             file_descriptor_source(test1.name(),
111                 BOOST_IOS::out | BOOST_IOS::trunc),
112             BOOST_IOSTREAMS_FAILURE);
113         BOOST_CHECK_THROW(
114             file_descriptor_source(test1.name(),
115                 BOOST_IOS::out | BOOST_IOS::app | BOOST_IOS::trunc),
116             BOOST_IOSTREAMS_FAILURE);
117     }
118 
119     //--------------Test file_descriptor_sink---------------------------------//
120 
121     {
122         temp_file             temp;
123         file_descriptor_sink  file(temp.name(), BOOST_IOS::trunc);
124         fdostream             out(file, 0);
125         BOOST_CHECK(out->is_open());
126         write_data_in_chars(out);
127         out.close();
128         BOOST_CHECK_MESSAGE(
129             compare_files(test1.name(), temp.name()),
130             "failed writing to file_descriptor_sink in chars with no buffer"
131         );
132         file.close();
133         BOOST_CHECK(!file.is_open());
134     }
135 
136     {
137         temp_file             temp;
138         file_descriptor_sink  file(temp.name(), BOOST_IOS::trunc);
139         fdostream             out(file, 0);
140         BOOST_CHECK(out->is_open());
141         write_data_in_chunks(out);
142         out.close();
143         BOOST_CHECK_MESSAGE(
144             compare_files(test1.name(), temp.name()),
145             "failed writing to file_descriptor_sink in chunks with no buffer"
146         );
147         file.close();
148         BOOST_CHECK(!file.is_open());
149     }
150 
151     {
152         temp_file             temp;
153         file_descriptor_sink  file(temp.name(), BOOST_IOS::trunc);
154         fdostream             out(file);
155         BOOST_CHECK(out->is_open());
156         write_data_in_chars(out);
157         out.close();
158         BOOST_CHECK_MESSAGE(
159             compare_files(test1.name(), temp.name()),
160             "failed writing to file_descriptor_sink in chars with buffer"
161         );
162         file.close();
163         BOOST_CHECK(!file.is_open());
164     }
165 
166     {
167         temp_file             temp;
168         file_descriptor_sink  file(temp.name(), BOOST_IOS::trunc);
169         fdostream             out(file);
170         BOOST_CHECK(out->is_open());
171         write_data_in_chunks(out);
172         out.close();
173         BOOST_CHECK_MESSAGE(
174             compare_files(test1.name(), temp.name()),
175             "failed writing to file_descriptor_sink in chunks with buffer"
176         );
177         file.close();
178         BOOST_CHECK(!file.is_open());
179     }
180 
181     {
182         temp_file             temp;
183         // set up the tests
184         {
185             file_descriptor_sink  file(temp.name(), BOOST_IOS::trunc);
186             fdostream             out(file);
187             write_data_in_chunks(out);
188             out.close();
189             file.close();
190         }
191         // test std::ios_base::app
192         {
193             file_descriptor_sink  file(temp.name(), BOOST_IOS::app);
194             fdostream             out(file);
195             BOOST_CHECK(out->is_open());
196             write_data_in_chars(out);
197             out.close();
198             std::string expected(narrow_data());
199             expected += narrow_data();
200             BOOST_CHECK_MESSAGE(
201                 compare_container_and_file(expected, temp.name()),
202                 "failed writing to file_descriptor_sink in append mode"
203             );
204             file.close();
205             BOOST_CHECK(!file.is_open());
206         }
207         // test std::ios_base::trunc
208         {
209             file_descriptor_sink  file(temp.name(), BOOST_IOS::trunc);
210             fdostream             out(file);
211             BOOST_CHECK(out->is_open());
212             write_data_in_chars(out);
213             out.close();
214             BOOST_CHECK_MESSAGE(
215                 compare_files(test1.name(), temp.name()),
216                 "failed writing to file_descriptor_sink in trunc mode"
217             );
218             file.close();
219             BOOST_CHECK(!file.is_open());
220         }
221 
222         // test illegal flag combinations
223         {
224             BOOST_CHECK_THROW(
225                 file_descriptor_sink(temp.name(),
226                     BOOST_IOS::trunc | BOOST_IOS::app),
227                 BOOST_IOSTREAMS_FAILURE);
228             BOOST_CHECK_THROW(
229                 file_descriptor_sink(temp.name(),
230                     BOOST_IOS::in),
231                 BOOST_IOSTREAMS_FAILURE);
232             BOOST_CHECK_THROW(
233                 file_descriptor_sink(temp.name(),
234                     BOOST_IOS::in | BOOST_IOS::app),
235                 BOOST_IOSTREAMS_FAILURE);
236             BOOST_CHECK_THROW(
237                 file_descriptor_sink(temp.name(),
238                     BOOST_IOS::in | BOOST_IOS::trunc),
239                 BOOST_IOSTREAMS_FAILURE);
240             BOOST_CHECK_THROW(
241                 file_descriptor_sink(temp.name(),
242                     BOOST_IOS::in | BOOST_IOS::trunc | BOOST_IOS::app),
243                 BOOST_IOSTREAMS_FAILURE);
244         }
245     }
246 
247     //--Test seeking with file_descriptor_source and file_descriptor_sink-----//
248 
249     test_file test3;
250     {
251         file_descriptor_sink  sink(test3.name());
252         fdostream             out(sink);
253         BOOST_CHECK(out->is_open());
254         BOOST_CHECK_MESSAGE(
255             test_output_seekable(out),
256             "failed seeking within a file_descriptor_sink"
257         );
258         out->close();
259         BOOST_CHECK(!out->is_open());
260 
261         file_descriptor_source  source(test3.name());
262         fdistream               in(source);
263         BOOST_CHECK(in->is_open());
264         BOOST_CHECK_MESSAGE(
265             test_input_seekable(in),
266             "failed seeking within a file_descriptor_source"
267         );
268         in->close();
269         BOOST_CHECK(!in->is_open());
270     }
271 
272     //--------------Test file_descriptor--------------------------------------//
273 
274     {
275         temp_file                  temp;
276         file_descriptor            file( temp.name(),
277                                          BOOST_IOS::in |
278                                          BOOST_IOS::out |
279                                          BOOST_IOS::trunc |
280                                          BOOST_IOS::binary );
281         fdstream                   io(file, BUFSIZ);
282         BOOST_CHECK_MESSAGE(
283             test_seekable_in_chars(io),
284             "failed seeking within a file_descriptor, in chars"
285         );
286     }
287 
288     {
289         temp_file                  temp;
290         file_descriptor            file( temp.name(),
291                                          BOOST_IOS::in |
292                                          BOOST_IOS::out |
293                                          BOOST_IOS::trunc |
294                                          BOOST_IOS::binary );
295         fdstream                   io(file, BUFSIZ);
296         BOOST_CHECK_MESSAGE(
297             test_seekable_in_chunks(io),
298             "failed seeking within a file_descriptor, in chunks"
299         );
300     }
301 
302     //--------------Test read-only file_descriptor----------------------------//
303 
304     {
305         fdstream   first(file_descriptor(test1.name(), BOOST_IOS::in), 0);
306         ifstream   second(test2.name().c_str());
307         BOOST_CHECK(first->is_open());
308         write_data_in_chars(first);
309         BOOST_CHECK(first.fail());
310         first.clear();
311         BOOST_CHECK_MESSAGE(
312             compare_streams_in_chars(first, second),
313             "failed reading from file_descriptor in chars with no buffer"
314         );
315         first->close();
316         BOOST_CHECK(!first->is_open());
317     }
318 
319     {
320         fdstream   first(file_descriptor(test1.name(), BOOST_IOS::in), 0);
321         ifstream   second(test2.name().c_str());
322         BOOST_CHECK(first->is_open());
323         write_data_in_chunks(first);
324         BOOST_CHECK(first.fail());
325         first.clear();
326         BOOST_CHECK_MESSAGE(
327             compare_streams_in_chunks(first, second),
328             "failed reading from file_descriptor in chunks with no buffer"
329         );
330         first->close();
331         BOOST_CHECK(!first->is_open());
332     }
333 
334     {
335         file_descriptor         file(test1.name(), BOOST_IOS::in);
336         fdstream                first(file);
337         ifstream                second(test2.name().c_str());
338         BOOST_CHECK(first->is_open());
339         write_data_in_chars(first);
340         BOOST_CHECK(first.fail());
341         first.clear();
342         first.seekg(0, BOOST_IOS::beg);
343         BOOST_CHECK_MESSAGE(
344             compare_streams_in_chars(first, second),
345             "failed reading from file_descriptor in chars with buffer"
346         );
347         first->close();
348         BOOST_CHECK(!first->is_open());
349     }
350 
351     {
352         file_descriptor         file(test1.name(), BOOST_IOS::in);
353         fdstream                first(file);
354         ifstream                second(test2.name().c_str());
355         BOOST_CHECK(first->is_open());
356         write_data_in_chunks(first);
357         BOOST_CHECK(first.fail());
358         first.clear();
359         first.seekg(0, BOOST_IOS::beg);
360         BOOST_CHECK_MESSAGE(
361             compare_streams_in_chunks(first, second),
362             "failed reading from file_descriptor in chunks with buffer"
363         );
364         first->close();
365         BOOST_CHECK(!first->is_open());
366     }
367 
368     //--------------Test write-only file_descriptor---------------------------//
369     {
370         temp_file             temp;
371         file_descriptor       file( temp.name(),
372                                     BOOST_IOS::out |
373                                     BOOST_IOS::trunc );
374         fdstream              out(file, 0);
375         BOOST_CHECK(out->is_open());
376         out.get();
377         BOOST_CHECK(out.fail());
378         out.clear();
379         write_data_in_chars(out);
380         out.seekg(0, BOOST_IOS::beg);
381         out.get();
382         BOOST_CHECK(out.fail());
383         out.clear();
384         out.close();
385         BOOST_CHECK_MESSAGE(
386             compare_files(test1.name(), temp.name()),
387             "failed writing to file_descriptor in chars with no buffer"
388         );
389         file.close();
390         BOOST_CHECK(!file.is_open());
391     }
392 
393     {
394         temp_file             temp;
395         file_descriptor       file( temp.name(),
396                                     BOOST_IOS::out |
397                                     BOOST_IOS::trunc );
398         fdstream              out(file, 0);
399         BOOST_CHECK(out->is_open());
400         out.get();
401         BOOST_CHECK(out.fail());
402         out.clear();
403         write_data_in_chunks(out);
404         out.seekg(0, BOOST_IOS::beg);
405         out.get();
406         BOOST_CHECK(out.fail());
407         out.clear();
408         out.close();
409         BOOST_CHECK_MESSAGE(
410             compare_files(test1.name(), temp.name()),
411             "failed writing to file_descriptor_sink in chunks with no buffer"
412         );
413         file.close();
414         BOOST_CHECK(!file.is_open());
415     }
416 
417     {
418         temp_file             temp;
419         file_descriptor       file( temp.name(),
420                                     BOOST_IOS::out |
421                                     BOOST_IOS::trunc );
422         fdstream              out(file);
423         BOOST_CHECK(out->is_open());
424         out.get();
425         BOOST_CHECK(out.fail());
426         out.clear();
427         write_data_in_chars(out);
428         out.seekg(0, BOOST_IOS::beg);
429         out.get();
430         BOOST_CHECK(out.fail());
431         out.clear();
432         out.close();
433         BOOST_CHECK_MESSAGE(
434             compare_files(test1.name(), temp.name()),
435             "failed writing to file_descriptor_sink in chars with buffer"
436         );
437         file.close();
438         BOOST_CHECK(!file.is_open());
439     }
440 
441     {
442         temp_file             temp;
443         file_descriptor       file( temp.name(),
444                                     BOOST_IOS::out |
445                                     BOOST_IOS::trunc );
446         fdstream              out(file);
447         BOOST_CHECK(out->is_open());
448         out.get();
449         BOOST_CHECK(out.fail());
450         out.clear();
451         write_data_in_chunks(out);
452         out.seekg(0, BOOST_IOS::beg);
453         out.get();
454         BOOST_CHECK(out.fail());
455         out.clear();
456         out.close();
457         BOOST_CHECK_MESSAGE(
458             compare_files(test1.name(), temp.name()),
459             "failed writing to file_descriptor_sink in chunks with buffer"
460         );
461         file.close();
462         BOOST_CHECK(!file.is_open());
463     }
464 
465     // test illegal flag combinations
466     {
467         BOOST_CHECK_THROW(
468             file_descriptor(test1.name(),
469                 BOOST_IOS::openmode(0)),
470             BOOST_IOSTREAMS_FAILURE);
471         BOOST_CHECK_THROW(
472             file_descriptor(test1.name(),
473                 BOOST_IOS::app),
474             BOOST_IOSTREAMS_FAILURE);
475         BOOST_CHECK_THROW(
476             file_descriptor(test1.name(),
477                 BOOST_IOS::trunc),
478             BOOST_IOSTREAMS_FAILURE);
479         BOOST_CHECK_THROW(
480             file_descriptor(test1.name(),
481                 BOOST_IOS::app | BOOST_IOS::trunc),
482             BOOST_IOSTREAMS_FAILURE);
483         BOOST_CHECK_THROW(
484             file_descriptor(test1.name(),
485                 BOOST_IOS::in | BOOST_IOS::app),
486             BOOST_IOSTREAMS_FAILURE);
487         BOOST_CHECK_THROW(
488             file_descriptor(test1.name(),
489                 BOOST_IOS::in | BOOST_IOS::trunc),
490             BOOST_IOSTREAMS_FAILURE);
491         BOOST_CHECK_THROW(
492             file_descriptor(test1.name(),
493                 BOOST_IOS::in | BOOST_IOS::app | BOOST_IOS::trunc),
494             BOOST_IOSTREAMS_FAILURE);
495         BOOST_CHECK_THROW(
496             file_descriptor(test1.name(),
497                 BOOST_IOS::out | BOOST_IOS::app | BOOST_IOS::trunc),
498             BOOST_IOSTREAMS_FAILURE);
499         BOOST_CHECK_THROW(
500             file_descriptor(test1.name(),
501                 BOOST_IOS::in | BOOST_IOS::out | BOOST_IOS::app),
502             BOOST_IOSTREAMS_FAILURE);
503         BOOST_CHECK_THROW(
504             file_descriptor(test1.name(),
505                 BOOST_IOS::in |
506                 BOOST_IOS::out |
507                 BOOST_IOS::app |
508                 BOOST_IOS::trunc),
509             BOOST_IOSTREAMS_FAILURE);
510     }
511 }
512 
513 template <class FileDescriptor>
file_handle_test_impl(FileDescriptor *)514 void file_handle_test_impl(FileDescriptor*)
515 {
516     test_file  test1;
517     test_file  test2;
518 
519     {
520         boost_ios::detail::file_handle handle = open_file_handle(test1.name());
521         {
522             FileDescriptor device1(handle, boost_ios::never_close_handle);
523             BOOST_CHECK(device1.handle() == handle);
524         }
525         BOOST_CHECK_HANDLE_OPEN(handle);
526         close_file_handle(handle);
527     }
528 
529     {
530         boost_ios::detail::file_handle handle = open_file_handle(test1.name());
531         {
532             FileDescriptor device1(handle, boost_ios::close_handle);
533             BOOST_CHECK(device1.handle() == handle);
534         }
535         BOOST_CHECK_HANDLE_CLOSED(handle);
536     }
537 
538     {
539         boost_ios::detail::file_handle handle = open_file_handle(test1.name());
540         FileDescriptor device1(handle, boost_ios::never_close_handle);
541         BOOST_CHECK(device1.handle() == handle);
542         device1.close();
543         BOOST_CHECK(!device1.is_open());
544         BOOST_CHECK_HANDLE_OPEN(handle);
545         close_file_handle(handle);
546     }
547 
548     {
549         boost_ios::detail::file_handle handle = open_file_handle(test1.name());
550         FileDescriptor device1(handle, boost_ios::close_handle);
551         BOOST_CHECK(device1.handle() == handle);
552         device1.close();
553         BOOST_CHECK(!device1.is_open());
554         BOOST_CHECK_HANDLE_CLOSED(handle);
555     }
556 
557     {
558         boost_ios::detail::file_handle handle1 = open_file_handle(test1.name());
559         boost_ios::detail::file_handle handle2 = open_file_handle(test2.name());
560         {
561             FileDescriptor device1(handle1, boost_ios::never_close_handle);
562             BOOST_CHECK(device1.handle() == handle1);
563             device1.open(handle2, boost_ios::never_close_handle);
564             BOOST_CHECK(device1.handle() == handle2);
565         }
566         BOOST_CHECK_HANDLE_OPEN(handle1);
567         BOOST_CHECK_HANDLE_OPEN(handle2);
568         close_file_handle(handle1);
569         close_file_handle(handle2);
570     }
571 
572     {
573         boost_ios::detail::file_handle handle1 = open_file_handle(test1.name());
574         boost_ios::detail::file_handle handle2 = open_file_handle(test2.name());
575         {
576             FileDescriptor device1(handle1, boost_ios::close_handle);
577             BOOST_CHECK(device1.handle() == handle1);
578             device1.open(handle2, boost_ios::close_handle);
579             BOOST_CHECK(device1.handle() == handle2);
580             BOOST_CHECK_HANDLE_CLOSED(handle1);
581             BOOST_CHECK_HANDLE_OPEN(handle2);
582         }
583         BOOST_CHECK_HANDLE_CLOSED(handle1);
584         BOOST_CHECK_HANDLE_CLOSED(handle2);
585     }
586 
587     {
588         boost_ios::detail::file_handle handle1 = open_file_handle(test1.name());
589         boost_ios::detail::file_handle handle2 = open_file_handle(test2.name());
590         {
591             FileDescriptor device1(handle1, boost_ios::close_handle);
592             BOOST_CHECK(device1.handle() == handle1);
593             device1.open(handle2, boost_ios::never_close_handle);
594             BOOST_CHECK(device1.handle() == handle2);
595             BOOST_CHECK_HANDLE_CLOSED(handle1);
596             BOOST_CHECK_HANDLE_OPEN(handle2);
597         }
598         BOOST_CHECK_HANDLE_CLOSED(handle1);
599         BOOST_CHECK_HANDLE_OPEN(handle2);
600         close_file_handle(handle2);
601     }
602 
603     {
604         boost_ios::detail::file_handle handle = open_file_handle(test1.name());
605         {
606             FileDescriptor device1;
607             BOOST_CHECK(!device1.is_open());
608             device1.open(handle, boost_ios::never_close_handle);
609             BOOST_CHECK(device1.handle() == handle);
610             BOOST_CHECK_HANDLE_OPEN(handle);
611         }
612         BOOST_CHECK_HANDLE_OPEN(handle);
613         close_file_handle(handle);
614     }
615 
616     {
617         boost_ios::detail::file_handle handle = open_file_handle(test1.name());
618         {
619             FileDescriptor device1;
620             BOOST_CHECK(!device1.is_open());
621             device1.open(handle, boost_ios::close_handle);
622             BOOST_CHECK(device1.handle() == handle);
623             BOOST_CHECK_HANDLE_OPEN(handle);
624         }
625         BOOST_CHECK_HANDLE_CLOSED(handle);
626     }
627 }
628 
file_handle_test()629 void file_handle_test()
630 {
631     file_handle_test_impl((boost_ios::file_descriptor*) 0);
632     file_handle_test_impl((boost_ios::file_descriptor_source*) 0);
633     file_handle_test_impl((boost_ios::file_descriptor_sink*) 0);
634 }
635 
init_unit_test_suite(int,char * [])636 test_suite* init_unit_test_suite(int, char* [])
637 {
638     test_suite* test = BOOST_TEST_SUITE("file_descriptor test");
639     test->add(BOOST_TEST_CASE(&file_descriptor_test));
640     test->add(BOOST_TEST_CASE(&file_handle_test));
641     return test;
642 }
643