LCOV - code coverage report
Current view: top level - tests - catch_archiver.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 478 479 99.8 %
Date: 2024-10-28 20:58:35 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/edhttp
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : /** \file
      20             :  * \brief Verify the archiver classes.
      21             :  *
      22             :  * This file implements tests to verify that the archiver classes.
      23             :  */
      24             : 
      25             : // edhttp
      26             : //
      27             : #include    <edhttp/compression/archiver.h>
      28             : 
      29             : #include    <edhttp/exception.h>
      30             : 
      31             : 
      32             : // self
      33             : //
      34             : #include    "catch_main.h"
      35             : 
      36             : 
      37             : // snapdev
      38             : //
      39             : #include    <snapdev/string_replace_many.h>
      40             : 
      41             : 
      42             : // snaplogger
      43             : //
      44             : #include    <snaplogger/message.h>
      45             : 
      46             : 
      47             : // C
      48             : //
      49             : #include    <tar.h>
      50             : 
      51             : 
      52             : // last include
      53             : //
      54             : #include    <snapdev/poison.h>
      55             : 
      56             : 
      57             : 
      58             : namespace
      59             : {
      60             : 
      61             : 
      62             : 
      63          14 : std::uint32_t check_sum(unsigned char const * s)
      64             : {
      65          14 :     std::uint32_t result = 8 * ' '; // the checksum field
      66             : 
      67             :     // name + mode + uid + gid + size + mtime = 148 bytes
      68             :     //
      69        2086 :     for(int n(148); n > 0; --n, ++s)
      70             :     {
      71        2072 :         result += *s;
      72             :     }
      73             : 
      74          14 :     s += 8; // skip the checksum field
      75             : 
      76             :     // everything after the checksum is another 356 bytes
      77             :     //
      78        4998 :     for(int n(356); n > 0; --n, ++s)
      79             :     {
      80        4984 :         result += *s;
      81             :     }
      82             : 
      83          14 :     return result;
      84             : }
      85             : 
      86             : 
      87             : 
      88             : } // no name namespace
      89             : 
      90             : 
      91             : 
      92           2 : CATCH_TEST_CASE("archiver_file", "[archiver]")
      93             : {
      94           2 :     CATCH_START_SECTION("archiver_file: verify archiver file defaults")
      95             :     {
      96           1 :         edhttp::archiver_file file;
      97             : 
      98           1 :         CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
      99           1 :         CATCH_REQUIRE(file.get_data() == edhttp::buffer_t());
     100           1 :         CATCH_REQUIRE(file.get_filename() == std::string());
     101           1 :         CATCH_REQUIRE(file.get_user() == std::string());
     102           1 :         CATCH_REQUIRE(file.get_group() == std::string());
     103           1 :         CATCH_REQUIRE(file.get_uid() == 0);
     104           1 :         CATCH_REQUIRE(file.get_gid() == 0);
     105           1 :         CATCH_REQUIRE(file.get_mode() == 0);
     106           1 :         CATCH_REQUIRE(file.get_mtime() == snapdev::timespec_ex());
     107           1 :     }
     108           2 :     CATCH_END_SECTION()
     109             : 
     110           2 :     CATCH_START_SECTION("archiver_file: verify set/get file metadata")
     111             :     {
     112           1 :         edhttp::archiver_file file;
     113             : 
     114           1 :         CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
     115           1 :         file.set_type(edhttp::file_type_t::FILE_TYPE_DIRECTORY);
     116           1 :         CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_DIRECTORY);
     117           1 :         file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     118           1 :         CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
     119             : 
     120           1 :         CATCH_REQUIRE(file.get_data() == edhttp::buffer_t());
     121           1 :         auto const data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024 * 16));
     122           1 :         file.set_data(data);
     123           1 :         CATCH_REQUIRE(file.get_data() == data);
     124             : 
     125           1 :         CATCH_REQUIRE(file.get_filename() == std::string());
     126           1 :         file.set_filename("/this/file/here");
     127           1 :         CATCH_REQUIRE(file.get_filename() == "/this/file/here");
     128             : 
     129           1 :         CATCH_REQUIRE(file.get_user() == std::string());
     130           1 :         CATCH_REQUIRE(file.get_uid() == 0);
     131           1 :         file.set_user("edhttp", 1'000);
     132           1 :         CATCH_REQUIRE(file.get_user() == "edhttp");
     133           1 :         CATCH_REQUIRE(file.get_uid() == 1'000);
     134             : 
     135           1 :         CATCH_REQUIRE(file.get_group() == std::string());
     136           1 :         CATCH_REQUIRE(file.get_gid() == 0);
     137           1 :         file.set_group("edhttp", 1'230);
     138           1 :         CATCH_REQUIRE(file.get_group() == "edhttp");
     139           1 :         CATCH_REQUIRE(file.get_gid() == 1'230);
     140             : 
     141           1 :         CATCH_REQUIRE(file.get_mode() == 0);
     142           1 :         file.set_mode(0750);
     143           1 :         CATCH_REQUIRE(file.get_mode() == 0750);
     144             : 
     145           1 :         snapdev::timespec_ex const now(snapdev::now());
     146           1 :         CATCH_REQUIRE(file.get_mtime() == snapdev::timespec_ex());
     147           1 :         file.set_mtime(now);
     148           1 :         CATCH_REQUIRE(file.get_mtime() == now);
     149           1 :     }
     150           2 :     CATCH_END_SECTION()
     151           2 : }
     152             : 
     153             : 
     154           3 : CATCH_TEST_CASE("archiver_tar", "[archiver]")
     155             : {
     156           3 :     CATCH_START_SECTION("archiver_tar: verify tar archiver")
     157             :     {
     158           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     159           1 :         CATCH_REQUIRE(tar != nullptr);
     160           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     161             : 
     162           1 :         edhttp::archiver_archive archive;
     163             : 
     164             :         // add files to the tarball using randomly generated buffers
     165             :         // and metadata
     166             :         //
     167           1 :         constexpr std::size_t const FILE_COUNT = 15;
     168           2 :         std::vector<edhttp::buffer_t> file_data(FILE_COUNT);
     169           2 :         std::vector<snapdev::timespec_ex> file_mtime(FILE_COUNT);
     170          16 :         for(std::size_t i(0); i < FILE_COUNT; ++i)
     171             :         {
     172          15 :             file_data[i] = SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024 * 64);
     173          15 :             file_mtime[i] = snapdev::now();
     174             : 
     175          15 :             edhttp::archiver_file file;
     176          15 :             file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     177          15 :             file.set_data(file_data[i]);
     178          15 :             file.set_filename("this/file-" + std::to_string(i + 1));
     179          15 :             file.set_user("user-" + std::to_string(i + 1), 1000 + i);
     180          15 :             file.set_group("group-" + std::to_string(i + 1), 2000 + i);
     181          15 :             file.set_mode(0640);
     182          15 :             file.set_mtime(file_mtime[i]);
     183             : 
     184          15 :             tar->append_file(archive, file);
     185          15 :         }
     186             : 
     187          16 :         for(std::size_t i(0); i < FILE_COUNT; ++i)
     188             :         {
     189          15 :             edhttp::archiver_file file;
     190          15 :             CATCH_REQUIRE(tar->next_file(archive, file));
     191             : 
     192          15 :             CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
     193          15 :             CATCH_REQUIRE(file.get_data() == file_data[i]);
     194          15 :             CATCH_REQUIRE(file.get_filename() == "this/file-" + std::to_string(i + 1));
     195          15 :             CATCH_REQUIRE(file.get_user() == "user-" + std::to_string(i + 1));
     196          15 :             CATCH_REQUIRE(file.get_uid() == 1000 + i);
     197          15 :             CATCH_REQUIRE(file.get_group() == "group-" + std::to_string(i + 1));
     198          15 :             CATCH_REQUIRE(file.get_gid() == 2000 + i);
     199          15 :             CATCH_REQUIRE(file.get_mode() == 0640);
     200          15 :             CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime[i].tv_sec);
     201          15 :             CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
     202          15 :         }
     203             : 
     204             :         {
     205           1 :             edhttp::archiver_file file;
     206           1 :             CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
     207           1 :             CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
     208           1 :             CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
     209           1 :             CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
     210           1 :         }
     211           1 :     }
     212           3 :     CATCH_END_SECTION()
     213             : 
     214           3 :     CATCH_START_SECTION("archiver_tar: verify tar archiver with long filenames")
     215             :     {
     216           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     217           1 :         CATCH_REQUIRE(tar != nullptr);
     218           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     219             : 
     220           1 :         edhttp::archiver_archive archive;
     221             : 
     222             :         // add files to the tarball using randomly generated buffers
     223             :         // and metadata
     224             :         //
     225           1 :         constexpr std::size_t const FILE_COUNT = 15;
     226           1 :         std::size_t file_count(0);
     227           1 :         std::vector<edhttp::buffer_t> file_data;
     228           1 :         std::vector<snapdev::timespec_ex> file_mtime;
     229           1 :         std::vector<std::string> file_filename;
     230          16 :         for(std::size_t i(0); i < FILE_COUNT; ++i)
     231             :         {
     232          15 :             std::string filename;
     233          15 :             std::size_t max_segments(rand() % 10 + 5);
     234          30 :             std::vector<std::string> segments(max_segments);
     235             :             for(;;)
     236             :             {
     237          15 :                 segments.clear();
     238         166 :                 for(std::size_t j(0); j < max_segments; ++j)
     239             :                 {
     240         302 :                     segments.push_back(snapdev::string_replace_many(SNAP_CATCH2_NAMESPACE::random_string(
     241             :                                       1
     242             :                                     , 24
     243             :                                     , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII)
     244             :                                 , {{"/", "-"}}));
     245             :                 }
     246          15 :                 segments[max_segments - 1] += '_';
     247          15 :                 segments[max_segments - 1] += std::to_string(file_count + 1);
     248             : 
     249          15 :                 std::size_t filename_len(0);
     250          15 :                 std::size_t prefix_len(0);
     251         166 :                 for(std::size_t j(max_segments); j > 0; )
     252             :                 {
     253         151 :                     --j;
     254         151 :                     if(prefix_len == 0
     255         151 :                     && filename_len + segments[j].length() <= 100)
     256             :                     {
     257         100 :                         if(filename_len != 0)
     258             :                         {
     259          85 :                             ++filename_len; // for the '/'
     260             :                         }
     261         100 :                         filename_len += segments[j].length();
     262             :                     }
     263             :                     else
     264             :                     {
     265          51 :                         if(prefix_len != 0)
     266             :                         {
     267          38 :                             ++prefix_len; // for the '/'
     268             :                         }
     269          51 :                         prefix_len += segments[j].length();
     270             :                     }
     271             :                 }
     272             : 
     273          15 :                 if(prefix_len <= 155)
     274             :                 {
     275          15 :                     filename = snapdev::join_strings(segments, "/");
     276          15 :                     break;
     277             :                 }
     278             : 
     279             :                 // prefix too long, try again
     280           0 :             }
     281             : 
     282             :             // do we have folders?
     283             :             //
     284          15 :             if(max_segments > 1)
     285             :             {
     286         151 :                 for(std::size_t j(1); j < max_segments; ++j)
     287             :                 {
     288         680 :                     std::string const dir_name(snapdev::join_strings(std::vector<std::string>(segments.begin(), segments.begin() + j), "/"));
     289             : 
     290             :                     // make the data buffer very small because we don't
     291             :                     // need it for directories (it gets ignored)
     292             :                     //
     293         136 :                     file_data.push_back(SNAP_CATCH2_NAMESPACE::random_buffer(0, 1024));
     294         136 :                     file_mtime.push_back(snapdev::now());
     295         136 :                     file_filename.push_back(dir_name);
     296             : 
     297         136 :                     edhttp::archiver_file dir;
     298         136 :                     dir.set_type(edhttp::file_type_t::FILE_TYPE_DIRECTORY);
     299         136 :                     dir.set_data(file_data[file_count]); // this must be ignored for directories
     300         136 :                     dir.set_filename(dir_name);
     301         136 :                     dir.set_user("user-" + std::to_string(file_count + 1), 1000 + file_count);
     302         136 :                     dir.set_group("group-" + std::to_string(file_count + 1), 2000 + file_count);
     303         136 :                     dir.set_mode(0750);
     304         136 :                     dir.set_mtime(file_mtime[file_count]);
     305             : 
     306         136 :                     tar->append_file(archive, dir);
     307         136 :                     ++file_count;
     308         136 :                 }
     309             :             }
     310             : 
     311             :             {
     312          15 :                 file_data.push_back(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024 * 64));
     313          15 :                 file_mtime.push_back(snapdev::now());
     314          15 :                 file_filename.push_back(filename);
     315             : 
     316          15 :                 edhttp::archiver_file file;
     317          15 :                 file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     318          15 :                 file.set_data(file_data[file_count]);
     319          15 :                 file.set_filename(filename);
     320          15 :                 file.set_user("user-" + std::to_string(file_count + 1), 1000 + file_count);
     321          15 :                 file.set_group("group-" + std::to_string(file_count + 1), 2000 + file_count);
     322          15 :                 file.set_mode(0640);
     323          15 :                 file.set_mtime(file_mtime[file_count]);
     324             : 
     325          15 :                 std::size_t const header_pos(archive.get().size());
     326             : 
     327          15 :                 tar->append_file(archive, file);
     328          15 :                 ++file_count;
     329             : 
     330             :                 // once in a while, add a '/' at the end of the prefix
     331             :                 //
     332          15 :                 if(filename.length() > 100 && (rand() & 1) != 0)
     333             :                 {
     334           9 :                     unsigned char * header(archive.get().data() + header_pos);
     335           9 :                     std::size_t const len(strlen(reinterpret_cast<char const *>(header) + 345));
     336           9 :                     if(len < 155)
     337             :                     {
     338           9 :                         header[345 + len] = '/';
     339             : 
     340             :                         // if this happens with a regular file, also test
     341             :                         // with CONTTYPE instead
     342             :                         //
     343           9 :                         if(header[156] == REGTYPE)
     344             :                         {
     345             :                             header[156] == CONTTYPE;
     346             :                         }
     347             : 
     348             :                         // adjust the checksum
     349             :                         //
     350           9 :                         std::uint32_t const checksum(check_sum(header));
     351           9 :                         std::stringstream ss;
     352           9 :                         ss << std::oct
     353             :                             << std::setw(6)
     354           9 :                             << std::setfill('0')
     355           9 :                             << checksum;
     356           9 :                         strncpy(reinterpret_cast<char *>(header) + 148, ss.str().c_str(), 6);
     357           9 :                     }
     358             :                 }
     359          15 :             }
     360          15 :         }
     361             : 
     362             : #if 0
     363             : edhttp::buffer_t data(archive.get());
     364             : std::ofstream out("data.tar");
     365             : out.write(reinterpret_cast<char const *>(data.data()), data.size());
     366             : #endif
     367             : 
     368           3 :         for(int a(0); a < 2; ++a)
     369             :         {
     370         304 :             for(std::size_t i(0); i < file_count; ++i)
     371             :             {
     372         302 :                 edhttp::archiver_file file;
     373         302 :                 CATCH_REQUIRE(tar->next_file(archive, file));
     374             : 
     375         302 :                 if(file.get_type() == edhttp::file_type_t::FILE_TYPE_DIRECTORY)
     376             :                 {
     377         272 :                     CATCH_REQUIRE(file.get_data().size() == 0);
     378         272 :                     CATCH_REQUIRE(file.get_data() == edhttp::buffer_t());
     379         272 :                     CATCH_REQUIRE(file.get_filename() == file_filename[i]);
     380         272 :                     CATCH_REQUIRE(file.get_user() == "user-" + std::to_string(i + 1));
     381         272 :                     CATCH_REQUIRE(file.get_uid() == 1000 + i);
     382         272 :                     CATCH_REQUIRE(file.get_group() == "group-" + std::to_string(i + 1));
     383         272 :                     CATCH_REQUIRE(file.get_gid() == 2000 + i);
     384         272 :                     CATCH_REQUIRE(file.get_mode() == 0750);
     385         272 :                     CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime[i].tv_sec);
     386         272 :                     CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
     387             :                 }
     388             :                 else
     389             :                 {
     390          30 :                     CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
     391          30 :                     CATCH_REQUIRE(file.get_data().size() == file_data[i].size());
     392          30 :                     CATCH_REQUIRE(file.get_data() == file_data[i]);
     393          30 :                     CATCH_REQUIRE(file.get_filename() == file_filename[i]);
     394          30 :                     CATCH_REQUIRE(file.get_user() == "user-" + std::to_string(i + 1));
     395          30 :                     CATCH_REQUIRE(file.get_uid() == 1000 + i);
     396          30 :                     CATCH_REQUIRE(file.get_group() == "group-" + std::to_string(i + 1));
     397          30 :                     CATCH_REQUIRE(file.get_gid() == 2000 + i);
     398          30 :                     CATCH_REQUIRE(file.get_mode() == 0640);
     399          30 :                     CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime[i].tv_sec);
     400          30 :                     CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
     401             :                 }
     402         302 :             }
     403             : 
     404             :             // once we reached the end, we get `false`
     405             :             {
     406           2 :                 edhttp::archiver_file file;
     407           2 :                 CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
     408           2 :             }
     409             : 
     410             :             // append an empty header, which is legal at the end of tar files
     411           4 :             edhttp::buffer_t zeroes(512);
     412           2 :             archive.get().insert(archive.get().end(), zeroes.begin(), zeroes.end());
     413             : 
     414             :             // we can repeat the verification by rewinding
     415             :             //
     416           2 :             tar->rewind(archive);
     417           2 :         }
     418           1 :     }
     419           3 :     CATCH_END_SECTION()
     420             : 
     421           3 :     CATCH_START_SECTION("archiver_tar: verify large checksum")
     422             :     {
     423           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     424           1 :         CATCH_REQUIRE(tar != nullptr);
     425           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     426             : 
     427           1 :         edhttp::archiver_archive archive;
     428             : 
     429             :         // create a filename that will make the checksum go over 32,767
     430             :         //
     431             :         // TODO: see whether a defined UTF-8 character can be used instead
     432             :         //       of 0x10FFFD which is considered valid but is not (yet)
     433             :         //       defined
     434             :         //
     435           1 :         std::string filename;
     436           1 :         std::string const last_utf8{  // 0x10FFFD
     437             :             static_cast<char>(0xF4),
     438             :             static_cast<char>(0x8F),
     439             :             static_cast<char>(0xBF),
     440             :             static_cast<char>(0xBD),
     441           2 :         };
     442          39 :         for(int i(0); i < 155 / 4; ++i)
     443             :         {
     444          38 :             filename += last_utf8;
     445             :         }
     446             :         //filename += std::string(155, '\xFF'); // prefix
     447           1 :         filename += '/';
     448             :         //filename += std::string(100, '\xFF'); // filename
     449          26 :         for(int i(0); i < 100 / 4; ++i)
     450             :         {
     451          25 :             filename += last_utf8;
     452             :         }
     453             : 
     454           1 :         edhttp::buffer_t const data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 25));
     455           1 :         snapdev::timespec_ex const now(snapdev::now());
     456             : 
     457             :         {
     458           1 :             edhttp::archiver_file file;
     459           1 :             file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     460           1 :             file.set_data(data);
     461           1 :             file.set_filename(filename);
     462           1 :             file.set_user("edhttp", 1000);
     463           1 :             file.set_group("edhttp", 1000);
     464           1 :             file.set_mode(0444);
     465           1 :             file.set_mtime(now);
     466           1 :             tar->append_file(archive, file);
     467           1 :         }
     468             : 
     469             :         {
     470           1 :             edhttp::archiver_file file;
     471           1 :             CATCH_REQUIRE(tar->next_file(archive, file));
     472             : 
     473           1 :             CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
     474           1 :             CATCH_REQUIRE(file.get_data().size() == data.size());
     475           1 :             CATCH_REQUIRE(file.get_data() == data);
     476           1 :             CATCH_REQUIRE(file.get_filename() == filename);
     477           1 :             CATCH_REQUIRE(file.get_user() == "edhttp");
     478           1 :             CATCH_REQUIRE(file.get_uid() == 1000);
     479           1 :             CATCH_REQUIRE(file.get_group() == "edhttp");
     480           1 :             CATCH_REQUIRE(file.get_gid() == 1000);
     481           1 :             CATCH_REQUIRE(file.get_mode() == 0444);
     482           1 :             CATCH_REQUIRE(file.get_mtime().tv_sec == now.tv_sec);
     483           1 :             CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
     484           1 :         }
     485             : 
     486             :         {
     487           1 :             edhttp::archiver_file file;
     488           1 :             CATCH_REQUIRE_FALSE(tar->next_file(archive, file));
     489           1 :         }
     490           1 :     }
     491           3 :     CATCH_END_SECTION()
     492           3 : }
     493             : 
     494             : 
     495           2 : CATCH_TEST_CASE("archiver", "[archiver]")
     496             : {
     497           2 :     CATCH_START_SECTION("archiver: verify list of archivers in our library")
     498             :     {
     499           1 :         advgetopt::string_list_t const list(edhttp::archiver_list());
     500             : 
     501           1 :         CATCH_REQUIRE(list.size() == 1);
     502             : 
     503             :         // internally it's in a map so it remains sorted
     504             :         //
     505           1 :         CATCH_REQUIRE(list[0] == "tar");
     506             : 
     507           1 :         auto const input(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024 * 16));
     508           2 :         for(auto const & name : list)
     509             :         {
     510           1 :             edhttp::archiver * a(edhttp::get_archiver(name));
     511           1 :             CATCH_REQUIRE(a != nullptr);
     512             :         }
     513           1 :     }
     514           2 :     CATCH_END_SECTION()
     515             : 
     516           2 :     CATCH_START_SECTION("archiver: verify the \"unknown\" archiver does not exist")
     517             :     {
     518           1 :         edhttp::archiver * unknown(edhttp::get_archiver("unknown"));
     519           1 :         CATCH_REQUIRE(unknown == nullptr);
     520             :     }
     521           2 :     CATCH_END_SECTION()
     522             : 
     523             : //    CATCH_START_SECTION("compressor: compress() with an empty input buffer")
     524             : //    {
     525             : //        // compress() with an empty buffer ignores the other parameters
     526             : //        //
     527             : //        advgetopt::string_list_t const list(edhttp::compressor_list());
     528             : //        for(int i(0); i < 10; ++i)
     529             : //        {
     530             : //            edhttp::buffer_t const buffer;
     531             : //            std::string compressor_name(list[rand() % list.size()]);
     532             : //            edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, rand() % 1 == 0));
     533             : //            CATCH_REQUIRE(compressed.first == buffer);
     534             : //            CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
     535             : //        }
     536             : //    }
     537             : //    CATCH_END_SECTION()
     538             : //
     539             : //    CATCH_START_SECTION("compressor: compress() with too small a level")
     540             : //    {
     541             : //        // compress() with an empty buffer ignores the other parameters
     542             : //        //
     543             : //        advgetopt::string_list_t const list(edhttp::compressor_list());
     544             : //        for(int i(0); i < 10; ++i)
     545             : //        {
     546             : //            edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
     547             : //            std::string compressor_name(list[rand() % list.size()]);
     548             : //            edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 5, rand() % 1 == 0));
     549             : //            CATCH_REQUIRE(compressed.first == buffer);
     550             : //            CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
     551             : //        }
     552             : //    }
     553             : //    CATCH_END_SECTION()
     554             : //
     555             : //    CATCH_START_SECTION("compressor: compress() with unknown compressor")
     556             : //    {
     557             : //        // compress() with an empty buffer ignores the other parameters
     558             : //        //
     559             : //        for(int i(0); i < 10; ++i)
     560             : //        {
     561             : //            edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024 * 16));
     562             : //            std::string compressor_name("unknown");
     563             : //            edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, rand() % 1 == 0));
     564             : //            CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
     565             : //            CATCH_REQUIRE(compressed.first == buffer);
     566             : //        }
     567             : //    }
     568             : //    CATCH_END_SECTION()
     569             : //
     570             : //    CATCH_START_SECTION("compressor: compress() small buffers with bz2 return input")
     571             : //    {
     572             : //        // compress() with an empty buffer ignores the other parameters
     573             : //        //
     574             : //        for(int i(1); i < 10; ++i)
     575             : //        {
     576             : //            edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, i));
     577             : //            std::string compressor_name("bz2");
     578             : //            edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, 100, rand() % 1 == 0));
     579             : //            CATCH_REQUIRE(compressed.first == buffer);
     580             : //            CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
     581             : //        }
     582             : //    }
     583             : //    CATCH_END_SECTION()
     584             : //
     585             : //    CATCH_START_SECTION("compressor: compress() small buffers with gzip return input")
     586             : //    {
     587             : //        // compress() with an empty buffer ignores the other parameters
     588             : //        //
     589             : //        for(int i(1); i < 10; ++i)
     590             : //        {
     591             : //            edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, i));
     592             : //            edhttp::result_t const compressed(edhttp::compress({"gzip"}, buffer, rand() % 96 + 5, rand() % 1 == 0));
     593             : //            CATCH_REQUIRE(compressed.first == buffer);
     594             : //            CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
     595             : //        }
     596             : //    }
     597             : //    CATCH_END_SECTION()
     598             : //
     599             : //    CATCH_START_SECTION("compressor: compress() with deflate; decompress explicitly")
     600             : //    {
     601             : //        // get text because it compresses well and the test will work
     602             : //        //
     603             : //        snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
     604             : //        CATCH_REQUIRE(source.read_all());
     605             : //        std::string const data(source.contents());
     606             : //        edhttp::buffer_t const buffer(data.begin(), data.end());
     607             : //        edhttp::compressor * deflate(edhttp::get_compressor("deflate"));
     608             : //        CATCH_REQUIRE(deflate != nullptr);
     609             : //        CATCH_REQUIRE(strcmp(deflate->get_name(), "deflate") == 0);
     610             : //        for(int i(1); i < 10; ++i)
     611             : //        {
     612             : //            std::string compressor_name("deflate");
     613             : //            edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, true));
     614             : //            CATCH_REQUIRE(compressed.second == "deflate");
     615             : //
     616             : //            // deflate data as no magic, so calling decompress() "fails"
     617             : //            //
     618             : //            edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
     619             : //            CATCH_REQUIRE(decompressed.second == edhttp::compressor::NO_COMPRESSION);
     620             : //            CATCH_REQUIRE(decompressed.first == compressed.first); // still compressed
     621             : //
     622             : //            // instead we have to explicitly decompress
     623             : //            //
     624             : //            edhttp::buffer_t const original(deflate->decompress(compressed.first, buffer.size()));
     625             : //            CATCH_REQUIRE(original == buffer);
     626             : //        }
     627             : //    }
     628             : //    CATCH_END_SECTION()
     629             : //
     630             : //    CATCH_START_SECTION("compressor: compress()/decompress() with bz2")
     631             : //    {
     632             : //        // get text because it compresses well and the test will work
     633             : //        //
     634             : //        snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
     635             : //        CATCH_REQUIRE(source.read_all());
     636             : //        std::string const data(source.contents());
     637             : //        edhttp::buffer_t const buffer(data.begin(), data.end());
     638             : //        edhttp::compressor * bz2(edhttp::get_compressor("bz2"));
     639             : //        CATCH_REQUIRE(bz2 != nullptr);
     640             : //        CATCH_REQUIRE(strcmp(bz2->get_name(), "bz2") == 0);
     641             : //        for(int i(1); i < 10; ++i)
     642             : //        {
     643             : //            std::string compressor_name("bz2");
     644             : //            edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, true));
     645             : //            CATCH_REQUIRE(compressed.second == "bz2");
     646             : //            CATCH_REQUIRE(bz2->compatible(compressed.first));
     647             : //            CATCH_REQUIRE(compressed.first != buffer);
     648             : //            edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
     649             : //            CATCH_REQUIRE(decompressed.second == "bz2");
     650             : //            CATCH_REQUIRE(decompressed.first == buffer);
     651             : //        }
     652             : //    }
     653             : //    CATCH_END_SECTION()
     654             : //
     655             : //    CATCH_START_SECTION("compressor: compress()/decompress() a large buffer with bz2")
     656             : //    {
     657             : //        // get text because it compresses well and the test will work
     658             : //        //
     659             : //        snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
     660             : //        CATCH_REQUIRE(source.read_all());
     661             : //        std::string data(source.contents());
     662             : //        while(data.length() < 1024 * 100 + 1)
     663             : //        {
     664             : //            data += data;
     665             : //        }
     666             : //        edhttp::buffer_t const buffer(data.begin(), data.end());
     667             : //        edhttp::compressor * bz2(edhttp::get_compressor("bz2"));
     668             : //        CATCH_REQUIRE(bz2 != nullptr);
     669             : //        CATCH_REQUIRE(strcmp(bz2->get_name(), "bz2") == 0);
     670             : //        for(int i(1); i < 10; ++i)
     671             : //        {
     672             : //            edhttp::result_t const compressed(edhttp::compress({"bz2"}, buffer, rand() % 96 + 5, true));
     673             : //            CATCH_REQUIRE(compressed.second == "bz2");
     674             : //            CATCH_REQUIRE(bz2->compatible(compressed.first));
     675             : //            CATCH_REQUIRE(compressed.first != buffer);
     676             : //            edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
     677             : //            CATCH_REQUIRE(decompressed.second == "bz2");
     678             : //            CATCH_REQUIRE(decompressed.first == buffer);
     679             : //
     680             : //            // also test with the size
     681             : //            //
     682             : //            edhttp::buffer_t const decompressed2(bz2->decompress(compressed.first, buffer.size()));
     683             : //            CATCH_REQUIRE(decompressed2 == buffer);
     684             : //        }
     685             : //    }
     686             : //    CATCH_END_SECTION()
     687             : //
     688             : //    CATCH_START_SECTION("compressor: compress()/decompress() with gzip")
     689             : //    {
     690             : //        // get text because it compresses well and the test will work
     691             : //        //
     692             : //        snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
     693             : //        CATCH_REQUIRE(source.read_all());
     694             : //        std::string const data(source.contents());
     695             : //        edhttp::buffer_t const buffer(data.begin(), data.end());
     696             : //        edhttp::compressor * gzip(edhttp::get_compressor("gzip"));
     697             : //        CATCH_REQUIRE(gzip != nullptr);
     698             : //        CATCH_REQUIRE(strcmp(gzip->get_name(), "gzip") == 0);
     699             : //        for(int i(1); i < 10; ++i)
     700             : //        {
     701             : //            std::string compressor_name("gzip");
     702             : //            edhttp::result_t const compressed(edhttp::compress({compressor_name}, buffer, rand() % 96 + 5, true));
     703             : //            CATCH_REQUIRE(compressed.second == "gzip");
     704             : //            CATCH_REQUIRE(gzip->compatible(compressed.first));
     705             : //            CATCH_REQUIRE(compressed.first != buffer);
     706             : //            edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
     707             : //            CATCH_REQUIRE(decompressed.second == "gzip");
     708             : //            CATCH_REQUIRE(decompressed.first == buffer);
     709             : //        }
     710             : //    }
     711             : //    CATCH_END_SECTION()
     712             : //
     713             : //    CATCH_START_SECTION("compressor: compress()/decompress() with best compressor")
     714             : //    {
     715             : //        // get text because it compresses well and the test works every time
     716             : //        //
     717             : //        snapdev::file_contents source(SNAP_CATCH2_NAMESPACE::g_source_dir() + "/tests/catch_compressor.cpp");
     718             : //        CATCH_REQUIRE(source.read_all());
     719             : //        std::string const data(source.contents());
     720             : //        edhttp::buffer_t const buffer(data.begin(), data.end());
     721             : //        for(int i(0); i < 10; ++i)
     722             : //        {
     723             : //            advgetopt::string_list_t names;
     724             : //            if((i & 1) == 1)
     725             : //            {
     726             : //                names.push_back("bz2");
     727             : //                names.push_back("gzip");
     728             : //                std::random_shuffle(names.begin(), names.end());
     729             : //            }
     730             : //            edhttp::result_t const compressed(edhttp::compress({}, buffer, rand() % 96 + 5, true));
     731             : //            if(compressed.second == edhttp::compressor::NO_COMPRESSION)
     732             : //            {
     733             : //                // no compression possible
     734             : //                //
     735             : //                CATCH_REQUIRE(compressed.first == buffer);
     736             : //            }
     737             : //            else
     738             : //            {
     739             : //                edhttp::compressor * c(edhttp::get_compressor(compressed.second));
     740             : //                CATCH_REQUIRE(c != nullptr);
     741             : //                CATCH_REQUIRE(c->get_name() == compressed.second);
     742             : //SNAP_LOG_WARNING << "--- c = " << c->get_name() << SNAP_LOG_SEND;
     743             : //                if(compressed.second != "deflate")
     744             : //                {
     745             : //                    CATCH_REQUIRE(c->compatible(compressed.first));
     746             : //                    edhttp::result_t const decompressed(edhttp::decompress(compressed.first));
     747             : //                    CATCH_REQUIRE(decompressed.second == compressed.second);
     748             : //                    CATCH_REQUIRE(decompressed.first == buffer);
     749             : //                }
     750             : //                else
     751             : //                {
     752             : //                    // deflate cannot be accessed from edhttp::decompress() because
     753             : //                    // there is no magic and thus it cannot be a sure thing
     754             : //                    //
     755             : //                    edhttp::buffer_t const decompressed(c->decompress(compressed.first, buffer.size()));
     756             : //                    CATCH_REQUIRE(decompressed == buffer);
     757             : //                }
     758             : //            }
     759             : //        }
     760             : //    }
     761             : //    CATCH_END_SECTION()
     762             : //
     763             : //    CATCH_START_SECTION("compressor: compress() too small a buffer with any compressor")
     764             : //    {
     765             : //        for(int i(0); i < 10; ++i)
     766             : //        {
     767             : //            // generate a very small random buffer to compress so that the
     768             : //            // result is larger than the input and thus "fails" in a slightly
     769             : //            // different path
     770             : //            //
     771             : //            edhttp::buffer_t const buffer(SNAP_CATCH2_NAMESPACE::random_buffer(1, 5));
     772             : //            edhttp::result_t const compressed(edhttp::compress({}, buffer, rand() % 96 + 5, true));
     773             : //            CATCH_REQUIRE(compressed.second == edhttp::compressor::NO_COMPRESSION);
     774             : //            CATCH_REQUIRE(compressed.first == buffer);
     775             : //        }
     776             : //    }
     777             : //    CATCH_END_SECTION()
     778           2 : }
     779             : 
     780             : 
     781           8 : CATCH_TEST_CASE("archiver_error", "[archiver][error]")
     782             : {
     783           8 :     CATCH_START_SECTION("archiver_error: tar filename missing")
     784             :     {
     785           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     786           1 :         CATCH_REQUIRE(tar != nullptr);
     787           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     788             : 
     789           1 :         edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
     790             : 
     791           1 :         edhttp::archiver_file file;
     792           1 :         file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     793           1 :         file.set_data(file_data);
     794             :         // file.set_filename(...); -- this parameter is mandatory
     795           1 :         file.set_user("edhttp", 1000);
     796           1 :         file.set_group("www-data", 128);
     797           1 :         file.set_mode(0644);
     798           1 :         file.set_mtime(snapdev::now());
     799             : 
     800           1 :         edhttp::archiver_archive archive;
     801             : 
     802           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     803             :                   tar->append_file(archive, file)
     804             :                 , edhttp::missing_name
     805             :                 , Catch::Matchers::ExceptionMessage(
     806             :                           "edhttp_exception: a filename is required for an archive file."));
     807           1 :     }
     808           8 :     CATCH_END_SECTION()
     809             : 
     810           8 :     CATCH_START_SECTION("archiver_error: tar filename too long")
     811             :     {
     812           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     813           1 :         CATCH_REQUIRE(tar != nullptr);
     814           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     815             : 
     816             :         // generate a random filename (string)
     817             :         //
     818           1 :         std::string filename(SNAP_CATCH2_NAMESPACE::random_string(
     819             :                   101
     820             :                 , 1024
     821           1 :                 , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
     822           3 :         filename = snapdev::string_replace_many(
     823             :               filename
     824           1 :             , {{"/", "-"}});
     825             : 
     826           1 :         edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
     827             : 
     828           1 :         edhttp::archiver_file file;
     829           1 :         file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     830           1 :         file.set_data(file_data);
     831           1 :         file.set_filename(filename);
     832           1 :         file.set_user("edhttp", 1000);
     833           1 :         file.set_group("www-data", 128);
     834           1 :         file.set_mode(0644);
     835           1 :         file.set_mtime(snapdev::now());
     836             : 
     837           1 :         edhttp::archiver_archive archive;
     838             : 
     839           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     840             :                   tar->append_file(archive, file)
     841             :                 , edhttp::name_too_large
     842             :                 , Catch::Matchers::ExceptionMessage(
     843             :                           "edhttp_exception: this file cannot be added to a tar archive at this point (filename too long)."));
     844           1 :     }
     845           8 :     CATCH_END_SECTION()
     846             : 
     847           8 :     CATCH_START_SECTION("archiver_error: tar prefix too long")
     848             :     {
     849           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     850           1 :         CATCH_REQUIRE(tar != nullptr);
     851           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     852             : 
     853             :         // generate a random filename of the perfect size +1
     854             :         //
     855           1 :         std::string filename(SNAP_CATCH2_NAMESPACE::random_string(
     856             :                   100 + 1 + 155 + 1
     857             :                 , 100 + 1 + 155 + 1
     858           1 :                 , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
     859             : 
     860             :         // remove any slashes
     861             :         //
     862           3 :         filename = snapdev::string_replace_many(
     863             :               filename
     864           1 :             , {{"/", "-"}});
     865             : 
     866             :         // place the slash at the exact right position so the filename
     867             :         // is exactly 100 characters
     868             :         //
     869           1 :         filename[156] = '/';
     870             : 
     871           1 :         edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
     872             : 
     873           1 :         edhttp::archiver_file file;
     874           1 :         file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     875           1 :         file.set_data(file_data);
     876           1 :         file.set_filename(filename);
     877           1 :         file.set_user("edhttp", 1000);
     878           1 :         file.set_group("www-data", 128);
     879           1 :         file.set_mode(0644);
     880           1 :         file.set_mtime(snapdev::now());
     881             : 
     882           1 :         edhttp::archiver_archive archive;
     883             : 
     884           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     885             :                   tar->append_file(archive, file)
     886             :                 , edhttp::name_too_large
     887             :                 , Catch::Matchers::ExceptionMessage(
     888             :                           "edhttp_exception: this prefix + file names cannot be added to a tar archive at this point (filename too long)."));
     889           1 :     }
     890           8 :     CATCH_END_SECTION()
     891             : 
     892           8 :     CATCH_START_SECTION("archiver_error: tar user name too long")
     893             :     {
     894           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     895           1 :         CATCH_REQUIRE(tar != nullptr);
     896           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     897             : 
     898             :         // generate a random user_name (string)
     899             :         //
     900           1 :         std::string const user_name(SNAP_CATCH2_NAMESPACE::random_string(
     901             :                   33
     902             :                 , 100
     903           1 :                 , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
     904             : 
     905           1 :         edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
     906             : 
     907           1 :         edhttp::archiver_file file;
     908           1 :         file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     909           1 :         file.set_data(file_data);
     910           1 :         file.set_filename("long-user-name.pdf");
     911           1 :         file.set_user(user_name, 1000);
     912           1 :         file.set_group("www-data", 128);
     913           1 :         file.set_mode(0644);
     914           1 :         file.set_mtime(snapdev::now());
     915             : 
     916           1 :         edhttp::archiver_archive archive;
     917             : 
     918           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     919             :                   tar->append_file(archive, file)
     920             :                 , edhttp::name_too_large
     921             :                 , Catch::Matchers::ExceptionMessage(
     922             :                           "edhttp_exception: this file cannot be added to a tar archive at this point (user name too long)."));
     923           1 :     }
     924           8 :     CATCH_END_SECTION()
     925             : 
     926           8 :     CATCH_START_SECTION("archiver_error: tar group name too long")
     927             :     {
     928           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     929           1 :         CATCH_REQUIRE(tar != nullptr);
     930           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     931             : 
     932             :         // generate a random group_name (string)
     933             :         //
     934           1 :         std::string const group_name(SNAP_CATCH2_NAMESPACE::random_string(
     935             :                   33
     936             :                 , 100
     937           1 :                 , SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_ASCII));
     938             : 
     939           1 :         edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1, 1024));
     940             : 
     941           1 :         edhttp::archiver_file file;
     942           1 :         file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     943           1 :         file.set_data(file_data);
     944           1 :         file.set_filename("long-user-name.pdf");
     945           1 :         file.set_user("edhttp", 1000);
     946           1 :         file.set_group(group_name, 128);
     947           1 :         file.set_mode(0644);
     948           1 :         file.set_mtime(snapdev::now());
     949             : 
     950           1 :         edhttp::archiver_archive archive;
     951             : 
     952           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     953             :                   tar->append_file(archive, file)
     954             :                 , edhttp::name_too_large
     955             :                 , Catch::Matchers::ExceptionMessage(
     956             :                           "edhttp_exception: this file cannot be added to a tar archive at this point (group name too long)."));
     957           1 :     }
     958           8 :     CATCH_END_SECTION()
     959             : 
     960           8 :     CATCH_START_SECTION("archiver_error: tar file data missing")
     961             :     {
     962           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
     963           1 :         CATCH_REQUIRE(tar != nullptr);
     964           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
     965             : 
     966           1 :         edhttp::archiver_archive archive;
     967             : 
     968             :         // generate a random file
     969             :         //
     970           1 :         edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024));
     971             : 
     972           1 :         snapdev::timespec_ex const file_mtime(snapdev::now());
     973             : 
     974             :         {
     975           1 :             edhttp::archiver_file file;
     976           1 :             file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
     977           1 :             file.set_data(file_data);
     978           1 :             file.set_filename("document.pdf");
     979           1 :             file.set_user("edhttp", 1000);
     980           1 :             file.set_group("edhttp", 1001);
     981           1 :             file.set_mode(0664);
     982           1 :             file.set_mtime(file_mtime);
     983             : 
     984           1 :             tar->append_file(archive, file);
     985           1 :         }
     986             : 
     987             :         // re-reading this one works as expected at first
     988             :         {
     989           1 :             edhttp::archiver_file file;
     990           1 :             CATCH_REQUIRE(tar->next_file(archive, file));
     991             : 
     992           1 :             CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
     993           1 :             CATCH_REQUIRE(file.get_data() == file_data);
     994           1 :             CATCH_REQUIRE(file.get_filename() == "document.pdf");
     995           1 :             CATCH_REQUIRE(file.get_user() == "edhttp");
     996           1 :             CATCH_REQUIRE(file.get_uid() == 1000);
     997           1 :             CATCH_REQUIRE(file.get_group() == "edhttp");
     998           1 :             CATCH_REQUIRE(file.get_gid() == 1001);
     999           1 :             CATCH_REQUIRE(file.get_mode() == 0664);
    1000           1 :             CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime.tv_sec);
    1001           1 :             CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
    1002           1 :         }
    1003             : 
    1004             :         // now try again, but first let's destroy half the file
    1005             :         {
    1006           1 :             tar->rewind(archive);
    1007             : 
    1008           1 :             archive.get().resize(1024); // block is 512, file was 1024, now block is 512 and file is 512
    1009             : 
    1010           1 :             edhttp::archiver_file file;
    1011           1 :             CATCH_REQUIRE_THROWS_MATCHES(
    1012             :                       tar->next_file(archive, file)
    1013             :                     , edhttp::out_of_range
    1014             :                     , Catch::Matchers::ExceptionMessage(
    1015             :                               "out_of_range: file data not available (archive too small)."));
    1016           1 :         }
    1017           1 :     }
    1018           8 :     CATCH_END_SECTION()
    1019             : 
    1020           8 :     CATCH_START_SECTION("archiver_error: tar file invalid checksum & file type")
    1021             :     {
    1022           1 :         char const unsupported_types[] =
    1023             :         {
    1024             :             //REGTYPE,
    1025             :             //AREGTYPE,
    1026             :             LNKTYPE,
    1027             :             SYMTYPE,
    1028             :             CHRTYPE,
    1029             :             BLKTYPE,
    1030             :             //DIRTYPE,
    1031             :             FIFOTYPE,
    1032             :             //CONTTYPE,
    1033             :         };
    1034             : 
    1035           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
    1036           1 :         CATCH_REQUIRE(tar != nullptr);
    1037           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
    1038             : 
    1039           1 :         edhttp::archiver_archive archive;
    1040             : 
    1041             :         // generate a random file
    1042             :         //
    1043           1 :         edhttp::buffer_t file_data(SNAP_CATCH2_NAMESPACE::random_buffer(1024, 1024));
    1044             : 
    1045           1 :         snapdev::timespec_ex const file_mtime(snapdev::now());
    1046             : 
    1047             :         {
    1048           1 :             edhttp::archiver_file file;
    1049           1 :             file.set_type(edhttp::file_type_t::FILE_TYPE_REGULAR);
    1050           1 :             file.set_data(file_data);
    1051           1 :             file.set_filename("document.pdf");
    1052           1 :             file.set_user("edhttp", 1000);
    1053           1 :             file.set_group("edhttp", 1001);
    1054           1 :             file.set_mode(0664);
    1055           1 :             file.set_mtime(file_mtime);
    1056             : 
    1057           1 :             tar->append_file(archive, file);
    1058           1 :         }
    1059             : 
    1060             :         // re-reading this one works as expected at first
    1061             :         {
    1062           1 :             edhttp::archiver_file file;
    1063           1 :             CATCH_REQUIRE(tar->next_file(archive, file));
    1064             : 
    1065           1 :             CATCH_REQUIRE(file.get_type() == edhttp::file_type_t::FILE_TYPE_REGULAR);
    1066           1 :             CATCH_REQUIRE(file.get_data() == file_data);
    1067           1 :             CATCH_REQUIRE(file.get_filename() == "document.pdf");
    1068           1 :             CATCH_REQUIRE(file.get_user() == "edhttp");
    1069           1 :             CATCH_REQUIRE(file.get_uid() == 1000);
    1070           1 :             CATCH_REQUIRE(file.get_group() == "edhttp");
    1071           1 :             CATCH_REQUIRE(file.get_gid() == 1001);
    1072           1 :             CATCH_REQUIRE(file.get_mode() == 0664);
    1073           1 :             CATCH_REQUIRE(file.get_mtime().tv_sec == file_mtime.tv_sec);
    1074           1 :             CATCH_REQUIRE(file.get_mtime().tv_nsec == 0);
    1075           1 :         }
    1076             : 
    1077             :         // now try again, but first let's destroy half the file
    1078           6 :         for(auto const c : unsupported_types)
    1079             :         {
    1080           5 :             tar->rewind(archive);
    1081             : 
    1082           5 :             archive.get()[156] = c;
    1083             : 
    1084           5 :             std::uint32_t const new_checksum(check_sum(archive.get().data()));
    1085             : 
    1086           5 :             char buf[8];
    1087           5 :             memcpy(buf, reinterpret_cast<char *>(archive.get().data()) + 148, 8);
    1088           5 :             long const file_checksum(strtol(buf, nullptr, 8));
    1089             : 
    1090             :             // try "too soon" and the checksum breaks
    1091             :             //
    1092           5 :             edhttp::archiver_file file;
    1093           5 :             CATCH_REQUIRE_THROWS_MATCHES(
    1094             :                       tar->next_file(archive, file)
    1095             :                     , edhttp::invalid_checksum
    1096             :                     , Catch::Matchers::ExceptionMessage(
    1097             :                               "edhttp_exception: ustar checksum code ("
    1098             :                             + std::to_string(new_checksum)
    1099             :                             + ") does not match what was expected ("
    1100             :                             + std::to_string(file_checksum)
    1101             :                             + ")."));
    1102             : 
    1103             :             // fix the checksum
    1104             :             //
    1105           5 :             std::stringstream ss;
    1106           5 :             ss << std::oct
    1107             :                 << std::setw(6)
    1108           5 :                 << std::setfill('0')
    1109           5 :                 << new_checksum;
    1110           5 :             strncpy(reinterpret_cast<char *>(archive.get().data()) + 148, ss.str().c_str(), 6);
    1111             : 
    1112             :             // next attempt bypasses the checksum check, but now fails on
    1113             :             // the file type which we do not support
    1114             :             //
    1115           5 :             CATCH_REQUIRE_THROWS_MATCHES(
    1116             :                       tar->next_file(archive, file)
    1117             :                     , edhttp::incompatible
    1118             :                     , Catch::Matchers::ExceptionMessage(
    1119             :                               "edhttp_exception: file type in tarball not supported (we accept regular and directory files only)."));
    1120           5 :         }
    1121           1 :     }
    1122           8 :     CATCH_END_SECTION()
    1123             : 
    1124           8 :     CATCH_START_SECTION("archiver_error: tar file invalid checksum & file type")
    1125             :     {
    1126           1 :         edhttp::archiver * tar(edhttp::get_archiver("tar"));
    1127           1 :         CATCH_REQUIRE(tar != nullptr);
    1128           1 :         CATCH_REQUIRE(strcmp(tar->get_name(), "tar") == 0);
    1129             : 
    1130           1 :         edhttp::archiver_archive archive;
    1131             : 
    1132             :         // generate a random header block
    1133             :         //
    1134           1 :         edhttp::buffer_t header;
    1135             :         for(;;)
    1136             :         {
    1137           1 :             header = SNAP_CATCH2_NAMESPACE::random_buffer(512, 512);
    1138           1 :             if(header[257] != 'u' || header[258] != 's' || header[259] != 't' || header[260] != 'a'
    1139           1 :             || header[261] != 'r' || (header[262] != ' ' && header[262] != '\0'))
    1140             :             {
    1141           1 :                 break;
    1142             :             }
    1143             :             // try again in the extremely remote chance the randomizer
    1144             :             // generated the "ustar( |\0)" file magic
    1145             :         }
    1146             : 
    1147           1 :         archive.set(header);
    1148           1 :         CATCH_REQUIRE(const_cast<edhttp::archiver_archive const &>(archive).get() == header);
    1149             : 
    1150           1 :         edhttp::archiver_file file;
    1151           1 :         CATCH_REQUIRE_THROWS_MATCHES(
    1152             :                   tar->next_file(archive, file)
    1153             :                 , edhttp::incompatible
    1154             :                 , Catch::Matchers::ExceptionMessage(
    1155             :                           "edhttp_exception: ustar magic code missing at position 0."));
    1156           1 :     }
    1157           8 :     CATCH_END_SECTION()
    1158           8 : }
    1159             : 
    1160             : 
    1161             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

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