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

Generated by: LCOV version 1.14

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