LCOV - code coverage report
Current view: top level - tests - catch_file_changed.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.0 % 96 95
Test Date: 2025-05-30 15:24:13 Functions: 100.0 % 11 11
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2012-2024  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/eventdispatcher
       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              : // eventdispatcher
      25              : //
      26              : #include    <eventdispatcher/communicator.h>
      27              : #include    <eventdispatcher/file_changed.h>
      28              : 
      29              : //#include    <eventdispatcher/local_stream_server_client_message_connection.h>
      30              : //#include    <eventdispatcher/local_stream_server_connection.h>
      31              : //#include    <eventdispatcher/dispatcher.h>
      32              : 
      33              : 
      34              : // cppthread
      35              : //
      36              : #include    <cppthread/runner.h>
      37              : #include    <cppthread/thread.h>
      38              : 
      39              : 
      40              : // C
      41              : //
      42              : #include    <unistd.h>
      43              : 
      44              : 
      45              : // last include
      46              : //
      47              : #include    <snapdev/poison.h>
      48              : 
      49              : 
      50              : 
      51              : namespace
      52              : {
      53              : 
      54              : 
      55              : 
      56              : bool                            g_event_processed = false;
      57              : 
      58              : 
      59              : typedef std::function<void()>   tweak_callback_t;
      60              : 
      61              : 
      62              : class tweak_files
      63              :     : public cppthread::runner
      64              : {
      65              : public:
      66              :     typedef std::shared_ptr<tweak_files>        pointer_t;
      67              : 
      68              :                                 tweak_files(
      69              :                                       std::string const & name
      70              :                                     , tweak_callback_t callback);
      71              : 
      72              :     virtual void                run() override;
      73              : 
      74              : private:
      75              :     tweak_callback_t            f_callback = tweak_callback_t();
      76              : };
      77              : 
      78              : 
      79            2 : tweak_files::tweak_files(
      80              :           std::string const & name
      81            2 :         , tweak_callback_t callback)
      82              :     : runner(name)
      83            2 :     , f_callback(callback)
      84              : {
      85            2 : }
      86              : 
      87              : 
      88            2 : void tweak_files::run()
      89              : {
      90            2 :     f_callback();
      91            2 : }
      92              : 
      93              : 
      94              : 
      95              : 
      96              : 
      97              : 
      98              : class file_listener
      99              :     : public ed::file_changed
     100              : {
     101              : public:
     102              :     typedef std::shared_ptr<file_listener>        pointer_t;
     103              : 
     104              :                                 file_listener();
     105              :     virtual                     ~file_listener();
     106              : 
     107              :     void                        add_expected(
     108              :                                       std::string const & watched_path
     109              :                                     , ed::file_event_mask_t events
     110              :                                     , std::string const & filename);
     111              :     void                        run_test(
     112              :                                       std::string const & name
     113              :                                     , tweak_callback_t callback);
     114              : 
     115              :     // implementation of file_changed
     116              :     //
     117              :     virtual void                process_event(ed::file_event const & watch_event) override;
     118              :     virtual void                process_timeout() override;
     119              : 
     120              : private:
     121              :     std::list<ed::file_event>   f_expected = std::list<ed::file_event>();
     122              :     tweak_files::pointer_t      f_tweak_files = tweak_files::pointer_t();
     123              :     cppthread::thread::pointer_t
     124              :                                 f_thread = cppthread::thread::pointer_t();
     125              : };
     126              : 
     127              : 
     128              : 
     129              : 
     130              : 
     131              : 
     132              : 
     133              : 
     134              : 
     135            2 : file_listener::file_listener()
     136              : {
     137            6 :     set_name("file-listener");
     138              : 
     139            2 :     g_event_processed = false;
     140            2 : }
     141              : 
     142              : 
     143            2 : file_listener::~file_listener()
     144              : {
     145            2 :     g_event_processed = f_expected.empty();
     146            2 : }
     147              : 
     148              : 
     149            9 : void file_listener::process_event(ed::file_event const & watch_event)
     150              : {
     151              :     // if the vector is empty, then we received more events than expected
     152              :     // and there is a bug somewhere (test or implementation)
     153              :     //
     154              :     // Note: this can happen because once the vector is empty we wait
     155              :     //       another second to know whether we're done or not
     156              :     //
     157            9 :     CATCH_REQUIRE_FALSE(f_expected.empty());
     158              : 
     159            9 :     CATCH_REQUIRE(f_expected.front().get_watched_path() == watch_event.get_watched_path());
     160            9 :     CATCH_REQUIRE(f_expected.front().get_events()       == watch_event.get_events());
     161            9 :     CATCH_REQUIRE(f_expected.front().get_filename()     == watch_event.get_filename());
     162              : 
     163            9 :     f_expected.pop_front();
     164              : 
     165            9 :     if(f_expected.empty())
     166              :     {
     167              :         // wait another 3 seconds to make sure that no more events occur
     168              :         // after the last one
     169              :         //
     170            2 :         set_timeout_delay(3'000'000);
     171              :     }
     172            9 : }
     173              : 
     174              : 
     175            2 : void file_listener::process_timeout()
     176              : {
     177            2 :     remove_from_communicator();
     178            2 : }
     179              : 
     180              : 
     181            9 : void file_listener::add_expected(
     182              :       std::string const & watched_path
     183              :     , ed::file_event_mask_t events
     184              :     , std::string const & filename)
     185              : {
     186            9 :     f_expected.push_back(ed::file_event(watched_path, events, filename));
     187            9 : }
     188              : 
     189              : 
     190            2 : void file_listener::run_test(
     191              :       std::string const & name
     192              :     , tweak_callback_t callback)
     193              : {
     194            2 :     f_tweak_files = std::make_shared<tweak_files>(name, callback);
     195            2 :     f_thread = std::make_shared<cppthread::thread>(name, f_tweak_files);
     196            2 :     f_thread->start();
     197            2 : }
     198              : 
     199              : 
     200              : 
     201              : } // no name namespace
     202              : 
     203              : 
     204              : 
     205            2 : CATCH_TEST_CASE("file_changed_events", "[file_changed]")
     206              : {
     207            2 :     CATCH_START_SECTION("file_changed_events: attributes")
     208              :     {
     209            1 :         ed::communicator::pointer_t communicator(ed::communicator::instance());
     210              : 
     211            3 :         std::string const dir(SNAP_CATCH2_NAMESPACE::get_tmp_dir("attributes"));
     212              : 
     213              :         {
     214            1 :             file_listener::pointer_t listener(std::make_shared<file_listener>());
     215            1 :             listener->watch_files(dir, ed::SNAP_FILE_CHANGED_EVENT_ATTRIBUTES);
     216              : 
     217            1 :             listener->add_expected(
     218              :                   dir
     219              :                 , ed::SNAP_FILE_CHANGED_EVENT_ATTRIBUTES
     220              :                 | ed::SNAP_FILE_CHANGED_EVENT_DIRECTORY
     221            1 :                 , std::string());
     222              : 
     223            1 :             communicator->add_connection(listener);
     224              : 
     225            1 :             int r(0);
     226            3 :             listener->run_test("attributes", [dir, &r]() {
     227            1 :                     sleep(rand() % 3);
     228            1 :                     r = chmod(dir.c_str(), 0770);
     229            1 :                 });
     230              : 
     231            1 :             communicator->run();
     232              : 
     233            1 :             CATCH_REQUIRE(r == 0);
     234            1 :         }
     235              : 
     236            1 :         CATCH_REQUIRE(g_event_processed);
     237            1 :     }
     238            2 :     CATCH_END_SECTION()
     239              : 
     240            2 :     CATCH_START_SECTION("file_changed_events: create, write, close file, then open, read, close, finally delete")
     241              :     {
     242            1 :         ed::communicator::pointer_t communicator(ed::communicator::instance());
     243              : 
     244            3 :         std::string const dir(SNAP_CATCH2_NAMESPACE::get_tmp_dir("file-changed"));
     245            1 :         std::string const filename(dir + "/test.txt");
     246              : 
     247              :         {
     248            1 :             file_listener::pointer_t listener(std::make_shared<file_listener>());
     249            1 :             listener->watch_files(dir, ed::SNAP_FILE_CHANGED_EVENT_ALL);
     250              : 
     251              :             // create/write/close events
     252              :             //
     253            3 :             listener->add_expected(
     254              :                   dir
     255              :                 , ed::SNAP_FILE_CHANGED_EVENT_CREATED
     256              :                 , "test.txt");
     257              : 
     258            3 :             listener->add_expected(
     259              :                   dir
     260              :                 , ed::SNAP_FILE_CHANGED_EVENT_ACCESS
     261              :                 , "test.txt");
     262              : 
     263            3 :             listener->add_expected(
     264              :                   dir
     265              :                 , ed::SNAP_FILE_CHANGED_EVENT_WRITE
     266              :                 , "test.txt");
     267              : 
     268            3 :             listener->add_expected(
     269              :                   dir
     270              :                 , ed::SNAP_FILE_CHANGED_EVENT_ACCESS
     271              :                 | ed::SNAP_FILE_CHANGED_EVENT_UPDATED
     272              :                 , "test.txt");
     273              : 
     274              :             // open/read/close events
     275              :             //
     276            3 :             listener->add_expected(
     277              :                   dir
     278              :                 , ed::SNAP_FILE_CHANGED_EVENT_ACCESS
     279              :                 , "test.txt");
     280              : 
     281            3 :             listener->add_expected(
     282              :                   dir
     283              :                 , ed::SNAP_FILE_CHANGED_EVENT_READ
     284              :                 , "test.txt");
     285              : 
     286            3 :             listener->add_expected(
     287              :                   dir
     288              :                 , ed::SNAP_FILE_CHANGED_EVENT_ACCESS
     289              :                 , "test.txt");
     290              : 
     291              :             // delete events
     292              :             //
     293            3 :             listener->add_expected(
     294              :                   dir
     295              :                 , ed::SNAP_FILE_CHANGED_EVENT_DELETED
     296              :                 , "test.txt");
     297              : 
     298            1 :             communicator->add_connection(listener);
     299              : 
     300            1 :             int r(0);
     301            3 :             listener->run_test("file", [filename, &r]() {
     302            3 :                     std::string const message("this is a test file");
     303            1 :                     sleep(rand() % 3);
     304              : 
     305              :                     // create/write/close
     306              :                     {
     307            1 :                         std::ofstream out(filename);
     308            1 :                         out << message << std::endl;
     309            1 :                         if(r == 0)
     310              :                         {
     311            1 :                             r = out.fail() ? 1 : 0;
     312              :                         }
     313            1 :                     }
     314              : 
     315            1 :                     sleep(rand() % 3);
     316              : 
     317              :                     // open/read/close
     318              :                     {
     319            1 :                         std::ifstream in(filename);
     320            1 :                         std::string line;
     321            1 :                         std::getline(in, line, '\n');
     322            1 :                         if(r == 0)
     323              :                         {
     324            1 :                             r = in.fail() || line != message ? 1 : 0;
     325              :                         }
     326            1 :                     }
     327              : 
     328            1 :                     sleep(rand() % 3);
     329              : 
     330              :                     // delete
     331              :                     {
     332            1 :                         if(unlink(filename.c_str()) != 0)
     333              :                         {
     334            0 :                             r = 1;
     335              :                         }
     336              :                     }
     337            2 :                 });
     338              : 
     339            1 :             communicator->run();
     340              : 
     341            1 :             CATCH_REQUIRE(r == 0);
     342            1 :         }
     343              : 
     344            1 :         CATCH_REQUIRE(g_event_processed);
     345            1 :     }
     346            2 :     CATCH_END_SECTION()
     347            2 : }
     348              : 
     349              : 
     350              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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