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

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions