1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/fswatcher/fswatchertest.cpp
3 // Purpose: wxFileSystemWatcher unit test
4 // Author: Bartosz Bekier
7 // Copyright: (c) 2009 Bartosz Bekier
8 ///////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
20 #include "wx/evtloop.h"
21 #include "wx/filename.h"
22 #include "wx/filefn.h"
23 #include "wx/stdpaths.h"
24 #include "wx/fswatcher.h"
28 // ----------------------------------------------------------------------------
30 // ----------------------------------------------------------------------------
32 // class generating file system events
36 static EventGenerator
& Get()
39 ms_instance
= new EventGenerator(GetWatchDir());
44 EventGenerator(const wxFileName
& path
) : m_base(path
)
47 m_file
= RandomName();
54 wxFile
file(m_file
.GetFullPath(), wxFile::write
);
55 return file
.IsOpened() && m_file
.FileExists();
60 CPPUNIT_ASSERT(m_file
.FileExists());
62 wxLogDebug("Renaming %s=>%s", m_file
.GetFullPath(), m_new
.GetFullPath());
64 bool ret
= wxRenameFile(m_file
.GetFullPath(), m_new
.GetFullPath());
77 CPPUNIT_ASSERT(m_file
.FileExists());
79 bool ret
= wxRemoveFile(m_file
.GetFullPath());
92 return m_file
.Touch();
97 wxFile
f(m_file
.GetFullPath());
98 CPPUNIT_ASSERT(f
.IsOpened());
101 ssize_t count
= f
.Read(buf
, sizeof(buf
));
102 CPPUNIT_ASSERT(count
> 0);
109 CPPUNIT_ASSERT(m_file
.FileExists());
111 wxFile
file(m_file
.GetFullPath(), wxFile::write_append
);
112 CPPUNIT_ASSERT(file
.IsOpened());
114 CPPUNIT_ASSERT(file
.Write("Words of Wisdom, Lloyd. Words of wisdom\n"));
119 wxFileName
RandomName(int length
= 10)
121 return RandomName(m_base
, length
);
125 static const wxFileName
& GetWatchDir()
127 static wxFileName dir
;
132 wxString tmp
= wxStandardPaths::Get().GetTempDir();
135 // XXX look for more unique name? there is no function to generate
136 // unique filename, the file always get created...
137 dir
.AppendDir("fswatcher_test");
138 CPPUNIT_ASSERT(!dir
.DirExists());
139 CPPUNIT_ASSERT(dir
.Mkdir());
144 static void RemoveWatchDir()
146 wxFileName dir
= GetWatchDir();
147 CPPUNIT_ASSERT(dir
.DirExists());
149 // just to be really sure we know what we remove
150 CPPUNIT_ASSERT(dir
.GetDirs().Last() == "fswatcher_test");
151 CPPUNIT_ASSERT(dir
.Rmdir(wxPATH_RMDIR_RECURSIVE
));
154 static wxFileName
RandomName(const wxFileName
& base
, int length
= 10)
156 static int ALFA_CNT
= 'z' - 'a';
159 for (int i
= 0 ; i
< length
; ++i
)
161 char c
= 'a' + (rand() % ALFA_CNT
);
165 return wxFileName(base
.GetFullPath(), s
);
169 wxFileName m_base
; // base dir for doing operations
170 wxFileName m_file
; // current file name
171 wxFileName m_old
; // previous file name
172 wxFileName m_new
; // name after renaming
175 static EventGenerator
* ms_instance
;
178 EventGenerator
* EventGenerator::ms_instance
= 0;
181 // custom event handler
182 class EventHandler
: public wxEvtHandler
185 const static int WAIT_DURATION
= 3;
188 eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0)
190 m_loop
= new wxEventLoop();
191 Connect(wxEVT_IDLE
, wxIdleEventHandler(EventHandler::OnIdle
));
192 Connect(wxEVT_FSWATCHER
, wxFileSystemWatcherEventHandler(
193 EventHandler::OnFileSystemEvent
));
196 virtual ~EventHandler()
201 if (m_loop
->IsRunning())
212 // sends idle event, so we get called in a moment
215 wxIdleEvent
* e
= new wxIdleEvent();
225 void OnIdle(wxIdleEvent
& /*evt*/)
227 bool more
= Action();
236 // returns whether we should produce more idle events
237 virtual bool Action()
242 CPPUNIT_ASSERT(Init());
249 CPPUNIT_ASSERT(CheckResult());
253 // TODO a mechanism that will break the loop in case we
254 // don't receive a file system event
255 // this below doesn't quite work, so all tests must pass :-)
260 CPPUNIT_ASSERT(KeepWaiting());
266 CPPUNIT_ASSERT(AfterWait());
269 } // switch (m_count)
276 // test we're good to go
277 CPPUNIT_ASSERT(wxEventLoopBase::GetActive());
279 // XXX only now can we construct Watcher, because we need
281 m_watcher
= new wxFileSystemWatcher();
282 m_watcher
->SetOwner(this);
284 // add dir to be watched
285 wxFileName dir
= EventGenerator::GetWatchDir();
286 CPPUNIT_ASSERT(m_watcher
->Add(dir
, wxFSW_EVENT_ALL
));
291 virtual bool KeepWaiting()
293 // did we receive event already?
296 // well, lets wait a bit more
297 wxSleep(WAIT_DURATION
);
303 virtual bool AfterWait()
305 // fail if still no events
309 s
.Printf("No events from watcher during %d seconds!",
311 CPPUNIT_FAIL((const char*)s
);
317 virtual void OnFileSystemEvent(wxFileSystemWatcherEvent
& evt
)
319 wxLogDebug("--- %s ---", evt
.ToString());
320 m_lastEvent
= wxDynamicCast(evt
.Clone(), wxFileSystemWatcherEvent
);
321 m_events
.Add(m_lastEvent
);
328 virtual bool CheckResult()
330 CPPUNIT_ASSERT_EQUAL( 1, m_events
.size() );
331 const wxFileSystemWatcherEvent
* const e
= m_events
.front();
333 // this is our "reference event"
334 const wxFileSystemWatcherEvent expected
= ExpectedEvent();
336 CPPUNIT_ASSERT_EQUAL( expected
.GetChangeType(), e
->GetChangeType() );
338 CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER
, e
->GetEventType());
340 // XXX this needs change
341 CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN
, e
->GetEventCategory());
343 CPPUNIT_ASSERT_EQUAL(expected
.GetPath(), e
->GetPath());
344 CPPUNIT_ASSERT_EQUAL(expected
.GetNewPath(), e
->GetNewPath());
345 CPPUNIT_ASSERT_EQUAL(expected
.GetChangeType(), e
->GetChangeType());
350 virtual void GenerateEvent() = 0;
352 virtual wxFileSystemWatcherEvent
ExpectedEvent() = 0;
357 wxEventLoopBase
* m_loop
; // loop reference
358 int m_count
; // idle events count
360 wxFileSystemWatcher
* m_watcher
;
361 bool tested
; // indicates, whether we have already passed the test
363 #include "wx/arrimpl.cpp"
364 WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent
*, wxArrayEvent
);
365 wxArrayEvent m_events
;
366 wxFileSystemWatcherEvent
* m_lastEvent
;
370 // ----------------------------------------------------------------------------
372 // ----------------------------------------------------------------------------
374 class FileSystemWatcherTestCase
: public CppUnit::TestCase
377 FileSystemWatcherTestCase() { }
379 virtual void setUp();
380 virtual void tearDown();
383 wxEventLoopBase
* m_loop
;
386 CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase
);
387 CPPUNIT_TEST( TestEventCreate
);
388 CPPUNIT_TEST( TestEventDelete
);
390 // kqueue-based implementation doesn't collapse create/delete pairs in
391 // renames and doesn't detect neither modifications nor access to the
392 // files reliably currently so disable these tests
394 // FIXME: fix the code and reenable them
396 CPPUNIT_TEST( TestEventRename
);
397 CPPUNIT_TEST( TestEventModify
);
399 // MSW implementation doesn't detect file access events currently
401 CPPUNIT_TEST( TestEventAccess
);
403 #endif // !wxHAS_KQUEUE
404 CPPUNIT_TEST_SUITE_END();
406 void TestEventCreate();
407 void TestEventDelete();
408 void TestEventRename();
409 void TestEventModify();
410 void TestEventAccess();
412 DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase
)
415 // register in the unnamed registry so that these tests are run by default
416 CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase
);
418 // also include in it's own registry so that these tests can be run alone
419 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase
,
420 "FileSystemWatcherTestCase" );
422 void FileSystemWatcherTestCase::setUp()
424 wxLog::AddTraceMask(wxTRACE_FSWATCHER
);
425 EventGenerator::Get().GetWatchDir();
428 void FileSystemWatcherTestCase::tearDown()
430 EventGenerator::Get().RemoveWatchDir();
433 // ----------------------------------------------------------------------------
435 // ----------------------------------------------------------------------------
436 void FileSystemWatcherTestCase::TestEventCreate()
438 wxLogDebug("TestEventCreate()");
440 class EventTester
: public EventHandler
443 virtual void GenerateEvent()
445 CPPUNIT_ASSERT(eg
.CreateFile());
448 virtual wxFileSystemWatcherEvent
ExpectedEvent()
450 wxFileSystemWatcherEvent
event(wxFSW_EVENT_CREATE
);
451 event
.SetPath(eg
.m_file
);
452 event
.SetNewPath(eg
.m_file
);
459 wxLogTrace(wxTRACE_FSWATCHER
, "TestEventCreate tester created()");
464 // ----------------------------------------------------------------------------
466 // ----------------------------------------------------------------------------
467 void FileSystemWatcherTestCase::TestEventDelete()
469 wxLogDebug("TestEventDelete()");
471 class EventTester
: public EventHandler
474 virtual void GenerateEvent()
476 CPPUNIT_ASSERT(eg
.DeleteFile());
479 virtual wxFileSystemWatcherEvent
ExpectedEvent()
481 wxFileSystemWatcherEvent
event(wxFSW_EVENT_DELETE
);
482 event
.SetPath(eg
.m_old
);
484 // CHECK maybe new path here could be NULL or sth?
485 event
.SetNewPath(eg
.m_old
);
490 // we need to create a file now, so we can delete it
491 EventGenerator::Get().CreateFile();
497 // ----------------------------------------------------------------------------
499 // ----------------------------------------------------------------------------
500 void FileSystemWatcherTestCase::TestEventRename()
502 wxLogDebug("TestEventRename()");
504 class EventTester
: public EventHandler
507 virtual void GenerateEvent()
509 CPPUNIT_ASSERT(eg
.RenameFile());
512 virtual wxFileSystemWatcherEvent
ExpectedEvent()
514 wxFileSystemWatcherEvent
event(wxFSW_EVENT_RENAME
);
515 event
.SetPath(eg
.m_old
);
516 event
.SetNewPath(eg
.m_file
);
521 // need a file to rename later
522 EventGenerator::Get().CreateFile();
528 // ----------------------------------------------------------------------------
530 // ----------------------------------------------------------------------------
531 void FileSystemWatcherTestCase::TestEventModify()
533 wxLogDebug("TestEventModify()");
535 class EventTester
: public EventHandler
538 virtual void GenerateEvent()
540 CPPUNIT_ASSERT(eg
.ModifyFile());
543 virtual wxFileSystemWatcherEvent
ExpectedEvent()
545 wxFileSystemWatcherEvent
event(wxFSW_EVENT_MODIFY
);
546 event
.SetPath(eg
.m_file
);
547 event
.SetNewPath(eg
.m_file
);
552 // we need to create a file to modify
553 EventGenerator::Get().CreateFile();
559 // ----------------------------------------------------------------------------
561 // ----------------------------------------------------------------------------
562 void FileSystemWatcherTestCase::TestEventAccess()
564 wxLogDebug("TestEventAccess()");
566 class EventTester
: public EventHandler
569 virtual void GenerateEvent()
571 CPPUNIT_ASSERT(eg
.ReadFile());
574 virtual wxFileSystemWatcherEvent
ExpectedEvent()
576 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ACCESS
);
577 event
.SetPath(eg
.m_file
);
578 event
.SetNewPath(eg
.m_file
);
583 // we need to create a file to read from it and write sth to it
584 EventGenerator::Get().CreateFile();
585 EventGenerator::Get().ModifyFile();