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

Generated by: LCOV version 1.12