wxWidgets/tests/fswatcher/fswatchertest.cpp
Lauri Nurmi b6803cdfd2 Fix double negatives used with 'neither' in docs and comments
In many cases it should be 'either', and 'nor' should be 'or'
accordingly.

No changes to actual code.

See #22723.

(cherry picked from commit 66916c74a3d44e8bc1b30cf42309e36e5d9e9135)
2022-08-19 16:30:25 +02:00

922 lines
26 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: tests/fswatcher/fswatchertest.cpp
// Purpose: wxFileSystemWatcher unit test
// Author: Bartosz Bekier
// Created: 2009-06-11
// Copyright: (c) 2009 Bartosz Bekier
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifndef WX_PRECOMP
#include "wx/timer.h"
#endif
#if wxUSE_FSWATCHER
#include "wx/evtloop.h"
#include "wx/filename.h"
#include "wx/filefn.h"
#include "wx/fswatcher.h"
#include "wx/log.h"
#include "wx/scopedptr.h"
#include "wx/stdpaths.h"
#include "wx/vector.h"
#include "testfile.h"
// ----------------------------------------------------------------------------
// local functions
// ----------------------------------------------------------------------------
// class generating file system events
class EventGenerator
{
public:
static EventGenerator& Get()
{
if (!ms_instance)
ms_instance = new EventGenerator(GetWatchDir());
return *ms_instance;
}
EventGenerator(const wxFileName& path) : m_base(path)
{
m_old = wxFileName();
m_file = RandomName();
m_new = RandomName();
}
// operations
bool CreateFile()
{
wxFile file(m_file.GetFullPath(), wxFile::write);
return file.IsOpened() && m_file.FileExists();
}
bool RenameFile()
{
REQUIRE(m_file.FileExists());
wxLogDebug("Renaming %s=>%s", m_file.GetFullPath(), m_new.GetFullPath());
bool ret = wxRenameFile(m_file.GetFullPath(), m_new.GetFullPath());
if (ret)
{
m_old = m_file;
m_file = m_new;
m_new = RandomName();
}
return ret;
}
bool DeleteFile()
{
REQUIRE(m_file.FileExists());
bool ret = wxRemoveFile(m_file.GetFullPath());
if (ret)
{
m_old = m_file;
m_file = m_new;
m_new = RandomName();
}
return ret;
}
bool TouchFile()
{
return m_file.Touch();
}
bool ReadFile()
{
wxFile f(m_file.GetFullPath());
REQUIRE(f.IsOpened());
char buf[1];
ssize_t count = f.Read(buf, sizeof(buf));
CHECK(count > 0);
return true;
}
bool ModifyFile()
{
REQUIRE(m_file.FileExists());
wxFile file(m_file.GetFullPath(), wxFile::write_append);
REQUIRE(file.IsOpened());
CHECK(file.Write("Words of Wisdom, Lloyd. Words of wisdom\n"));
return file.Close();
}
// helpers
wxFileName RandomName(int length = 10)
{
return RandomName(m_base, length);
}
// static helpers
static const wxFileName& GetWatchDir()
{
if (ms_watchDir.DirExists())
return ms_watchDir;
wxString tmp = wxStandardPaths::Get().GetTempDir();
ms_watchDir.AssignDir(tmp);
// XXX look for more unique name? there is no function to generate
// unique filename, the file always get created...
ms_watchDir.AppendDir("fswatcher_test");
REQUIRE(!ms_watchDir.DirExists());
TestLogEnabler enableLogs;
REQUIRE(ms_watchDir.Mkdir());
REQUIRE(ms_watchDir.DirExists());
return ms_watchDir;
}
static void RemoveWatchDir()
{
REQUIRE(ms_watchDir.DirExists());
// just to be really sure we know what we remove
REQUIRE( ms_watchDir.GetDirs().Last() == "fswatcher_test" );
TestLogEnabler enableLogs;
CHECK( ms_watchDir.Rmdir(wxPATH_RMDIR_RECURSIVE) );
ms_watchDir = wxFileName();
}
static wxFileName RandomName(const wxFileName& base, int length = 10)
{
static int ALFA_CNT = 'z' - 'a';
wxString s;
for (int i = 0 ; i < length; ++i)
{
char c = 'a' + (rand() % ALFA_CNT);
s += c;
}
return wxFileName(base.GetFullPath(), s);
}
public:
wxFileName m_base; // base dir for doing operations
wxFileName m_file; // current file name
wxFileName m_old; // previous file name
wxFileName m_new; // name after renaming
protected:
static EventGenerator* ms_instance;
private:
static wxFileName ms_watchDir;
};
EventGenerator* EventGenerator::ms_instance = 0;
wxFileName EventGenerator::ms_watchDir;
// Abstract base class from which concrete event tests inherit.
//
// This class provides the common test skeleton which various virtual hooks
// that should or can be reimplemented by the derived classes.
class FSWTesterBase : public wxEvtHandler
{
public:
FSWTesterBase(int types = wxFSW_EVENT_ALL) :
eg(EventGenerator::Get()),
m_eventTypes(types)
{
Bind(wxEVT_FSWATCHER, &FSWTesterBase::OnFileSystemEvent, this);
// wxFileSystemWatcher can be created only once the event loop is
// running, so we can't do it from here and will do it from inside the
// loop when this event handler is invoked.
CallAfter(&FSWTesterBase::OnIdleInit);
}
virtual ~FSWTesterBase()
{
if (m_loop.IsRunning())
m_loop.Exit();
}
void Exit()
{
m_loop.Exit();
}
// sends idle event, so we get called in a moment
void SendIdle()
{
wxIdleEvent* e = new wxIdleEvent();
QueueEvent(e);
}
void Run()
{
m_loop.Run();
}
void OnIdleInit()
{
REQUIRE(Init());
GenerateEvent();
// Check the result when the next idle event comes: note that we can't
// use CallAfter() here, unfortunately, because OnIdleCheckResult()
// would then be called immediately, from the same event loop iteration
// as we're called from, because the idle/pending events are processed
// for as long as there any. Instead, we need to return to the event
// loop itself to give it a chance to dispatch wxFileSystemWatcherEvent
// and wait until our handler for it calls SendIdle() which will then
// end up calling OnIdleCheckResult() afterwards.
Bind(wxEVT_IDLE, &FSWTesterBase::OnIdleCheckResult, this);
}
void OnIdleCheckResult(wxIdleEvent& WXUNUSED(event))
{
Unbind(wxEVT_IDLE, &FSWTesterBase::OnIdleCheckResult, this);
CheckResult();
Exit();
}
virtual bool Init()
{
// test we're good to go
CHECK(wxEventLoopBase::GetActive());
// XXX only now can we construct Watcher, because we need
// active loop here
m_watcher.reset(new wxFileSystemWatcher());
m_watcher->SetOwner(this);
// add dir to be watched
wxFileName dir = EventGenerator::GetWatchDir();
CHECK(m_watcher->Add(dir, m_eventTypes));
return true;
}
virtual void CheckResult()
{
REQUIRE( !m_events.empty() );
const wxFileSystemWatcherEvent * const e = m_events.front();
// this is our "reference event"
const wxFileSystemWatcherEvent expected = ExpectedEvent();
CHECK( e->GetChangeType() == expected.GetChangeType() );
CHECK( e->GetEventType() == wxEVT_FSWATCHER );
// XXX this needs change
CHECK( e->GetEventCategory() == wxEVT_CATEGORY_UNKNOWN );
CHECK( e->GetPath() == expected.GetPath() );
CHECK( e->GetNewPath() == expected.GetNewPath() );
// Under MSW extra modification events are sometimes reported after a
// rename and we just can't get rid of them, so ignore them in this
// test if they do happen.
if ( e->GetChangeType() == wxFSW_EVENT_RENAME &&
m_events.size() == 2 )
{
const wxFileSystemWatcherEvent* const e2 = m_events.back();
if ( e2->GetChangeType() == wxFSW_EVENT_MODIFY &&
e2->GetPath() == e->GetNewPath() )
{
// This is a modify event for the new file, ignore it.
return;
}
}
WX_ASSERT_EQUAL_MESSAGE
(
(
"Extra events received, last one is of type %x, path=\"%s\" "
"(the original event was for \"%s\" (\"%s\")",
m_events.back()->GetChangeType(),
m_events.back()->GetPath().GetFullPath(),
e->GetPath().GetFullPath(),
e->GetNewPath().GetFullPath()
),
1, m_events.size()
);
delete e;
}
virtual void GenerateEvent() = 0;
virtual wxFileSystemWatcherEvent ExpectedEvent() = 0;
protected:
EventGenerator& eg;
wxEventLoop m_loop; // loop reference
wxScopedPtr<wxFileSystemWatcher> m_watcher;
int m_eventTypes; // Which event-types to watch. Normally all of them
wxVector<wxFileSystemWatcherEvent*> m_events;
private:
void OnFileSystemEvent(wxFileSystemWatcherEvent& evt)
{
wxLogDebug("--- %s ---", evt.ToString());
m_events.push_back(wxDynamicCast(evt.Clone(), wxFileSystemWatcherEvent));
// test finished
SendIdle();
}
};
// ----------------------------------------------------------------------------
// test fixture
// ----------------------------------------------------------------------------
class FileSystemWatcherTestCase
{
public:
FileSystemWatcherTestCase()
{
// Before each test, remove the dir if it exists.
// It would exist if the previous test run was aborted.
wxString tmp = wxStandardPaths::Get().GetTempDir();
wxFileName dir;
dir.AssignDir(tmp);
dir.AppendDir("fswatcher_test");
dir.Rmdir(wxPATH_RMDIR_RECURSIVE);
EventGenerator::Get().GetWatchDir();
}
~FileSystemWatcherTestCase()
{
EventGenerator::Get().RemoveWatchDir();
}
};
// ----------------------------------------------------------------------------
// TestEventCreate
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::EventCreate", "[fsw]")
{
class EventTester : public FSWTesterBase
{
public:
virtual void GenerateEvent() wxOVERRIDE
{
CHECK(eg.CreateFile());
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
wxFileSystemWatcherEvent event(wxFSW_EVENT_CREATE);
event.SetPath(eg.m_file);
event.SetNewPath(eg.m_file);
return event;
}
};
EventTester tester;
tester.Run();
}
// ----------------------------------------------------------------------------
// TestEventDelete
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::EventDelete", "[fsw]")
{
class EventTester : public FSWTesterBase
{
public:
virtual void GenerateEvent() wxOVERRIDE
{
CHECK(eg.DeleteFile());
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
wxFileSystemWatcherEvent event(wxFSW_EVENT_DELETE);
event.SetPath(eg.m_old);
// CHECK maybe new path here could be NULL or sth?
event.SetNewPath(eg.m_old);
return event;
}
};
// we need to create a file now, so we can delete it
EventGenerator::Get().CreateFile();
EventTester tester;
tester.Run();
}
// kqueue-based implementation doesn't collapse create/delete pairs in
// renames and detects neither modifications nor access to the
// files reliably currently so disable these tests
//
// FIXME: fix the code and reenable them
#ifndef wxHAS_KQUEUE
// ----------------------------------------------------------------------------
// TestEventRename
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::EventRename", "[fsw]")
{
class EventTester : public FSWTesterBase
{
public:
virtual void GenerateEvent() wxOVERRIDE
{
CHECK(eg.RenameFile());
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
wxFileSystemWatcherEvent event(wxFSW_EVENT_RENAME);
event.SetPath(eg.m_old);
event.SetNewPath(eg.m_file);
return event;
}
};
// need a file to rename later
EventGenerator::Get().CreateFile();
EventTester tester;
tester.Run();
}
// ----------------------------------------------------------------------------
// TestEventModify
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::EventModify", "[fsw]")
{
class EventTester : public FSWTesterBase
{
public:
virtual void GenerateEvent() wxOVERRIDE
{
CHECK(eg.ModifyFile());
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
wxFileSystemWatcherEvent event(wxFSW_EVENT_MODIFY);
event.SetPath(eg.m_file);
event.SetNewPath(eg.m_file);
return event;
}
};
// we need to create a file to modify
EventGenerator::Get().CreateFile();
EventTester tester;
tester.Run();
}
// MSW implementation doesn't detect file access events currently
#ifndef __WINDOWS__
// ----------------------------------------------------------------------------
// TestEventAccess
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::EventAccess", "[fsw]")
{
class EventTester : public FSWTesterBase
{
public:
virtual void GenerateEvent() wxOVERRIDE
{
CHECK(eg.ReadFile());
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS);
event.SetPath(eg.m_file);
event.SetNewPath(eg.m_file);
return event;
}
};
// we need to create a file to read from it and write sth to it
EventGenerator::Get().CreateFile();
EventGenerator::Get().ModifyFile();
EventTester tester;
tester.Run();
}
#endif // __WINDOWS__
#endif // !wxHAS_KQUEUE
#ifdef wxHAS_INOTIFY
// ----------------------------------------------------------------------------
// TestEventAttribute
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::EventAttribute", "[fsw]")
{
class EventTester : public FSWTesterBase
{
public:
virtual void GenerateEvent() wxOVERRIDE
{
CHECK(eg.TouchFile());
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
wxFileSystemWatcherEvent event(wxFSW_EVENT_ATTRIB);
event.SetPath(eg.m_file);
event.SetNewPath(eg.m_file);
return event;
}
};
// we need to create a file to touch
EventGenerator::Get().CreateFile();
EventTester tester;
tester.Run();
}
// ----------------------------------------------------------------------------
// TestSingleWatchtypeEvent: Watch only wxFSW_EVENT_ACCESS
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::SingleWatchtypeEvent", "[fsw]")
{
class EventTester : public FSWTesterBase
{
public:
// We could pass wxFSW_EVENT_CREATE or MODIFY instead, but not RENAME or
// DELETE as the event path fields would be wrong in CheckResult()
EventTester() : FSWTesterBase(wxFSW_EVENT_ACCESS) {}
virtual void GenerateEvent() wxOVERRIDE
{
// As wxFSW_EVENT_ACCESS is passed to the ctor only ReadFile() will
// generate an event. Without it they all will, and the test fails
CHECK(eg.CreateFile());
CHECK(eg.ModifyFile());
CHECK(eg.ReadFile());
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS);
event.SetPath(eg.m_file);
event.SetNewPath(eg.m_file);
return event;
}
};
EventTester tester;
tester.Run();
}
#endif // wxHAS_INOTIFY
// ----------------------------------------------------------------------------
// TestTrees
// ----------------------------------------------------------------------------
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::Trees", "[fsw]")
{
class TreeTester : public FSWTesterBase
{
const size_t subdirs;
const size_t files;
public:
TreeTester() : subdirs(5), files(3) {}
void GrowTree(wxFileName dir
#ifdef __UNIX__
, bool withSymlinks = false
#endif
)
{
REQUIRE(dir.Mkdir());
// Now add a subdir with an easy name to remember in WatchTree()
dir.AppendDir("child");
REQUIRE(dir.Mkdir());
wxFileName child(dir); // Create a copy to which to symlink
// Create a branch of 5 numbered subdirs, each containing 3
// numbered files
for ( unsigned d = 0; d < subdirs; ++d )
{
dir.AppendDir(wxString::Format("subdir%u", d+1));
REQUIRE(dir.Mkdir());
const wxString prefix = dir.GetPathWithSep();
const wxString ext[] = { ".txt", ".log", "" };
for ( unsigned f = 0; f < files; ++f )
{
// Just create the files.
wxFile(prefix + wxString::Format("file%u", f+1) + ext[f],
wxFile::write);
}
#if defined(__UNIX__)
if ( withSymlinks )
{
// Create a symlink to a files, and another to 'child'
CHECK
(
symlink(wxString(prefix + "file1").c_str(),
wxString(prefix + "file.lnk").c_str()) == 0
);
CHECK
(
symlink(child.GetFullPath().c_str(),
wxString(prefix + "dir.lnk").c_str()) == 0
);
}
#endif // __UNIX__
}
}
void RmDir(wxFileName dir)
{
REQUIRE(dir.DirExists());
CHECK(dir.Rmdir(wxPATH_RMDIR_RECURSIVE));
}
void WatchDir(wxFileName dir)
{
REQUIRE(m_watcher);
// Store the initial count; there may already be some watches
const int initial = m_watcher->GetWatchedPathsCount();
m_watcher->Add(dir);
CHECK( m_watcher->GetWatchedPathsCount() == initial + 1 );
}
void RemoveSingleWatch(wxFileName dir)
{
REQUIRE(m_watcher);
const int initial = m_watcher->GetWatchedPathsCount();
m_watcher->Remove(dir);
CHECK( m_watcher->GetWatchedPathsCount() == initial - 1 );
}
void WatchTree(const wxFileName& dir)
{
REQUIRE(m_watcher);
int treeitems = 1; // the trunk
#if !defined(__WINDOWS__) && !defined(wxHAVE_FSEVENTS_FILE_NOTIFICATIONS)
// When there's no file mask, wxMSW and wxOSX set a single watch
// on the trunk which is implemented recursively.
// wxGTK always sets an additional watch for each subdir
treeitems += subdirs + 1; // +1 for 'child'
#endif // !__WINDOWS__ && !wxHAVE_FSEVENTS_FILE_NOTIFICATIONS
// Store the initial count; there may already be some watches
const int initial = m_watcher->GetWatchedPathsCount();
GrowTree(dir);
m_watcher->AddTree(dir);
const int plustree = m_watcher->GetWatchedPathsCount();
CHECK( plustree == initial + treeitems );
m_watcher->RemoveTree(dir);
CHECK( m_watcher->GetWatchedPathsCount() == initial );
// Now test the refcount mechanism by watching items more than once
wxFileName child(dir);
child.AppendDir("child");
m_watcher->AddTree(child);
// Check some watches were added; we don't care about the number
CHECK(initial < m_watcher->GetWatchedPathsCount());
// Now watch the whole tree and check that the count is the same
// as it was the first time, despite also adding 'child' separately
// Except that in wxMSW this isn't true: each watch will be a
// single, recursive dir; so fudge the count
int fudge = 0;
#if defined(__WINDOWS__) || defined(wxHAVE_FSEVENTS_FILE_NOTIFICATIONS)
fudge = 1;
#endif // __WINDOWS__ || wxHAVE_FSEVENTS_FILE_NOTIFICATIONS
m_watcher->AddTree(dir);
CHECK( m_watcher->GetWatchedPathsCount() == plustree + fudge );
m_watcher->RemoveTree(child);
CHECK(initial < m_watcher->GetWatchedPathsCount());
m_watcher->RemoveTree(dir);
CHECK( m_watcher->GetWatchedPathsCount() == initial );
#if defined(__UNIX__)
// Finally, test a tree containing internal symlinks
RmDir(dir);
GrowTree(dir, true /* test symlinks */);
// Without the DontFollowLink() call AddTree() would now assert
// (and without the assert, it would infinitely loop)
wxFileName fn = dir;
fn.DontFollowLink();
CHECK(m_watcher->AddTree(fn));
CHECK(m_watcher->RemoveTree(fn));
// Regrow the tree without symlinks, ready for the next test
RmDir(dir);
GrowTree(dir, false);
#endif // __UNIX__
}
void WatchTreeWithFilespec(const wxFileName& dir)
{
REQUIRE(m_watcher);
REQUIRE(dir.DirExists()); // Was built in WatchTree()
// Store the initial count; there may already be some watches
const int initial = m_watcher->GetWatchedPathsCount();
// When we use a filter, both wxMSW and wxGTK implementations set
// an additional watch for each subdir (+1 for the root dir itself
// and another +1 for "child").
// On OS X, if we use FSEvents then we still only have 1 watch.
#ifdef wxHAVE_FSEVENTS_FILE_NOTIFICATIONS
const int treeitems = 1;
#else
const int treeitems = subdirs + 2;
#endif
m_watcher->AddTree(dir, wxFSW_EVENT_ALL, "*.txt");
CHECK( m_watcher->GetWatchedPathsCount() == initial + treeitems );
// RemoveTree should try to remove only those files that were added
m_watcher->RemoveTree(dir);
CHECK( m_watcher->GetWatchedPathsCount() == initial );
}
void RemoveAllWatches()
{
REQUIRE(m_watcher);
m_watcher->RemoveAll();
CHECK( m_watcher->GetWatchedPathsCount() == 0 );
}
virtual void GenerateEvent() wxOVERRIDE
{
// We don't use this function for events. Just run the tests
wxFileName watchdir = EventGenerator::GetWatchDir();
wxFileName treedir(watchdir);
treedir.AppendDir("treetrunk");
CHECK(!treedir.DirExists());
wxFileName singledir(watchdir);
singledir.AppendDir("single");
CHECK(!singledir.DirExists());
CHECK(singledir.Mkdir());
WatchDir(singledir);
WatchTree(treedir);
// Now test adding and removing a tree using a filespec
// wxMSW uses the generic method to add matching files; which fails
// as it doesn't support adding files :/ So disable the test
#ifndef __WINDOWS__
WatchTreeWithFilespec(treedir);
#endif // __WINDOWS__
RemoveSingleWatch(singledir);
// Add it back again, ready to test RemoveAll()
WatchDir(singledir);
RemoveAllWatches();
// Clean up
RmDir(treedir);
RmDir(singledir);
Exit();
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
FAIL("Shouldn't be called");
return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR);
}
virtual void CheckResult() wxOVERRIDE
{
// Do nothing. We override this to prevent receiving events in
// ExpectedEvent()
}
};
TreeTester tester;
tester.Run();
}
namespace
{
// We can't define this class locally inside TestNoEventsAfterRemove() for some
// reason with g++ 4.0 under OS X 10.5, it results in the following mysterious
// error:
//
// /var/tmp//ccTkNCkc.s:unknown:Non-global symbol:
// __ZThn80_ZN25FileSystemWatcherTestCase23TestNoEventsAfterRemoveEvEN11EventTester6NotifyEv.eh
// can't be a weak_definition
//
// So define this class outside the function instead.
class NoEventsAfterRemoveEventTester : public FSWTesterBase,
public wxTimer
{
public:
NoEventsAfterRemoveEventTester()
{
// We need to use an inactivity timer as we never get any file
// system events in this test, so we consider that the test is
// finished when this 1s timeout expires instead of, as usual,
// stopping after getting the file system events.
Start(1000, true);
}
virtual void GenerateEvent() wxOVERRIDE
{
m_watcher->Remove(EventGenerator::GetWatchDir());
CHECK(eg.CreateFile());
}
virtual void CheckResult() wxOVERRIDE
{
REQUIRE( m_events.empty() );
}
virtual wxFileSystemWatcherEvent ExpectedEvent() wxOVERRIDE
{
FAIL( "Shouldn't be called" );
return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR);
}
virtual void Notify() wxOVERRIDE
{
SendIdle();
}
};
} // anonymous namespace
TEST_CASE_METHOD(FileSystemWatcherTestCase,
"wxFileSystemWatcher::NoEventsAfterRemove", "[fsw]")
{
NoEventsAfterRemoveEventTester tester;
tester.Run();
}
#endif // wxUSE_FSWATCHER