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 // ----------------------------------------------------------------------------
24 #include "wx/evtloop.h"
25 #include "wx/filename.h"
26 #include "wx/filefn.h"
27 #include "wx/stdpaths.h"
28 #include "wx/fswatcher.h"
32 // ----------------------------------------------------------------------------
34 // ----------------------------------------------------------------------------
36 // class generating file system events
40 static EventGenerator
& Get()
43 ms_instance
= new EventGenerator(GetWatchDir());
48 EventGenerator(const wxFileName
& path
) : m_base(path
)
51 m_file
= RandomName();
58 wxFile
file(m_file
.GetFullPath(), wxFile::write
);
59 return file
.IsOpened() && m_file
.FileExists();
64 CPPUNIT_ASSERT(m_file
.FileExists());
66 wxLogDebug("Renaming %s=>%s", m_file
.GetFullPath(), m_new
.GetFullPath());
68 bool ret
= wxRenameFile(m_file
.GetFullPath(), m_new
.GetFullPath());
81 CPPUNIT_ASSERT(m_file
.FileExists());
83 bool ret
= wxRemoveFile(m_file
.GetFullPath());
96 return m_file
.Touch();
101 wxFile
f(m_file
.GetFullPath());
102 CPPUNIT_ASSERT(f
.IsOpened());
105 ssize_t count
= f
.Read(buf
, sizeof(buf
));
106 CPPUNIT_ASSERT(count
> 0);
113 CPPUNIT_ASSERT(m_file
.FileExists());
115 wxFile
file(m_file
.GetFullPath(), wxFile::write_append
);
116 CPPUNIT_ASSERT(file
.IsOpened());
118 CPPUNIT_ASSERT(file
.Write("Words of Wisdom, Lloyd. Words of wisdom\n"));
123 wxFileName
RandomName(int length
= 10)
125 return RandomName(m_base
, length
);
129 static const wxFileName
& GetWatchDir()
131 static wxFileName dir
;
136 wxString tmp
= wxStandardPaths::Get().GetTempDir();
139 // XXX look for more unique name? there is no function to generate
140 // unique filename, the file always get created...
141 dir
.AppendDir("fswatcher_test");
142 CPPUNIT_ASSERT(!dir
.DirExists());
143 CPPUNIT_ASSERT(dir
.Mkdir());
148 static void RemoveWatchDir()
150 wxFileName dir
= GetWatchDir();
151 CPPUNIT_ASSERT(dir
.DirExists());
153 // just to be really sure we know what we remove
154 CPPUNIT_ASSERT_EQUAL( "fswatcher_test", dir
.GetDirs().Last() );
156 // FIXME-VC6: using non-static Rmdir() results in ICE
157 CPPUNIT_ASSERT( wxFileName::Rmdir(dir
.GetFullPath(), wxPATH_RMDIR_RECURSIVE
) );
160 static wxFileName
RandomName(const wxFileName
& base
, int length
= 10)
162 static int ALFA_CNT
= 'z' - 'a';
165 for (int i
= 0 ; i
< length
; ++i
)
167 char c
= 'a' + (rand() % ALFA_CNT
);
171 return wxFileName(base
.GetFullPath(), s
);
175 wxFileName m_base
; // base dir for doing operations
176 wxFileName m_file
; // current file name
177 wxFileName m_old
; // previous file name
178 wxFileName m_new
; // name after renaming
181 static EventGenerator
* ms_instance
;
184 EventGenerator
* EventGenerator::ms_instance
= 0;
187 // custom event handler
188 class EventHandler
: public wxEvtHandler
191 enum { WAIT_DURATION
= 3 };
193 EventHandler(int types
= wxFSW_EVENT_ALL
) :
194 eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0),
197 m_loop
= new wxEventLoop();
198 Connect(wxEVT_IDLE
, wxIdleEventHandler(EventHandler::OnIdle
));
199 Connect(wxEVT_FSWATCHER
, wxFileSystemWatcherEventHandler(
200 EventHandler::OnFileSystemEvent
));
203 virtual ~EventHandler()
208 if (m_loop
->IsRunning())
219 // sends idle event, so we get called in a moment
222 wxIdleEvent
* e
= new wxIdleEvent();
232 void OnIdle(wxIdleEvent
& /*evt*/)
234 bool more
= Action();
243 // returns whether we should produce more idle events
244 virtual bool Action()
249 CPPUNIT_ASSERT(Init());
260 // TODO a mechanism that will break the loop in case we
261 // don't receive a file system event
262 // this below doesn't quite work, so all tests must pass :-)
267 CPPUNIT_ASSERT(KeepWaiting());
273 CPPUNIT_ASSERT(AfterWait());
276 } // switch (m_count)
283 // test we're good to go
284 CPPUNIT_ASSERT(wxEventLoopBase::GetActive());
286 // XXX only now can we construct Watcher, because we need
288 m_watcher
= new wxFileSystemWatcher();
289 m_watcher
->SetOwner(this);
291 // add dir to be watched
292 wxFileName dir
= EventGenerator::GetWatchDir();
293 CPPUNIT_ASSERT(m_watcher
->Add(dir
, m_eventTypes
));
298 virtual bool KeepWaiting()
300 // did we receive event already?
303 // well, let's wait a bit more
304 wxSleep(WAIT_DURATION
);
310 virtual bool AfterWait()
312 // fail if still no events
315 ("No events during %d seconds!", static_cast<int>(WAIT_DURATION
)),
322 virtual void OnFileSystemEvent(wxFileSystemWatcherEvent
& evt
)
324 wxLogDebug("--- %s ---", evt
.ToString());
325 m_lastEvent
= wxDynamicCast(evt
.Clone(), wxFileSystemWatcherEvent
);
326 m_events
.Add(m_lastEvent
);
333 virtual void CheckResult()
335 CPPUNIT_ASSERT_MESSAGE( "No events received", !m_events
.empty() );
337 const wxFileSystemWatcherEvent
* const e
= m_events
.front();
339 // this is our "reference event"
340 const wxFileSystemWatcherEvent expected
= ExpectedEvent();
342 CPPUNIT_ASSERT_EQUAL( expected
.GetChangeType(), e
->GetChangeType() );
344 CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER
, e
->GetEventType());
346 // XXX this needs change
347 CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN
, e
->GetEventCategory());
349 CPPUNIT_ASSERT_EQUAL(expected
.GetPath(), e
->GetPath());
350 CPPUNIT_ASSERT_EQUAL(expected
.GetNewPath(), e
->GetNewPath());
352 // Under MSW extra modification events are sometimes reported after a
353 // rename and we just can't get rid of them, so ignore them in this
354 // test if they do happen.
355 if ( e
->GetChangeType() == wxFSW_EVENT_RENAME
&&
356 m_events
.size() == 2 )
358 const wxFileSystemWatcherEvent
* const e2
= m_events
.back();
359 if ( e2
->GetChangeType() == wxFSW_EVENT_MODIFY
&&
360 e2
->GetPath() == e
->GetNewPath() )
362 // This is a modify event for the new file, ignore it.
367 WX_ASSERT_EQUAL_MESSAGE
370 "Extra events received, last one is of type %x, path=\"%s\" "
371 "(the original event was for \"%s\" (\"%s\")",
372 m_events
.back()->GetChangeType(),
373 m_events
.back()->GetPath().GetFullPath(),
374 e
->GetPath().GetFullPath(),
375 e
->GetNewPath().GetFullPath()
382 virtual void GenerateEvent() = 0;
384 virtual wxFileSystemWatcherEvent
ExpectedEvent() = 0;
389 wxEventLoopBase
* m_loop
; // loop reference
390 int m_count
; // idle events count
392 wxFileSystemWatcher
* m_watcher
;
393 int m_eventTypes
; // Which event-types to watch. Normally all of them
394 bool tested
; // indicates, whether we have already passed the test
396 #include "wx/arrimpl.cpp"
397 WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent
*, wxArrayEvent
);
398 wxArrayEvent m_events
;
399 wxFileSystemWatcherEvent
* m_lastEvent
;
403 // ----------------------------------------------------------------------------
405 // ----------------------------------------------------------------------------
407 class FileSystemWatcherTestCase
: public CppUnit::TestCase
410 FileSystemWatcherTestCase() { }
412 virtual void setUp();
413 virtual void tearDown();
416 wxEventLoopBase
* m_loop
;
419 CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase
);
420 CPPUNIT_TEST( TestEventCreate
);
421 CPPUNIT_TEST( TestEventDelete
);
422 #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
423 CPPUNIT_TEST( TestTrees
);
426 // kqueue-based implementation doesn't collapse create/delete pairs in
427 // renames and doesn't detect neither modifications nor access to the
428 // files reliably currently so disable these tests
430 // FIXME: fix the code and reenable them
432 CPPUNIT_TEST( TestEventRename
);
433 CPPUNIT_TEST( TestEventModify
);
435 // MSW implementation doesn't detect file access events currently
437 CPPUNIT_TEST( TestEventAccess
);
438 #endif // __WINDOWS__
439 #endif // !wxHAS_KQUEUE
442 CPPUNIT_TEST( TestEventAttribute
);
443 CPPUNIT_TEST( TestSingleWatchtypeEvent
);
444 #endif // wxHAS_INOTIFY
446 CPPUNIT_TEST( TestNoEventsAfterRemove
);
447 CPPUNIT_TEST_SUITE_END();
449 void TestEventCreate();
450 void TestEventDelete();
451 void TestEventRename();
452 void TestEventModify();
453 void TestEventAccess();
455 void TestEventAttribute();
456 void TestSingleWatchtypeEvent();
457 #endif // wxHAS_INOTIFY
458 #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
459 void TestTrees(); // Visual C++ 6 can't build this
461 void TestNoEventsAfterRemove();
463 DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase
)
466 // the test currently hangs under OS X for some reason and this prevents tests
467 // ran by buildbot from completing so disable it until someone has time to
470 // FIXME: debug and fix this!
472 // register in the unnamed registry so that these tests are run by default
473 CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase
);
476 // also include in its own registry so that these tests can be run alone
477 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase
,
478 "FileSystemWatcherTestCase" );
480 void FileSystemWatcherTestCase::setUp()
482 wxLog::AddTraceMask(wxTRACE_FSWATCHER
);
483 EventGenerator::Get().GetWatchDir();
486 void FileSystemWatcherTestCase::tearDown()
488 EventGenerator::Get().RemoveWatchDir();
491 // ----------------------------------------------------------------------------
493 // ----------------------------------------------------------------------------
494 void FileSystemWatcherTestCase::TestEventCreate()
496 wxLogDebug("TestEventCreate()");
498 class EventTester
: public EventHandler
501 virtual void GenerateEvent()
503 CPPUNIT_ASSERT(eg
.CreateFile());
506 virtual wxFileSystemWatcherEvent
ExpectedEvent()
508 wxFileSystemWatcherEvent
event(wxFSW_EVENT_CREATE
);
509 event
.SetPath(eg
.m_file
);
510 event
.SetNewPath(eg
.m_file
);
517 wxLogTrace(wxTRACE_FSWATCHER
, "TestEventCreate tester created()");
522 // ----------------------------------------------------------------------------
524 // ----------------------------------------------------------------------------
525 void FileSystemWatcherTestCase::TestEventDelete()
527 wxLogDebug("TestEventDelete()");
529 class EventTester
: public EventHandler
532 virtual void GenerateEvent()
534 CPPUNIT_ASSERT(eg
.DeleteFile());
537 virtual wxFileSystemWatcherEvent
ExpectedEvent()
539 wxFileSystemWatcherEvent
event(wxFSW_EVENT_DELETE
);
540 event
.SetPath(eg
.m_old
);
542 // CHECK maybe new path here could be NULL or sth?
543 event
.SetNewPath(eg
.m_old
);
548 // we need to create a file now, so we can delete it
549 EventGenerator::Get().CreateFile();
555 // ----------------------------------------------------------------------------
557 // ----------------------------------------------------------------------------
558 void FileSystemWatcherTestCase::TestEventRename()
560 wxLogDebug("TestEventRename()");
562 class EventTester
: public EventHandler
565 virtual void GenerateEvent()
567 CPPUNIT_ASSERT(eg
.RenameFile());
570 virtual wxFileSystemWatcherEvent
ExpectedEvent()
572 wxFileSystemWatcherEvent
event(wxFSW_EVENT_RENAME
);
573 event
.SetPath(eg
.m_old
);
574 event
.SetNewPath(eg
.m_file
);
579 // need a file to rename later
580 EventGenerator::Get().CreateFile();
586 // ----------------------------------------------------------------------------
588 // ----------------------------------------------------------------------------
589 void FileSystemWatcherTestCase::TestEventModify()
591 wxLogDebug("TestEventModify()");
593 class EventTester
: public EventHandler
596 virtual void GenerateEvent()
598 CPPUNIT_ASSERT(eg
.ModifyFile());
601 virtual wxFileSystemWatcherEvent
ExpectedEvent()
603 wxFileSystemWatcherEvent
event(wxFSW_EVENT_MODIFY
);
604 event
.SetPath(eg
.m_file
);
605 event
.SetNewPath(eg
.m_file
);
610 // we need to create a file to modify
611 EventGenerator::Get().CreateFile();
617 // ----------------------------------------------------------------------------
619 // ----------------------------------------------------------------------------
620 void FileSystemWatcherTestCase::TestEventAccess()
622 wxLogDebug("TestEventAccess()");
624 class EventTester
: public EventHandler
627 virtual void GenerateEvent()
629 CPPUNIT_ASSERT(eg
.ReadFile());
632 virtual wxFileSystemWatcherEvent
ExpectedEvent()
634 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ACCESS
);
635 event
.SetPath(eg
.m_file
);
636 event
.SetNewPath(eg
.m_file
);
641 // we need to create a file to read from it and write sth to it
642 EventGenerator::Get().CreateFile();
643 EventGenerator::Get().ModifyFile();
650 // ----------------------------------------------------------------------------
651 // TestEventAttribute
652 // ----------------------------------------------------------------------------
653 void FileSystemWatcherTestCase::TestEventAttribute()
655 wxLogDebug("TestEventAttribute()");
657 class EventTester
: public EventHandler
660 virtual void GenerateEvent()
662 CPPUNIT_ASSERT(eg
.TouchFile());
665 virtual wxFileSystemWatcherEvent
ExpectedEvent()
667 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ATTRIB
);
668 event
.SetPath(eg
.m_file
);
669 event
.SetNewPath(eg
.m_file
);
674 // we need to create a file to touch
675 EventGenerator::Get().CreateFile();
681 // ----------------------------------------------------------------------------
682 // TestSingleWatchtypeEvent: Watch only wxFSW_EVENT_ACCESS
683 // ----------------------------------------------------------------------------
684 void FileSystemWatcherTestCase::TestSingleWatchtypeEvent()
686 wxLogDebug("TestSingleWatchtypeEvent()");
688 class EventTester
: public EventHandler
691 // We could pass wxFSW_EVENT_CREATE or MODIFY instead, but not RENAME or
692 // DELETE as the event path fields would be wrong in CheckResult()
693 EventTester() : EventHandler(wxFSW_EVENT_ACCESS
) {}
695 virtual void GenerateEvent()
697 // As wxFSW_EVENT_ACCESS is passed to the ctor only ReadFile() will
698 // generate an event. Without it they all will, and the test fails
699 CPPUNIT_ASSERT(eg
.CreateFile());
700 CPPUNIT_ASSERT(eg
.ModifyFile());
701 CPPUNIT_ASSERT(eg
.ReadFile());
704 virtual wxFileSystemWatcherEvent
ExpectedEvent()
706 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ACCESS
);
707 event
.SetPath(eg
.m_file
);
708 event
.SetNewPath(eg
.m_file
);
716 #endif // wxHAS_INOTIFY
718 // ----------------------------------------------------------------------------
720 // ----------------------------------------------------------------------------
722 #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
723 void FileSystemWatcherTestCase::TestTrees()
725 class TreeTester
: public EventHandler
727 const size_t subdirs
;
731 TreeTester() : subdirs(5), files(3) {}
733 void GrowTree(wxFileName dir
735 , bool withSymlinks
= false
739 CPPUNIT_ASSERT(dir
.Mkdir());
740 // Now add a subdir with an easy name to remember in WatchTree()
741 dir
.AppendDir("child");
742 CPPUNIT_ASSERT(dir
.Mkdir());
743 wxFileName
child(dir
); // Create a copy to which to symlink
745 // Create a branch of 5 numbered subdirs, each containing 3
747 for ( unsigned d
= 0; d
< subdirs
; ++d
)
749 dir
.AppendDir(wxString::Format("subdir%u", d
+1));
750 CPPUNIT_ASSERT(dir
.Mkdir());
752 const wxString prefix
= dir
.GetPathWithSep();
753 const wxString ext
[] = { ".txt", ".log", "" };
754 for ( unsigned f
= 0; f
< files
; ++f
)
756 // Just create the files.
757 wxFile(prefix
+ wxString::Format("file%u", f
+1) + ext
[f
],
760 #if defined(__UNIX__)
763 // Create a symlink to a files, and another to 'child'
764 CPPUNIT_ASSERT_EQUAL(0,
765 symlink(wxString(prefix
+ "file1").c_str(),
766 wxString(prefix
+ "file.lnk").c_str()));
767 CPPUNIT_ASSERT_EQUAL(0,
768 symlink(child
.GetFullPath().c_str(),
769 wxString(prefix
+ "dir.lnk").c_str()));
775 void RmDir(wxFileName dir
)
777 CPPUNIT_ASSERT(dir
.DirExists());
779 CPPUNIT_ASSERT(dir
.Rmdir(wxPATH_RMDIR_RECURSIVE
));
782 void WatchDir(wxFileName dir
)
784 CPPUNIT_ASSERT(m_watcher
);
786 // Store the initial count; there may already be some watches
787 const int initial
= m_watcher
->GetWatchedPathsCount();
790 CPPUNIT_ASSERT_EQUAL(initial
+ 1,
791 m_watcher
->GetWatchedPathsCount());
794 void RemoveSingleWatch(wxFileName dir
)
796 CPPUNIT_ASSERT(m_watcher
);
798 const int initial
= m_watcher
->GetWatchedPathsCount();
800 m_watcher
->Remove(dir
);
801 CPPUNIT_ASSERT_EQUAL(initial
- 1,
802 m_watcher
->GetWatchedPathsCount());
805 void WatchTree(const wxFileName
& dir
)
807 CPPUNIT_ASSERT(m_watcher
);
809 size_t treeitems
= 1; // the trunk
811 // When there's no file mask, wxMSW sets a single watch
812 // on the trunk which is implemented recursively.
813 // wxGTK always sets an additional watch for each subdir
814 treeitems
+= subdirs
+ 1; // +1 for 'child'
815 #endif // __WINDOWS__
817 // Store the initial count; there may already be some watches
818 const int initial
= m_watcher
->GetWatchedPathsCount();
822 m_watcher
->AddTree(dir
);
823 const int plustree
= m_watcher
->GetWatchedPathsCount();
825 CPPUNIT_ASSERT_EQUAL(initial
+ treeitems
, plustree
);
827 m_watcher
->RemoveTree(dir
);
828 CPPUNIT_ASSERT_EQUAL(initial
, m_watcher
->GetWatchedPathsCount());
830 // Now test the refcount mechanism by watching items more than once
831 wxFileName
child(dir
);
832 child
.AppendDir("child");
833 m_watcher
->AddTree(child
);
834 // Check some watches were added; we don't care about the number
835 CPPUNIT_ASSERT(initial
< m_watcher
->GetWatchedPathsCount());
836 // Now watch the whole tree and check that the count is the same
837 // as it was the first time, despite also adding 'child' separately
838 // Except that in wxMSW this isn't true: each watch will be a
839 // single, recursive dir; so fudge the count
843 #endif // __WINDOWS__
844 m_watcher
->AddTree(dir
);
845 CPPUNIT_ASSERT_EQUAL(plustree
+ fudge
, m_watcher
->GetWatchedPathsCount());
846 m_watcher
->RemoveTree(child
);
847 CPPUNIT_ASSERT(initial
< m_watcher
->GetWatchedPathsCount());
848 m_watcher
->RemoveTree(dir
);
849 CPPUNIT_ASSERT_EQUAL(initial
, m_watcher
->GetWatchedPathsCount());
850 #if defined(__UNIX__)
851 // Finally, test a tree containing internal symlinks
853 GrowTree(dir
, true /* test symlinks */);
855 // Without the DontFollowLink() call AddTree() would now assert
856 // (and without the assert, it would infinitely loop)
859 CPPUNIT_ASSERT(m_watcher
->AddTree(fn
));
860 CPPUNIT_ASSERT(m_watcher
->RemoveTree(fn
));
862 // Regrow the tree without symlinks, ready for the next test
864 GrowTree(dir
, false);
868 void WatchTreeWithFilespec(const wxFileName
& dir
)
870 CPPUNIT_ASSERT(m_watcher
);
871 CPPUNIT_ASSERT(dir
.DirExists()); // Was built in WatchTree()
873 // Store the initial count; there may already be some watches
874 const int initial
= m_watcher
->GetWatchedPathsCount();
876 // When we use a filter, both wxMSW and wxGTK implementations set
877 // an additional watch for each subdir (+1 for the root dir itself
878 // and another +1 for "child").
879 const size_t treeitems
= subdirs
+ 2;
880 m_watcher
->AddTree(dir
, wxFSW_EVENT_ALL
, "*.txt");
882 const int plustree
= m_watcher
->GetWatchedPathsCount();
883 CPPUNIT_ASSERT_EQUAL(initial
+ treeitems
, plustree
);
885 // RemoveTree should try to remove only those files that were added
886 m_watcher
->RemoveTree(dir
);
887 CPPUNIT_ASSERT_EQUAL(initial
, m_watcher
->GetWatchedPathsCount());
890 void RemoveAllWatches()
892 CPPUNIT_ASSERT(m_watcher
);
894 m_watcher
->RemoveAll();
895 CPPUNIT_ASSERT_EQUAL(0, m_watcher
->GetWatchedPathsCount());
898 virtual void GenerateEvent()
900 // We don't use this function for events. Just run the tests
902 wxFileName watchdir
= EventGenerator::GetWatchDir();
903 CPPUNIT_ASSERT(watchdir
.DirExists());
905 wxFileName
treedir(watchdir
);
906 treedir
.AppendDir("treetrunk");
907 CPPUNIT_ASSERT(!treedir
.DirExists());
909 wxFileName
singledir(watchdir
);
910 singledir
.AppendDir("single");
911 CPPUNIT_ASSERT(!singledir
.DirExists());
912 CPPUNIT_ASSERT(singledir
.Mkdir());
916 // Now test adding and removing a tree using a filespec
917 // wxMSW uses the generic method to add matching files; which fails
918 // as it doesn't support adding files :/ So disable the test
920 WatchTreeWithFilespec(treedir
);
921 #endif // __WINDOWS__
923 RemoveSingleWatch(singledir
);
924 // Add it back again, ready to test RemoveAll()
936 virtual wxFileSystemWatcherEvent
ExpectedEvent()
938 CPPUNIT_FAIL("Shouldn't be called");
940 return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR
);
943 virtual void CheckResult()
945 // Do nothing. We override this to prevent receiving events in
953 #endif // !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
959 // We can't define this class locally inside TestNoEventsAfterRemove() for some
960 // reason with g++ 4.0 under OS X 10.5, it results in the following mysterious
963 // /var/tmp//ccTkNCkc.s:unknown:Non-global symbol:
964 // __ZThn80_ZN25FileSystemWatcherTestCase23TestNoEventsAfterRemoveEvEN11EventTester6NotifyEv.eh
965 // can't be a weak_definition
967 // So define this class outside the function instead.
968 class NoEventsAfterRemoveEventTester
: public EventHandler
,
972 NoEventsAfterRemoveEventTester()
974 // We need to use an inactivity timer as we never get any file
975 // system events in this test, so we consider that the test is
976 // finished when this 1s timeout expires instead of, as usual,
977 // stopping after getting the file system events.
981 virtual void GenerateEvent()
983 m_watcher
->Remove(EventGenerator::GetWatchDir());
984 CPPUNIT_ASSERT(eg
.CreateFile());
987 virtual void CheckResult()
989 CPPUNIT_ASSERT( m_events
.empty() );
992 virtual wxFileSystemWatcherEvent
ExpectedEvent()
994 CPPUNIT_FAIL( "Shouldn't be called" );
996 return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR
);
999 virtual void Notify()
1005 } // anonymous namespace
1007 void FileSystemWatcherTestCase::TestNoEventsAfterRemove()
1009 NoEventsAfterRemoveEventTester tester
;