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