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_EQUAL( "fswatcher_test", dir
.GetDirs().Last() );
152 // FIXME-VC6: using non-static Rmdir() results in ICE
153 CPPUNIT_ASSERT( wxFileName::Rmdir(dir
.GetFullPath(), wxPATH_RMDIR_RECURSIVE
) );
156 static wxFileName
RandomName(const wxFileName
& base
, int length
= 10)
158 static int ALFA_CNT
= 'z' - 'a';
161 for (int i
= 0 ; i
< length
; ++i
)
163 char c
= 'a' + (rand() % ALFA_CNT
);
167 return wxFileName(base
.GetFullPath(), s
);
171 wxFileName m_base
; // base dir for doing operations
172 wxFileName m_file
; // current file name
173 wxFileName m_old
; // previous file name
174 wxFileName m_new
; // name after renaming
177 static EventGenerator
* ms_instance
;
180 EventGenerator
* EventGenerator::ms_instance
= 0;
183 // custom event handler
184 class EventHandler
: public wxEvtHandler
187 enum { WAIT_DURATION
= 3 };
190 eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0)
192 m_loop
= new wxEventLoop();
193 Connect(wxEVT_IDLE
, wxIdleEventHandler(EventHandler::OnIdle
));
194 Connect(wxEVT_FSWATCHER
, wxFileSystemWatcherEventHandler(
195 EventHandler::OnFileSystemEvent
));
198 virtual ~EventHandler()
203 if (m_loop
->IsRunning())
214 // sends idle event, so we get called in a moment
217 wxIdleEvent
* e
= new wxIdleEvent();
227 void OnIdle(wxIdleEvent
& /*evt*/)
229 bool more
= Action();
238 // returns whether we should produce more idle events
239 virtual bool Action()
244 CPPUNIT_ASSERT(Init());
255 // TODO a mechanism that will break the loop in case we
256 // don't receive a file system event
257 // this below doesn't quite work, so all tests must pass :-)
262 CPPUNIT_ASSERT(KeepWaiting());
268 CPPUNIT_ASSERT(AfterWait());
271 } // switch (m_count)
278 // test we're good to go
279 CPPUNIT_ASSERT(wxEventLoopBase::GetActive());
281 // XXX only now can we construct Watcher, because we need
283 m_watcher
= new wxFileSystemWatcher();
284 m_watcher
->SetOwner(this);
286 // add dir to be watched
287 wxFileName dir
= EventGenerator::GetWatchDir();
288 CPPUNIT_ASSERT(m_watcher
->Add(dir
, wxFSW_EVENT_ALL
));
293 virtual bool KeepWaiting()
295 // did we receive event already?
298 // well, let's wait a bit more
299 wxSleep(WAIT_DURATION
);
305 virtual bool AfterWait()
307 // fail if still no events
310 ("No events during %d seconds!", static_cast<int>(WAIT_DURATION
)),
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 void CheckResult()
330 CPPUNIT_ASSERT_MESSAGE( "No events received", !m_events
.empty() );
332 const wxFileSystemWatcherEvent
* const e
= m_events
.front();
334 WX_ASSERT_EQUAL_MESSAGE
337 "Extra events received, first is of type %x, for path=\"%s\","
338 "last is of type %x, path=\"%s\"",
340 e
->GetPath().GetFullPath(),
341 m_events
.back()->GetChangeType(),
342 m_events
.back()->GetPath().GetFullPath()
347 // this is our "reference event"
348 const wxFileSystemWatcherEvent expected
= ExpectedEvent();
350 CPPUNIT_ASSERT_EQUAL( expected
.GetChangeType(), e
->GetChangeType() );
352 CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER
, e
->GetEventType());
354 // XXX this needs change
355 CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN
, e
->GetEventCategory());
357 CPPUNIT_ASSERT_EQUAL(expected
.GetPath(), e
->GetPath());
358 CPPUNIT_ASSERT_EQUAL(expected
.GetNewPath(), e
->GetNewPath());
361 virtual void GenerateEvent() = 0;
363 virtual wxFileSystemWatcherEvent
ExpectedEvent() = 0;
368 wxEventLoopBase
* m_loop
; // loop reference
369 int m_count
; // idle events count
371 wxFileSystemWatcher
* m_watcher
;
372 bool tested
; // indicates, whether we have already passed the test
374 #include "wx/arrimpl.cpp"
375 WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent
*, wxArrayEvent
);
376 wxArrayEvent m_events
;
377 wxFileSystemWatcherEvent
* m_lastEvent
;
381 // ----------------------------------------------------------------------------
383 // ----------------------------------------------------------------------------
385 class FileSystemWatcherTestCase
: public CppUnit::TestCase
388 FileSystemWatcherTestCase() { }
390 virtual void setUp();
391 virtual void tearDown();
394 wxEventLoopBase
* m_loop
;
397 CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase
);
398 CPPUNIT_TEST( TestEventCreate
);
399 CPPUNIT_TEST( TestEventDelete
);
401 // kqueue-based implementation doesn't collapse create/delete pairs in
402 // renames and doesn't detect neither modifications nor access to the
403 // files reliably currently so disable these tests
405 // FIXME: fix the code and reenable them
407 CPPUNIT_TEST( TestEventRename
);
408 CPPUNIT_TEST( TestEventModify
);
410 // MSW implementation doesn't detect file access events currently
412 CPPUNIT_TEST( TestEventAccess
);
414 #endif // !wxHAS_KQUEUE
416 CPPUNIT_TEST( TestNoEventsAfterRemove
);
417 CPPUNIT_TEST_SUITE_END();
419 void TestEventCreate();
420 void TestEventDelete();
421 void TestEventRename();
422 void TestEventModify();
423 void TestEventAccess();
425 void TestNoEventsAfterRemove();
427 DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase
)
430 // the test currently hangs under OS X for some reason and this prevents tests
431 // ran by buildbot from completing so disable it until someone has time to
434 // FIXME: debug and fix this!
436 // register in the unnamed registry so that these tests are run by default
437 CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase
);
440 // also include in its own registry so that these tests can be run alone
441 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase
,
442 "FileSystemWatcherTestCase" );
444 void FileSystemWatcherTestCase::setUp()
446 wxLog::AddTraceMask(wxTRACE_FSWATCHER
);
447 EventGenerator::Get().GetWatchDir();
450 void FileSystemWatcherTestCase::tearDown()
452 EventGenerator::Get().RemoveWatchDir();
455 // ----------------------------------------------------------------------------
457 // ----------------------------------------------------------------------------
458 void FileSystemWatcherTestCase::TestEventCreate()
460 wxLogDebug("TestEventCreate()");
462 class EventTester
: public EventHandler
465 virtual void GenerateEvent()
467 CPPUNIT_ASSERT(eg
.CreateFile());
470 virtual wxFileSystemWatcherEvent
ExpectedEvent()
472 wxFileSystemWatcherEvent
event(wxFSW_EVENT_CREATE
);
473 event
.SetPath(eg
.m_file
);
474 event
.SetNewPath(eg
.m_file
);
481 wxLogTrace(wxTRACE_FSWATCHER
, "TestEventCreate tester created()");
486 // ----------------------------------------------------------------------------
488 // ----------------------------------------------------------------------------
489 void FileSystemWatcherTestCase::TestEventDelete()
491 wxLogDebug("TestEventDelete()");
493 class EventTester
: public EventHandler
496 virtual void GenerateEvent()
498 CPPUNIT_ASSERT(eg
.DeleteFile());
501 virtual wxFileSystemWatcherEvent
ExpectedEvent()
503 wxFileSystemWatcherEvent
event(wxFSW_EVENT_DELETE
);
504 event
.SetPath(eg
.m_old
);
506 // CHECK maybe new path here could be NULL or sth?
507 event
.SetNewPath(eg
.m_old
);
512 // we need to create a file now, so we can delete it
513 EventGenerator::Get().CreateFile();
519 // ----------------------------------------------------------------------------
521 // ----------------------------------------------------------------------------
522 void FileSystemWatcherTestCase::TestEventRename()
524 wxLogDebug("TestEventRename()");
526 class EventTester
: public EventHandler
529 virtual void GenerateEvent()
531 CPPUNIT_ASSERT(eg
.RenameFile());
534 virtual wxFileSystemWatcherEvent
ExpectedEvent()
536 wxFileSystemWatcherEvent
event(wxFSW_EVENT_RENAME
);
537 event
.SetPath(eg
.m_old
);
538 event
.SetNewPath(eg
.m_file
);
543 // need a file to rename later
544 EventGenerator::Get().CreateFile();
550 // ----------------------------------------------------------------------------
552 // ----------------------------------------------------------------------------
553 void FileSystemWatcherTestCase::TestEventModify()
555 wxLogDebug("TestEventModify()");
557 class EventTester
: public EventHandler
560 virtual void GenerateEvent()
562 CPPUNIT_ASSERT(eg
.ModifyFile());
565 virtual wxFileSystemWatcherEvent
ExpectedEvent()
567 wxFileSystemWatcherEvent
event(wxFSW_EVENT_MODIFY
);
568 event
.SetPath(eg
.m_file
);
569 event
.SetNewPath(eg
.m_file
);
574 // we need to create a file to modify
575 EventGenerator::Get().CreateFile();
581 // ----------------------------------------------------------------------------
583 // ----------------------------------------------------------------------------
584 void FileSystemWatcherTestCase::TestEventAccess()
586 wxLogDebug("TestEventAccess()");
588 class EventTester
: public EventHandler
591 virtual void GenerateEvent()
593 CPPUNIT_ASSERT(eg
.ReadFile());
596 virtual wxFileSystemWatcherEvent
ExpectedEvent()
598 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ACCESS
);
599 event
.SetPath(eg
.m_file
);
600 event
.SetNewPath(eg
.m_file
);
605 // we need to create a file to read from it and write sth to it
606 EventGenerator::Get().CreateFile();
607 EventGenerator::Get().ModifyFile();
613 void FileSystemWatcherTestCase::TestNoEventsAfterRemove()
615 class EventTester
: public EventHandler
,
621 // We need to use an inactivity timer as we never get any file
622 // system events in this test, so we consider that the test is
623 // finished when this 1s timeout expires instead of, as usual,
624 // stopping after getting the file system events.
628 virtual void GenerateEvent()
630 m_watcher
->Remove(EventGenerator::GetWatchDir());
631 CPPUNIT_ASSERT(eg
.CreateFile());
634 virtual void CheckResult()
636 CPPUNIT_ASSERT( m_events
.empty() );
639 virtual wxFileSystemWatcherEvent
ExpectedEvent()
641 CPPUNIT_FAIL( "Shouldn't be called" );
643 return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR
);
646 virtual void Notify()