LCOV - code coverage report
Current view: top level - tests - catch_string.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 210 221 95.0 %
Date: 2023-06-11 18:21:25 Functions: 4 5 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2019-2023  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/safepasswords
       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             : // self
      20             : //
      21             : #include    "catch_main.h"
      22             : 
      23             : 
      24             : // safepasswords
      25             : //
      26             : #include    <safepasswords/string.h>
      27             : 
      28             : #include    <safepasswords/exception.h>
      29             : 
      30             : 
      31             : // snapdev
      32             : //
      33             : #include    <snapdev/not_used.h>
      34             : 
      35             : 
      36             : // libutf8
      37             : //
      38             : #include    <libutf8/libutf8.h>
      39             : 
      40             : 
      41             : // C++
      42             : //
      43             : #include    <set>
      44             : 
      45             : 
      46             : // C
      47             : //
      48             : #include    <malloc.h>
      49             : 
      50             : 
      51             : // last include
      52             : //
      53             : #include    <snapdev/poison.h>
      54             : 
      55             : 
      56             : 
      57             : namespace safepasswords
      58             : {
      59             : namespace detail
      60             : {
      61             : 
      62             : // for test purposes, we can capture the free() call, ignore otherwise
      63             : typedef void (*free_callback_t)(void * ptr);
      64             : void set_free_callback(free_callback_t callback);
      65             : 
      66             : } // namespace detail
      67             : } // namespace safepasswords
      68             : 
      69             : 
      70             : namespace
      71             : {
      72             : 
      73             : 
      74          16 : bool is_zero(char const * ptr, std::size_t size)
      75             : {
      76         225 :     for(std::size_t idx(0); idx < size; ++idx)
      77             :     {
      78         209 :         if(ptr[idx] != '\0')
      79             :         {
      80           0 :             std::cerr << "--- bad character at " << idx
      81           0 :                 << ": '0x" << std::hex << static_cast<int>(ptr[idx] & 0xFF)
      82           0 :                 << std::dec
      83           0 :                 << "' (ptr: " << static_cast<void const *>(ptr) << ")"
      84           0 :                 << "\n";
      85           0 :             return false;
      86             :         }
      87             :     }
      88             : 
      89          16 :     return true;
      90             : }
      91             : 
      92             : 
      93           0 : void free_callback(void * ptr)
      94             : {
      95           0 :     std::size_t const size(malloc_usable_size(ptr));
      96           0 :     is_zero(static_cast<char const *>(ptr), size);
      97           0 :     ::free(ptr);
      98           0 : }
      99             : 
     100             : 
     101             : }
     102             : // no name namespace
     103             : 
     104             : 
     105             : 
     106          10 : CATCH_TEST_CASE("string", "[string]")
     107             : {
     108          10 :     safepasswords::detail::set_free_callback(free_callback);
     109             : 
     110          10 :     CATCH_START_SECTION("string: verify constructor (empty)")
     111             :     {
     112           1 :         safepasswords::string empty;
     113           1 :         CATCH_REQUIRE(empty.empty());
     114           1 :         CATCH_REQUIRE(empty.length() == 0);
     115           1 :         CATCH_REQUIRE(empty.data() == nullptr);
     116           1 :     }
     117          10 :     CATCH_END_SECTION()
     118             : 
     119          10 :     CATCH_START_SECTION("string: verify constructor (char const *)")
     120             :     {
     121           1 :         safepasswords::string simple("password1");
     122           1 :         CATCH_REQUIRE_FALSE(simple.empty());
     123           1 :         CATCH_REQUIRE(simple.length() == 9);
     124           1 :         char const * ptr(simple.data());
     125           1 :         CATCH_REQUIRE(ptr != nullptr);
     126           1 :         CATCH_REQUIRE(memcmp(ptr, "password1", 9) == 0);
     127           1 :     }
     128          10 :     CATCH_END_SECTION()
     129             : 
     130          10 :     CATCH_START_SECTION("string: verify constructor (char const * + length)")
     131             :     {
     132           1 :         safepasswords::string full("password1-and-length", 13);
     133           1 :         CATCH_REQUIRE_FALSE(full.empty());
     134           1 :         CATCH_REQUIRE(full.length() == 13);
     135           1 :         char const * ptr(full.data());
     136           1 :         CATCH_REQUIRE(ptr != nullptr);
     137           1 :         CATCH_REQUIRE(memcmp(ptr, "password1-and", 13) == 0);
     138           1 :     }
     139          10 :     CATCH_END_SECTION()
     140             : 
     141          10 :     CATCH_START_SECTION("string: verify to_std_string()")
     142             :     {
     143           1 :         char const * secrets[] = {
     144             :             "pwd1",
     145             :             "top-secret",
     146             :             "hidden",
     147             :             "invisible",
     148             :         };
     149           5 :         for(std::size_t idx(0); idx < std::size(secrets); ++idx)
     150             :         {
     151           4 :             std::size_t const len(strlen(secrets[idx]));
     152           4 :             safepasswords::string simple(secrets[idx]);
     153           4 :             CATCH_REQUIRE_FALSE(simple.empty());
     154           4 :             CATCH_REQUIRE(simple.length() == len);
     155           4 :             CATCH_REQUIRE(simple.to_std_string() == secrets[idx]);
     156           4 :             char const * ptr(simple.data());
     157           4 :             CATCH_REQUIRE(ptr != nullptr);
     158           4 :             CATCH_REQUIRE(memcmp(ptr, secrets[idx], len) == 0);
     159           4 :         }
     160             :     }
     161          10 :     CATCH_END_SECTION()
     162             : 
     163          10 :     CATCH_START_SECTION("string: verify clear()")
     164             :     {
     165           1 :         char const * secrets[] = {
     166             :             "clear",
     167             :             "this",
     168             :             "secret",
     169             :             "now",
     170             :         };
     171           5 :         for(std::size_t idx(0); idx < std::size(secrets); ++idx)
     172             :         {
     173           4 :             std::size_t const len(strlen(secrets[idx]));
     174           4 :             safepasswords::string p(secrets[idx]);
     175           4 :             CATCH_REQUIRE_FALSE(p.empty());
     176           4 :             CATCH_REQUIRE(p.length() == len);
     177           4 :             CATCH_REQUIRE(p.to_std_string() == secrets[idx]);
     178           4 :             char const * ptr(p.data());
     179           4 :             CATCH_REQUIRE(ptr != nullptr);
     180           4 :             CATCH_REQUIRE(memcmp(p.data(), secrets[idx], len) == 0);
     181           4 :             p.clear();
     182           4 :             CATCH_REQUIRE(is_zero(ptr, len));
     183           4 :         }
     184             :     }
     185          10 :     CATCH_END_SECTION()
     186             : 
     187          10 :     CATCH_START_SECTION("string: append one character at a time")
     188             :     {
     189           1 :         char const * secrets[] = {
     190             :             "clear",
     191             :             "this",
     192             :             "secret",
     193             :             "now",
     194             :         };
     195           5 :         for(std::size_t idx(0); idx < std::size(secrets); ++idx)
     196             :         {
     197           4 :             std::size_t const len(strlen(secrets[idx]));
     198           4 :             safepasswords::string p;
     199           4 :             CATCH_REQUIRE(p.empty());
     200          22 :             for(std::size_t pos(0); pos < len; ++pos)
     201             :             {
     202          18 :                 p += secrets[idx][pos];
     203             :             }
     204           4 :             CATCH_REQUIRE(p.length() == len);
     205           4 :             CATCH_REQUIRE(p.to_std_string() == secrets[idx]);
     206           4 :             char const * ptr(p.data());
     207           4 :             CATCH_REQUIRE(ptr != nullptr);
     208           4 :             CATCH_REQUIRE(memcmp(p.data(), secrets[idx], len) == 0);
     209           4 :             p.clear();
     210           4 :             CATCH_REQUIRE(is_zero(ptr, len));
     211             : 
     212             :             // try, but this time transform the character in a char32_t
     213             :             //
     214           4 :             p.clear();
     215           4 :             CATCH_REQUIRE(p.empty());
     216          22 :             for(std::size_t pos(0); pos < len; ++pos)
     217             :             {
     218          18 :                 char32_t c(secrets[idx][pos]);
     219          18 :                 p += c;
     220             :             }
     221           4 :             CATCH_REQUIRE(p.length() == len);
     222           4 :             CATCH_REQUIRE(p.to_std_string() == secrets[idx]);
     223           4 :             ptr = p.data();
     224           4 :             CATCH_REQUIRE(ptr != nullptr);
     225           4 :             CATCH_REQUIRE(memcmp(p.data(), secrets[idx], len) == 0);
     226           4 :             p.clear();
     227           4 :             CATCH_REQUIRE(is_zero(ptr, len));
     228           4 :         }
     229             :     }
     230          10 :     CATCH_END_SECTION()
     231             : 
     232          10 :     CATCH_START_SECTION("string: append half a string at a time")
     233             :     {
     234           1 :         char const * secrets[] = {
     235             :             "a longer password is better here",
     236             :             "because we want to add two halves",
     237             :             "and then make sure that it worked as expected",
     238             :             "plus the smallest password should be 8 chars.",
     239             :         };
     240           5 :         for(std::size_t idx(0); idx < std::size(secrets); ++idx)
     241             :         {
     242           4 :             std::size_t const len(strlen(secrets[idx]));
     243           4 :             char const * ptr(nullptr);
     244             :             {
     245           4 :                 safepasswords::string p;
     246           4 :                 CATCH_REQUIRE(p.empty());
     247             : 
     248             :                 // first half
     249             :                 //
     250           4 :                 char buf[256];
     251           4 :                 memcpy(buf, secrets[idx], len / 2);
     252           4 :                 buf[len / 2] = '\0';
     253           4 :                 p += buf;
     254           4 :                 CATCH_REQUIRE(p.length() == len / 2);
     255           4 :                 CATCH_REQUIRE(p.to_std_string() == buf);
     256             : 
     257             :                 // second half
     258             :                 //
     259           4 :                 p += secrets[idx] + len / 2;
     260           4 :                 CATCH_REQUIRE(p.length() == len);
     261           4 :                 CATCH_REQUIRE(p.to_std_string() == secrets[idx]);
     262           4 :                 ptr = p.data();
     263           4 :                 CATCH_REQUIRE(ptr != nullptr);
     264           4 :                 CATCH_REQUIRE(memcmp(p.data(), secrets[idx], len) == 0);
     265             : 
     266             :                 // reset
     267             :                 //
     268           4 :                 p.clear();
     269           4 :                 CATCH_REQUIRE(is_zero(ptr, len));
     270           4 :             }
     271             :         }
     272             :     }
     273          10 :     CATCH_END_SECTION()
     274             : 
     275          10 :     CATCH_START_SECTION("string: append passwords together")
     276             :     {
     277           1 :         char const * secrets[] = {
     278             :             "a longer password is better here",
     279             :             "because we want to add two halves",
     280             :             "and then make sure that it worked as expected",
     281             :             "plus the smallest password should be 8 chars.",
     282             :         };
     283             : 
     284           1 :         std::string concatenation;
     285             : 
     286           1 :         safepasswords::string p;
     287           1 :         CATCH_REQUIRE(p.empty());
     288             : 
     289           5 :         for(std::size_t idx(0); idx < std::size(secrets); ++idx)
     290             :         {
     291           4 :             safepasswords::string end(secrets[idx], strlen(secrets[idx]));
     292           4 :             p += end;
     293           4 :             concatenation += secrets[idx];
     294             : 
     295           4 :             CATCH_REQUIRE(p.to_std_string() == concatenation);
     296           4 :         }
     297           1 :     }
     298          10 :     CATCH_END_SECTION()
     299             : 
     300          10 :     CATCH_START_SECTION("string: append passwords to new ones")
     301             :     {
     302           1 :         char const * secrets[] = {
     303             :             "A longer password is better here",
     304             :             "because we want to add two halves",
     305             :             "and then make sure that it worked as expected",
     306             :             "plus the smallest password should be 8 chars.",
     307             :             "This time we want an even number of passwords",
     308             :             "so we can pair them into one",
     309             :         };
     310           1 :         CATCH_REQUIRE((std::size(secrets) & 1) == 0);
     311             : 
     312           1 :         safepasswords::string p;
     313           1 :         CATCH_REQUIRE(p.empty());
     314             : 
     315           4 :         for(std::size_t idx(0); idx < std::size(secrets); idx += 2)
     316             :         {
     317           3 :             safepasswords::string b(secrets[idx], strlen(secrets[idx]));
     318           3 :             safepasswords::string c(secrets[idx + 1], strlen(secrets[idx + 1]));
     319           3 :             safepasswords::string a;
     320           3 :             a = b + c;
     321             : 
     322           6 :             std::string concatenation(secrets[idx]);
     323           3 :             concatenation += secrets[idx + 1];
     324             : 
     325           3 :             CATCH_REQUIRE(a.to_std_string() == concatenation);
     326           3 :             CATCH_REQUIRE(b.to_std_string() == std::string(secrets[idx]));
     327           3 :             CATCH_REQUIRE(c.to_std_string() == std::string(secrets[idx + 1]));
     328             : 
     329           3 :             safepasswords::string d(b + "string");
     330           3 :             concatenation = secrets[idx];
     331           3 :             concatenation += "string";
     332           3 :             CATCH_REQUIRE(d.to_std_string() == concatenation);
     333             : 
     334           3 :             safepasswords::string e(c + '!');
     335           3 :             concatenation = secrets[idx + 1];
     336           3 :             concatenation += "!";
     337           3 :             CATCH_REQUIRE(e.to_std_string() == concatenation);
     338             : 
     339           3 :             char32_t wc(SNAP_CATCH2_NAMESPACE::random_char(SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_UNICODE));
     340           3 :             safepasswords::string f(b + wc);
     341           3 :             concatenation = secrets[idx];
     342           3 :             concatenation += libutf8::to_u8string(wc);
     343           3 :             CATCH_REQUIRE(f.to_std_string() == concatenation);
     344           3 :         }
     345           1 :     }
     346          10 :     CATCH_END_SECTION()
     347             : 
     348          10 :     CATCH_START_SECTION("string: compare")
     349             :     {
     350           1 :         char const * secrets[] = {
     351             :             "A longer password is better here",
     352             :             "because we want to add two halves",
     353             :             "and then make sure that it worked as expected",
     354             :             "plus the smallest password should be 8 chars.",
     355             :             "This time we want an even number of passwords",
     356             :             "so we can pair them into one",
     357             :         };
     358             : 
     359           6 :         for(std::size_t idx(0); idx < std::size(secrets) - 1; ++idx)
     360             :         {
     361           5 :             safepasswords::string a(secrets[idx], strlen(secrets[idx]));
     362           5 :             safepasswords::string b(secrets[idx + 1], strlen(secrets[idx + 1]));
     363             : 
     364           5 :             auto const r(a <=> b);
     365           5 :             auto const q(std::string(secrets[idx]) <=> std::string(secrets[idx + 1]));
     366             : 
     367           5 :             CATCH_REQUIRE(r == q);
     368           5 :             CATCH_REQUIRE(a != b);
     369             : 
     370           5 :             auto const s(a <=> a);
     371           5 :             CATCH_REQUIRE(s == std::strong_ordering::equal);
     372           5 :             CATCH_REQUIRE(a == a);
     373             : 
     374           5 :             safepasswords::string p;
     375         200 :             for(std::size_t pos(0); pos < a.length() - 1; ++pos)
     376             :             {
     377         195 :                 p += a.data()[pos];
     378             : 
     379         195 :                 auto const t(a <=> p);
     380         195 :                 CATCH_REQUIRE(t == std::strong_ordering::greater);
     381             : 
     382         195 :                 auto const u(p <=> a);
     383         195 :                 CATCH_REQUIRE(u == std::strong_ordering::less);
     384             : 
     385         195 :                 safepasswords::string c(a);
     386         195 :                 c.resize(pos);
     387             : 
     388         195 :                 auto const v(a <=> c);
     389         195 :                 CATCH_REQUIRE(v == std::strong_ordering::greater);
     390             : 
     391         195 :                 auto const w(c <=> a);
     392         195 :                 CATCH_REQUIRE(w == std::strong_ordering::less);
     393         195 :             }
     394           5 :         }
     395             :     }
     396          10 :     CATCH_END_SECTION()
     397          10 : }
     398             : 
     399             : 
     400           1 : CATCH_TEST_CASE("large_string", "[string]")
     401             : {
     402           1 :     safepasswords::detail::set_free_callback(free_callback);
     403             : 
     404           1 :     CATCH_START_SECTION("large_string: create a very large string spanning multiple pages")
     405             :     {
     406           1 :         long const page_size(sysconf(_SC_PAGESIZE));
     407           1 :         std::size_t const max_size(page_size * 5 + 100);
     408           1 :         safepasswords::string large;
     409           1 :         std::string copy;
     410        5226 :         while(large.length() < max_size)
     411             :         {
     412        5225 :             char32_t const wc(SNAP_CATCH2_NAMESPACE::random_char(SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_UNICODE));
     413        5225 :             large += wc;
     414        5225 :             copy += libutf8::to_u8string(wc);
     415             : 
     416        5225 :             CATCH_REQUIRE_FALSE(large.empty());
     417        5225 :             CATCH_REQUIRE(large.to_std_string() == copy);
     418             :         }
     419           1 :     }
     420           1 :     CATCH_END_SECTION()
     421           1 : }
     422             : 
     423             : 
     424           1 : CATCH_TEST_CASE("string_with_errors", "[string][error]")
     425             : {
     426           1 :     CATCH_START_SECTION("string_with_errors: verify invalid unicode character")
     427             :     {
     428           1 :         safepasswords::string p;
     429           1 :         char32_t wc(0x2022);        // valid
     430           1 :         p += wc;
     431           1 :         wc = 0xD999;                // surrogate not valid in char32_t
     432           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     433             :                   p += wc
     434             :                 , safepasswords::invalid_parameter
     435             :                 , Catch::Matchers::ExceptionMessage(
     436             :                           "safepasswords_exception: wc passed to this function does not represent a"
     437             :                           " valid Unicode character."));
     438           1 :     }
     439           1 :     CATCH_END_SECTION()
     440           1 : }
     441             : 
     442             : 
     443             : 
     444             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14