1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/fswatcher/fswatchertest.cpp
3 // Purpose: wxFileSystemWatcher unit test
4 // Author: Bartosz Bekier
6 // Copyright: (c) 2009 Bartosz Bekier
7 ///////////////////////////////////////////////////////////////////////////////
9 // ----------------------------------------------------------------------------
11 // ----------------------------------------------------------------------------
23 #include "wx/evtloop.h"
24 #include "wx/filename.h"
25 #include "wx/filefn.h"
26 #include "wx/stdpaths.h"
27 #include "wx/fswatcher.h"
31 // ----------------------------------------------------------------------------
33 // ----------------------------------------------------------------------------
35 // class generating file system events
39 static EventGenerator
& Get()
42 ms_instance
= new EventGenerator(GetWatchDir());
47 EventGenerator(const wxFileName
& path
) : m_base(path
)
50 m_file
= RandomName();
57 wxFile
file(m_file
.GetFullPath(), wxFile::write
);
58 return file
.IsOpened() && m_file
.FileExists();
63 CPPUNIT_ASSERT(m_file
.FileExists());
65 wxLogDebug("Renaming %s=>%s", m_file
.GetFullPath(), m_new
.GetFullPath());
67 bool ret
= wxRenameFile(m_file
.GetFullPath(), m_new
.GetFullPath());
80 CPPUNIT_ASSERT(m_file
.FileExists());
82 bool ret
= wxRemoveFile(m_file
.GetFullPath());
95 return m_file
.Touch();
100 wxFile
f(m_file
.GetFullPath());
101 CPPUNIT_ASSERT(f
.IsOpened());
104 ssize_t count
= f
.Read(buf
, sizeof(buf
));
105 CPPUNIT_ASSERT(count
> 0);
112 CPPUNIT_ASSERT(m_file
.FileExists());
114 wxFile
file(m_file
.GetFullPath(), wxFile::write_append
);
115 CPPUNIT_ASSERT(file
.IsOpened());
117 CPPUNIT_ASSERT(file
.Write("Words of Wisdom, Lloyd. Words of wisdom\n"));
122 wxFileName
RandomName(int length
= 10)
124 return RandomName(m_base
, length
);
128 static const wxFileName
& GetWatchDir()
130 static wxFileName dir
;
135 wxString tmp
= wxStandardPaths::Get().GetTempDir();
138 // XXX look for more unique name? there is no function to generate
139 // unique filename, the file always get created...
140 dir
.AppendDir("fswatcher_test");
141 CPPUNIT_ASSERT(!dir
.DirExists());
142 CPPUNIT_ASSERT(dir
.Mkdir());
147 static void RemoveWatchDir()
149 wxFileName dir
= GetWatchDir();
150 CPPUNIT_ASSERT(dir
.DirExists());
152 // just to be really sure we know what we remove
153 CPPUNIT_ASSERT_EQUAL( "fswatcher_test", dir
.GetDirs().Last() );
155 // FIXME-VC6: using non-static Rmdir() results in ICE
156 CPPUNIT_ASSERT( wxFileName::Rmdir(dir
.GetFullPath(), wxPATH_RMDIR_RECURSIVE
) );
159 static wxFileName
RandomName(const wxFileName
& base
, int length
= 10)
161 static int ALFA_CNT
= 'z' - 'a';
164 for (int i
= 0 ; i
< length
; ++i
)
166 char c
= 'a' + (rand() % ALFA_CNT
);
170 return wxFileName(base
.GetFullPath(), s
);
174 wxFileName m_base
; // base dir for doing operations
175 wxFileName m_file
; // current file name
176 wxFileName m_old
; // previous file name
177 wxFileName m_new
; // name after renaming
180 static EventGenerator
* ms_instance
;
183 EventGenerator
* EventGenerator::ms_instance
= 0;
186 // custom event handler
187 class EventHandler
: public wxEvtHandler
190 enum { WAIT_DURATION
= 3 };
192 EventHandler(int types
= wxFSW_EVENT_ALL
) :
193 eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0),
196 m_loop
= new wxEventLoop();
197 Connect(wxEVT_IDLE
, wxIdleEventHandler(EventHandler::OnIdle
));
198 Connect(wxEVT_FSWATCHER
, wxFileSystemWatcherEventHandler(
199 EventHandler::OnFileSystemEvent
));
202 virtual ~EventHandler()
207 if (m_loop
->IsRunning())
218 // sends idle event, so we get called in a moment
221 wxIdleEvent
* e
= new wxIdleEvent();
231 void OnIdle(wxIdleEvent
& /*evt*/)
233 bool more
= Action();
242 // returns whether we should produce more idle events
243 virtual bool Action()
248 CPPUNIT_ASSERT(Init());
259 // TODO a mechanism that will break the loop in case we
260 // don't receive a file system event
261 // this below doesn't quite work, so all tests must pass :-)
266 CPPUNIT_ASSERT(KeepWaiting());
272 CPPUNIT_ASSERT(AfterWait());
275 } // switch (m_count)
282 // test we're good to go
283 CPPUNIT_ASSERT(wxEventLoopBase::GetActive());
285 // XXX only now can we construct Watcher, because we need
287 m_watcher
= new wxFileSystemWatcher();
288 m_watcher
->SetOwner(this);
290 // add dir to be watched
291 wxFileName dir
= EventGenerator::GetWatchDir();
292 CPPUNIT_ASSERT(m_watcher
->Add(dir
, m_eventTypes
));
297 virtual bool KeepWaiting()
299 // did we receive event already?
302 // well, let's wait a bit more
303 wxSleep(WAIT_DURATION
);
309 virtual bool AfterWait()
311 // fail if still no events
314 ("No events during %d seconds!", static_cast<int>(WAIT_DURATION
)),
321 virtual void OnFileSystemEvent(wxFileSystemWatcherEvent
& evt
)
323 wxLogDebug("--- %s ---", evt
.ToString());
324 m_lastEvent
= wxDynamicCast(evt
.Clone(), wxFileSystemWatcherEvent
);
325 m_events
.Add(m_lastEvent
);
332 virtual void CheckResult()
334 CPPUNIT_ASSERT_MESSAGE( "No events received", !m_events
.empty() );
336 const wxFileSystemWatcherEvent
* const e
= m_events
.front();
338 // this is our "reference event"
339 const wxFileSystemWatcherEvent expected
= ExpectedEvent();
341 CPPUNIT_ASSERT_EQUAL( expected
.GetChangeType(), e
->GetChangeType() );
343 CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER
, e
->GetEventType());
345 // XXX this needs change
346 CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN
, e
->GetEventCategory());
348 CPPUNIT_ASSERT_EQUAL(expected
.GetPath(), e
->GetPath());
349 CPPUNIT_ASSERT_EQUAL(expected
.GetNewPath(), e
->GetNewPath());
351 // Under MSW extra modification events are sometimes reported after a
352 // rename and we just can't get rid of them, so ignore them in this
353 // test if they do happen.
354 if ( e
->GetChangeType() == wxFSW_EVENT_RENAME
&&
355 m_events
.size() == 2 )
357 const wxFileSystemWatcherEvent
* const e2
= m_events
.back();
358 if ( e2
->GetChangeType() == wxFSW_EVENT_MODIFY
&&
359 e2
->GetPath() == e
->GetNewPath() )
361 // This is a modify event for the new file, ignore it.
366 WX_ASSERT_EQUAL_MESSAGE
369 "Extra events received, last one is of type %x, path=\"%s\" "
370 "(the original event was for \"%s\" (\"%s\")",
371 m_events
.back()->GetChangeType(),
372 m_events
.back()->GetPath().GetFullPath(),
373 e
->GetPath().GetFullPath(),
374 e
->GetNewPath().GetFullPath()
381 virtual void GenerateEvent() = 0;
383 virtual wxFileSystemWatcherEvent
ExpectedEvent() = 0;
388 wxEventLoopBase
* m_loop
; // loop reference
389 int m_count
; // idle events count
391 wxFileSystemWatcher
* m_watcher
;
392 int m_eventTypes
; // Which event-types to watch. Normally all of them
393 bool tested
; // indicates, whether we have already passed the test
395 #include "wx/arrimpl.cpp"
396 WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent
*, wxArrayEvent
);
397 wxArrayEvent m_events
;
398 wxFileSystemWatcherEvent
* m_lastEvent
;
402 // ----------------------------------------------------------------------------
404 // ----------------------------------------------------------------------------
406 class FileSystemWatcherTestCase
: public CppUnit::TestCase
409 FileSystemWatcherTestCase() { }
411 virtual void setUp();
412 virtual void tearDown();
415 wxEventLoopBase
* m_loop
;
418 CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase
);
419 CPPUNIT_TEST( TestEventCreate
);
420 CPPUNIT_TEST( TestEventDelete
);
421 #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
422 CPPUNIT_TEST( TestTrees
);
425 // kqueue-based implementation doesn't collapse create/delete pairs in
426 // renames and doesn't detect neither modifications nor access to the
427 // files reliably currently so disable these tests
429 // FIXME: fix the code and reenable them
431 CPPUNIT_TEST( TestEventRename
);
432 CPPUNIT_TEST( TestEventModify
);
434 // MSW implementation doesn't detect file access events currently
436 CPPUNIT_TEST( TestEventAccess
);
437 #endif // __WINDOWS__
438 #endif // !wxHAS_KQUEUE
441 CPPUNIT_TEST( TestEventAttribute
);
442 CPPUNIT_TEST( TestSingleWatchtypeEvent
);
443 #endif // wxHAS_INOTIFY
445 CPPUNIT_TEST( TestNoEventsAfterRemove
);
446 CPPUNIT_TEST_SUITE_END();
448 void TestEventCreate();
449 void TestEventDelete();
450 void TestEventRename();
451 void TestEventModify();
452 void TestEventAccess();
454 void TestEventAttribute();
455 void TestSingleWatchtypeEvent();
456 #endif // wxHAS_INOTIFY
457 #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
458 void TestTrees(); // Visual C++ 6 can't build this
460 void TestNoEventsAfterRemove();
462 DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase
)
465 // the test currently hangs under OS X for some reason and this prevents tests
466 // ran by buildbot from completing so disable it until someone has time to
469 // FIXME: debug and fix this!
471 // register in the unnamed registry so that these tests are run by default
472 CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase
);
475 // also include in its own registry so that these tests can be run alone
476 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase
,
477 "FileSystemWatcherTestCase" );
479 void FileSystemWatcherTestCase::setUp()
481 wxLog::AddTraceMask(wxTRACE_FSWATCHER
);
482 EventGenerator::Get().GetWatchDir();
485 void FileSystemWatcherTestCase::tearDown()
487 EventGenerator::Get().RemoveWatchDir();
490 // ----------------------------------------------------------------------------
492 // ----------------------------------------------------------------------------
493 void FileSystemWatcherTestCase::TestEventCreate()
495 wxLogDebug("TestEventCreate()");
497 class EventTester
: public EventHandler
500 virtual void GenerateEvent()
502 CPPUNIT_ASSERT(eg
.CreateFile());
505 virtual wxFileSystemWatcherEvent
ExpectedEvent()
507 wxFileSystemWatcherEvent
event(wxFSW_EVENT_CREATE
);
508 event
.SetPath(eg
.m_file
);
509 event
.SetNewPath(eg
.m_file
);
516 wxLogTrace(wxTRACE_FSWATCHER
, "TestEventCreate tester created()");
521 // ----------------------------------------------------------------------------
523 // ----------------------------------------------------------------------------
524 void FileSystemWatcherTestCase::TestEventDelete()
526 wxLogDebug("TestEventDelete()");
528 class EventTester
: public EventHandler
531 virtual void GenerateEvent()
533 CPPUNIT_ASSERT(eg
.DeleteFile());
536 virtual wxFileSystemWatcherEvent
ExpectedEvent()
538 wxFileSystemWatcherEvent
event(wxFSW_EVENT_DELETE
);
539 event
.SetPath(eg
.m_old
);
541 // CHECK maybe new path here could be NULL or sth?
542 event
.SetNewPath(eg
.m_old
);
547 // we need to create a file now, so we can delete it
548 EventGenerator::Get().CreateFile();
554 // ----------------------------------------------------------------------------
556 // ----------------------------------------------------------------------------
557 void FileSystemWatcherTestCase::TestEventRename()
559 wxLogDebug("TestEventRename()");
561 class EventTester
: public EventHandler
564 virtual void GenerateEvent()
566 CPPUNIT_ASSERT(eg
.RenameFile());
569 virtual wxFileSystemWatcherEvent
ExpectedEvent()
571 wxFileSystemWatcherEvent
event(wxFSW_EVENT_RENAME
);
572 event
.SetPath(eg
.m_old
);
573 event
.SetNewPath(eg
.m_file
);
578 // need a file to rename later
579 EventGenerator::Get().CreateFile();
585 // ----------------------------------------------------------------------------
587 // ----------------------------------------------------------------------------
588 void FileSystemWatcherTestCase::TestEventModify()
590 wxLogDebug("TestEventModify()");
592 class EventTester
: public EventHandler
595 virtual void GenerateEvent()
597 CPPUNIT_ASSERT(eg
.ModifyFile());
600 virtual wxFileSystemWatcherEvent
ExpectedEvent()
602 wxFileSystemWatcherEvent
event(wxFSW_EVENT_MODIFY
);
603 event
.SetPath(eg
.m_file
);
604 event
.SetNewPath(eg
.m_file
);
609 // we need to create a file to modify
610 EventGenerator::Get().CreateFile();
616 // ----------------------------------------------------------------------------
618 // ----------------------------------------------------------------------------
619 void FileSystemWatcherTestCase::TestEventAccess()
621 wxLogDebug("TestEventAccess()");
623 class EventTester
: public EventHandler
626 virtual void GenerateEvent()
628 CPPUNIT_ASSERT(eg
.ReadFile());
631 virtual wxFileSystemWatcherEvent
ExpectedEvent()
633 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ACCESS
);
634 event
.SetPath(eg
.m_file
);
635 event
.SetNewPath(eg
.m_file
);
640 // we need to create a file to read from it and write sth to it
641 EventGenerator::Get().CreateFile();
642 EventGenerator::Get().ModifyFile();
649 // ----------------------------------------------------------------------------
650 // TestEventAttribute
651 // ----------------------------------------------------------------------------
652 void FileSystemWatcherTestCase::TestEventAttribute()
654 wxLogDebug("TestEventAttribute()");
656 class EventTester
: public EventHandler
659 virtual void GenerateEvent()
661 CPPUNIT_ASSERT(eg
.TouchFile());
664 virtual wxFileSystemWatcherEvent
ExpectedEvent()
666 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ATTRIB
);
667 event
.SetPath(eg
.m_file
);
668 event
.SetNewPath(eg
.m_file
);
673 // we need to create a file to touch
674 EventGenerator::Get().CreateFile();
680 // ----------------------------------------------------------------------------
681 // TestSingleWatchtypeEvent: Watch only wxFSW_EVENT_ACCESS
682 // ----------------------------------------------------------------------------
683 void FileSystemWatcherTestCase::TestSingleWatchtypeEvent()
685 wxLogDebug("TestSingleWatchtypeEvent()");
687 class EventTester
: public EventHandler
690 // We could pass wxFSW_EVENT_CREATE or MODIFY instead, but not RENAME or
691 // DELETE as the event path fields would be wrong in CheckResult()
692 EventTester() : EventHandler(wxFSW_EVENT_ACCESS
) {}
694 virtual void GenerateEvent()
696 // As wxFSW_EVENT_ACCESS is passed to the ctor only ReadFile() will
697 // generate an event. Without it they all will, and the test fails
698 CPPUNIT_ASSERT(eg
.CreateFile());
699 CPPUNIT_ASSERT(eg
.ModifyFile());
700 CPPUNIT_ASSERT(eg
.ReadFile());
703 virtual wxFileSystemWatcherEvent
ExpectedEvent()
705 wxFileSystemWatcherEvent
event(wxFSW_EVENT_ACCESS
);
706 event
.SetPath(eg
.m_file
);
707 event
.SetNewPath(eg
.m_file
);
715 #endif // wxHAS_INOTIFY
717 // ----------------------------------------------------------------------------
719 // ----------------------------------------------------------------------------
721 #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
722 void FileSystemWatcherTestCase::TestTrees()
724 class TreeTester
: public EventHandler
726 const size_t subdirs
;
730 TreeTester() : subdirs(5), files(3) {}
732 void GrowTree(wxFileName dir
734 , bool withSymlinks
= false
738 CPPUNIT_ASSERT(dir
.Mkdir());
739 // Now add a subdir with an easy name to remember in WatchTree()
740 dir
.AppendDir("child");
741 CPPUNIT_ASSERT(dir
.Mkdir());
742 wxFileName
child(dir
); // Create a copy to which to symlink
744 // Create a branch of 5 numbered subdirs, each containing 3
746 for ( unsigned d
= 0; d
< subdirs
; ++d
)
748 dir
.AppendDir(wxString::Format("subdir%u", d
+1));
749 CPPUNIT_ASSERT(dir
.Mkdir());
751 const wxString prefix
= dir
.GetPathWithSep();
752 const wxString ext
[] = { ".txt", ".log", "" };
753 for ( unsigned f
= 0; f
< files
; ++f
)
755 // Just create the files.
756 wxFile(prefix
+ wxString::Format("file%u", f
+1) + ext
[f
],
759 #if defined(__UNIX__)
762 // Create a symlink to a files, and another to 'child'
763 CPPUNIT_ASSERT_EQUAL(0,
764 symlink(wxString(prefix
+ "file1").c_str(),
765 wxString(prefix
+ "file.lnk").c_str()));
766 CPPUNIT_ASSERT_EQUAL(0,
767 symlink(child
.GetFullPath().c_str(),
768 wxString(prefix
+ "dir.lnk").c_str()));
774 void RmDir(wxFileName dir
)
776 CPPUNIT_ASSERT(dir
.DirExists());
778 CPPUNIT_ASSERT(dir
.Rmdir(wxPATH_RMDIR_RECURSIVE
));
781 void WatchDir(wxFileName dir
)
783 CPPUNIT_ASSERT(m_watcher
);
785 // Store the initial count; there may already be some watches
786 const int initial
= m_watcher
->GetWatchedPathsCount();
789 CPPUNIT_ASSERT_EQUAL(initial
+ 1,
790 m_watcher
->GetWatchedPathsCount());
793 void RemoveSingleWatch(wxFileName dir
)
795 CPPUNIT_ASSERT(m_watcher
);
797 const int initial
= m_watcher
->GetWatchedPathsCount();
799 m_watcher
->Remove(dir
);
800 CPPUNIT_ASSERT_EQUAL(initial
- 1,
801 m_watcher
->GetWatchedPathsCount());
804 void WatchTree(const wxFileName
& dir
)
806 CPPUNIT_ASSERT(m_watcher
);
808 size_t treeitems
= 1; // the trunk
810 // When there's no file mask, wxMSW sets a single watch
811 // on the trunk which is implemented recursively.
812 // wxGTK always sets an additional watch for each subdir
813 treeitems
+= subdirs
+ 1; // +1 for 'child'
814 #endif // __WINDOWS__
816 // Store the initial count; there may already be some watches
817 const int initial
= m_watcher
->GetWatchedPathsCount();
821 m_watcher
->AddTree(dir
);
822 const int plustree
= m_watcher
->GetWatchedPathsCount();
824 CPPUNIT_ASSERT_EQUAL(initial
+ treeitems
, plustree
);
826 m_watcher
->RemoveTree(dir
);
827 CPPUNIT_ASSERT_EQUAL(initial
, m_watcher
->GetWatchedPathsCount());
829 // Now test the refcount mechanism by watching items more than once
830 wxFileName
child(dir
);
831 child
.AppendDir("child");
832 m_watcher
->AddTree(child
);
833 // Check some watches were added; we don't care about the number
834 CPPUNIT_ASSERT(initial
< m_watcher
->GetWatchedPathsCount());
835 // Now watch the whole tree and check that the count is the same
836 // as it was the first time, despite also adding 'child' separately
837 // Except that in wxMSW this isn't true: each watch will be a
838 // single, recursive dir; so fudge the count
842 #endif // __WINDOWS__
843 m_watcher
->AddTree(dir
);
844 CPPUNIT_ASSERT_EQUAL(plustree
+ fudge
, m_watcher
->GetWatchedPathsCount());
845 m_watcher
->RemoveTree(child
);
846 CPPUNIT_ASSERT(initial
< m_watcher
->GetWatchedPathsCount());
847 m_watcher
->RemoveTree(dir
);
848 CPPUNIT_ASSERT_EQUAL(initial
, m_watcher
->GetWatchedPathsCount());
849 #if defined(__UNIX__)
850 // Finally, test a tree containing internal symlinks
852 GrowTree(dir
, true /* test symlinks */);
854 // Without the DontFollowLink() call AddTree() would now assert
855 // (and without the assert, it would infinitely loop)
858 CPPUNIT_ASSERT(m_watcher
->AddTree(fn
));
859 CPPUNIT_ASSERT(m_watcher
->RemoveTree(fn
));
861 // Regrow the tree without symlinks, ready for the next test
863 GrowTree(dir
, false);
867 void WatchTreeWithFilespec(const wxFileName
& dir
)
869 CPPUNIT_ASSERT(m_watcher
);
870 CPPUNIT_ASSERT(dir
.DirExists()); // Was built in WatchTree()
872 // Store the initial count; there may already be some watches
873 const int initial
= m_watcher
->GetWatchedPathsCount();
875 // When we use a filter, both wxMSW and wxGTK implementations set
876 // an additional watch for each subdir (+1 for the root dir itself
877 // and another +1 for "child").
878 const size_t treeitems
= subdirs
+ 2;
879 m_watcher
->AddTree(dir
, wxFSW_EVENT_ALL
, "*.txt");
881 const int plustree
= m_watcher
->GetWatchedPathsCount();
882 CPPUNIT_ASSERT_EQUAL(initial
+ treeitems
, plustree
);
884 // RemoveTree should try to remove only those files that were added
885 m_watcher
->RemoveTree(dir
);
886 CPPUNIT_ASSERT_EQUAL(initial
, m_watcher
->GetWatchedPathsCount());
889 void RemoveAllWatches()
891 CPPUNIT_ASSERT(m_watcher
);
893 m_watcher
->RemoveAll();
894 CPPUNIT_ASSERT_EQUAL(0, m_watcher
->GetWatchedPathsCount());
897 virtual void GenerateEvent()
899 // We don't use this function for events. Just run the tests
901 wxFileName watchdir
= EventGenerator::GetWatchDir();
902 CPPUNIT_ASSERT(watchdir
.DirExists());
904 wxFileName
treedir(watchdir
);
905 treedir
.AppendDir("treetrunk");
906 CPPUNIT_ASSERT(!treedir
.DirExists());
908 wxFileName
singledir(watchdir
);
909 singledir
.AppendDir("single");
910 CPPUNIT_ASSERT(!singledir
.DirExists());
911 CPPUNIT_ASSERT(singledir
.Mkdir());
915 // Now test adding and removing a tree using a filespec
916 // wxMSW uses the generic method to add matching files; which fails
917 // as it doesn't support adding files :/ So disable the test
919 WatchTreeWithFilespec(treedir
);
920 #endif // __WINDOWS__
922 RemoveSingleWatch(singledir
);
923 // Add it back again, ready to test RemoveAll()
935 virtual wxFileSystemWatcherEvent
ExpectedEvent()
937 CPPUNIT_FAIL("Shouldn't be called");
939 return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR
);
942 virtual void CheckResult()
944 // Do nothing. We override this to prevent receiving events in
952 #endif // !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
958 // We can't define this class locally inside TestNoEventsAfterRemove() for some
959 // reason with g++ 4.0 under OS X 10.5, it results in the following mysterious
962 // /var/tmp//ccTkNCkc.s:unknown:Non-global symbol:
963 // __ZThn80_ZN25FileSystemWatcherTestCase23TestNoEventsAfterRemoveEvEN11EventTester6NotifyEv.eh
964 // can't be a weak_definition
966 // So define this class outside the function instead.
967 class NoEventsAfterRemoveEventTester
: public EventHandler
,
971 NoEventsAfterRemoveEventTester()
973 // We need to use an inactivity timer as we never get any file
974 // system events in this test, so we consider that the test is
975 // finished when this 1s timeout expires instead of, as usual,
976 // stopping after getting the file system events.
980 virtual void GenerateEvent()
982 m_watcher
->Remove(EventGenerator::GetWatchDir());
983 CPPUNIT_ASSERT(eg
.CreateFile());
986 virtual void CheckResult()
988 CPPUNIT_ASSERT( m_events
.empty() );
991 virtual wxFileSystemWatcherEvent
ExpectedEvent()
993 CPPUNIT_FAIL( "Shouldn't be called" );
995 return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR
);
998 virtual void Notify()
1004 } // anonymous namespace
1006 void FileSystemWatcherTestCase::TestNoEventsAfterRemove()
1008 NoEventsAfterRemoveEventTester tester
;