f31f9900e4
This flag allows to monitor changes to the file attributes, such as file modification time. This patch adds the flag, support for it under Linux and the corresponding modifications to the sample and the test suite. Closes #14833. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72977 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
1012 lines
28 KiB
C++
1012 lines
28 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: tests/fswatcher/fswatchertest.cpp
|
|
// Purpose: wxFileSystemWatcher unit test
|
|
// Author: Bartosz Bekier
|
|
// Created: 2009-06-11
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2009 Bartosz Bekier
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "testprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/timer.h"
|
|
#endif
|
|
|
|
#include "wx/evtloop.h"
|
|
#include "wx/filename.h"
|
|
#include "wx/filefn.h"
|
|
#include "wx/stdpaths.h"
|
|
#include "wx/fswatcher.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()
|
|
{
|
|
CPPUNIT_ASSERT(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()
|
|
{
|
|
CPPUNIT_ASSERT(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());
|
|
CPPUNIT_ASSERT(f.IsOpened());
|
|
|
|
char buf[1];
|
|
ssize_t count = f.Read(buf, sizeof(buf));
|
|
CPPUNIT_ASSERT(count > 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ModifyFile()
|
|
{
|
|
CPPUNIT_ASSERT(m_file.FileExists());
|
|
|
|
wxFile file(m_file.GetFullPath(), wxFile::write_append);
|
|
CPPUNIT_ASSERT(file.IsOpened());
|
|
|
|
CPPUNIT_ASSERT(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()
|
|
{
|
|
static wxFileName dir;
|
|
|
|
if (dir.DirExists())
|
|
return dir;
|
|
|
|
wxString tmp = wxStandardPaths::Get().GetTempDir();
|
|
dir.AssignDir(tmp);
|
|
|
|
// XXX look for more unique name? there is no function to generate
|
|
// unique filename, the file always get created...
|
|
dir.AppendDir("fswatcher_test");
|
|
CPPUNIT_ASSERT(!dir.DirExists());
|
|
CPPUNIT_ASSERT(dir.Mkdir());
|
|
|
|
return dir;
|
|
}
|
|
|
|
static void RemoveWatchDir()
|
|
{
|
|
wxFileName dir = GetWatchDir();
|
|
CPPUNIT_ASSERT(dir.DirExists());
|
|
|
|
// just to be really sure we know what we remove
|
|
CPPUNIT_ASSERT_EQUAL( "fswatcher_test", dir.GetDirs().Last() );
|
|
|
|
// FIXME-VC6: using non-static Rmdir() results in ICE
|
|
CPPUNIT_ASSERT( wxFileName::Rmdir(dir.GetFullPath(), wxPATH_RMDIR_RECURSIVE) );
|
|
}
|
|
|
|
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;
|
|
};
|
|
|
|
EventGenerator* EventGenerator::ms_instance = 0;
|
|
|
|
|
|
// custom event handler
|
|
class EventHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
enum { WAIT_DURATION = 3 };
|
|
|
|
EventHandler(int types = wxFSW_EVENT_ALL) :
|
|
eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0),
|
|
m_eventTypes(types)
|
|
{
|
|
m_loop = new wxEventLoop();
|
|
Connect(wxEVT_IDLE, wxIdleEventHandler(EventHandler::OnIdle));
|
|
Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(
|
|
EventHandler::OnFileSystemEvent));
|
|
}
|
|
|
|
virtual ~EventHandler()
|
|
{
|
|
delete m_watcher;
|
|
if (m_loop)
|
|
{
|
|
if (m_loop->IsRunning())
|
|
m_loop->Exit();
|
|
delete m_loop;
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
SendIdle();
|
|
m_loop->Run();
|
|
}
|
|
|
|
void OnIdle(wxIdleEvent& /*evt*/)
|
|
{
|
|
bool more = Action();
|
|
m_count++;
|
|
|
|
if (more)
|
|
{
|
|
SendIdle();
|
|
}
|
|
}
|
|
|
|
// returns whether we should produce more idle events
|
|
virtual bool Action()
|
|
{
|
|
switch (m_count)
|
|
{
|
|
case 0:
|
|
CPPUNIT_ASSERT(Init());
|
|
break;
|
|
case 1:
|
|
GenerateEvent();
|
|
break;
|
|
case 2:
|
|
// actual test
|
|
CheckResult();
|
|
Exit();
|
|
break;
|
|
|
|
// TODO a mechanism that will break the loop in case we
|
|
// don't receive a file system event
|
|
// this below doesn't quite work, so all tests must pass :-)
|
|
#if 0
|
|
case 2:
|
|
m_loop.Yield();
|
|
m_loop.WakeUp();
|
|
CPPUNIT_ASSERT(KeepWaiting());
|
|
m_loop.Yield();
|
|
break;
|
|
case 3:
|
|
break;
|
|
case 4:
|
|
CPPUNIT_ASSERT(AfterWait());
|
|
break;
|
|
#endif
|
|
} // switch (m_count)
|
|
|
|
return m_count <= 0;
|
|
}
|
|
|
|
virtual bool Init()
|
|
{
|
|
// test we're good to go
|
|
CPPUNIT_ASSERT(wxEventLoopBase::GetActive());
|
|
|
|
// XXX only now can we construct Watcher, because we need
|
|
// active loop here
|
|
m_watcher = new wxFileSystemWatcher();
|
|
m_watcher->SetOwner(this);
|
|
|
|
// add dir to be watched
|
|
wxFileName dir = EventGenerator::GetWatchDir();
|
|
CPPUNIT_ASSERT(m_watcher->Add(dir, m_eventTypes));
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool KeepWaiting()
|
|
{
|
|
// did we receive event already?
|
|
if (!tested)
|
|
{
|
|
// well, let's wait a bit more
|
|
wxSleep(WAIT_DURATION);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool AfterWait()
|
|
{
|
|
// fail if still no events
|
|
WX_ASSERT_MESSAGE
|
|
(
|
|
("No events during %d seconds!", static_cast<int>(WAIT_DURATION)),
|
|
tested
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual void OnFileSystemEvent(wxFileSystemWatcherEvent& evt)
|
|
{
|
|
wxLogDebug("--- %s ---", evt.ToString());
|
|
m_lastEvent = wxDynamicCast(evt.Clone(), wxFileSystemWatcherEvent);
|
|
m_events.Add(m_lastEvent);
|
|
|
|
// test finished
|
|
SendIdle();
|
|
tested = true;
|
|
}
|
|
|
|
virtual void CheckResult()
|
|
{
|
|
CPPUNIT_ASSERT_MESSAGE( "No events received", !m_events.empty() );
|
|
|
|
const wxFileSystemWatcherEvent * const e = m_events.front();
|
|
|
|
// this is our "reference event"
|
|
const wxFileSystemWatcherEvent expected = ExpectedEvent();
|
|
|
|
CPPUNIT_ASSERT_EQUAL( expected.GetChangeType(), e->GetChangeType() );
|
|
|
|
CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER, e->GetEventType());
|
|
|
|
// XXX this needs change
|
|
CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN, e->GetEventCategory());
|
|
|
|
CPPUNIT_ASSERT_EQUAL(expected.GetPath(), e->GetPath());
|
|
CPPUNIT_ASSERT_EQUAL(expected.GetNewPath(), e->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()
|
|
);
|
|
|
|
}
|
|
|
|
virtual void GenerateEvent() = 0;
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent() = 0;
|
|
|
|
|
|
protected:
|
|
EventGenerator& eg;
|
|
wxEventLoopBase* m_loop; // loop reference
|
|
int m_count; // idle events count
|
|
|
|
wxFileSystemWatcher* m_watcher;
|
|
int m_eventTypes; // Which event-types to watch. Normally all of them
|
|
bool tested; // indicates, whether we have already passed the test
|
|
|
|
#include "wx/arrimpl.cpp"
|
|
WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent*, wxArrayEvent);
|
|
wxArrayEvent m_events;
|
|
wxFileSystemWatcherEvent* m_lastEvent;
|
|
};
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// test class
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class FileSystemWatcherTestCase : public CppUnit::TestCase
|
|
{
|
|
public:
|
|
FileSystemWatcherTestCase() { }
|
|
|
|
virtual void setUp();
|
|
virtual void tearDown();
|
|
|
|
protected:
|
|
wxEventLoopBase* m_loop;
|
|
|
|
private:
|
|
CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase );
|
|
CPPUNIT_TEST( TestEventCreate );
|
|
CPPUNIT_TEST( TestEventDelete );
|
|
#if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
|
|
CPPUNIT_TEST( TestTrees );
|
|
#endif
|
|
|
|
// kqueue-based implementation doesn't collapse create/delete pairs in
|
|
// renames and doesn't detect neither modifications nor access to the
|
|
// files reliably currently so disable these tests
|
|
//
|
|
// FIXME: fix the code and reenable them
|
|
#ifndef wxHAS_KQUEUE
|
|
CPPUNIT_TEST( TestEventRename );
|
|
CPPUNIT_TEST( TestEventModify );
|
|
|
|
// MSW implementation doesn't detect file access events currently
|
|
#ifndef __WINDOWS__
|
|
CPPUNIT_TEST( TestEventAccess );
|
|
#endif // __WINDOWS__
|
|
#endif // !wxHAS_KQUEUE
|
|
|
|
#ifdef wxHAS_INOTIFY
|
|
CPPUNIT_TEST( TestEventAttribute );
|
|
CPPUNIT_TEST( TestSingleWatchtypeEvent );
|
|
#endif // wxHAS_INOTIFY
|
|
|
|
CPPUNIT_TEST( TestNoEventsAfterRemove );
|
|
CPPUNIT_TEST_SUITE_END();
|
|
|
|
void TestEventCreate();
|
|
void TestEventDelete();
|
|
void TestEventRename();
|
|
void TestEventModify();
|
|
void TestEventAccess();
|
|
#ifdef wxHAS_INOTIFY
|
|
void TestEventAttribute();
|
|
void TestSingleWatchtypeEvent();
|
|
#endif // wxHAS_INOTIFY
|
|
#if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
|
|
void TestTrees(); // Visual C++ 6 can't build this
|
|
#endif
|
|
void TestNoEventsAfterRemove();
|
|
|
|
DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase)
|
|
};
|
|
|
|
// the test currently hangs under OS X for some reason and this prevents tests
|
|
// ran by buildbot from completing so disable it until someone has time to
|
|
// debug it
|
|
//
|
|
// FIXME: debug and fix this!
|
|
#ifndef __WXOSX__
|
|
// register in the unnamed registry so that these tests are run by default
|
|
CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase );
|
|
#endif
|
|
|
|
// also include in its own registry so that these tests can be run alone
|
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase,
|
|
"FileSystemWatcherTestCase" );
|
|
|
|
void FileSystemWatcherTestCase::setUp()
|
|
{
|
|
wxLog::AddTraceMask(wxTRACE_FSWATCHER);
|
|
EventGenerator::Get().GetWatchDir();
|
|
}
|
|
|
|
void FileSystemWatcherTestCase::tearDown()
|
|
{
|
|
EventGenerator::Get().RemoveWatchDir();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TestEventCreate
|
|
// ----------------------------------------------------------------------------
|
|
void FileSystemWatcherTestCase::TestEventCreate()
|
|
{
|
|
wxLogDebug("TestEventCreate()");
|
|
|
|
class EventTester : public EventHandler
|
|
{
|
|
public:
|
|
virtual void GenerateEvent()
|
|
{
|
|
CPPUNIT_ASSERT(eg.CreateFile());
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
wxFileSystemWatcherEvent event(wxFSW_EVENT_CREATE);
|
|
event.SetPath(eg.m_file);
|
|
event.SetNewPath(eg.m_file);
|
|
return event;
|
|
}
|
|
};
|
|
|
|
EventTester tester;
|
|
|
|
wxLogTrace(wxTRACE_FSWATCHER, "TestEventCreate tester created()");
|
|
|
|
tester.Run();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TestEventDelete
|
|
// ----------------------------------------------------------------------------
|
|
void FileSystemWatcherTestCase::TestEventDelete()
|
|
{
|
|
wxLogDebug("TestEventDelete()");
|
|
|
|
class EventTester : public EventHandler
|
|
{
|
|
public:
|
|
virtual void GenerateEvent()
|
|
{
|
|
CPPUNIT_ASSERT(eg.DeleteFile());
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
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();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TestEventRename
|
|
// ----------------------------------------------------------------------------
|
|
void FileSystemWatcherTestCase::TestEventRename()
|
|
{
|
|
wxLogDebug("TestEventRename()");
|
|
|
|
class EventTester : public EventHandler
|
|
{
|
|
public:
|
|
virtual void GenerateEvent()
|
|
{
|
|
CPPUNIT_ASSERT(eg.RenameFile());
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
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
|
|
// ----------------------------------------------------------------------------
|
|
void FileSystemWatcherTestCase::TestEventModify()
|
|
{
|
|
wxLogDebug("TestEventModify()");
|
|
|
|
class EventTester : public EventHandler
|
|
{
|
|
public:
|
|
virtual void GenerateEvent()
|
|
{
|
|
CPPUNIT_ASSERT(eg.ModifyFile());
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
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();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TestEventAccess
|
|
// ----------------------------------------------------------------------------
|
|
void FileSystemWatcherTestCase::TestEventAccess()
|
|
{
|
|
wxLogDebug("TestEventAccess()");
|
|
|
|
class EventTester : public EventHandler
|
|
{
|
|
public:
|
|
virtual void GenerateEvent()
|
|
{
|
|
CPPUNIT_ASSERT(eg.ReadFile());
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
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();
|
|
}
|
|
|
|
#ifdef wxHAS_INOTIFY
|
|
// ----------------------------------------------------------------------------
|
|
// TestEventAttribute
|
|
// ----------------------------------------------------------------------------
|
|
void FileSystemWatcherTestCase::TestEventAttribute()
|
|
{
|
|
wxLogDebug("TestEventAttribute()");
|
|
|
|
class EventTester : public EventHandler
|
|
{
|
|
public:
|
|
virtual void GenerateEvent()
|
|
{
|
|
CPPUNIT_ASSERT(eg.TouchFile());
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
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
|
|
// ----------------------------------------------------------------------------
|
|
void FileSystemWatcherTestCase::TestSingleWatchtypeEvent()
|
|
{
|
|
wxLogDebug("TestSingleWatchtypeEvent()");
|
|
|
|
class EventTester : public EventHandler
|
|
{
|
|
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() : EventHandler(wxFSW_EVENT_ACCESS) {}
|
|
|
|
virtual void GenerateEvent()
|
|
{
|
|
// As wxFSW_EVENT_ACCESS is passed to the ctor only ReadFile() will
|
|
// generate an event. Without it they all will, and the test fails
|
|
CPPUNIT_ASSERT(eg.CreateFile());
|
|
CPPUNIT_ASSERT(eg.ModifyFile());
|
|
CPPUNIT_ASSERT(eg.ReadFile());
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
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
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
|
|
void FileSystemWatcherTestCase::TestTrees()
|
|
{
|
|
class TreeTester : public EventHandler
|
|
{
|
|
const size_t subdirs;
|
|
const size_t files;
|
|
|
|
public:
|
|
TreeTester() : subdirs(5), files(3) {}
|
|
|
|
void GrowTree(wxFileName dir
|
|
#ifdef __UNIX__
|
|
, bool withSymlinks = false
|
|
#endif
|
|
)
|
|
{
|
|
CPPUNIT_ASSERT(dir.Mkdir());
|
|
// Now add a subdir with an easy name to remember in WatchTree()
|
|
dir.AppendDir("child");
|
|
CPPUNIT_ASSERT(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));
|
|
CPPUNIT_ASSERT(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'
|
|
CPPUNIT_ASSERT_EQUAL(0,
|
|
symlink(wxString(prefix + "file1").c_str(),
|
|
wxString(prefix + "file.lnk").c_str()));
|
|
CPPUNIT_ASSERT_EQUAL(0,
|
|
symlink(child.GetFullPath().c_str(),
|
|
wxString(prefix + "dir.lnk").c_str()));
|
|
}
|
|
#endif // __UNIX__
|
|
}
|
|
}
|
|
|
|
void RmDir(wxFileName dir)
|
|
{
|
|
CPPUNIT_ASSERT(dir.DirExists());
|
|
|
|
CPPUNIT_ASSERT(dir.Rmdir(wxPATH_RMDIR_RECURSIVE));
|
|
}
|
|
|
|
void WatchDir(wxFileName dir)
|
|
{
|
|
CPPUNIT_ASSERT(m_watcher);
|
|
|
|
// Store the initial count; there may already be some watches
|
|
const int initial = m_watcher->GetWatchedPathsCount();
|
|
|
|
m_watcher->Add(dir);
|
|
CPPUNIT_ASSERT_EQUAL(initial + 1,
|
|
m_watcher->GetWatchedPathsCount());
|
|
}
|
|
|
|
void RemoveSingleWatch(wxFileName dir)
|
|
{
|
|
CPPUNIT_ASSERT(m_watcher);
|
|
|
|
const int initial = m_watcher->GetWatchedPathsCount();
|
|
|
|
m_watcher->Remove(dir);
|
|
CPPUNIT_ASSERT_EQUAL(initial - 1,
|
|
m_watcher->GetWatchedPathsCount());
|
|
}
|
|
|
|
void WatchTree(const wxFileName& dir)
|
|
{
|
|
CPPUNIT_ASSERT(m_watcher);
|
|
|
|
size_t treeitems = 1; // the trunk
|
|
#ifndef __WINDOWS__
|
|
// When there's no file mask, wxMSW sets 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__
|
|
|
|
// 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();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(initial + treeitems, plustree);
|
|
|
|
m_watcher->RemoveTree(dir);
|
|
CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
|
|
|
|
// 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
|
|
CPPUNIT_ASSERT(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
|
|
size_t fudge = 0;
|
|
#ifdef __WINDOWS__
|
|
fudge = 1;
|
|
#endif // __WINDOWS__
|
|
m_watcher->AddTree(dir);
|
|
CPPUNIT_ASSERT_EQUAL(plustree + fudge, m_watcher->GetWatchedPathsCount());
|
|
m_watcher->RemoveTree(child);
|
|
CPPUNIT_ASSERT(initial < m_watcher->GetWatchedPathsCount());
|
|
m_watcher->RemoveTree(dir);
|
|
CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
|
|
#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();
|
|
CPPUNIT_ASSERT(m_watcher->AddTree(fn));
|
|
CPPUNIT_ASSERT(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)
|
|
{
|
|
CPPUNIT_ASSERT(m_watcher);
|
|
CPPUNIT_ASSERT(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").
|
|
const size_t treeitems = subdirs + 2;
|
|
m_watcher->AddTree(dir, wxFSW_EVENT_ALL, "*.txt");
|
|
|
|
const int plustree = m_watcher->GetWatchedPathsCount();
|
|
CPPUNIT_ASSERT_EQUAL(initial + treeitems, plustree);
|
|
|
|
// RemoveTree should try to remove only those files that were added
|
|
m_watcher->RemoveTree(dir);
|
|
CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
|
|
}
|
|
|
|
void RemoveAllWatches()
|
|
{
|
|
CPPUNIT_ASSERT(m_watcher);
|
|
|
|
m_watcher->RemoveAll();
|
|
CPPUNIT_ASSERT_EQUAL(0, m_watcher->GetWatchedPathsCount());
|
|
}
|
|
|
|
virtual void GenerateEvent()
|
|
{
|
|
// We don't use this function for events. Just run the tests
|
|
|
|
wxFileName watchdir = EventGenerator::GetWatchDir();
|
|
CPPUNIT_ASSERT(watchdir.DirExists());
|
|
|
|
wxFileName treedir(watchdir);
|
|
treedir.AppendDir("treetrunk");
|
|
CPPUNIT_ASSERT(!treedir.DirExists());
|
|
|
|
wxFileName singledir(watchdir);
|
|
singledir.AppendDir("single");
|
|
CPPUNIT_ASSERT(!singledir.DirExists());
|
|
CPPUNIT_ASSERT(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(singledir);
|
|
RmDir(treedir);
|
|
|
|
Exit();
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
CPPUNIT_FAIL("Shouldn't be called");
|
|
|
|
return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR);
|
|
}
|
|
|
|
virtual void CheckResult()
|
|
{
|
|
// Do nothing. We override this to prevent receiving events in
|
|
// ExpectedEvent()
|
|
}
|
|
};
|
|
|
|
TreeTester tester;
|
|
tester.Run();
|
|
}
|
|
#endif // !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
|
|
|
|
|
|
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 EventHandler,
|
|
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()
|
|
{
|
|
m_watcher->Remove(EventGenerator::GetWatchDir());
|
|
CPPUNIT_ASSERT(eg.CreateFile());
|
|
}
|
|
|
|
virtual void CheckResult()
|
|
{
|
|
CPPUNIT_ASSERT( m_events.empty() );
|
|
}
|
|
|
|
virtual wxFileSystemWatcherEvent ExpectedEvent()
|
|
{
|
|
CPPUNIT_FAIL( "Shouldn't be called" );
|
|
|
|
return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR);
|
|
}
|
|
|
|
virtual void Notify()
|
|
{
|
|
SendIdle();
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
void FileSystemWatcherTestCase::TestNoEventsAfterRemove()
|
|
{
|
|
NoEventsAfterRemoveEventTester tester;
|
|
tester.Run();
|
|
}
|