LCOV - code coverage report
Current view: top level - tests - catch_zipfile.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 831 831 100.0 %
Date: 2015-04-12 Functions: 20 20 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   Zipios++ - a small C++ library that provides easy access to .zip files.
       3             : 
       4             :   Copyright (C) 2000-2007  Thomas Sondergaard
       5             :   Copyright (C) 2015  Made to Order Software Corporation
       6             : 
       7             :   This library is free software; you can redistribute it and/or
       8             :   modify it under the terms of the GNU Lesser General Public
       9             :   License as published by the Free Software Foundation; either
      10             :   version 2 of the License, or (at your option) any later version.
      11             : 
      12             :   This library is distributed in the hope that it will be useful,
      13             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :   Lesser General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU Lesser General Public
      18             :   License along with this library; if not, write to the Free Software
      19             :   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
      20             : */
      21             : 
      22             : /** \file
      23             :  *
      24             :  * Zipios++ unit tests for the ZipFile class.
      25             :  */
      26             : 
      27             : #include "catch_tests.hpp"
      28             : 
      29             : #include "zipios++/zipfile.hpp"
      30             : #include "zipios++/directorycollection.hpp"
      31             : #include "zipios++/zipiosexceptions.hpp"
      32             : 
      33             : #include "src/dostime.h"
      34             : 
      35             : #include <algorithm>
      36             : #include <fstream>
      37             : 
      38             : #include <unistd.h>
      39             : #include <string.h>
      40             : #include <zlib.h>
      41             : 
      42             : 
      43             : 
      44             : namespace
      45             : {
      46             : 
      47             : 
      48             : zipios::StorageMethod const g_supported_storage_methods[]
      49             : {
      50             :     zipios::StorageMethod::STORED,
      51             :     zipios::StorageMethod::DEFLATED
      52             : };
      53             : 
      54             : 
      55             : } // no name namespace
      56             : 
      57             : 
      58             : 
      59             : 
      60           2 : TEST_CASE("An Empty ZipFile", "[ZipFile] [FileCollection]")
      61             : {
      62           1 :     zipios::ZipFile zf;
      63             : 
      64           1 :     REQUIRE(zf.isValid());
      65           1 :     REQUIRE(zf.entries().empty());
      66           1 :     REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
      67           1 :     REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
      68           1 :     REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
      69           1 :     REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
      70           1 :     REQUIRE(zf.getName() == "-");
      71           1 :     REQUIRE(zf.size() == 0);
      72           1 :     zf.mustBeValid();
      73           1 : }
      74             : 
      75             : 
      76           2 : TEST_CASE("A ZipFile with an invalid name", "[ZipFile] [FileCollection]")
      77             : {
      78           1 :     REQUIRE_THROWS_AS(zipios::ZipFile zf("this/file/does/not/exists/so/the/constructor/throws"), zipios::IOException);
      79           1 : }
      80             : 
      81             : 
      82           2 : TEST_CASE("A ZipFile with an invalid file", "[ZipFile] [FileCollection]")
      83             : {
      84             :     // create a totally random file which means there is still a very slight
      85             :     // chance that it represents a valid ZipFile, but frankly... no.
      86           1 :     zipios_test::auto_unlink_t auto_unlink("invalid.zip");
      87             :     {
      88           1 :         std::ofstream os("invalid.zip", std::ios::out | std::ios::binary);
      89           1 :         size_t const max_size(rand() % 1024 + 1024);
      90        1254 :         for(size_t i(0); i < max_size; ++i)
      91             :         {
      92        1253 :             os << static_cast<char>(rand());
      93           1 :         }
      94             :     }
      95           1 :     REQUIRE_THROWS_AS(zipios::ZipFile zf("invalid.zip"), zipios::FileCollectionException);
      96           1 : }
      97             : 
      98             : 
      99           2 : TEST_CASE("An empty ZipFile", "[ZipFile] [FileCollection]")
     100             : {
     101             :     // this is a special case where the file is composed of one
     102             :     // End of Central Directory with 0 entries
     103           1 :     zipios_test::auto_unlink_t auto_unlink("empty.zip");
     104             :     {
     105           1 :         std::ofstream os("empty.zip", std::ios::out | std::ios::binary);
     106           1 :         os << static_cast<char>(0x50);
     107           1 :         os << static_cast<char>(0x4B);
     108           1 :         os << static_cast<char>(0x05);
     109           1 :         os << static_cast<char>(0x06);
     110           1 :         os << static_cast<char>(0x00);
     111           1 :         os << static_cast<char>(0x00);
     112           1 :         os << static_cast<char>(0x00);
     113           1 :         os << static_cast<char>(0x00);
     114           1 :         os << static_cast<char>(0x00);
     115           1 :         os << static_cast<char>(0x00);
     116           1 :         os << static_cast<char>(0x00);
     117           1 :         os << static_cast<char>(0x00);
     118           1 :         os << static_cast<char>(0x00);
     119           1 :         os << static_cast<char>(0x00);
     120           1 :         os << static_cast<char>(0x00);
     121           1 :         os << static_cast<char>(0x00);
     122           1 :         os << static_cast<char>(0x00);
     123           1 :         os << static_cast<char>(0x00);
     124           1 :         os << static_cast<char>(0x00);
     125           1 :         os << static_cast<char>(0x00);
     126           1 :         os << static_cast<char>(0x00);
     127           1 :         os << static_cast<char>(0x00);
     128             :     }
     129           2 :     zipios::ZipFile zf("empty.zip");
     130             : 
     131           1 :     REQUIRE(zf.isValid());
     132           1 :     REQUIRE(zf.entries().empty());
     133           1 :     REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     134           1 :     REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     135           1 :     REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     136           1 :     REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     137           1 :     REQUIRE(zf.getName() == "empty.zip");
     138           1 :     REQUIRE(zf.size() == 0);
     139           2 :     zf.mustBeValid(); // not throwing
     140           1 : }
     141             : 
     142             : 
     143           7 : SCENARIO("ZipFile with a valid zip archive", "[ZipFile] [FileCollection]")
     144             : {
     145           6 :     GIVEN("a tree directory")
     146             :     {
     147           5 :         system("rm -rf tree"); // clean up, just in case
     148           5 :         size_t const start_count(rand() % 40 + 80);
     149           5 :         zipios_test::file_t tree(zipios_test::file_t::type_t::DIRECTORY, start_count, "tree");
     150          10 :         zipios_test::auto_unlink_t remove_zip("tree.zip");
     151           5 :         system("zip -r tree.zip tree >/dev/null");
     152             : 
     153             :         // first, check that the object is setup as expected
     154           5 :         WHEN("we load the zip file")
     155             :         {
     156           4 :             zipios::ZipFile zf("tree.zip");
     157             : 
     158           4 :             THEN("it is valid and includes all the files in the tree")
     159             :             {
     160           1 :                 REQUIRE(zf.isValid());
     161           1 :                 REQUIRE_FALSE(zf.entries().empty());
     162           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     163           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     164           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     165           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     166           1 :                 REQUIRE(zf.getName() == "tree.zip");
     167           1 :                 REQUIRE(zf.size() == tree.size());
     168           1 :                 zf.mustBeValid(); // not throwing
     169             : 
     170           1 :                 zipios::FileEntry::vector_t v(zf.entries());
     171        1116 :                 for(auto it(v.begin()); it != v.end(); ++it)
     172             :                 {
     173        1115 :                     zipios::FileEntry::pointer_t entry(*it);
     174             : 
     175             :                     // verify that our tree knows about this file
     176        1115 :                     zipios_test::file_t::type_t t(tree.find(entry->getName()));
     177        1115 :                     REQUIRE(t != zipios_test::file_t::type_t::UNKNOWN);
     178             : 
     179             :                     struct stat file_stats;
     180        1115 :                     REQUIRE(stat(entry->getName().c_str(), &file_stats) == 0);
     181             : 
     182        1115 :                     REQUIRE((*it)->getComment().empty());
     183             :                     //REQUIRE((*it)->getCompressedSize() == (*it)->getSize()); -- not too sure how we could verify this size in this case
     184             :                     //REQUIRE((*it)->getCrc() == ...); -- not too sure how to compute that right now, but once we have it we'll test it
     185             :                     //REQUIRE((*it)->getEntryOffset() == ...); -- that's also difficult to test
     186             :                     //REQUIRE((*it)->getExtra().empty());
     187             :                     //REQUIRE((*it)->getHeaderSize() == 0); -- the header size varies
     188        1115 :                     if((*it)->getMethod() == zipios::StorageMethod::STORED)
     189             :                     {
     190         430 :                         REQUIRE((*it)->getCompressedSize() == (*it)->getSize());
     191             :                     }
     192             :                     else
     193             :                     {
     194             :                          // you would think that the compressed size would
     195             :                          // either be equal to the size or smaller, but never
     196             :                          // larger, that's not the case with zip under Linux...
     197             :                          //
     198             :                          // they probably use a streaming mechanism and thus
     199             :                          // cannot fix the problem later if the compressed
     200             :                          // version ends up being larger than the
     201             :                          // non-compressed version...
     202             :                          //
     203             :                          //REQUIRE((*it)->getCompressedSize() < (*it)->getSize());
     204             :                     }
     205             :                     //REQUIRE((*it)->getName() == ...);
     206             :                     //REQUIRE((*it)->getFileName() == ...);
     207        1115 :                     REQUIRE((*it)->getTime() == unix2dostime(file_stats.st_mtime));  // invalid date
     208        1115 :                     size_t ut(dos2unixtime(unix2dostime(file_stats.st_mtime)));
     209        1115 :                     REQUIRE((*it)->getUnixTime() == ut);
     210        1115 :                     REQUIRE_FALSE((*it)->hasCrc());
     211        1115 :                     REQUIRE((*it)->isValid());
     212             :                     //REQUIRE((*it)->toString() == "... (0 bytes)");
     213             : 
     214        1115 :                     if(t == zipios_test::file_t::type_t::DIRECTORY)
     215             :                     {
     216         113 :                         REQUIRE((*it)->isDirectory());
     217         113 :                         REQUIRE((*it)->getSize() == 0); // size is zero for directories
     218             :                     }
     219             :                     else
     220             :                     {
     221        1002 :                         REQUIRE_FALSE((*it)->isDirectory());
     222        1002 :                         REQUIRE((*it)->getSize() == file_stats.st_size);
     223             : 
     224             :                         // now read both files (if not a directory) and make sure
     225             :                         // they are equal
     226        1002 :                         zipios::FileCollection::stream_pointer_t is(zf.getInputStream(entry->getName()));
     227        1002 :                         REQUIRE(is);
     228        2004 :                         std::ifstream in(entry->getName(), std::ios::in | std::ios::binary);
     229             : 
     230        8798 :                         while(in && *is)
     231             :                         {
     232             :                             char buf1[BUFSIZ], buf2[BUFSIZ];
     233             : 
     234        6794 :                             in.read(buf1, sizeof(buf1));
     235        6794 :                             std::streamsize sz1(in.gcount());
     236             : 
     237        6794 :                             is->read(buf2, sizeof(buf2));
     238        6794 :                             std::streamsize sz2(is->gcount());
     239             : 
     240        6794 :                             REQUIRE(sz1 == sz2);
     241        6794 :                             REQUIRE(memcmp(buf1, buf2, sz1) == 0);
     242             :                         }
     243             : 
     244        1002 :                         REQUIRE(!in);
     245        2004 :                         REQUIRE(!*is);
     246             :                     }
     247             : 
     248             :                     // I don't think we will test those directly...
     249             :                     //REQUIRE_THROWS_AS((*it)->read(std::cin), zipios::IOException);
     250             :                     //REQUIRE_THROWS_AS((*it)->write(std::cout), zipios::IOException);
     251        1116 :                 }
     252           4 :             }
     253             : 
     254           4 :             THEN("we can create a totally valid clone")
     255             :             {
     256             :                 // we do not have a specific copy constructor and
     257             :                 // assignment operator so we only try to clone() function
     258           1 :                 zipios::ZipFile::pointer_t clone(zf.clone());
     259             : 
     260             :                 // zf is unaffected
     261           1 :                 REQUIRE(zf.isValid());
     262           1 :                 REQUIRE_FALSE(zf.entries().empty());
     263           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     264           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     265           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     266           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     267           1 :                 REQUIRE(zf.getName() == "tree.zip");
     268           1 :                 REQUIRE(zf.size() == tree.size());
     269           1 :                 zf.mustBeValid(); // not throwing
     270             : 
     271             :                 // clone is valid
     272           1 :                 REQUIRE(clone->isValid());
     273           1 :                 REQUIRE_FALSE(clone->entries().empty());
     274           1 :                 REQUIRE_FALSE(clone->getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     275           1 :                 REQUIRE_FALSE(clone->getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     276           1 :                 REQUIRE_FALSE(clone->getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     277           1 :                 REQUIRE_FALSE(clone->getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     278           1 :                 REQUIRE(clone->getName() == "tree.zip");
     279           1 :                 REQUIRE(clone->size() == tree.size());
     280           1 :                 clone->mustBeValid(); // not throwing
     281             : 
     282           2 :                 zipios::FileEntry::vector_t v(clone->entries());
     283         755 :                 for(auto it(v.begin()); it != v.end(); ++it)
     284             :                 {
     285         754 :                     zipios::FileEntry::pointer_t entry(*it);
     286             : 
     287             :                     // verify that our tree knows about this file
     288         754 :                     zipios_test::file_t::type_t t(tree.find(entry->getName()));
     289         754 :                     REQUIRE(t != zipios_test::file_t::type_t::UNKNOWN);
     290             : 
     291             :                     struct stat file_stats;
     292         754 :                     REQUIRE(stat(entry->getName().c_str(), &file_stats) == 0);
     293             : 
     294         754 :                     REQUIRE((*it)->getComment().empty());
     295             :                     //REQUIRE((*it)->getCompressedSize() == (*it)->getSize()); -- not too sure how we could verify this size in this case
     296             :                     //REQUIRE((*it)->getCrc() == ...); -- not too sure how to compute that right now, but once we have it we'll test it
     297             :                     //REQUIRE((*it)->getEntryOffset() == ...); -- that's also difficult to test
     298             :                     //REQUIRE((*it)->getExtra().empty());
     299             :                     //REQUIRE((*it)->getHeaderSize() == 0); -- the header size varies
     300         754 :                     if((*it)->getMethod() == zipios::StorageMethod::STORED)
     301             :                     {
     302         299 :                         REQUIRE((*it)->getCompressedSize() == (*it)->getSize());
     303             :                     }
     304             :                     else
     305             :                     {
     306             :                          // you would think that the compressed size would
     307             :                          // either be equal to the size or smaller, but never
     308             :                          // larger, that's not the case with zip under Linux...
     309             :                          //
     310             :                          // they probably use a streaming mechanism and thus
     311             :                          // cannot fix the problem later if the compressed
     312             :                          // version ends up being larger than the
     313             :                          // non-compressed version...
     314             :                          //
     315             :                          //REQUIRE((*it)->getCompressedSize() < (*it)->getSize());
     316             :                     }
     317             :                     //REQUIRE((*it)->getName() == ...);
     318             :                     //REQUIRE((*it)->getFileName() == ...);
     319         754 :                     REQUIRE((*it)->getTime() == unix2dostime(file_stats.st_mtime));  // invalid date
     320         754 :                     size_t ut(dos2unixtime(unix2dostime(file_stats.st_mtime)));
     321         754 :                     REQUIRE((*it)->getUnixTime() == ut);
     322         754 :                     REQUIRE_FALSE((*it)->hasCrc());
     323         754 :                     REQUIRE((*it)->isValid());
     324             :                     //REQUIRE((*it)->toString() == "... (0 bytes)");
     325             : 
     326         754 :                     if(t == zipios_test::file_t::type_t::DIRECTORY)
     327             :                     {
     328          78 :                         REQUIRE((*it)->isDirectory());
     329          78 :                         REQUIRE((*it)->getSize() == 0); // size is zero for directories
     330             :                     }
     331             :                     else
     332             :                     {
     333         676 :                         REQUIRE_FALSE((*it)->isDirectory());
     334         676 :                         REQUIRE((*it)->getSize() == file_stats.st_size);
     335             : 
     336             :                         // now read both files (if not a directory) and make sure
     337             :                         // they are equal
     338         676 :                         zipios::FileCollection::stream_pointer_t is(clone->getInputStream(entry->getName()));
     339         676 :                         REQUIRE(is);
     340        1352 :                         std::ifstream in(entry->getName(), std::ios::in | std::ios::binary);
     341             : 
     342        5957 :                         while(in && *is)
     343             :                         {
     344             :                             char buf1[BUFSIZ], buf2[BUFSIZ];
     345             : 
     346        4605 :                             in.read(buf1, sizeof(buf1));
     347        4605 :                             std::streamsize sz1(in.gcount());
     348             : 
     349        4605 :                             is->read(buf2, sizeof(buf2));
     350        4605 :                             std::streamsize sz2(is->gcount());
     351             : 
     352        4605 :                             REQUIRE(sz1 == sz2);
     353        4605 :                             REQUIRE(memcmp(buf1, buf2, sz1) == 0);
     354             :                         }
     355             : 
     356         676 :                         REQUIRE(!in);
     357        1352 :                         REQUIRE(!*is);
     358             :                     }
     359             : 
     360             :                     // I don't think we will test those directly...
     361             :                     //REQUIRE_THROWS_AS((*it)->read(std::cin), zipios::IOException);
     362             :                     //REQUIRE_THROWS_AS((*it)->write(std::cout), zipios::IOException);
     363         755 :                 }
     364           4 :             }
     365             : 
     366           4 :             THEN("we can compare incompatible entries against each others")
     367             :             {
     368             :                 // we read the tree as a directory so we have
     369             :                 // incompatible entries
     370           1 :                 zipios::DirectoryCollection dc("tree");
     371             : 
     372           2 :                 zipios::FileEntry::vector_t e(dc.entries());
     373           2 :                 zipios::FileEntry::vector_t v(zf.entries());
     374           1 :                 REQUIRE(e.size() == v.size()); // same tree so same size
     375             :                 //size_t const max_entries(std::min(e.size(), v.size());
     376        1098 :                 for(size_t idx(0); idx < e.size(); ++idx)
     377             :                 {
     378        1097 :                     REQUIRE_FALSE(e[idx]->isEqual(*v[idx]));
     379        1097 :                     REQUIRE_FALSE(v[idx]->isEqual(*e[idx]));
     380           1 :                 }
     381           4 :             }
     382          10 :         }
     383           6 :     }
     384           6 : }
     385             : 
     386             : 
     387           6 : SCENARIO("use Zipios++ to create a zip archive", "[ZipFile] [FileCollection]")
     388             : {
     389           5 :     GIVEN("a tree directory")
     390             :     {
     391           4 :         system("rm -rf tree tree.zip"); // clean up, just in case
     392           4 :         size_t const start_count(rand() % 40 + 80);
     393           4 :         zipios_test::file_t tree(zipios_test::file_t::type_t::DIRECTORY, start_count, "tree");
     394           8 :         zipios_test::auto_unlink_t remove_zip("tree.zip");
     395           8 :         zipios::DirectoryCollection dc("tree");
     396             : 
     397             :         // first, check that the object is setup as expected
     398           4 :         WHEN("we save the directory tree in a .zip file")
     399             :         {
     400             :             {
     401           2 :                 dc.setMethod(1024, zipios::StorageMethod::STORED, zipios::StorageMethod::DEFLATED);
     402           2 :                 dc.setLevel(1024, zipios::FileEntry::COMPRESSION_LEVEL_NONE, zipios::FileEntry::COMPRESSION_LEVEL_MAXIMUM);
     403           2 :                 std::ofstream out("tree.zip", std::ios::out | std::ios::binary);
     404           2 :                 zipios::ZipFile::saveCollectionToArchive(out, dc);
     405             :             }
     406             : 
     407           2 :             THEN("it is valid and includes all the files in the tree as expected")
     408             :             {
     409           1 :                 zipios::ZipFile zf("tree.zip");
     410             : 
     411           1 :                 REQUIRE(zf.isValid());
     412           1 :                 REQUIRE_FALSE(zf.entries().empty());
     413           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     414           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     415           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     416           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     417           1 :                 REQUIRE(zf.getName() == "tree.zip");
     418           1 :                 REQUIRE(zf.size() == tree.size());
     419           1 :                 zf.mustBeValid(); // not throwing
     420             : 
     421           2 :                 zipios::FileEntry::vector_t v(zf.entries());
     422         927 :                 for(auto it(v.begin()); it != v.end(); ++it)
     423             :                 {
     424         926 :                     zipios::FileEntry::pointer_t entry(*it);
     425             : 
     426             :                     // verify that our tree knows about this file
     427         926 :                     zipios_test::file_t::type_t t(tree.find(entry->getName()));
     428         926 :                     REQUIRE(t != zipios_test::file_t::type_t::UNKNOWN);
     429             : 
     430             :                     struct stat file_stats;
     431         926 :                     REQUIRE(stat(entry->getName().c_str(), &file_stats) == 0);
     432             : 
     433         926 :                     REQUIRE((*it)->getComment().empty());
     434             :                     //REQUIRE((*it)->getCompressedSize() == (*it)->getSize()); -- not too sure how we could verify this size in this case
     435             :                     //REQUIRE((*it)->getCrc() == ...); -- not too sure how to compute that right now, but once we have it we'll test it
     436             :                     //REQUIRE((*it)->getEntryOffset() == ...); -- that's also difficult to test
     437             :                     //REQUIRE((*it)->getExtra().empty());
     438             :                     //REQUIRE((*it)->getHeaderSize() == 0); -- the header size varies
     439         926 :                     if((*it)->getMethod() == zipios::StorageMethod::STORED)
     440             :                     {
     441          98 :                         REQUIRE((*it)->getCompressedSize() == (*it)->getSize());
     442             :                     }
     443             :                     else
     444             :                     {
     445             :                          // you would think that the compressed size would
     446             :                          // either be equal to the size or smaller, but never
     447             :                          // larger, that's not the case with zip under Linux...
     448             :                          //
     449             :                          // they probably use a streaming mechanism and thus
     450             :                          // cannot fix the problem later if the compressed
     451             :                          // version ends up being larger than the
     452             :                          // non-compressed version...
     453             :                          //
     454             :                          //REQUIRE((*it)->getCompressedSize() < (*it)->getSize());
     455             :                     }
     456             :                     //REQUIRE((*it)->getName() == ...);
     457             :                     //REQUIRE((*it)->getFileName() == ...);
     458         926 :                     time_t const dostime(unix2dostime(file_stats.st_mtime));
     459         926 :                     REQUIRE((*it)->getTime() == dostime);  // invalid date
     460         926 :                     size_t const ut(dos2unixtime(dostime));
     461         926 :                     REQUIRE((*it)->getUnixTime() == ut);
     462         926 :                     REQUIRE_FALSE((*it)->hasCrc());
     463         926 :                     REQUIRE((*it)->isValid());
     464             :                     //REQUIRE((*it)->toString() == "... (0 bytes)");
     465             : 
     466         926 :                     if(t == zipios_test::file_t::type_t::DIRECTORY)
     467             :                     {
     468          90 :                         REQUIRE((*it)->isDirectory());
     469          90 :                         REQUIRE((*it)->getSize() == 0); // size is zero for directories
     470             :                     }
     471             :                     else
     472             :                     {
     473         836 :                         REQUIRE_FALSE((*it)->isDirectory());
     474         836 :                         REQUIRE((*it)->getSize() == file_stats.st_size);
     475             : 
     476             :                         // now read both files (if not a directory) and make sure
     477             :                         // they are equal
     478         836 :                         zipios::FileCollection::stream_pointer_t is(zf.getInputStream(entry->getName()));
     479         836 :                         REQUIRE(is);
     480        1672 :                         std::ifstream in(entry->getName(), std::ios::in | std::ios::binary);
     481             : 
     482        7169 :                         while(in && *is)
     483             :                         {
     484             :                             char buf1[BUFSIZ], buf2[BUFSIZ];
     485             : 
     486        5497 :                             in.read(buf1, sizeof(buf1));
     487        5497 :                             std::streamsize sz1(in.gcount());
     488             : 
     489        5497 :                             is->read(buf2, sizeof(buf2));
     490        5497 :                             std::streamsize sz2(is->gcount());
     491             : 
     492        5497 :                             REQUIRE(sz1 == sz2);
     493        5497 :                             REQUIRE(memcmp(buf1, buf2, sz1) == 0);
     494             :                         }
     495             : 
     496         836 :                         REQUIRE(!in);
     497        1672 :                         REQUIRE(!*is);
     498             :                     }
     499             : 
     500             :                     // I don't think we will test those directly...
     501             :                     //REQUIRE_THROWS_AS((*it)->read(std::cin), zipios::IOException);
     502             :                     //REQUIRE_THROWS_AS((*it)->write(std::cout), zipios::IOException);
     503         927 :                 }
     504           2 :             }
     505           4 :         }
     506             : 
     507             :         // test with all the possible levels
     508           4 :         SECTION("test creating zip with all available levels")
     509             :         {
     510         105 :             for(zipios::FileEntry::CompressionLevel level(-3); level <= 100; ++level)
     511             :             {
     512             :                 // Note that a level of COMPRESSION_LEVEL_NONE and method of
     513             :                 // DEFLATED is valid, the system will ignore the DEFLATED
     514             :                 // when saving the file and just use STORED instead.
     515         104 :                 dc.setMethod(1024, zipios::StorageMethod::STORED, zipios::StorageMethod::DEFLATED);
     516         104 :                 dc.setLevel(1024, zipios::FileEntry::COMPRESSION_LEVEL_NONE, level);
     517             :                 {
     518         104 :                     std::ofstream out("tree.zip", std::ios::out | std::ios::binary | std::ios::trunc);
     519         104 :                     zipios::ZipFile::saveCollectionToArchive(out, dc);
     520             :                 }
     521             : 
     522         104 :                 zipios::ZipFile zf("tree.zip");
     523             : 
     524         104 :                 REQUIRE(zf.isValid());
     525         104 :                 REQUIRE_FALSE(zf.entries().empty());
     526         104 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     527         104 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     528         104 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     529         104 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     530         104 :                 REQUIRE(zf.getName() == "tree.zip");
     531         104 :                 REQUIRE(zf.size() == tree.size());
     532         104 :                 zf.mustBeValid(); // not throwing
     533             : 
     534         208 :                 zipios::FileEntry::vector_t v(zf.entries());
     535       64376 :                 for(auto it(v.begin()); it != v.end(); ++it)
     536             :                 {
     537       64272 :                     zipios::FileEntry::pointer_t entry(*it);
     538             : 
     539             :                     // verify that our tree knows about this file
     540       64272 :                     zipios_test::file_t::type_t t(tree.find(entry->getName()));
     541       64272 :                     REQUIRE(t != zipios_test::file_t::type_t::UNKNOWN);
     542             : 
     543             :                     struct stat file_stats;
     544       64272 :                     REQUIRE(stat(entry->getName().c_str(), &file_stats) == 0);
     545             : 
     546       64272 :                     REQUIRE((*it)->getComment().empty());
     547             :                     //REQUIRE((*it)->getCompressedSize() == (*it)->getSize()); -- not too sure how we could verify this size in this case
     548             :                     //REQUIRE((*it)->getCrc() == ...); -- not too sure how to compute that right now, but once we have it we'll test it
     549             :                     //REQUIRE((*it)->getEntryOffset() == ...); -- that's also difficult to test
     550             :                     //REQUIRE((*it)->getExtra().empty());
     551             :                     //REQUIRE((*it)->getHeaderSize() == 0); -- the header size varies
     552       64272 :                     if((*it)->getMethod() == zipios::StorageMethod::STORED)
     553             :                     {
     554        6592 :                         REQUIRE((*it)->getCompressedSize() == (*it)->getSize());
     555             :                     }
     556             :                     else
     557             :                     {
     558             :                          // you would think that the compressed size would
     559             :                          // either be equal to the size or smaller, but never
     560             :                          // larger, that's not the case with zip under Linux...
     561             :                          //
     562             :                          // they probably use a streaming mechanism and thus
     563             :                          // cannot fix the problem later if the compressed
     564             :                          // version ends up being larger than the
     565             :                          // non-compressed version...
     566             :                          //
     567             :                          //REQUIRE((*it)->getCompressedSize() < (*it)->getSize());
     568             :                     }
     569             :                     //REQUIRE((*it)->getName() == ...);
     570             :                     //REQUIRE((*it)->getFileName() == ...);
     571       64272 :                     time_t const dostime(unix2dostime(file_stats.st_mtime));
     572       64272 :                     REQUIRE((*it)->getTime() == dostime);  // invalid date
     573       64272 :                     size_t const ut(dos2unixtime(dostime));
     574       64272 :                     REQUIRE((*it)->getUnixTime() == ut);
     575       64272 :                     REQUIRE_FALSE((*it)->hasCrc());
     576       64272 :                     REQUIRE((*it)->isValid());
     577             :                     //REQUIRE((*it)->toString() == "... (0 bytes)");
     578             : 
     579       64272 :                     if(t == zipios_test::file_t::type_t::DIRECTORY)
     580             :                     {
     581        5720 :                         REQUIRE((*it)->isDirectory());
     582        5720 :                         REQUIRE((*it)->getSize() == 0); // size is zero for directories
     583             :                     }
     584             :                     else
     585             :                     {
     586       58552 :                         REQUIRE_FALSE((*it)->isDirectory());
     587       58552 :                         REQUIRE((*it)->getSize() == file_stats.st_size);
     588             : 
     589             :                         // now read both files (if not a directory) and make sure
     590             :                         // they are equal
     591       58552 :                         zipios::FileCollection::stream_pointer_t is(zf.getInputStream(entry->getName()));
     592       58552 :                         REQUIRE(is);
     593      117104 :                         std::ifstream in(entry->getName(), std::ios::in | std::ios::binary);
     594             : 
     595      513032 :                         while(in && *is)
     596             :                         {
     597             :                             char buf1[BUFSIZ], buf2[BUFSIZ];
     598             : 
     599      395928 :                             in.read(buf1, sizeof(buf1));
     600      395928 :                             std::streamsize sz1(in.gcount());
     601             : 
     602      395928 :                             is->read(buf2, sizeof(buf2));
     603      395928 :                             std::streamsize sz2(is->gcount());
     604             : 
     605      395928 :                             REQUIRE(sz1 == sz2);
     606      395928 :                             REQUIRE(memcmp(buf1, buf2, sz1) == 0);
     607             :                         }
     608             : 
     609       58552 :                         REQUIRE_FALSE(in);
     610      117104 :                         REQUIRE_FALSE(*is);
     611             :                     }
     612             : 
     613             :                     // I don't think we will test those directly...
     614             :                     //REQUIRE_THROWS_AS((*it)->read(std::cin), zipios::IOException);
     615             :                     //REQUIRE_THROWS_AS((*it)->write(std::cout), zipios::IOException);
     616       64272 :                 }
     617         104 :             }
     618           8 :         }
     619           5 :     }
     620           5 : }
     621             : 
     622             : 
     623          14 : SCENARIO("use Zipios++ to create zip archives with 1 or 3 files each", "[ZipFile] [FileCollection]")
     624             : {
     625          13 :     GIVEN("a one file zip file")
     626             :     {
     627           9 :         system("rm -f file.bin"); // clean up, just in case
     628             :         {
     629           9 :             std::ofstream file_bin("file.bin", std::ios::out | std::ios::binary);
     630           9 :             file_bin << "this zip file contents.\n";
     631             :         }
     632           9 :         zipios_test::auto_unlink_t remove_bin("file.bin");
     633             : 
     634             :         // first, check that the object is setup as expected
     635           9 :         WHEN("we save the file in a .zip")
     636             :         {
     637           2 :             zipios::DirectoryCollection dc("file.bin");
     638           4 :             zipios_test::auto_unlink_t remove_zip("file.zip");
     639             :             {
     640           2 :                 std::ofstream out("file.zip", std::ios::out | std::ios::binary);
     641           2 :                 zipios::ZipFile::saveCollectionToArchive(out, dc);
     642             :             }
     643             : 
     644           2 :             THEN("it is valid and includes the file as expected")
     645             :             {
     646           1 :                 zipios::ZipFile zf("file.zip");
     647             : 
     648           1 :                 REQUIRE(zf.isValid());
     649           1 :                 REQUIRE_FALSE(zf.entries().empty());
     650           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     651           1 :                 REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     652           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     653           1 :                 REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     654           1 :                 REQUIRE(zf.getName() == "file.zip");
     655           1 :                 REQUIRE(zf.size() == 1);
     656           1 :                 zf.mustBeValid(); // not throwing
     657             : 
     658           2 :                 zipios::FileEntry::vector_t v(zf.entries());
     659           1 :                 REQUIRE(v.size() == 1);
     660           2 :                 for(auto it(v.begin()); it != v.end(); ++it)
     661             :                 {
     662           1 :                     zipios::FileEntry::pointer_t entry(*it);
     663             : 
     664             :                     struct stat file_stats;
     665           1 :                     REQUIRE(stat(entry->getName().c_str(), &file_stats) == 0);
     666             : 
     667           1 :                     REQUIRE((*it)->getComment().empty());
     668           1 :                     REQUIRE((*it)->getCompressedSize() == (*it)->getSize()); // we keep STORED as the method
     669             :                     //REQUIRE((*it)->getCrc() == ...); -- not too sure how to compute that right now, but once we have it we'll test it
     670             :                     //REQUIRE((*it)->getEntryOffset() == ...); -- that's also difficult to test
     671             :                     //REQUIRE((*it)->getExtra().empty());
     672             :                     //REQUIRE((*it)->getHeaderSize() == 0); -- the header size varies
     673           1 :                     if((*it)->getMethod() == zipios::StorageMethod::STORED)
     674             :                     {
     675           1 :                         REQUIRE((*it)->getCompressedSize() == (*it)->getSize());
     676             :                     }
     677             :                     else
     678             :                     {
     679             :                          // you would think that the compressed size would
     680             :                          // either be equal to the size or smaller, but never
     681             :                          // larger, that is not the case with zip under Linux...
     682             :                          //
     683             :                          // they probably use a streaming mechanism and thus
     684             :                          // cannot fix the problem later if the compressed
     685             :                          // version ends up being larger than the
     686             :                          // non-compressed version...
     687             :                          //
     688             :                          //REQUIRE((*it)->getCompressedSize() < (*it)->getSize());
     689             :                     }
     690             :                     //REQUIRE((*it)->getName() == ...);
     691             :                     //REQUIRE((*it)->getFileName() == ...);
     692           1 :                     time_t const dostime(unix2dostime(file_stats.st_mtime));
     693           1 :                     REQUIRE((*it)->getTime() == dostime);  // invalid date
     694           1 :                     size_t const ut(dos2unixtime(dostime));
     695           1 :                     REQUIRE((*it)->getUnixTime() == ut);
     696           1 :                     REQUIRE_FALSE((*it)->hasCrc());
     697           1 :                     REQUIRE((*it)->isValid());
     698             :                     //REQUIRE((*it)->toString() == "... (0 bytes)");
     699             : 
     700           1 :                     REQUIRE_FALSE((*it)->isDirectory());
     701           1 :                     REQUIRE((*it)->getSize() == file_stats.st_size);
     702             : 
     703             :                     // now read both files (if not a directory) and make sure
     704             :                     // they are equal
     705           2 :                     zipios::FileCollection::stream_pointer_t is(zf.getInputStream(entry->getName()));
     706           1 :                     REQUIRE(is);
     707           2 :                     std::ifstream in(entry->getName(), std::ios::in | std::ios::binary);
     708             : 
     709           3 :                     while(in && *is)
     710             :                     {
     711             :                         char buf1[BUFSIZ], buf2[BUFSIZ];
     712             : 
     713           1 :                         in.read(buf1, sizeof(buf1));
     714           1 :                         std::streamsize sz1(in.gcount());
     715             : 
     716           1 :                         is->read(buf2, sizeof(buf2));
     717           1 :                         std::streamsize sz2(is->gcount());
     718             : 
     719           1 :                         REQUIRE(sz1 == sz2);
     720           1 :                         REQUIRE(memcmp(buf1, buf2, sz1) == 0);
     721             :                     }
     722             : 
     723           1 :                     REQUIRE_FALSE(in);
     724           1 :                     REQUIRE_FALSE(*is);
     725             : 
     726             :                     // I don't think we will test those directly...
     727             :                     //REQUIRE_THROWS_AS((*it)->read(std::cin), zipios::IOException);
     728             :                     //REQUIRE_THROWS_AS((*it)->write(std::cout), zipios::IOException);
     729           2 :                 }
     730           4 :             }
     731           9 :         }
     732             : 
     733             : /** \TODO
     734             :  * Once clang is fixed, remove those tests. clang does not clear the
     735             :  * std::unchecked_exception() flag when we have a re-throw in a catch.
     736             :  */
     737             : #ifndef __clang__
     738             :         // test with a comment that's too large
     739           9 :         WHEN("we make sure that saving the file fails if the comment is too large")
     740             :         {
     741           2 :             zipios::DirectoryCollection dc("file.bin");
     742           4 :             zipios::FileEntry::vector_t v(dc.entries());
     743           2 :             REQUIRE(v.size() == 1);
     744           2 :             auto it(v.begin());
     745             :             // generate a random comment of 65Kb
     746           4 :             std::string comment;
     747      133122 :             for(int i(0); i < 65 * 1024; ++i)
     748             :             {
     749      133120 :                 comment += rand() % 26 + 'A';
     750             :             }
     751           2 :             (*it)->setComment(comment);
     752             : 
     753           2 :             THEN("it is invalid and fails as expected")
     754             :             {
     755           1 :                 zipios_test::auto_unlink_t remove_zip("file.zip");
     756             :                 {
     757           1 :                     std::ofstream out("file.zip", std::ios::out | std::ios::binary);
     758           1 :                     REQUIRE_THROWS_AS(zipios::ZipFile::saveCollectionToArchive(out, dc), zipios::InvalidStateException);
     759           1 :                     REQUIRE_FALSE(out);
     760           1 :                 }
     761           4 :             }
     762           9 :         }
     763             : #endif
     764             : 
     765             : #ifndef __clang__
     766             :         // check that extra buffers that are too large make the save fail
     767           9 :         WHEN("we make sure that saving the file fails if the extra buffer is too large")
     768             :         {
     769           2 :             zipios::DirectoryCollection dc("file.bin");
     770           4 :             zipios::FileEntry::vector_t v(dc.entries());
     771           2 :             REQUIRE(v.size() == 1);
     772           2 :             auto it(v.begin());
     773             :             // generate a random extra buffer of 65Kb
     774           4 :             zipios::FileEntry::buffer_t buffer;
     775      133122 :             for(int i(0); i < 65 * 1024; ++i)
     776             :             {
     777      133120 :                 buffer.push_back(static_cast<unsigned char>(rand()));
     778             :             }
     779           2 :             (*it)->setExtra(buffer);
     780             : 
     781           2 :             THEN("it is invalid and fails as expected")
     782             :             {
     783           1 :                 zipios_test::auto_unlink_t remove_zip("file.zip");
     784             :                 {
     785           1 :                     std::ofstream out("file.zip", std::ios::out | std::ios::binary);
     786           1 :                     REQUIRE_THROWS_AS(zipios::ZipFile::saveCollectionToArchive(out, dc), zipios::InvalidStateException);
     787           1 :                     REQUIRE_FALSE(out);
     788           1 :                 }
     789           4 :             }
     790           9 :         }
     791             : #endif
     792             : 
     793             : #ifndef __clang__
     794             :         // check with a global comment which is too large
     795           9 :         WHEN("we make sure that saving the file fails if the Zip (gloabl) comment is too large")
     796             :         {
     797           2 :             zipios::DirectoryCollection dc("file.bin");
     798             :             // generate a random comment of 65Kb
     799           4 :             std::string comment;
     800      133122 :             for(int i(0); i < 65 * 1024; ++i)
     801             :             {
     802      133120 :                 comment += rand() % 26 + 'A';
     803             :             }
     804             : 
     805           2 :             THEN("it is invalid and fails as expected")
     806             :             {
     807           1 :                 zipios_test::auto_unlink_t remove_zip("file.zip");
     808             :                 {
     809           1 :                     std::ofstream out("file.zip", std::ios::out | std::ios::binary);
     810           1 :                     REQUIRE_THROWS_AS(zipios::ZipFile::saveCollectionToArchive(out, dc, comment), zipios::InvalidStateException);
     811           1 :                     REQUIRE_FALSE(out);
     812           1 :                 }
     813           4 :             }
     814           9 :         }
     815             : #endif
     816          13 :     }
     817             : 
     818             : #ifndef __clang__
     819          13 :     GIVEN("a very small file")
     820             :     {
     821           3 :         system("rm -f file.bin"); // clean up, just in case
     822             :         {
     823             :             // one byte file
     824           3 :             std::ofstream file_bin("file.bin", std::ios::out | std::ios::binary);
     825           3 :             file_bin << "1";
     826             :         }
     827           3 :         zipios_test::auto_unlink_t remove_bin("file.bin");
     828             : 
     829             :         // first, check that the object is setup as expected
     830           3 :         WHEN("we add it more than 64Kb times to the directory")
     831             :         {
     832           2 :             zipios::DirectoryCollection dc("file.bin");
     833             : 
     834             :             // add another 64Kb file entries! (all the same name, ouch!)
     835      131095 :             for(int i(0); i < 64 * 1024 + rand() % 100; ++i)
     836             :             {
     837      131093 :                 zipios::DirectoryEntry other_entry(zipios::FilePath("file.bin"));
     838      131093 :                 dc.addEntry(other_entry);
     839      131093 :             }
     840             : 
     841           2 :             THEN("the creating of the zip archive fails: too many file entries")
     842             :             {
     843           1 :                 zipios_test::auto_unlink_t remove_zip("file.zip");
     844             :                 {
     845           1 :                     std::ofstream out("file.zip", std::ios::out | std::ios::binary);
     846           1 :                     REQUIRE_THROWS_AS(zipios::ZipFile::saveCollectionToArchive(out, dc), zipios::InvalidStateException);
     847           1 :                 }
     848           2 :             }
     849           3 :         }
     850          13 :     }
     851             : #endif
     852          13 : }
     853             : 
     854             : 
     855           4 : TEST_CASE("Simple Valid and Invalid ZipFile Archives", "[ZipFile] [FileCollection]")
     856             : {
     857           3 :     SECTION("try one uncompressed file of many sizes")
     858             :     {
     859           1 :         system("rm -f file.bin"); // clean up, just in case
     860          74 :         for(int sz(0); sz <= 128 * 1024; sz += sz < 10 ? 1 : rand() % (1024 * 4))
     861             :         {
     862          73 :             zipios_test::auto_unlink_t remove_bin("file.bin");
     863         146 :             zipios_test::auto_unlink_t remove_zip("file.zip");
     864             : 
     865             :             // create a file of various sizes (increasingly big though)
     866             :             {
     867          73 :                 std::ofstream file_bin("file.bin", std::ios::out | std::ios::binary);
     868     4136171 :                 for(int pos(0); pos < sz; ++pos)
     869             :                 {
     870     4136098 :                     file_bin << static_cast<char>(rand());
     871          73 :                 }
     872             :             }
     873             : 
     874         146 :             zipios::DirectoryCollection dc("file.bin");
     875         146 :             zipios::FileEntry::vector_t v(dc.entries());
     876          73 :             (*v.begin())->setLevel(zipios::FileEntry::COMPRESSION_LEVEL_NONE);
     877          73 :             (*v.begin())->setMethod(zipios::StorageMethod::DEFLATED);
     878             :             {
     879          73 :                 std::ofstream out("file.zip", std::ios::out | std::ios::binary | std::ios::trunc);
     880          73 :                 zipios::ZipFile::saveCollectionToArchive(out, dc);
     881             :             }
     882             : 
     883         146 :             zipios::ZipFile zf("file.zip");
     884             : 
     885          73 :             REQUIRE(zf.isValid());
     886          73 :             REQUIRE_FALSE(zf.entries().empty());
     887          73 :             REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     888          73 :             REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     889          73 :             REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     890          73 :             REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     891          73 :             REQUIRE(zf.getName() == "file.zip");
     892          73 :             REQUIRE(zf.size() == 1);
     893          73 :             zf.mustBeValid(); // not throwing
     894          73 :         }
     895           3 :     }
     896             : 
     897           3 :     SECTION("try three uncompressed files of many sizes")
     898             :     {
     899           1 :         system("rm -f file.zip file?.bin"); // clean up, just in case
     900          72 :         for(int sz(0); sz <= 128 * 1024; sz += sz < 10 ? 1 : rand() % (1024 * 4))
     901             :         {
     902          71 :             zipios_test::auto_unlink_t remove_bin1("file1.bin");
     903         142 :             zipios_test::auto_unlink_t remove_bin2("file2.bin");
     904         142 :             zipios_test::auto_unlink_t remove_bin3("file3.bin");
     905         142 :             zipios_test::auto_unlink_t remove_zip("file.zip");
     906             : 
     907             :             // create a file of various sizes (increasingly big though)
     908             :             {
     909          71 :                 std::ofstream file_bin("file1.bin", std::ios::out | std::ios::binary);
     910     3918252 :                 for(int pos(0); pos < sz; ++pos)
     911             :                 {
     912     3918181 :                     file_bin << static_cast<char>(rand());
     913          71 :                 }
     914             :             }
     915             :             {
     916          71 :                 std::ofstream file_bin("file2.bin", std::ios::out | std::ios::binary);
     917     3918252 :                 for(int pos(0); pos < sz; ++pos)
     918             :                 {
     919     3918181 :                     file_bin << static_cast<char>(rand());
     920          71 :                 }
     921             :             }
     922             :             {
     923          71 :                 std::ofstream file_bin("file3.bin", std::ios::out | std::ios::binary);
     924     3918252 :                 for(int pos(0); pos < sz; ++pos)
     925             :                 {
     926     3918181 :                     file_bin << static_cast<char>(rand());
     927          71 :                 }
     928             :             }
     929             : 
     930         142 :             zipios::DirectoryCollection dc("file1.bin");
     931             :             {
     932          71 :                 zipios::DirectoryEntry other_entry2(zipios::FilePath("file2.bin"));
     933          71 :                 dc.addEntry(other_entry2);
     934         142 :                 zipios::DirectoryEntry other_entry3(zipios::FilePath("file3.bin"));
     935         142 :                 dc.addEntry(other_entry3);
     936             :             }
     937         142 :             zipios::FileEntry::vector_t v(dc.entries());
     938          71 :             (*v.begin())->setLevel(zipios::FileEntry::COMPRESSION_LEVEL_NONE);
     939          71 :             (*v.begin())->setMethod(zipios::StorageMethod::DEFLATED);
     940             :             {
     941          71 :                 std::ofstream out("file.zip", std::ios::out | std::ios::binary | std::ios::trunc);
     942          71 :                 zipios::ZipFile::saveCollectionToArchive(out, dc);
     943             :             }
     944             : 
     945         142 :             zipios::ZipFile zf("file.zip");
     946             : 
     947          71 :             REQUIRE(zf.isValid());
     948          71 :             REQUIRE_FALSE(zf.entries().empty());
     949          71 :             REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::MATCH));
     950          71 :             REQUIRE_FALSE(zf.getEntry("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     951          71 :             REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::MATCH));
     952          71 :             REQUIRE_FALSE(zf.getInputStream("inexistant", zipios::FileCollection::MatchPath::IGNORE));
     953          71 :             REQUIRE(zf.getName() == "file.zip");
     954          71 :             REQUIRE(zf.size() == 3);
     955          71 :             zf.mustBeValid(); // not throwing
     956          71 :         }
     957           3 :     }
     958           3 : }
     959             : 
     960             : 
     961             : // For this one we have a set of structures and we "manually"
     962             : // create Zip archive files that we can thus tweak with totally
     963             : // invalid parameters
     964          50 : struct local_header_t
     965             : {
     966             :     typedef std::vector<unsigned char>      buffer_t;
     967             : 
     968             :     uint32_t            m_signature;            // "PK 3.4"
     969             :     uint16_t            m_version;              // 10 or 20
     970             :     uint16_t            m_flags;                // generally zero ("general purpose field")
     971             :     uint16_t            m_compression_method;   // zipios++ only supports STORED and DEFLATE
     972             :     uint32_t            m_time_and_date;        // MS-DOS date and time
     973             :     uint32_t            m_crc32;                // CRC-32 of the file data
     974             :     uint32_t            m_compressed_size;      // size of data once compressed
     975             :     uint32_t            m_uncompressed_size;    // size of data uncompressed
     976             :     //uint16_t            m_filename_length;     // length name of this file
     977             :     //uint16_t            m_extra_field_length;   // length of extra buffer, zipios++ ignore those
     978             :     //uint8_t             m_filename[m_filename_length];
     979             :     std::string         m_filename;
     980             :     //uint8_t             m_extra_field[m_extra_field_length];
     981             :     buffer_t            m_extra_field;
     982             : 
     983          50 :     local_header_t()
     984             :         : m_signature(0x04034B50)
     985             :         , m_version(10)
     986             :         , m_flags(0)
     987             :         , m_compression_method(0)   // 0 == STORED
     988          50 :         , m_time_and_date(unix2dostime(time(nullptr)))
     989             :         , m_crc32(0)
     990             :         , m_compressed_size(0)      // undefined is compression method is 0
     991         100 :         , m_uncompressed_size(0)
     992             :         //, m_filename("") -- auto-init
     993             :         //, m_extra_field() -- auto-init
     994             :     {
     995          50 :     }
     996             : 
     997          50 :     void write(std::ostream& os)
     998             :     {
     999          50 :         if(m_filename.empty())
    1000             :         {
    1001             :             std::cerr << "bug: local_header_t::write() called without a filename." << std::endl; // LCOV_EXCL_LINE
    1002             :             exit(1); // LCOV_EXCL_LINE
    1003             :         }
    1004             : 
    1005             :         // IMPORTANT NOTE:
    1006             :         // We do not verify any field other than the filename because
    1007             :         // we want to be able to use this class to create anything
    1008             :         // (i.e. including invalid headers.)
    1009             : 
    1010          50 :         os << static_cast<unsigned char>(m_signature >>  0);
    1011          50 :         os << static_cast<unsigned char>(m_signature >>  8);
    1012          50 :         os << static_cast<unsigned char>(m_signature >> 16);
    1013          50 :         os << static_cast<unsigned char>(m_signature >> 24);
    1014          50 :         os << static_cast<unsigned char>(m_version >> 0);
    1015          50 :         os << static_cast<unsigned char>(m_version >> 8);
    1016          50 :         os << static_cast<unsigned char>(m_flags >> 0);
    1017          50 :         os << static_cast<unsigned char>(m_flags >> 8);
    1018          50 :         os << static_cast<unsigned char>(m_compression_method >> 0);
    1019          50 :         os << static_cast<unsigned char>(m_compression_method >> 8);
    1020          50 :         os << static_cast<unsigned char>(m_time_and_date >>  0);
    1021          50 :         os << static_cast<unsigned char>(m_time_and_date >>  8);
    1022          50 :         os << static_cast<unsigned char>(m_time_and_date >> 16);
    1023          50 :         os << static_cast<unsigned char>(m_time_and_date >> 24);
    1024          50 :         os << static_cast<unsigned char>(m_crc32 >>  0);
    1025          50 :         os << static_cast<unsigned char>(m_crc32 >>  8);
    1026          50 :         os << static_cast<unsigned char>(m_crc32 >> 16);
    1027          50 :         os << static_cast<unsigned char>(m_crc32 >> 24);
    1028          50 :         os << static_cast<unsigned char>(m_compressed_size >>  0);
    1029          50 :         os << static_cast<unsigned char>(m_compressed_size >>  8);
    1030          50 :         os << static_cast<unsigned char>(m_compressed_size >> 16);
    1031          50 :         os << static_cast<unsigned char>(m_compressed_size >> 24);
    1032          50 :         os << static_cast<unsigned char>(m_uncompressed_size >>  0);
    1033          50 :         os << static_cast<unsigned char>(m_uncompressed_size >>  8);
    1034          50 :         os << static_cast<unsigned char>(m_uncompressed_size >> 16);
    1035          50 :         os << static_cast<unsigned char>(m_uncompressed_size >> 24);
    1036          50 :         uint16_t filename_length(m_filename.length());
    1037          50 :         os << static_cast<unsigned char>(filename_length >> 0);
    1038          50 :         os << static_cast<unsigned char>(filename_length >> 8);
    1039          50 :         uint16_t extra_field_length(m_extra_field.size());
    1040          50 :         os << static_cast<unsigned char>(extra_field_length >> 0);
    1041          50 :         os << static_cast<unsigned char>(extra_field_length >> 8);
    1042          50 :         os << m_filename;
    1043          50 :         os.write(reinterpret_cast<char const *>(&m_extra_field[0]), m_extra_field.size());
    1044          50 :     }
    1045             : };
    1046             : 
    1047             : 
    1048          70 : struct central_directory_header_t
    1049             : {
    1050             :     typedef std::vector<unsigned char>      buffer_t;
    1051             : 
    1052             :     uint32_t            m_signature;            // 00 -- "PK 2.1"
    1053             :     uint16_t            m_version;              // 04 -- 10 or 20
    1054             :     uint16_t            m_extract_version;      // 06 -- generally zero
    1055             :     uint16_t            m_flags;                // 08 -- various flags
    1056             :     uint16_t            m_compression_method;   // 0A -- method used to compress the data
    1057             :     uint32_t            m_time_and_date;        // 0C -- MS-DOS date and time
    1058             :     uint32_t            m_crc32;                // 10 -- CRC-32 of the file data
    1059             :     uint32_t            m_compressed_size;      // 14 -- size of data once compressed
    1060             :     uint32_t            m_uncompressed_size;    // 18 -- size of data uncompressed
    1061             :     //uint16_t            m_filename_length;      // 1C -- length name of this file
    1062             :     //uint16_t            m_extra_field_length;   // 1E -- length of extra buffer, zipios++ ignore those
    1063             :     //uint16_t            m_file_comment_length;  // 20 -- length of comment
    1064             :     uint16_t            m_disk_number_start;                // 22 -- disk number of split archives
    1065             :     uint16_t            m_internal_file_attributes;         // 24 -- file attributes
    1066             :     uint32_t            m_external_file_attributes;         // 26 -- file attributes
    1067             :     uint32_t            m_relative_offset_to_local_header;  // 2A -- offset to actual file
    1068             :     //uint8_t             m_filename[m_filename_length];    // 2E -- filename (variable size
    1069             :     std::string         m_filename;
    1070             :     //uint8_t             m_extra_field[m_extra_field_length];      // .. -- extra field(s)
    1071             :     buffer_t            m_extra_field;
    1072             :     //uint8_t             m_file_comment[m_file_comment_length];    // .. -- file comment
    1073             :     std::string         m_file_comment;
    1074             : 
    1075          70 :     central_directory_header_t()
    1076             :         : m_signature(0x02014B50)
    1077             :         , m_version(10)
    1078             :         , m_extract_version(10)
    1079             :         , m_flags(0)
    1080             :         , m_compression_method(0)   // 0 == STORED
    1081          70 :         , m_time_and_date(unix2dostime(time(nullptr)))
    1082             :         , m_crc32(0)
    1083             :         , m_compressed_size(0)      // undefined is compression method is 0
    1084             :         , m_uncompressed_size(0)
    1085             :         , m_disk_number_start(0)
    1086             :         , m_internal_file_attributes(0)
    1087             :         , m_external_file_attributes(0)
    1088         140 :         , m_relative_offset_to_local_header(0)
    1089             :         //, m_filename("") -- auto-init
    1090             :         //, m_extra_field() -- auto-init
    1091             :         //, m_file_comment("") -- auto-init
    1092             :     {
    1093          70 :     }
    1094             : 
    1095          70 :     void write(std::ostream& os)
    1096             :     {
    1097          70 :         if(m_filename.empty())
    1098             :         {
    1099             :             std::cerr << "bug: central_directory_header_t::write() called without a filename." << std::endl; // LCOV_EXCL_LINE
    1100             :             exit(1); // LCOV_EXCL_LINE
    1101             :         }
    1102             : 
    1103             :         // IMPORTANT NOTE:
    1104             :         // We do not verify any field other than the filename because
    1105             :         // we want to be able to use this class to create anything
    1106             :         // (i.e. including invalid headers.)
    1107             : 
    1108          70 :         os << static_cast<unsigned char>(m_signature >>  0);
    1109          70 :         os << static_cast<unsigned char>(m_signature >>  8);
    1110          70 :         os << static_cast<unsigned char>(m_signature >> 16);
    1111          70 :         os << static_cast<unsigned char>(m_signature >> 24);
    1112          70 :         os << static_cast<unsigned char>(m_version >> 0);
    1113          70 :         os << static_cast<unsigned char>(m_version >> 8);
    1114          70 :         os << static_cast<unsigned char>(m_extract_version >> 0);
    1115          70 :         os << static_cast<unsigned char>(m_extract_version >> 8);
    1116          70 :         os << static_cast<unsigned char>(m_flags >> 0);
    1117          70 :         os << static_cast<unsigned char>(m_flags >> 8);
    1118          70 :         os << static_cast<unsigned char>(m_compression_method >> 0);
    1119          70 :         os << static_cast<unsigned char>(m_compression_method >> 8);
    1120          70 :         os << static_cast<unsigned char>(m_time_and_date >>  0);
    1121          70 :         os << static_cast<unsigned char>(m_time_and_date >>  8);
    1122          70 :         os << static_cast<unsigned char>(m_time_and_date >> 16);
    1123          70 :         os << static_cast<unsigned char>(m_time_and_date >> 24);
    1124          70 :         os << static_cast<unsigned char>(m_crc32 >>  0);
    1125          70 :         os << static_cast<unsigned char>(m_crc32 >>  8);
    1126          70 :         os << static_cast<unsigned char>(m_crc32 >> 16);
    1127          70 :         os << static_cast<unsigned char>(m_crc32 >> 24);
    1128          70 :         os << static_cast<unsigned char>(m_compressed_size >>  0);
    1129          70 :         os << static_cast<unsigned char>(m_compressed_size >>  8);
    1130          70 :         os << static_cast<unsigned char>(m_compressed_size >> 16);
    1131          70 :         os << static_cast<unsigned char>(m_compressed_size >> 24);
    1132          70 :         os << static_cast<unsigned char>(m_uncompressed_size >>  0);
    1133          70 :         os << static_cast<unsigned char>(m_uncompressed_size >>  8);
    1134          70 :         os << static_cast<unsigned char>(m_uncompressed_size >> 16);
    1135          70 :         os << static_cast<unsigned char>(m_uncompressed_size >> 24);
    1136          70 :         uint16_t filename_length(m_filename.length());
    1137          70 :         os << static_cast<unsigned char>(filename_length >> 0);
    1138          70 :         os << static_cast<unsigned char>(filename_length >> 8);
    1139          70 :         uint16_t extra_field_length(m_extra_field.size());
    1140          70 :         os << static_cast<unsigned char>(extra_field_length >> 0);
    1141          70 :         os << static_cast<unsigned char>(extra_field_length >> 8);
    1142          70 :         uint16_t file_comment_length(m_file_comment.length());
    1143          70 :         os << static_cast<unsigned char>(file_comment_length >> 0);
    1144          70 :         os << static_cast<unsigned char>(file_comment_length >> 8);
    1145          70 :         os << static_cast<unsigned char>(m_disk_number_start >> 0);
    1146          70 :         os << static_cast<unsigned char>(m_disk_number_start >> 8);
    1147          70 :         os << static_cast<unsigned char>(m_internal_file_attributes >> 0);
    1148          70 :         os << static_cast<unsigned char>(m_internal_file_attributes >> 8);
    1149          70 :         os << static_cast<unsigned char>(m_external_file_attributes >>  0);
    1150          70 :         os << static_cast<unsigned char>(m_external_file_attributes >>  8);
    1151          70 :         os << static_cast<unsigned char>(m_external_file_attributes >> 16);
    1152          70 :         os << static_cast<unsigned char>(m_external_file_attributes >> 24);
    1153          70 :         os << static_cast<unsigned char>(m_relative_offset_to_local_header >>  0);
    1154          70 :         os << static_cast<unsigned char>(m_relative_offset_to_local_header >>  8);
    1155          70 :         os << static_cast<unsigned char>(m_relative_offset_to_local_header >> 16);
    1156          70 :         os << static_cast<unsigned char>(m_relative_offset_to_local_header >> 24);
    1157          70 :         os << m_filename;
    1158          70 :         os.write(reinterpret_cast<char const *>(&m_extra_field[0]), m_extra_field.size());
    1159          70 :         os << m_file_comment;
    1160          70 :     }
    1161             : };
    1162             : 
    1163             : 
    1164         112 : struct end_of_central_directory_t
    1165             : {
    1166             :     uint32_t            m_signature;        // "PK 5.6"
    1167             :     uint16_t            m_disk_number;
    1168             :     uint16_t            m_disk_start;
    1169             :     uint16_t            m_file_count;       // number of files in this archive
    1170             :     uint16_t            m_total_count;      // total number across all split files
    1171             :     uint32_t            m_central_directory_size;
    1172             :     uint32_t            m_central_directory_offset;
    1173             :     //uint16_t            m_comment_length;
    1174             :     //unsigned char       m_comment[m_comment_length];
    1175             :     std::string         m_comment;
    1176             : 
    1177         112 :     end_of_central_directory_t()
    1178             :         : m_signature(0x06054B50)
    1179             :         , m_disk_number(0)
    1180             :         , m_disk_start(0)
    1181             :         , m_file_count(0)
    1182             :         , m_total_count(0)
    1183             :         , m_central_directory_size(0)
    1184         112 :         , m_central_directory_offset(0)
    1185             :         //, m_comment_length(0)
    1186             :         //, m_comment("") -- auto-init
    1187             :     {
    1188         112 :     }
    1189             : 
    1190         112 :     void write(std::ostream& os)
    1191             :     {
    1192             :         // IMPORTANT NOTE:
    1193             :         // We do not verify any of the values on purpose, we want to be
    1194             :         // able to use this class to create anything (i.e. including invalid
    1195             :         // headers.)
    1196             : 
    1197         112 :         os << static_cast<unsigned char>(m_signature >>  0);
    1198         112 :         os << static_cast<unsigned char>(m_signature >>  8);
    1199         112 :         os << static_cast<unsigned char>(m_signature >> 16);
    1200         112 :         os << static_cast<unsigned char>(m_signature >> 24);
    1201         112 :         os << static_cast<unsigned char>(m_disk_number >> 0);
    1202         112 :         os << static_cast<unsigned char>(m_disk_number >> 8);
    1203         112 :         os << static_cast<unsigned char>(m_disk_start >> 0);
    1204         112 :         os << static_cast<unsigned char>(m_disk_start >> 8);
    1205         112 :         os << static_cast<unsigned char>(m_file_count >> 0);
    1206         112 :         os << static_cast<unsigned char>(m_file_count >> 8);
    1207         112 :         os << static_cast<unsigned char>(m_total_count >>  0);
    1208         112 :         os << static_cast<unsigned char>(m_total_count >>  8);
    1209         112 :         os << static_cast<unsigned char>(m_central_directory_size >>  0);
    1210         112 :         os << static_cast<unsigned char>(m_central_directory_size >>  8);
    1211         112 :         os << static_cast<unsigned char>(m_central_directory_size >> 16);
    1212         112 :         os << static_cast<unsigned char>(m_central_directory_size >> 24);
    1213         112 :         os << static_cast<unsigned char>(m_central_directory_offset >>  0);
    1214         112 :         os << static_cast<unsigned char>(m_central_directory_offset >>  8);
    1215         112 :         os << static_cast<unsigned char>(m_central_directory_offset >> 16);
    1216         112 :         os << static_cast<unsigned char>(m_central_directory_offset >> 24);
    1217         112 :         uint16_t comment_length(m_comment.length());
    1218         112 :         os << static_cast<unsigned char>(comment_length >>  0);
    1219         112 :         os << static_cast<unsigned char>(comment_length >>  8);
    1220         112 :         os << m_comment;
    1221         112 :     }
    1222             : };
    1223             : 
    1224             : 
    1225          12 : TEST_CASE("Valid and Invalid ZipFile Archives", "[ZipFile] [FileCollection]")
    1226             : {
    1227          11 :     SECTION("create files with End of Central Directory that are tool small")
    1228             :     {
    1229          23 :         for(ssize_t i(22 - 1); i >= 0; --i)
    1230             :         {
    1231             :             // create an empty header in the file
    1232          22 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1233             :             {
    1234          22 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1235             : 
    1236          44 :                 end_of_central_directory_t eocd;
    1237          44 :                 eocd.write(os);
    1238             :             }
    1239             : 
    1240             :             // truncate the file to 'i' size
    1241          22 :             truncate("file.zip", i);
    1242             : 
    1243          22 :             REQUIRE_THROWS_AS(zipios::ZipFile zf("file.zip"), zipios::FileCollectionException);
    1244          22 :         }
    1245          11 :     }
    1246             : 
    1247          11 :     SECTION("create files with End of Central Directory file except for the comment")
    1248             :     {
    1249          11 :         for(int i(0); i < 10; ++i)
    1250             :         {
    1251             :             // create an empty header in the file
    1252          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1253          10 :             size_t const comment_len(rand() % 20 + 5);
    1254             :             {
    1255          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1256             : 
    1257          20 :                 end_of_central_directory_t eocd;
    1258         157 :                 for(size_t j(0); j < comment_len; ++j)
    1259             :                 {
    1260         147 :                     eocd.m_comment += static_cast<char>('A' + rand() % 26);
    1261             :                 }
    1262          20 :                 eocd.write(os);
    1263             :             }
    1264             : 
    1265             :             // truncate the file to not include the whole comment
    1266             :             // (truncate at least one character though)
    1267          10 :             size_t const five(5);
    1268          10 :             truncate("file.zip", (22 + comment_len) - (rand() % std::min(five, comment_len) + 1));
    1269             : 
    1270          10 :             REQUIRE_THROWS_AS(zipios::ZipFile zf("file.zip"), zipios::IOException);
    1271          10 :         }
    1272          11 :     }
    1273             : 
    1274          11 :     SECTION("create files with End of Central Directory using counts that differ")
    1275             :     {
    1276          11 :         for(int i(0); i < 10; ++i)
    1277             :         {
    1278             :             // create an empty header in the file
    1279          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1280          10 :             size_t const size1(rand() & 0xFFFF);
    1281             :             size_t size2;
    1282          10 :             do
    1283             :             {
    1284          10 :                 size2 = rand() & 0xFFFF;
    1285             :             }
    1286             :             while(size1 == size2);
    1287             :             {
    1288          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1289             : 
    1290          20 :                 end_of_central_directory_t eocd;
    1291          10 :                 eocd.m_file_count = size1;
    1292          10 :                 eocd.m_total_count = size2;
    1293          20 :                 eocd.write(os);
    1294             :             }
    1295             : 
    1296          10 :             REQUIRE_THROWS_AS(zipios::ZipFile zf("file.zip"), zipios::FileCollectionException);
    1297          10 :         }
    1298          11 :     }
    1299             : 
    1300          11 :     SECTION("create files with one Local Entry using an invalid signature")
    1301             :     {
    1302          11 :         for(int i(0); i < 10; ++i)
    1303             :         {
    1304             :             // create an empty header in the file
    1305          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1306             :             {
    1307          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1308             : 
    1309             :                 // since the signature will be invalid, we can ignore the
    1310             :                 // rest too...
    1311          20 :                 central_directory_header_t cdh;
    1312          10 :                 cdh.m_signature = 0x01020304;       // an invalid signature
    1313          10 :                 cdh.m_filename = "invalid";
    1314          10 :                 cdh.write(os);
    1315             : 
    1316          20 :                 end_of_central_directory_t eocd;
    1317          10 :                 eocd.m_file_count = 1;
    1318          10 :                 eocd.m_total_count = 1;
    1319          10 :                 eocd.m_central_directory_size = 46 + 7; // structure + filename
    1320          20 :                 eocd.write(os);
    1321             :             }
    1322             : 
    1323          10 :             REQUIRE_THROWS_AS(zipios::ZipFile zf("file.zip"), zipios::IOException);
    1324          10 :         }
    1325          11 :     }
    1326             : 
    1327          11 :     SECTION("create files with a valid central directory but no local entries")
    1328             :     {
    1329          11 :         for(int i(0); i < 10; ++i)
    1330             :         {
    1331             :             // create an empty header in the file
    1332          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1333             :             {
    1334          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1335             : 
    1336             :                 // since the signature will be invalid, we can ignore the
    1337             :                 // rest too...
    1338          20 :                 central_directory_header_t cdh;
    1339          10 :                 cdh.m_filename = "invalid";
    1340          10 :                 cdh.write(os);
    1341             : 
    1342          20 :                 end_of_central_directory_t eocd;
    1343          10 :                 eocd.m_file_count = 1;
    1344          10 :                 eocd.m_total_count = 1;
    1345          10 :                 eocd.m_central_directory_size = 46 + 7; // structure + filename
    1346          20 :                 eocd.write(os);
    1347             :             }
    1348             : 
    1349          10 :             REQUIRE_THROWS_AS(zipios::ZipFile zf("file.zip"), zipios::IOException);
    1350          10 :         }
    1351          11 :     }
    1352             : 
    1353          11 :     SECTION("create files with one an unsupported compression method")
    1354             :     {
    1355          11 :         for(int i(0); i < 10; ++i)
    1356             :         {
    1357             :             // create an empty header in the file
    1358          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1359             :             {
    1360          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1361             : 
    1362             :                 // create a header (has to be equal to pass to the method check)
    1363          20 :                 local_header_t lh;
    1364          20 :                 central_directory_header_t cdh;
    1365          20 :                 end_of_central_directory_t eocd;
    1366             : 
    1367             :                 for(;;)
    1368             :                 {
    1369             :                     // this is saved as a 16 bit value so it probably should
    1370             :                     // support 16 bits
    1371          10 :                     lh.m_compression_method = rand() & 0xFFFF;
    1372             : 
    1373             :                     // make sure it is not a valid method
    1374          10 :                     bool found(false);
    1375          30 :                     for(size_t m(0); m < sizeof(g_supported_storage_methods) / sizeof(g_supported_storage_methods[0]); ++m)
    1376             :                     {
    1377          20 :                         if(static_cast<zipios::StorageMethod>(lh.m_compression_method) == g_supported_storage_methods[m])
    1378             :                         {
    1379             :                             // it is valid, we will try again
    1380             :                             // (it is going to be really rare, so we exclude
    1381             :                             // these lines from the coverage)
    1382             :                             found = true; // LCOV_EXCL_LINE
    1383             :                             break; // LCOV_EXCL_LINE
    1384             :                         }
    1385             :                     }
    1386          10 :                     if(!found)
    1387             :                     {
    1388          10 :                         break;
    1389             :                     }
    1390             :                 } // LCOV_EXCL_LINE
    1391          10 :                 lh.m_filename = "invalid";
    1392          10 :                 lh.write(os);
    1393             : 
    1394          10 :                 eocd.m_central_directory_offset = os.tellp();
    1395             : 
    1396          10 :                 cdh.m_compression_method = lh.m_compression_method;
    1397          10 :                 cdh.m_filename = "invalid";
    1398          10 :                 cdh.write(os);
    1399             : 
    1400          10 :                 eocd.m_file_count = 1;
    1401          10 :                 eocd.m_total_count = 1;
    1402          10 :                 eocd.m_central_directory_size = 46 + 7; // structure + filename
    1403          20 :                 eocd.write(os);
    1404             :             }
    1405             : 
    1406          20 :             zipios::ZipFile zf("file.zip");
    1407          10 :             REQUIRE_THROWS_AS(zf.getInputStream("invalid"), zipios::FileCollectionException);
    1408          10 :         }
    1409          11 :     }
    1410             : 
    1411          11 :     SECTION("create files with a trailing data descriptor")
    1412             :     {
    1413          11 :         for(int i(0); i < 10; ++i)
    1414             :         {
    1415             :             // create an empty header in the file
    1416          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1417             :             {
    1418          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1419             : 
    1420             :                 // create a header (has to be equal to pass to the method check)
    1421          20 :                 local_header_t lh;
    1422          20 :                 central_directory_header_t cdh;
    1423          20 :                 end_of_central_directory_t eocd;
    1424             : 
    1425             :                 // use a valid compression method
    1426          10 :                 lh.m_flags |= 1 << 3;  // <-- testing that trailing data is not supported!
    1427          10 :                 lh.m_compression_method = static_cast<uint16_t>(g_supported_storage_methods[rand() % (sizeof(g_supported_storage_methods) / sizeof(g_supported_storage_methods[0]))]);
    1428          10 :                 lh.m_filename = "invalid";
    1429          10 :                 lh.write(os);
    1430             : 
    1431          10 :                 eocd.m_central_directory_offset = os.tellp();
    1432             : 
    1433          10 :                 cdh.m_compression_method = lh.m_compression_method;
    1434          10 :                 cdh.m_flags = lh.m_flags;
    1435          10 :                 cdh.m_filename = "invalid";
    1436          10 :                 cdh.write(os);
    1437             : 
    1438          10 :                 eocd.m_file_count = 1;
    1439          10 :                 eocd.m_total_count = 1;
    1440          10 :                 eocd.m_central_directory_size = 46 + 7; // structure + filename
    1441          20 :                 eocd.write(os);
    1442             :             }
    1443             : 
    1444          20 :             zipios::ZipFile zf("file.zip");
    1445          10 :             REQUIRE_THROWS_AS(zf.getInputStream("invalid"), zipios::FileCollectionException);
    1446          10 :         }
    1447          11 :     }
    1448             : 
    1449             :     /** \TODO
    1450             :      * We need to write a similar test that verifies each and every field
    1451             :      * that proves there is an error and all the fields that do not prove
    1452             :      * anything.
    1453             :      */
    1454          11 :     SECTION("create files with a mismatched compression method")
    1455             :     {
    1456          11 :         for(int i(0); i < 10; ++i)
    1457             :         {
    1458             :             // create an empty header in the file
    1459          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1460             :             {
    1461          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1462             : 
    1463             :                 // create a header (has to be equal to pass to the method check)
    1464          20 :                 local_header_t lh;
    1465          20 :                 central_directory_header_t cdh;
    1466          20 :                 end_of_central_directory_t eocd;
    1467             : 
    1468             :                 // use a valid compression method
    1469          10 :                 lh.m_compression_method = static_cast<uint16_t>(zipios::StorageMethod::STORED);
    1470          10 :                 lh.m_filename = "invalid";
    1471          10 :                 lh.write(os);
    1472             : 
    1473          10 :                 eocd.m_central_directory_offset = os.tellp();
    1474             : 
    1475          10 :                 cdh.m_compression_method = static_cast<uint16_t>(zipios::StorageMethod::DEFLATED);
    1476          10 :                 cdh.m_flags = lh.m_flags;
    1477          10 :                 cdh.m_filename = "invalid";
    1478          10 :                 cdh.write(os);
    1479             : 
    1480          10 :                 eocd.m_file_count = 1;
    1481          10 :                 eocd.m_total_count = 1;
    1482          10 :                 eocd.m_central_directory_size = 46 + 7; // structure + filename
    1483          20 :                 eocd.write(os);
    1484             :             }
    1485             : 
    1486          10 :             REQUIRE_THROWS_AS(zipios::ZipFile zf("file.zip"), zipios::FileCollectionException);
    1487          10 :         }
    1488          11 :     }
    1489             : 
    1490          11 :     SECTION("create files with a trailing data descriptor")
    1491             :     {
    1492          11 :         for(int i(0); i < 10; ++i)
    1493             :         {
    1494             :             // create an empty header in the file
    1495          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1496             :             {
    1497          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1498             : 
    1499             :                 // create a header (has to be equal to pass to the method check)
    1500          20 :                 local_header_t lh;
    1501          20 :                 central_directory_header_t cdh;
    1502          20 :                 end_of_central_directory_t eocd;
    1503             : 
    1504             :                 // use a valid compression method
    1505          10 :                 lh.m_compression_method = static_cast<uint16_t>(g_supported_storage_methods[rand() % (sizeof(g_supported_storage_methods) / sizeof(g_supported_storage_methods[0]))]);
    1506          10 :                 lh.m_filename = "invalid";
    1507          10 :                 lh.write(os);
    1508             : 
    1509          10 :                 eocd.m_central_directory_offset = os.tellp();
    1510             : 
    1511          10 :                 cdh.m_compression_method = lh.m_compression_method;
    1512          10 :                 cdh.m_flags = lh.m_flags;
    1513          10 :                 cdh.m_filename = "invalid";
    1514          10 :                 cdh.write(os);
    1515             : 
    1516          10 :                 eocd.m_file_count = 1;
    1517          10 :                 eocd.m_total_count = 1;
    1518          10 :                 if(i & 1)
    1519             :                 {
    1520           5 :                     eocd.m_central_directory_size = 46 + 7 + rand() % 10 + 1; // structure + filename + erroneous size
    1521             :                 }
    1522             :                 else
    1523             :                 {
    1524           5 :                     eocd.m_central_directory_size = 46 + 7 - rand() % 10 - 1; // structure + filename - erroneous size
    1525             :                 }
    1526          20 :                 eocd.write(os);
    1527             :             }
    1528             : 
    1529          10 :             REQUIRE_THROWS_AS(zipios::ZipFile zf("file.zip"), zipios::FileCollectionException);
    1530          10 :         }
    1531          11 :     }
    1532             : 
    1533             : /** \TODO
    1534             :  * Once clang is fixed, remove those tests. clang does not clear the
    1535             :  * std::unchecked_exception() flag when we have a re-throw in a catch.
    1536             :  * In this case we have a problem with the exception raised in
    1537             :  * InflateInputStreambuf::underflow() when gzip finds an invalid
    1538             :  * input stream.
    1539             :  */
    1540             : #ifndef __clang__
    1541          11 :     SECTION("create files with a compressed file, only save only 50% of the data")
    1542             :     {
    1543          11 :         for(int i(0); i < 10; ++i)
    1544             :         {
    1545             :             // create an empty header in the file
    1546          10 :             zipios_test::auto_unlink_t auto_unlink("file.zip");
    1547          10 :             size_t uncompressed_size(0);
    1548             :             {
    1549          10 :                 std::ofstream os("file.zip", std::ios::out | std::ios::binary);
    1550             : 
    1551             :                 // create a header (has to be equal to pass to the method check)
    1552          20 :                 local_header_t lh;
    1553          20 :                 central_directory_header_t cdh;
    1554          20 :                 end_of_central_directory_t eocd;
    1555             : 
    1556             :                 // create a file in a buffer then compress it
    1557             :                 // make sure the file is large enough to ensure that the
    1558             :                 // decompression fails "as expected" by this test
    1559             :                 typedef std::vector<Bytef> buffer_t;
    1560          20 :                 buffer_t file_buffer;
    1561          10 :                 size_t const file_size(rand() % (80 * 1024) + zipios::getBufferSize() * 3);
    1562      571540 :                 for(size_t pos(0); pos < file_size; ++pos)
    1563             :                 {
    1564      571530 :                     file_buffer.push_back(static_cast<unsigned char>(rand()));
    1565             :                 }
    1566          20 :                 buffer_t compressed_buffer;
    1567          10 :                 uLongf compressed_size(file_size * 2);
    1568          10 :                 compressed_buffer.resize(compressed_size);
    1569          10 :                 compress2(&compressed_buffer[0], &compressed_size, &file_buffer[0], file_size, 9);
    1570          10 :                 compressed_buffer.resize(compressed_size); // the new size!
    1571          10 :                 std::fill(compressed_buffer.begin() + compressed_size / 2, compressed_buffer.end(), 0);
    1572             : 
    1573             :                 // use a valid compression method
    1574          10 :                 lh.m_compression_method = static_cast<uint16_t>(zipios::StorageMethod::DEFLATED);
    1575          10 :                 lh.m_compressed_size = compressed_size - 2;
    1576          10 :                 lh.m_uncompressed_size = file_size;
    1577          10 :                 lh.m_crc32 = crc32(0L, &file_buffer[0], file_size);
    1578          10 :                 lh.m_filename = "invalid";
    1579          10 :                 lh.write(os);
    1580             : 
    1581             :                 // write the first 50% of the compressed data then zeroes
    1582             :                 // make sure to skip the first 2 bytes which are the zlib
    1583             :                 // marker (0x78 0x9C)
    1584          10 :                 os.write(reinterpret_cast<char *>(&compressed_buffer[0]) + 2, (compressed_size - 2));
    1585             : 
    1586          10 :                 eocd.m_central_directory_offset = os.tellp();
    1587             : 
    1588          10 :                 cdh.m_compression_method = lh.m_compression_method;
    1589          10 :                 cdh.m_compressed_size = lh.m_compressed_size;
    1590          10 :                 cdh.m_uncompressed_size = lh.m_uncompressed_size;
    1591          10 :                 cdh.m_crc32 = lh.m_crc32;
    1592          10 :                 cdh.m_flags = lh.m_flags;
    1593          10 :                 cdh.m_filename = "invalid";
    1594          10 :                 cdh.write(os);
    1595             : 
    1596          10 :                 eocd.m_file_count = 1;
    1597          10 :                 eocd.m_total_count = 1;
    1598          10 :                 eocd.m_central_directory_size = 46 + 7; // structure + filename
    1599          10 :                 eocd.write(os);
    1600             : 
    1601             :                 // keep a copy of the uncompressed size to test after the
    1602             :                 // read stops
    1603          20 :                 uncompressed_size = file_size;
    1604             :             }
    1605             : 
    1606          20 :             zipios::ZipFile zf("file.zip");
    1607             :             // we cannot really know when exactly
    1608             :             // we are going to get the throw
    1609          20 :             zipios::ZipFile::stream_pointer_t in(zf.getInputStream("invalid"));
    1610          10 :             size_t amount_read(0);
    1611          56 :             do
    1612             :             {
    1613             :                 char buf[BUFSIZ];
    1614          56 :                 in->read(buf, sizeof(buf));
    1615          56 :                 amount_read += in->gcount();
    1616             :             }
    1617          56 :             while(*in);
    1618          10 :             REQUIRE(in->bad());
    1619          10 :             REQUIRE(in->fail());
    1620          10 :             REQUIRE(amount_read != uncompressed_size);
    1621          10 :         }
    1622          11 :     }
    1623             : #endif
    1624          14 : }
    1625             : 
    1626             : 
    1627             : // Local Variables:
    1628             : // mode: cpp
    1629             : // indent-tabs-mode: nil
    1630             : // c-basic-offset: 4
    1631             : // tab-width: 4
    1632             : // End:
    1633             : 
    1634             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.10