Merge SOC2009_FSWATCHER branch into trunk.
Merges everything from the branch with only some minor changes, mostly renamed wxUSE_FSWATCHER_{INOTIFY,KQUEUE} to wxHAS_{INOTIFY,KQUEUE}. Add wxFileSystemWatcher and related classes. Also introduces wxEventLoopSource. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62474 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
589
tests/fswatcher/fswatchertest.cpp
Normal file
589
tests/fswatcher/fswatchertest.cpp
Normal file
@@ -0,0 +1,589 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
|
||||
#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(dir.GetDirs().Last() == "fswatcher_test");
|
||||
CPPUNIT_ASSERT(dir.Rmdir(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:
|
||||
const static int WAIT_DURATION = 3;
|
||||
|
||||
EventHandler() :
|
||||
eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0)
|
||||
{
|
||||
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
|
||||
CPPUNIT_ASSERT(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, wxFSW_EVENT_ALL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool KeepWaiting()
|
||||
{
|
||||
// did we receive event already?
|
||||
if (!tested)
|
||||
{
|
||||
// well, lets wait a bit more
|
||||
wxSleep(WAIT_DURATION);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool AfterWait()
|
||||
{
|
||||
// fail if still no events
|
||||
if (!tested)
|
||||
{
|
||||
wxString s;
|
||||
s.Printf("No events from watcher during %d seconds!",
|
||||
WAIT_DURATION);
|
||||
CPPUNIT_FAIL((const char*)s);
|
||||
}
|
||||
|
||||
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 bool CheckResult()
|
||||
{
|
||||
CPPUNIT_ASSERT_EQUAL( 1, m_events.size() );
|
||||
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());
|
||||
CPPUNIT_ASSERT_EQUAL(expected.GetChangeType(), e->GetChangeType());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
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 );
|
||||
|
||||
// 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 __WXMSW__
|
||||
CPPUNIT_TEST( TestEventAccess );
|
||||
#endif // __WXMSW__
|
||||
#endif // !wxHAS_KQUEUE
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void TestEventCreate();
|
||||
void TestEventDelete();
|
||||
void TestEventRename();
|
||||
void TestEventModify();
|
||||
void TestEventAccess();
|
||||
|
||||
DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase)
|
||||
};
|
||||
|
||||
// register in the unnamed registry so that these tests are run by default
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase );
|
||||
|
||||
// also include in it's 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();
|
||||
}
|
Reference in New Issue
Block a user