fswatchertest.cpp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name: tests/fswatcher/fswatchertest.cpp
  3. // Purpose: wxFileSystemWatcher unit test
  4. // Author: Bartosz Bekier
  5. // Created: 2009-06-11
  6. // Copyright: (c) 2009 Bartosz Bekier
  7. ///////////////////////////////////////////////////////////////////////////////
  8. // ----------------------------------------------------------------------------
  9. // headers
  10. // ----------------------------------------------------------------------------
  11. #include "testprec.h"
  12. #ifdef __BORLANDC__
  13. #pragma hdrstop
  14. #endif
  15. #ifndef WX_PRECOMP
  16. #include "wx/timer.h"
  17. #endif
  18. #include "wx/evtloop.h"
  19. #include "wx/filename.h"
  20. #include "wx/filefn.h"
  21. #include "wx/stdpaths.h"
  22. #include "wx/fswatcher.h"
  23. #include "testfile.h"
  24. // ----------------------------------------------------------------------------
  25. // local functions
  26. // ----------------------------------------------------------------------------
  27. // class generating file system events
  28. class EventGenerator
  29. {
  30. public:
  31. static EventGenerator& Get()
  32. {
  33. if (!ms_instance)
  34. ms_instance = new EventGenerator(GetWatchDir());
  35. return *ms_instance;
  36. }
  37. EventGenerator(const wxFileName& path) : m_base(path)
  38. {
  39. m_old = wxFileName();
  40. m_file = RandomName();
  41. m_new = RandomName();
  42. }
  43. // operations
  44. bool CreateFile()
  45. {
  46. wxFile file(m_file.GetFullPath(), wxFile::write);
  47. return file.IsOpened() && m_file.FileExists();
  48. }
  49. bool RenameFile()
  50. {
  51. CPPUNIT_ASSERT(m_file.FileExists());
  52. wxLogDebug("Renaming %s=>%s", m_file.GetFullPath(), m_new.GetFullPath());
  53. bool ret = wxRenameFile(m_file.GetFullPath(), m_new.GetFullPath());
  54. if (ret)
  55. {
  56. m_old = m_file;
  57. m_file = m_new;
  58. m_new = RandomName();
  59. }
  60. return ret;
  61. }
  62. bool DeleteFile()
  63. {
  64. CPPUNIT_ASSERT(m_file.FileExists());
  65. bool ret = wxRemoveFile(m_file.GetFullPath());
  66. if (ret)
  67. {
  68. m_old = m_file;
  69. m_file = m_new;
  70. m_new = RandomName();
  71. }
  72. return ret;
  73. }
  74. bool TouchFile()
  75. {
  76. return m_file.Touch();
  77. }
  78. bool ReadFile()
  79. {
  80. wxFile f(m_file.GetFullPath());
  81. CPPUNIT_ASSERT(f.IsOpened());
  82. char buf[1];
  83. ssize_t count = f.Read(buf, sizeof(buf));
  84. CPPUNIT_ASSERT(count > 0);
  85. return true;
  86. }
  87. bool ModifyFile()
  88. {
  89. CPPUNIT_ASSERT(m_file.FileExists());
  90. wxFile file(m_file.GetFullPath(), wxFile::write_append);
  91. CPPUNIT_ASSERT(file.IsOpened());
  92. CPPUNIT_ASSERT(file.Write("Words of Wisdom, Lloyd. Words of wisdom\n"));
  93. return file.Close();
  94. }
  95. // helpers
  96. wxFileName RandomName(int length = 10)
  97. {
  98. return RandomName(m_base, length);
  99. }
  100. // static helpers
  101. static const wxFileName& GetWatchDir()
  102. {
  103. static wxFileName dir;
  104. if (dir.DirExists())
  105. return dir;
  106. wxString tmp = wxStandardPaths::Get().GetTempDir();
  107. dir.AssignDir(tmp);
  108. // XXX look for more unique name? there is no function to generate
  109. // unique filename, the file always get created...
  110. dir.AppendDir("fswatcher_test");
  111. CPPUNIT_ASSERT(!dir.DirExists());
  112. CPPUNIT_ASSERT(dir.Mkdir());
  113. return dir;
  114. }
  115. static void RemoveWatchDir()
  116. {
  117. wxFileName dir = GetWatchDir();
  118. CPPUNIT_ASSERT(dir.DirExists());
  119. // just to be really sure we know what we remove
  120. CPPUNIT_ASSERT_EQUAL( "fswatcher_test", dir.GetDirs().Last() );
  121. // FIXME-VC6: using non-static Rmdir() results in ICE
  122. CPPUNIT_ASSERT( wxFileName::Rmdir(dir.GetFullPath(), wxPATH_RMDIR_RECURSIVE) );
  123. }
  124. static wxFileName RandomName(const wxFileName& base, int length = 10)
  125. {
  126. static int ALFA_CNT = 'z' - 'a';
  127. wxString s;
  128. for (int i = 0 ; i < length; ++i)
  129. {
  130. char c = 'a' + (rand() % ALFA_CNT);
  131. s += c;
  132. }
  133. return wxFileName(base.GetFullPath(), s);
  134. }
  135. public:
  136. wxFileName m_base; // base dir for doing operations
  137. wxFileName m_file; // current file name
  138. wxFileName m_old; // previous file name
  139. wxFileName m_new; // name after renaming
  140. protected:
  141. static EventGenerator* ms_instance;
  142. };
  143. EventGenerator* EventGenerator::ms_instance = 0;
  144. // custom event handler
  145. class EventHandler : public wxEvtHandler
  146. {
  147. public:
  148. enum { WAIT_DURATION = 3 };
  149. EventHandler(int types = wxFSW_EVENT_ALL) :
  150. eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0),
  151. m_eventTypes(types)
  152. {
  153. m_loop = new wxEventLoop();
  154. Connect(wxEVT_IDLE, wxIdleEventHandler(EventHandler::OnIdle));
  155. Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(
  156. EventHandler::OnFileSystemEvent));
  157. }
  158. virtual ~EventHandler()
  159. {
  160. delete m_watcher;
  161. if (m_loop)
  162. {
  163. if (m_loop->IsRunning())
  164. m_loop->Exit();
  165. delete m_loop;
  166. }
  167. }
  168. void Exit()
  169. {
  170. m_loop->Exit();
  171. }
  172. // sends idle event, so we get called in a moment
  173. void SendIdle()
  174. {
  175. wxIdleEvent* e = new wxIdleEvent();
  176. QueueEvent(e);
  177. }
  178. void Run()
  179. {
  180. SendIdle();
  181. m_loop->Run();
  182. }
  183. void OnIdle(wxIdleEvent& /*evt*/)
  184. {
  185. bool more = Action();
  186. m_count++;
  187. if (more)
  188. {
  189. SendIdle();
  190. }
  191. }
  192. // returns whether we should produce more idle events
  193. virtual bool Action()
  194. {
  195. switch (m_count)
  196. {
  197. case 0:
  198. CPPUNIT_ASSERT(Init());
  199. break;
  200. case 1:
  201. GenerateEvent();
  202. break;
  203. case 2:
  204. // actual test
  205. CheckResult();
  206. Exit();
  207. break;
  208. // TODO a mechanism that will break the loop in case we
  209. // don't receive a file system event
  210. // this below doesn't quite work, so all tests must pass :-)
  211. #if 0
  212. case 2:
  213. m_loop.Yield();
  214. m_loop.WakeUp();
  215. CPPUNIT_ASSERT(KeepWaiting());
  216. m_loop.Yield();
  217. break;
  218. case 3:
  219. break;
  220. case 4:
  221. CPPUNIT_ASSERT(AfterWait());
  222. break;
  223. #endif
  224. } // switch (m_count)
  225. return m_count <= 0;
  226. }
  227. virtual bool Init()
  228. {
  229. // test we're good to go
  230. CPPUNIT_ASSERT(wxEventLoopBase::GetActive());
  231. // XXX only now can we construct Watcher, because we need
  232. // active loop here
  233. m_watcher = new wxFileSystemWatcher();
  234. m_watcher->SetOwner(this);
  235. // add dir to be watched
  236. wxFileName dir = EventGenerator::GetWatchDir();
  237. CPPUNIT_ASSERT(m_watcher->Add(dir, m_eventTypes));
  238. return true;
  239. }
  240. virtual bool KeepWaiting()
  241. {
  242. // did we receive event already?
  243. if (!tested)
  244. {
  245. // well, let's wait a bit more
  246. wxSleep(WAIT_DURATION);
  247. }
  248. return true;
  249. }
  250. virtual bool AfterWait()
  251. {
  252. // fail if still no events
  253. WX_ASSERT_MESSAGE
  254. (
  255. ("No events during %d seconds!", static_cast<int>(WAIT_DURATION)),
  256. tested
  257. );
  258. return true;
  259. }
  260. virtual void OnFileSystemEvent(wxFileSystemWatcherEvent& evt)
  261. {
  262. wxLogDebug("--- %s ---", evt.ToString());
  263. m_lastEvent = wxDynamicCast(evt.Clone(), wxFileSystemWatcherEvent);
  264. m_events.Add(m_lastEvent);
  265. // test finished
  266. SendIdle();
  267. tested = true;
  268. }
  269. virtual void CheckResult()
  270. {
  271. CPPUNIT_ASSERT_MESSAGE( "No events received", !m_events.empty() );
  272. const wxFileSystemWatcherEvent * const e = m_events.front();
  273. // this is our "reference event"
  274. const wxFileSystemWatcherEvent expected = ExpectedEvent();
  275. CPPUNIT_ASSERT_EQUAL( expected.GetChangeType(), e->GetChangeType() );
  276. CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER, e->GetEventType());
  277. // XXX this needs change
  278. CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN, e->GetEventCategory());
  279. CPPUNIT_ASSERT_EQUAL(expected.GetPath(), e->GetPath());
  280. CPPUNIT_ASSERT_EQUAL(expected.GetNewPath(), e->GetNewPath());
  281. // Under MSW extra modification events are sometimes reported after a
  282. // rename and we just can't get rid of them, so ignore them in this
  283. // test if they do happen.
  284. if ( e->GetChangeType() == wxFSW_EVENT_RENAME &&
  285. m_events.size() == 2 )
  286. {
  287. const wxFileSystemWatcherEvent* const e2 = m_events.back();
  288. if ( e2->GetChangeType() == wxFSW_EVENT_MODIFY &&
  289. e2->GetPath() == e->GetNewPath() )
  290. {
  291. // This is a modify event for the new file, ignore it.
  292. return;
  293. }
  294. }
  295. WX_ASSERT_EQUAL_MESSAGE
  296. (
  297. (
  298. "Extra events received, last one is of type %x, path=\"%s\" "
  299. "(the original event was for \"%s\" (\"%s\")",
  300. m_events.back()->GetChangeType(),
  301. m_events.back()->GetPath().GetFullPath(),
  302. e->GetPath().GetFullPath(),
  303. e->GetNewPath().GetFullPath()
  304. ),
  305. 1, m_events.size()
  306. );
  307. }
  308. virtual void GenerateEvent() = 0;
  309. virtual wxFileSystemWatcherEvent ExpectedEvent() = 0;
  310. protected:
  311. EventGenerator& eg;
  312. wxEventLoopBase* m_loop; // loop reference
  313. int m_count; // idle events count
  314. wxFileSystemWatcher* m_watcher;
  315. int m_eventTypes; // Which event-types to watch. Normally all of them
  316. bool tested; // indicates, whether we have already passed the test
  317. #include "wx/arrimpl.cpp"
  318. WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent*, wxArrayEvent);
  319. wxArrayEvent m_events;
  320. wxFileSystemWatcherEvent* m_lastEvent;
  321. };
  322. // ----------------------------------------------------------------------------
  323. // test class
  324. // ----------------------------------------------------------------------------
  325. class FileSystemWatcherTestCase : public CppUnit::TestCase
  326. {
  327. public:
  328. FileSystemWatcherTestCase() { }
  329. virtual void setUp();
  330. virtual void tearDown();
  331. protected:
  332. wxEventLoopBase* m_loop;
  333. private:
  334. CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase );
  335. CPPUNIT_TEST( TestEventCreate );
  336. CPPUNIT_TEST( TestEventDelete );
  337. #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
  338. CPPUNIT_TEST( TestTrees );
  339. #endif
  340. // kqueue-based implementation doesn't collapse create/delete pairs in
  341. // renames and doesn't detect neither modifications nor access to the
  342. // files reliably currently so disable these tests
  343. //
  344. // FIXME: fix the code and reenable them
  345. #ifndef wxHAS_KQUEUE
  346. CPPUNIT_TEST( TestEventRename );
  347. CPPUNIT_TEST( TestEventModify );
  348. // MSW implementation doesn't detect file access events currently
  349. #ifndef __WINDOWS__
  350. CPPUNIT_TEST( TestEventAccess );
  351. #endif // __WINDOWS__
  352. #endif // !wxHAS_KQUEUE
  353. #ifdef wxHAS_INOTIFY
  354. CPPUNIT_TEST( TestEventAttribute );
  355. CPPUNIT_TEST( TestSingleWatchtypeEvent );
  356. #endif // wxHAS_INOTIFY
  357. CPPUNIT_TEST( TestNoEventsAfterRemove );
  358. CPPUNIT_TEST_SUITE_END();
  359. void TestEventCreate();
  360. void TestEventDelete();
  361. void TestEventRename();
  362. void TestEventModify();
  363. void TestEventAccess();
  364. #ifdef wxHAS_INOTIFY
  365. void TestEventAttribute();
  366. void TestSingleWatchtypeEvent();
  367. #endif // wxHAS_INOTIFY
  368. #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
  369. void TestTrees(); // Visual C++ 6 can't build this
  370. #endif
  371. void TestNoEventsAfterRemove();
  372. DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase)
  373. };
  374. // the test currently hangs under OS X for some reason and this prevents tests
  375. // ran by buildbot from completing so disable it until someone has time to
  376. // debug it
  377. //
  378. // FIXME: debug and fix this!
  379. #ifndef __WXOSX__
  380. // register in the unnamed registry so that these tests are run by default
  381. CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase );
  382. #endif
  383. // also include in its own registry so that these tests can be run alone
  384. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase,
  385. "FileSystemWatcherTestCase" );
  386. void FileSystemWatcherTestCase::setUp()
  387. {
  388. wxLog::AddTraceMask(wxTRACE_FSWATCHER);
  389. EventGenerator::Get().GetWatchDir();
  390. }
  391. void FileSystemWatcherTestCase::tearDown()
  392. {
  393. EventGenerator::Get().RemoveWatchDir();
  394. }
  395. // ----------------------------------------------------------------------------
  396. // TestEventCreate
  397. // ----------------------------------------------------------------------------
  398. void FileSystemWatcherTestCase::TestEventCreate()
  399. {
  400. wxLogDebug("TestEventCreate()");
  401. class EventTester : public EventHandler
  402. {
  403. public:
  404. virtual void GenerateEvent()
  405. {
  406. CPPUNIT_ASSERT(eg.CreateFile());
  407. }
  408. virtual wxFileSystemWatcherEvent ExpectedEvent()
  409. {
  410. wxFileSystemWatcherEvent event(wxFSW_EVENT_CREATE);
  411. event.SetPath(eg.m_file);
  412. event.SetNewPath(eg.m_file);
  413. return event;
  414. }
  415. };
  416. EventTester tester;
  417. wxLogTrace(wxTRACE_FSWATCHER, "TestEventCreate tester created()");
  418. tester.Run();
  419. }
  420. // ----------------------------------------------------------------------------
  421. // TestEventDelete
  422. // ----------------------------------------------------------------------------
  423. void FileSystemWatcherTestCase::TestEventDelete()
  424. {
  425. wxLogDebug("TestEventDelete()");
  426. class EventTester : public EventHandler
  427. {
  428. public:
  429. virtual void GenerateEvent()
  430. {
  431. CPPUNIT_ASSERT(eg.DeleteFile());
  432. }
  433. virtual wxFileSystemWatcherEvent ExpectedEvent()
  434. {
  435. wxFileSystemWatcherEvent event(wxFSW_EVENT_DELETE);
  436. event.SetPath(eg.m_old);
  437. // CHECK maybe new path here could be NULL or sth?
  438. event.SetNewPath(eg.m_old);
  439. return event;
  440. }
  441. };
  442. // we need to create a file now, so we can delete it
  443. EventGenerator::Get().CreateFile();
  444. EventTester tester;
  445. tester.Run();
  446. }
  447. // ----------------------------------------------------------------------------
  448. // TestEventRename
  449. // ----------------------------------------------------------------------------
  450. void FileSystemWatcherTestCase::TestEventRename()
  451. {
  452. wxLogDebug("TestEventRename()");
  453. class EventTester : public EventHandler
  454. {
  455. public:
  456. virtual void GenerateEvent()
  457. {
  458. CPPUNIT_ASSERT(eg.RenameFile());
  459. }
  460. virtual wxFileSystemWatcherEvent ExpectedEvent()
  461. {
  462. wxFileSystemWatcherEvent event(wxFSW_EVENT_RENAME);
  463. event.SetPath(eg.m_old);
  464. event.SetNewPath(eg.m_file);
  465. return event;
  466. }
  467. };
  468. // need a file to rename later
  469. EventGenerator::Get().CreateFile();
  470. EventTester tester;
  471. tester.Run();
  472. }
  473. // ----------------------------------------------------------------------------
  474. // TestEventModify
  475. // ----------------------------------------------------------------------------
  476. void FileSystemWatcherTestCase::TestEventModify()
  477. {
  478. wxLogDebug("TestEventModify()");
  479. class EventTester : public EventHandler
  480. {
  481. public:
  482. virtual void GenerateEvent()
  483. {
  484. CPPUNIT_ASSERT(eg.ModifyFile());
  485. }
  486. virtual wxFileSystemWatcherEvent ExpectedEvent()
  487. {
  488. wxFileSystemWatcherEvent event(wxFSW_EVENT_MODIFY);
  489. event.SetPath(eg.m_file);
  490. event.SetNewPath(eg.m_file);
  491. return event;
  492. }
  493. };
  494. // we need to create a file to modify
  495. EventGenerator::Get().CreateFile();
  496. EventTester tester;
  497. tester.Run();
  498. }
  499. // ----------------------------------------------------------------------------
  500. // TestEventAccess
  501. // ----------------------------------------------------------------------------
  502. void FileSystemWatcherTestCase::TestEventAccess()
  503. {
  504. wxLogDebug("TestEventAccess()");
  505. class EventTester : public EventHandler
  506. {
  507. public:
  508. virtual void GenerateEvent()
  509. {
  510. CPPUNIT_ASSERT(eg.ReadFile());
  511. }
  512. virtual wxFileSystemWatcherEvent ExpectedEvent()
  513. {
  514. wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS);
  515. event.SetPath(eg.m_file);
  516. event.SetNewPath(eg.m_file);
  517. return event;
  518. }
  519. };
  520. // we need to create a file to read from it and write sth to it
  521. EventGenerator::Get().CreateFile();
  522. EventGenerator::Get().ModifyFile();
  523. EventTester tester;
  524. tester.Run();
  525. }
  526. #ifdef wxHAS_INOTIFY
  527. // ----------------------------------------------------------------------------
  528. // TestEventAttribute
  529. // ----------------------------------------------------------------------------
  530. void FileSystemWatcherTestCase::TestEventAttribute()
  531. {
  532. wxLogDebug("TestEventAttribute()");
  533. class EventTester : public EventHandler
  534. {
  535. public:
  536. virtual void GenerateEvent()
  537. {
  538. CPPUNIT_ASSERT(eg.TouchFile());
  539. }
  540. virtual wxFileSystemWatcherEvent ExpectedEvent()
  541. {
  542. wxFileSystemWatcherEvent event(wxFSW_EVENT_ATTRIB);
  543. event.SetPath(eg.m_file);
  544. event.SetNewPath(eg.m_file);
  545. return event;
  546. }
  547. };
  548. // we need to create a file to touch
  549. EventGenerator::Get().CreateFile();
  550. EventTester tester;
  551. tester.Run();
  552. }
  553. // ----------------------------------------------------------------------------
  554. // TestSingleWatchtypeEvent: Watch only wxFSW_EVENT_ACCESS
  555. // ----------------------------------------------------------------------------
  556. void FileSystemWatcherTestCase::TestSingleWatchtypeEvent()
  557. {
  558. wxLogDebug("TestSingleWatchtypeEvent()");
  559. class EventTester : public EventHandler
  560. {
  561. public:
  562. // We could pass wxFSW_EVENT_CREATE or MODIFY instead, but not RENAME or
  563. // DELETE as the event path fields would be wrong in CheckResult()
  564. EventTester() : EventHandler(wxFSW_EVENT_ACCESS) {}
  565. virtual void GenerateEvent()
  566. {
  567. // As wxFSW_EVENT_ACCESS is passed to the ctor only ReadFile() will
  568. // generate an event. Without it they all will, and the test fails
  569. CPPUNIT_ASSERT(eg.CreateFile());
  570. CPPUNIT_ASSERT(eg.ModifyFile());
  571. CPPUNIT_ASSERT(eg.ReadFile());
  572. }
  573. virtual wxFileSystemWatcherEvent ExpectedEvent()
  574. {
  575. wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS);
  576. event.SetPath(eg.m_file);
  577. event.SetNewPath(eg.m_file);
  578. return event;
  579. }
  580. };
  581. EventTester tester;
  582. tester.Run();
  583. }
  584. #endif // wxHAS_INOTIFY
  585. // ----------------------------------------------------------------------------
  586. // TestTrees
  587. // ----------------------------------------------------------------------------
  588. #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
  589. void FileSystemWatcherTestCase::TestTrees()
  590. {
  591. class TreeTester : public EventHandler
  592. {
  593. const size_t subdirs;
  594. const size_t files;
  595. public:
  596. TreeTester() : subdirs(5), files(3) {}
  597. void GrowTree(wxFileName dir
  598. #ifdef __UNIX__
  599. , bool withSymlinks = false
  600. #endif
  601. )
  602. {
  603. CPPUNIT_ASSERT(dir.Mkdir());
  604. // Now add a subdir with an easy name to remember in WatchTree()
  605. dir.AppendDir("child");
  606. CPPUNIT_ASSERT(dir.Mkdir());
  607. wxFileName child(dir); // Create a copy to which to symlink
  608. // Create a branch of 5 numbered subdirs, each containing 3
  609. // numbered files
  610. for ( unsigned d = 0; d < subdirs; ++d )
  611. {
  612. dir.AppendDir(wxString::Format("subdir%u", d+1));
  613. CPPUNIT_ASSERT(dir.Mkdir());
  614. const wxString prefix = dir.GetPathWithSep();
  615. const wxString ext[] = { ".txt", ".log", "" };
  616. for ( unsigned f = 0; f < files; ++f )
  617. {
  618. // Just create the files.
  619. wxFile(prefix + wxString::Format("file%u", f+1) + ext[f],
  620. wxFile::write);
  621. }
  622. #if defined(__UNIX__)
  623. if ( withSymlinks )
  624. {
  625. // Create a symlink to a files, and another to 'child'
  626. CPPUNIT_ASSERT_EQUAL(0,
  627. symlink(wxString(prefix + "file1").c_str(),
  628. wxString(prefix + "file.lnk").c_str()));
  629. CPPUNIT_ASSERT_EQUAL(0,
  630. symlink(child.GetFullPath().c_str(),
  631. wxString(prefix + "dir.lnk").c_str()));
  632. }
  633. #endif // __UNIX__
  634. }
  635. }
  636. void RmDir(wxFileName dir)
  637. {
  638. CPPUNIT_ASSERT(dir.DirExists());
  639. CPPUNIT_ASSERT(dir.Rmdir(wxPATH_RMDIR_RECURSIVE));
  640. }
  641. void WatchDir(wxFileName dir)
  642. {
  643. CPPUNIT_ASSERT(m_watcher);
  644. // Store the initial count; there may already be some watches
  645. const int initial = m_watcher->GetWatchedPathsCount();
  646. m_watcher->Add(dir);
  647. CPPUNIT_ASSERT_EQUAL(initial + 1,
  648. m_watcher->GetWatchedPathsCount());
  649. }
  650. void RemoveSingleWatch(wxFileName dir)
  651. {
  652. CPPUNIT_ASSERT(m_watcher);
  653. const int initial = m_watcher->GetWatchedPathsCount();
  654. m_watcher->Remove(dir);
  655. CPPUNIT_ASSERT_EQUAL(initial - 1,
  656. m_watcher->GetWatchedPathsCount());
  657. }
  658. void WatchTree(const wxFileName& dir)
  659. {
  660. CPPUNIT_ASSERT(m_watcher);
  661. size_t treeitems = 1; // the trunk
  662. #ifndef __WINDOWS__
  663. // When there's no file mask, wxMSW sets a single watch
  664. // on the trunk which is implemented recursively.
  665. // wxGTK always sets an additional watch for each subdir
  666. treeitems += subdirs + 1; // +1 for 'child'
  667. #endif // __WINDOWS__
  668. // Store the initial count; there may already be some watches
  669. const int initial = m_watcher->GetWatchedPathsCount();
  670. GrowTree(dir);
  671. m_watcher->AddTree(dir);
  672. const int plustree = m_watcher->GetWatchedPathsCount();
  673. CPPUNIT_ASSERT_EQUAL(initial + treeitems, plustree);
  674. m_watcher->RemoveTree(dir);
  675. CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
  676. // Now test the refcount mechanism by watching items more than once
  677. wxFileName child(dir);
  678. child.AppendDir("child");
  679. m_watcher->AddTree(child);
  680. // Check some watches were added; we don't care about the number
  681. CPPUNIT_ASSERT(initial < m_watcher->GetWatchedPathsCount());
  682. // Now watch the whole tree and check that the count is the same
  683. // as it was the first time, despite also adding 'child' separately
  684. // Except that in wxMSW this isn't true: each watch will be a
  685. // single, recursive dir; so fudge the count
  686. size_t fudge = 0;
  687. #ifdef __WINDOWS__
  688. fudge = 1;
  689. #endif // __WINDOWS__
  690. m_watcher->AddTree(dir);
  691. CPPUNIT_ASSERT_EQUAL(plustree + fudge, m_watcher->GetWatchedPathsCount());
  692. m_watcher->RemoveTree(child);
  693. CPPUNIT_ASSERT(initial < m_watcher->GetWatchedPathsCount());
  694. m_watcher->RemoveTree(dir);
  695. CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
  696. #if defined(__UNIX__)
  697. // Finally, test a tree containing internal symlinks
  698. RmDir(dir);
  699. GrowTree(dir, true /* test symlinks */);
  700. // Without the DontFollowLink() call AddTree() would now assert
  701. // (and without the assert, it would infinitely loop)
  702. wxFileName fn = dir;
  703. fn.DontFollowLink();
  704. CPPUNIT_ASSERT(m_watcher->AddTree(fn));
  705. CPPUNIT_ASSERT(m_watcher->RemoveTree(fn));
  706. // Regrow the tree without symlinks, ready for the next test
  707. RmDir(dir);
  708. GrowTree(dir, false);
  709. #endif // __UNIX__
  710. }
  711. void WatchTreeWithFilespec(const wxFileName& dir)
  712. {
  713. CPPUNIT_ASSERT(m_watcher);
  714. CPPUNIT_ASSERT(dir.DirExists()); // Was built in WatchTree()
  715. // Store the initial count; there may already be some watches
  716. const int initial = m_watcher->GetWatchedPathsCount();
  717. // When we use a filter, both wxMSW and wxGTK implementations set
  718. // an additional watch for each subdir (+1 for the root dir itself
  719. // and another +1 for "child").
  720. const size_t treeitems = subdirs + 2;
  721. m_watcher->AddTree(dir, wxFSW_EVENT_ALL, "*.txt");
  722. const int plustree = m_watcher->GetWatchedPathsCount();
  723. CPPUNIT_ASSERT_EQUAL(initial + treeitems, plustree);
  724. // RemoveTree should try to remove only those files that were added
  725. m_watcher->RemoveTree(dir);
  726. CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
  727. }
  728. void RemoveAllWatches()
  729. {
  730. CPPUNIT_ASSERT(m_watcher);
  731. m_watcher->RemoveAll();
  732. CPPUNIT_ASSERT_EQUAL(0, m_watcher->GetWatchedPathsCount());
  733. }
  734. virtual void GenerateEvent()
  735. {
  736. // We don't use this function for events. Just run the tests
  737. wxFileName watchdir = EventGenerator::GetWatchDir();
  738. CPPUNIT_ASSERT(watchdir.DirExists());
  739. wxFileName treedir(watchdir);
  740. treedir.AppendDir("treetrunk");
  741. CPPUNIT_ASSERT(!treedir.DirExists());
  742. wxFileName singledir(watchdir);
  743. singledir.AppendDir("single");
  744. CPPUNIT_ASSERT(!singledir.DirExists());
  745. CPPUNIT_ASSERT(singledir.Mkdir());
  746. WatchDir(singledir);
  747. WatchTree(treedir);
  748. // Now test adding and removing a tree using a filespec
  749. // wxMSW uses the generic method to add matching files; which fails
  750. // as it doesn't support adding files :/ So disable the test
  751. #ifndef __WINDOWS__
  752. WatchTreeWithFilespec(treedir);
  753. #endif // __WINDOWS__
  754. RemoveSingleWatch(singledir);
  755. // Add it back again, ready to test RemoveAll()
  756. WatchDir(singledir);
  757. RemoveAllWatches();
  758. // Clean up
  759. RmDir(singledir);
  760. RmDir(treedir);
  761. Exit();
  762. }
  763. virtual wxFileSystemWatcherEvent ExpectedEvent()
  764. {
  765. CPPUNIT_FAIL("Shouldn't be called");
  766. return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR);
  767. }
  768. virtual void CheckResult()
  769. {
  770. // Do nothing. We override this to prevent receiving events in
  771. // ExpectedEvent()
  772. }
  773. };
  774. TreeTester tester;
  775. tester.Run();
  776. }
  777. #endif // !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7)
  778. namespace
  779. {
  780. // We can't define this class locally inside TestNoEventsAfterRemove() for some
  781. // reason with g++ 4.0 under OS X 10.5, it results in the following mysterious
  782. // error:
  783. //
  784. // /var/tmp//ccTkNCkc.s:unknown:Non-global symbol:
  785. // __ZThn80_ZN25FileSystemWatcherTestCase23TestNoEventsAfterRemoveEvEN11EventTester6NotifyEv.eh
  786. // can't be a weak_definition
  787. //
  788. // So define this class outside the function instead.
  789. class NoEventsAfterRemoveEventTester : public EventHandler,
  790. public wxTimer
  791. {
  792. public:
  793. NoEventsAfterRemoveEventTester()
  794. {
  795. // We need to use an inactivity timer as we never get any file
  796. // system events in this test, so we consider that the test is
  797. // finished when this 1s timeout expires instead of, as usual,
  798. // stopping after getting the file system events.
  799. Start(1000, true);
  800. }
  801. virtual void GenerateEvent()
  802. {
  803. m_watcher->Remove(EventGenerator::GetWatchDir());
  804. CPPUNIT_ASSERT(eg.CreateFile());
  805. }
  806. virtual void CheckResult()
  807. {
  808. CPPUNIT_ASSERT( m_events.empty() );
  809. }
  810. virtual wxFileSystemWatcherEvent ExpectedEvent()
  811. {
  812. CPPUNIT_FAIL( "Shouldn't be called" );
  813. return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR);
  814. }
  815. virtual void Notify()
  816. {
  817. SendIdle();
  818. }
  819. };
  820. } // anonymous namespace
  821. void FileSystemWatcherTestCase::TestNoEventsAfterRemove()
  822. {
  823. NoEventsAfterRemoveEventTester tester;
  824. tester.Run();
  825. }