LCOV - code coverage report
Current view: top level - tests - catch_unique_number.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 47 47 100.0 %
Date: 2024-01-13 16:46:58 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2024  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/snapdev
       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 unique_number class.
      21             :  *
      22             :  * This file implements tests to verify that the unique_number implementation
      23             :  * works properly even when multiple processes use it simultaneously.
      24             :  */
      25             : 
      26             : // file being tested
      27             : //
      28             : #include    <snapdev/unique_number.h>
      29             : 
      30             : 
      31             : // self
      32             : //
      33             : #include    "catch_main.h"
      34             : 
      35             : 
      36             : // snapdev
      37             : //
      38             : #include    <snapdev/not_used.h>
      39             : 
      40             : 
      41             : 
      42             : // C++
      43             : //
      44             : #include    <random>
      45             : #include    <thread>
      46             : 
      47             : 
      48             : // last include
      49             : //
      50             : #include    <snapdev/poison.h>
      51             : 
      52             : 
      53             : 
      54             : // turn off pedantic for __int128
      55             : //
      56             : #pragma GCC diagnostic ignored "-Wpedantic"
      57             : 
      58             : 
      59             : namespace
      60             : {
      61             : 
      62             : 
      63             : class lockfile_thread
      64             : {
      65             : public:
      66             :     lockfile_thread(
      67             :               std::string const & filename
      68             :             , snapdev::operation_t operation = snapdev::operation_t::OPERATION_EXCLUSIVE)
      69             :         : f_filename(filename)
      70             :         , f_operation(operation)
      71             :     {
      72             :     }
      73             : 
      74             :     lockfile_thread(lockfile_thread const &) = delete;
      75             :     lockfile_thread & operator = (lockfile_thread const &) = delete;
      76             : 
      77             :     ~lockfile_thread()
      78             :     {
      79             :         if(f_thread != nullptr)
      80             :         {
      81             :             f_thread->join();
      82             :             delete f_thread;
      83             :         }
      84             :     }
      85             : 
      86             :     void start_thread(int try_lock = 0)
      87             :     {
      88             :         f_running = true;
      89             :         if(try_lock)
      90             :         {
      91             :             f_thread = new std::thread(&lockfile_thread::run_try_lock, this, try_lock);
      92             :         }
      93             :         else
      94             :         {
      95             :             f_thread = new std::thread(&lockfile_thread::run, this);
      96             :         }
      97             :     }
      98             : 
      99             :     void run()
     100             :     {
     101             :         snapdev::lockfile lock(f_filename, f_operation);
     102             :         CATCH_REQUIRE_FALSE(lock.is_locked());
     103             :         lock.lock();
     104             :         CATCH_REQUIRE(lock.is_locked());
     105             : 
     106             :         std::lock_guard<std::mutex> guard(f_mutex);
     107             :         f_running = false;
     108             :     }
     109             : 
     110             :     void run_try_lock(int try_lock)
     111             :     {
     112             :         snapdev::lockfile lock(f_filename, f_operation);
     113             :         CATCH_REQUIRE_FALSE(lock.is_locked());
     114             :         lock.try_lock();
     115             :         if(try_lock == 1)
     116             :         {
     117             :             CATCH_REQUIRE(lock.is_locked());
     118             :         }
     119             :         else
     120             :         {
     121             :             CATCH_REQUIRE_FALSE(lock.is_locked());
     122             :         }
     123             : 
     124             :         std::lock_guard<std::mutex> guard(f_mutex);
     125             :         f_running = false;
     126             :     }
     127             : 
     128             :     bool is_running() const
     129             :     {
     130             :         std::lock_guard<std::mutex> guard(f_mutex);
     131             :         return f_running;
     132             :     }
     133             : 
     134             : private:
     135             :     mutable std::mutex      f_mutex = std::mutex();
     136             :     std::string             f_filename = std::string();
     137             :     snapdev::operation_t    f_operation = snapdev::operation_t::OPERATION_EXCLUSIVE;
     138             :     std::thread *           f_thread = nullptr;
     139             :     bool                    f_running = false;
     140             : };
     141             : 
     142             : 
     143             : } // no name namespace
     144             : 
     145             : 
     146             : 
     147           2 : CATCH_TEST_CASE("unique_number", "[file]")
     148             : {
     149           2 :     CATCH_START_SECTION("unique_number: verify unique number basic counter")
     150             :     {
     151           1 :         std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     152           1 :         std::string const filename(path + "/test-1.number");
     153             : 
     154           1 :         snapdev::NOT_USED(unlink(filename.c_str()));
     155             : 
     156         101 :         for(unsigned int n(1); n <= 100; ++n)
     157             :         {
     158         100 :             unsigned __int128 const number(snapdev::unique_number(filename));
     159         100 :             CATCH_REQUIRE(n == number);
     160             :         }
     161           1 :     }
     162           2 :     CATCH_END_SECTION()
     163             : 
     164           2 :     CATCH_START_SECTION("unique_number: verify unique number indexes")
     165             :     {
     166           1 :         std::random_device rd;
     167           1 :         std::mt19937 g(rd());
     168             : 
     169           1 :         std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     170           1 :         std::string const filename(path + "/test-2.number");
     171           1 : std::cerr << "--- file: " << filename << "\n";
     172             : 
     173           1 :         snapdev::NOT_USED(unlink(filename.c_str()));
     174             : 
     175             :         // 100 indexes repeated 100 times
     176             :         //
     177             :         // that way we can shuffle all the indexes in order to count them
     178             :         // in different order
     179             :         //
     180           1 :         std::vector<unsigned int> indexes;
     181         101 :         for(unsigned int count(1); count <= 100; ++count)
     182             :         {
     183       10100 :             for(unsigned int idx(0); idx < 100; ++idx)
     184             :             {
     185       10000 :                 indexes.push_back(idx);
     186             :             }
     187             :         }
     188           1 :         std::shuffle(indexes.begin(), indexes.end(), g);
     189             : 
     190           2 :         std::vector<unsigned int> counters(100);
     191       10001 :         for(auto idx : indexes)
     192             :         {
     193       10000 :             unsigned __int128 const number(snapdev::unique_number(filename, idx));
     194       10000 :             ++counters[idx];
     195       10000 :             CATCH_REQUIRE(counters[idx] == number);
     196             :         }
     197           1 :     }
     198           2 :     CATCH_END_SECTION()
     199           2 : }
     200             : 
     201             : 
     202           3 : CATCH_TEST_CASE("unique_number_error", "[file][error]")
     203             : {
     204           3 :     CATCH_START_SECTION("unique_number_error: non-empty filename is required")
     205             :     {
     206           3 :         CATCH_REQUIRE_THROWS_MATCHES(
     207             :                   snapdev::unique_number(std::string())
     208             :                 , snapdev::path_missing
     209             :                 , Catch::Matchers::ExceptionMessage(
     210             :                           "unique_number_error: a counter filename must be specified when calling snapdev::unique_number."));
     211             :     }
     212           3 :     CATCH_END_SECTION()
     213             : 
     214           3 :     CATCH_START_SECTION("unique_number_error: index out of range")
     215             :     {
     216           1 :         std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
     217           1 :         std::string const filename(path + "/test-3.number");
     218             : 
     219         101 :         for(int index(-100); index < 0; ++index)
     220             :         {
     221         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     222             :                       snapdev::unique_number(filename, index)
     223             :                     , snapdev::out_of_range
     224             :                     , Catch::Matchers::ExceptionMessage(
     225             :                               "unique_number_error: counter index in unique_number must be defined between 0 and "
     226             :                             + std::to_string(snapdev::COUNTER_MAXIMUM_INDEX - 1)
     227             :                             + " inclusive."));
     228             :         }
     229             : 
     230         101 :         for(int index(snapdev::COUNTER_MAXIMUM_INDEX); index < snapdev::COUNTER_MAXIMUM_INDEX + 100; ++index)
     231             :         {
     232         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     233             :                       snapdev::unique_number(filename, index)
     234             :                     , snapdev::out_of_range
     235             :                     , Catch::Matchers::ExceptionMessage(
     236             :                               "unique_number_error: counter index in unique_number must be defined between 0 and "
     237             :                             + std::to_string(snapdev::COUNTER_MAXIMUM_INDEX - 1)
     238             :                             + " inclusive."));
     239             :         }
     240           1 :     }
     241           3 :     CATCH_END_SECTION()
     242             : 
     243           3 :     CATCH_START_SECTION("unique_number_error: file cannot be opened")
     244             :     {
     245           5 :         CATCH_REQUIRE_THROWS_MATCHES(
     246             :                   snapdev::unique_number("/this/wont/open/since/it/does/not/exist")
     247             :                 , snapdev::io_error
     248             :                 , Catch::Matchers::ExceptionMessage(
     249             :                           "unique_number_error: could not open unique_number file \"/this/wont/open/since/it/does/not/exist\"."));
     250             :     }
     251           3 :     CATCH_END_SECTION()
     252           3 : }
     253             : 
     254             : 
     255             : 
     256             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

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