]>
Commit | Line | Data |
---|---|---|
6b8ef0b3 VZ |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Name: tests/fswatcher/fswatchertest.cpp | |
3 | // Purpose: wxFileSystemWatcher unit test | |
4 | // Author: Bartosz Bekier | |
5 | // Created: 2009-06-11 | |
6b8ef0b3 VZ |
6 | // Copyright: (c) 2009 Bartosz Bekier |
7 | /////////////////////////////////////////////////////////////////////////////// | |
8 | ||
9 | // ---------------------------------------------------------------------------- | |
10 | // headers | |
11 | // ---------------------------------------------------------------------------- | |
12 | ||
13 | #include "testprec.h" | |
14 | ||
15 | #ifdef __BORLANDC__ | |
16 | #pragma hdrstop | |
17 | #endif | |
18 | ||
6a9455d3 VZ |
19 | #ifndef WX_PRECOMP |
20 | #include "wx/timer.h" | |
21 | #endif | |
22 | ||
6b8ef0b3 VZ |
23 | #include "wx/evtloop.h" |
24 | #include "wx/filename.h" | |
25 | #include "wx/filefn.h" | |
26 | #include "wx/stdpaths.h" | |
27 | #include "wx/fswatcher.h" | |
28 | ||
29 | #include "testfile.h" | |
30 | ||
31 | // ---------------------------------------------------------------------------- | |
32 | // local functions | |
33 | // ---------------------------------------------------------------------------- | |
34 | ||
35 | // class generating file system events | |
36 | class EventGenerator | |
37 | { | |
38 | public: | |
39 | static EventGenerator& Get() | |
40 | { | |
41 | if (!ms_instance) | |
42 | ms_instance = new EventGenerator(GetWatchDir()); | |
43 | ||
44 | return *ms_instance; | |
45 | } | |
46 | ||
47 | EventGenerator(const wxFileName& path) : m_base(path) | |
48 | { | |
49 | m_old = wxFileName(); | |
50 | m_file = RandomName(); | |
51 | m_new = RandomName(); | |
52 | } | |
53 | ||
54 | // operations | |
55 | bool CreateFile() | |
56 | { | |
57 | wxFile file(m_file.GetFullPath(), wxFile::write); | |
58 | return file.IsOpened() && m_file.FileExists(); | |
59 | } | |
60 | ||
61 | bool RenameFile() | |
62 | { | |
63 | CPPUNIT_ASSERT(m_file.FileExists()); | |
64 | ||
65 | wxLogDebug("Renaming %s=>%s", m_file.GetFullPath(), m_new.GetFullPath()); | |
66 | ||
67 | bool ret = wxRenameFile(m_file.GetFullPath(), m_new.GetFullPath()); | |
68 | if (ret) | |
69 | { | |
70 | m_old = m_file; | |
71 | m_file = m_new; | |
72 | m_new = RandomName(); | |
73 | } | |
74 | ||
75 | return ret; | |
76 | } | |
77 | ||
78 | bool DeleteFile() | |
79 | { | |
80 | CPPUNIT_ASSERT(m_file.FileExists()); | |
81 | ||
82 | bool ret = wxRemoveFile(m_file.GetFullPath()); | |
83 | if (ret) | |
84 | { | |
85 | m_old = m_file; | |
86 | m_file = m_new; | |
87 | m_new = RandomName(); | |
88 | } | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | bool TouchFile() | |
94 | { | |
95 | return m_file.Touch(); | |
96 | } | |
97 | ||
98 | bool ReadFile() | |
99 | { | |
100 | wxFile f(m_file.GetFullPath()); | |
101 | CPPUNIT_ASSERT(f.IsOpened()); | |
102 | ||
103 | char buf[1]; | |
104 | ssize_t count = f.Read(buf, sizeof(buf)); | |
105 | CPPUNIT_ASSERT(count > 0); | |
106 | ||
107 | return true; | |
108 | } | |
109 | ||
110 | bool ModifyFile() | |
111 | { | |
112 | CPPUNIT_ASSERT(m_file.FileExists()); | |
113 | ||
114 | wxFile file(m_file.GetFullPath(), wxFile::write_append); | |
115 | CPPUNIT_ASSERT(file.IsOpened()); | |
116 | ||
117 | CPPUNIT_ASSERT(file.Write("Words of Wisdom, Lloyd. Words of wisdom\n")); | |
118 | return file.Close(); | |
119 | } | |
120 | ||
121 | // helpers | |
122 | wxFileName RandomName(int length = 10) | |
123 | { | |
124 | return RandomName(m_base, length); | |
125 | } | |
126 | ||
127 | // static helpers | |
128 | static const wxFileName& GetWatchDir() | |
129 | { | |
130 | static wxFileName dir; | |
131 | ||
132 | if (dir.DirExists()) | |
133 | return dir; | |
134 | ||
135 | wxString tmp = wxStandardPaths::Get().GetTempDir(); | |
136 | dir.AssignDir(tmp); | |
137 | ||
138 | // XXX look for more unique name? there is no function to generate | |
139 | // unique filename, the file always get created... | |
140 | dir.AppendDir("fswatcher_test"); | |
141 | CPPUNIT_ASSERT(!dir.DirExists()); | |
142 | CPPUNIT_ASSERT(dir.Mkdir()); | |
143 | ||
144 | return dir; | |
145 | } | |
146 | ||
147 | static void RemoveWatchDir() | |
148 | { | |
149 | wxFileName dir = GetWatchDir(); | |
150 | CPPUNIT_ASSERT(dir.DirExists()); | |
151 | ||
152 | // just to be really sure we know what we remove | |
771ce939 VZ |
153 | CPPUNIT_ASSERT_EQUAL( "fswatcher_test", dir.GetDirs().Last() ); |
154 | ||
155 | // FIXME-VC6: using non-static Rmdir() results in ICE | |
156 | CPPUNIT_ASSERT( wxFileName::Rmdir(dir.GetFullPath(), wxPATH_RMDIR_RECURSIVE) ); | |
6b8ef0b3 VZ |
157 | } |
158 | ||
159 | static wxFileName RandomName(const wxFileName& base, int length = 10) | |
160 | { | |
161 | static int ALFA_CNT = 'z' - 'a'; | |
162 | ||
163 | wxString s; | |
164 | for (int i = 0 ; i < length; ++i) | |
165 | { | |
166 | char c = 'a' + (rand() % ALFA_CNT); | |
167 | s += c; | |
168 | } | |
169 | ||
170 | return wxFileName(base.GetFullPath(), s); | |
171 | } | |
172 | ||
173 | public: | |
174 | wxFileName m_base; // base dir for doing operations | |
175 | wxFileName m_file; // current file name | |
176 | wxFileName m_old; // previous file name | |
177 | wxFileName m_new; // name after renaming | |
178 | ||
179 | protected: | |
180 | static EventGenerator* ms_instance; | |
181 | }; | |
182 | ||
183 | EventGenerator* EventGenerator::ms_instance = 0; | |
184 | ||
185 | ||
186 | // custom event handler | |
187 | class EventHandler : public wxEvtHandler | |
188 | { | |
189 | public: | |
771ce939 | 190 | enum { WAIT_DURATION = 3 }; |
6b8ef0b3 | 191 | |
b77bb705 VZ |
192 | EventHandler(int types = wxFSW_EVENT_ALL) : |
193 | eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0), | |
194 | m_eventTypes(types) | |
6b8ef0b3 VZ |
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(); | |
b77bb705 | 292 | CPPUNIT_ASSERT(m_watcher->Add(dir, m_eventTypes)); |
6b8ef0b3 VZ |
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 | ||
386 | protected: | |
387 | EventGenerator& eg; | |
388 | wxEventLoopBase* m_loop; // loop reference | |
389 | int m_count; // idle events count | |
390 | ||
391 | wxFileSystemWatcher* m_watcher; | |
b77bb705 | 392 | int m_eventTypes; // Which event-types to watch. Normally all of them |
6b8ef0b3 VZ |
393 | bool tested; // indicates, whether we have already passed the test |
394 | ||
395 | #include "wx/arrimpl.cpp" | |
396 | WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent*, wxArrayEvent); | |
397 | wxArrayEvent m_events; | |
398 | wxFileSystemWatcherEvent* m_lastEvent; | |
399 | }; | |
400 | ||
401 | ||
402 | // ---------------------------------------------------------------------------- | |
403 | // test class | |
404 | // ---------------------------------------------------------------------------- | |
405 | ||
406 | class FileSystemWatcherTestCase : public CppUnit::TestCase | |
407 | { | |
408 | public: | |
409 | FileSystemWatcherTestCase() { } | |
410 | ||
411 | virtual void setUp(); | |
412 | virtual void tearDown(); | |
413 | ||
414 | protected: | |
415 | wxEventLoopBase* m_loop; | |
416 | ||
417 | private: | |
418 | CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase ); | |
419 | CPPUNIT_TEST( TestEventCreate ); | |
420 | CPPUNIT_TEST( TestEventDelete ); | |
227dee95 | 421 | #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7) |
2b6d227c | 422 | CPPUNIT_TEST( TestTrees ); |
227dee95 | 423 | #endif |
6b8ef0b3 VZ |
424 | |
425 | // kqueue-based implementation doesn't collapse create/delete pairs in | |
426 | // renames and doesn't detect neither modifications nor access to the | |
427 | // files reliably currently so disable these tests | |
428 | // | |
429 | // FIXME: fix the code and reenable them | |
430 | #ifndef wxHAS_KQUEUE | |
431 | CPPUNIT_TEST( TestEventRename ); | |
432 | CPPUNIT_TEST( TestEventModify ); | |
433 | ||
434 | // MSW implementation doesn't detect file access events currently | |
bb5a9514 | 435 | #ifndef __WINDOWS__ |
6b8ef0b3 | 436 | CPPUNIT_TEST( TestEventAccess ); |
bb5a9514 | 437 | #endif // __WINDOWS__ |
6b8ef0b3 | 438 | #endif // !wxHAS_KQUEUE |
51fb8678 | 439 | |
b77bb705 | 440 | #ifdef wxHAS_INOTIFY |
f31f9900 | 441 | CPPUNIT_TEST( TestEventAttribute ); |
b77bb705 VZ |
442 | CPPUNIT_TEST( TestSingleWatchtypeEvent ); |
443 | #endif // wxHAS_INOTIFY | |
444 | ||
51fb8678 | 445 | CPPUNIT_TEST( TestNoEventsAfterRemove ); |
6b8ef0b3 VZ |
446 | CPPUNIT_TEST_SUITE_END(); |
447 | ||
448 | void TestEventCreate(); | |
449 | void TestEventDelete(); | |
450 | void TestEventRename(); | |
451 | void TestEventModify(); | |
452 | void TestEventAccess(); | |
b77bb705 | 453 | #ifdef wxHAS_INOTIFY |
f31f9900 | 454 | void TestEventAttribute(); |
b77bb705 VZ |
455 | void TestSingleWatchtypeEvent(); |
456 | #endif // wxHAS_INOTIFY | |
227dee95 VZ |
457 | #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7) |
458 | void TestTrees(); // Visual C++ 6 can't build this | |
459 | #endif | |
51fb8678 VZ |
460 | void TestNoEventsAfterRemove(); |
461 | ||
6b8ef0b3 VZ |
462 | DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase) |
463 | }; | |
464 | ||
b66a6888 VZ |
465 | // the test currently hangs under OS X for some reason and this prevents tests |
466 | // ran by buildbot from completing so disable it until someone has time to | |
467 | // debug it | |
468 | // | |
469 | // FIXME: debug and fix this! | |
470 | #ifndef __WXOSX__ | |
6b8ef0b3 VZ |
471 | // register in the unnamed registry so that these tests are run by default |
472 | CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase ); | |
b66a6888 | 473 | #endif |
6b8ef0b3 | 474 | |
e3778b4d | 475 | // also include in its own registry so that these tests can be run alone |
6b8ef0b3 VZ |
476 | CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase, |
477 | "FileSystemWatcherTestCase" ); | |
478 | ||
479 | void FileSystemWatcherTestCase::setUp() | |
480 | { | |
481 | wxLog::AddTraceMask(wxTRACE_FSWATCHER); | |
482 | EventGenerator::Get().GetWatchDir(); | |
483 | } | |
484 | ||
485 | void FileSystemWatcherTestCase::tearDown() | |
486 | { | |
487 | EventGenerator::Get().RemoveWatchDir(); | |
488 | } | |
489 | ||
490 | // ---------------------------------------------------------------------------- | |
491 | // TestEventCreate | |
492 | // ---------------------------------------------------------------------------- | |
493 | void FileSystemWatcherTestCase::TestEventCreate() | |
494 | { | |
495 | wxLogDebug("TestEventCreate()"); | |
496 | ||
497 | class EventTester : public EventHandler | |
498 | { | |
499 | public: | |
500 | virtual void GenerateEvent() | |
501 | { | |
502 | CPPUNIT_ASSERT(eg.CreateFile()); | |
503 | } | |
504 | ||
505 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
506 | { | |
507 | wxFileSystemWatcherEvent event(wxFSW_EVENT_CREATE); | |
508 | event.SetPath(eg.m_file); | |
509 | event.SetNewPath(eg.m_file); | |
510 | return event; | |
511 | } | |
512 | }; | |
513 | ||
514 | EventTester tester; | |
515 | ||
516 | wxLogTrace(wxTRACE_FSWATCHER, "TestEventCreate tester created()"); | |
517 | ||
518 | tester.Run(); | |
519 | } | |
520 | ||
521 | // ---------------------------------------------------------------------------- | |
522 | // TestEventDelete | |
523 | // ---------------------------------------------------------------------------- | |
524 | void FileSystemWatcherTestCase::TestEventDelete() | |
525 | { | |
526 | wxLogDebug("TestEventDelete()"); | |
527 | ||
528 | class EventTester : public EventHandler | |
529 | { | |
530 | public: | |
531 | virtual void GenerateEvent() | |
532 | { | |
533 | CPPUNIT_ASSERT(eg.DeleteFile()); | |
534 | } | |
535 | ||
536 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
537 | { | |
538 | wxFileSystemWatcherEvent event(wxFSW_EVENT_DELETE); | |
539 | event.SetPath(eg.m_old); | |
540 | ||
541 | // CHECK maybe new path here could be NULL or sth? | |
542 | event.SetNewPath(eg.m_old); | |
543 | return event; | |
544 | } | |
545 | }; | |
546 | ||
547 | // we need to create a file now, so we can delete it | |
548 | EventGenerator::Get().CreateFile(); | |
549 | ||
550 | EventTester tester; | |
551 | tester.Run(); | |
552 | } | |
553 | ||
554 | // ---------------------------------------------------------------------------- | |
555 | // TestEventRename | |
556 | // ---------------------------------------------------------------------------- | |
557 | void FileSystemWatcherTestCase::TestEventRename() | |
558 | { | |
559 | wxLogDebug("TestEventRename()"); | |
560 | ||
561 | class EventTester : public EventHandler | |
562 | { | |
563 | public: | |
564 | virtual void GenerateEvent() | |
565 | { | |
566 | CPPUNIT_ASSERT(eg.RenameFile()); | |
567 | } | |
568 | ||
569 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
570 | { | |
571 | wxFileSystemWatcherEvent event(wxFSW_EVENT_RENAME); | |
572 | event.SetPath(eg.m_old); | |
573 | event.SetNewPath(eg.m_file); | |
574 | return event; | |
575 | } | |
576 | }; | |
577 | ||
578 | // need a file to rename later | |
579 | EventGenerator::Get().CreateFile(); | |
580 | ||
581 | EventTester tester; | |
582 | tester.Run(); | |
583 | } | |
584 | ||
585 | // ---------------------------------------------------------------------------- | |
586 | // TestEventModify | |
587 | // ---------------------------------------------------------------------------- | |
588 | void FileSystemWatcherTestCase::TestEventModify() | |
589 | { | |
590 | wxLogDebug("TestEventModify()"); | |
591 | ||
592 | class EventTester : public EventHandler | |
593 | { | |
594 | public: | |
595 | virtual void GenerateEvent() | |
596 | { | |
597 | CPPUNIT_ASSERT(eg.ModifyFile()); | |
598 | } | |
599 | ||
600 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
601 | { | |
602 | wxFileSystemWatcherEvent event(wxFSW_EVENT_MODIFY); | |
603 | event.SetPath(eg.m_file); | |
604 | event.SetNewPath(eg.m_file); | |
605 | return event; | |
606 | } | |
607 | }; | |
608 | ||
609 | // we need to create a file to modify | |
610 | EventGenerator::Get().CreateFile(); | |
611 | ||
612 | EventTester tester; | |
613 | tester.Run(); | |
614 | } | |
615 | ||
616 | // ---------------------------------------------------------------------------- | |
617 | // TestEventAccess | |
618 | // ---------------------------------------------------------------------------- | |
619 | void FileSystemWatcherTestCase::TestEventAccess() | |
620 | { | |
621 | wxLogDebug("TestEventAccess()"); | |
622 | ||
623 | class EventTester : public EventHandler | |
624 | { | |
625 | public: | |
626 | virtual void GenerateEvent() | |
627 | { | |
628 | CPPUNIT_ASSERT(eg.ReadFile()); | |
629 | } | |
630 | ||
631 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
632 | { | |
633 | wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS); | |
634 | event.SetPath(eg.m_file); | |
635 | event.SetNewPath(eg.m_file); | |
636 | return event; | |
637 | } | |
638 | }; | |
639 | ||
640 | // we need to create a file to read from it and write sth to it | |
641 | EventGenerator::Get().CreateFile(); | |
642 | EventGenerator::Get().ModifyFile(); | |
643 | ||
644 | EventTester tester; | |
645 | tester.Run(); | |
646 | } | |
51fb8678 | 647 | |
b77bb705 | 648 | #ifdef wxHAS_INOTIFY |
f31f9900 VZ |
649 | // ---------------------------------------------------------------------------- |
650 | // TestEventAttribute | |
651 | // ---------------------------------------------------------------------------- | |
652 | void FileSystemWatcherTestCase::TestEventAttribute() | |
653 | { | |
654 | wxLogDebug("TestEventAttribute()"); | |
655 | ||
656 | class EventTester : public EventHandler | |
657 | { | |
658 | public: | |
659 | virtual void GenerateEvent() | |
660 | { | |
661 | CPPUNIT_ASSERT(eg.TouchFile()); | |
662 | } | |
663 | ||
664 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
665 | { | |
666 | wxFileSystemWatcherEvent event(wxFSW_EVENT_ATTRIB); | |
667 | event.SetPath(eg.m_file); | |
668 | event.SetNewPath(eg.m_file); | |
669 | return event; | |
670 | } | |
671 | }; | |
672 | ||
673 | // we need to create a file to touch | |
674 | EventGenerator::Get().CreateFile(); | |
675 | ||
676 | EventTester tester; | |
677 | tester.Run(); | |
678 | } | |
679 | ||
b77bb705 VZ |
680 | // ---------------------------------------------------------------------------- |
681 | // TestSingleWatchtypeEvent: Watch only wxFSW_EVENT_ACCESS | |
682 | // ---------------------------------------------------------------------------- | |
683 | void FileSystemWatcherTestCase::TestSingleWatchtypeEvent() | |
684 | { | |
685 | wxLogDebug("TestSingleWatchtypeEvent()"); | |
686 | ||
687 | class EventTester : public EventHandler | |
688 | { | |
689 | public: | |
690 | // We could pass wxFSW_EVENT_CREATE or MODIFY instead, but not RENAME or | |
691 | // DELETE as the event path fields would be wrong in CheckResult() | |
692 | EventTester() : EventHandler(wxFSW_EVENT_ACCESS) {} | |
693 | ||
694 | virtual void GenerateEvent() | |
695 | { | |
696 | // As wxFSW_EVENT_ACCESS is passed to the ctor only ReadFile() will | |
697 | // generate an event. Without it they all will, and the test fails | |
698 | CPPUNIT_ASSERT(eg.CreateFile()); | |
699 | CPPUNIT_ASSERT(eg.ModifyFile()); | |
700 | CPPUNIT_ASSERT(eg.ReadFile()); | |
701 | } | |
702 | ||
703 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
704 | { | |
705 | wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS); | |
706 | event.SetPath(eg.m_file); | |
707 | event.SetNewPath(eg.m_file); | |
708 | return event; | |
709 | } | |
710 | }; | |
711 | ||
712 | EventTester tester; | |
713 | tester.Run(); | |
714 | } | |
715 | #endif // wxHAS_INOTIFY | |
716 | ||
2b6d227c VZ |
717 | // ---------------------------------------------------------------------------- |
718 | // TestTrees | |
719 | // ---------------------------------------------------------------------------- | |
227dee95 VZ |
720 | |
721 | #if !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7) | |
2b6d227c VZ |
722 | void FileSystemWatcherTestCase::TestTrees() |
723 | { | |
724 | class TreeTester : public EventHandler | |
725 | { | |
726 | const size_t subdirs; | |
727 | const size_t files; | |
728 | ||
729 | public: | |
730 | TreeTester() : subdirs(5), files(3) {} | |
731 | ||
46770031 VZ |
732 | void GrowTree(wxFileName dir |
733 | #ifdef __UNIX__ | |
734 | , bool withSymlinks = false | |
735 | #endif | |
736 | ) | |
2b6d227c VZ |
737 | { |
738 | CPPUNIT_ASSERT(dir.Mkdir()); | |
76cfd1bf VZ |
739 | // Now add a subdir with an easy name to remember in WatchTree() |
740 | dir.AppendDir("child"); | |
741 | CPPUNIT_ASSERT(dir.Mkdir()); | |
0fccda2c | 742 | wxFileName child(dir); // Create a copy to which to symlink |
2b6d227c VZ |
743 | |
744 | // Create a branch of 5 numbered subdirs, each containing 3 | |
745 | // numbered files | |
746 | for ( unsigned d = 0; d < subdirs; ++d ) | |
747 | { | |
748 | dir.AppendDir(wxString::Format("subdir%u", d+1)); | |
749 | CPPUNIT_ASSERT(dir.Mkdir()); | |
750 | ||
751 | const wxString prefix = dir.GetPathWithSep(); | |
227dee95 | 752 | const wxString ext[] = { ".txt", ".log", "" }; |
2b6d227c VZ |
753 | for ( unsigned f = 0; f < files; ++f ) |
754 | { | |
755 | // Just create the files. | |
227dee95 | 756 | wxFile(prefix + wxString::Format("file%u", f+1) + ext[f], |
2b6d227c VZ |
757 | wxFile::write); |
758 | } | |
0fccda2c VZ |
759 | #if defined(__UNIX__) |
760 | if ( withSymlinks ) | |
761 | { | |
762 | // Create a symlink to a files, and another to 'child' | |
763 | CPPUNIT_ASSERT_EQUAL(0, | |
764 | symlink(wxString(prefix + "file1").c_str(), | |
765 | wxString(prefix + "file.lnk").c_str())); | |
766 | CPPUNIT_ASSERT_EQUAL(0, | |
767 | symlink(child.GetFullPath().c_str(), | |
768 | wxString(prefix + "dir.lnk").c_str())); | |
769 | } | |
770 | #endif // __UNIX__ | |
2b6d227c VZ |
771 | } |
772 | } | |
773 | ||
774 | void RmDir(wxFileName dir) | |
775 | { | |
776 | CPPUNIT_ASSERT(dir.DirExists()); | |
777 | ||
778 | CPPUNIT_ASSERT(dir.Rmdir(wxPATH_RMDIR_RECURSIVE)); | |
779 | } | |
780 | ||
781 | void WatchDir(wxFileName dir) | |
782 | { | |
783 | CPPUNIT_ASSERT(m_watcher); | |
784 | ||
785 | // Store the initial count; there may already be some watches | |
786 | const int initial = m_watcher->GetWatchedPathsCount(); | |
787 | ||
788 | m_watcher->Add(dir); | |
789 | CPPUNIT_ASSERT_EQUAL(initial + 1, | |
790 | m_watcher->GetWatchedPathsCount()); | |
791 | } | |
792 | ||
793 | void RemoveSingleWatch(wxFileName dir) | |
794 | { | |
795 | CPPUNIT_ASSERT(m_watcher); | |
796 | ||
797 | const int initial = m_watcher->GetWatchedPathsCount(); | |
798 | ||
799 | m_watcher->Remove(dir); | |
800 | CPPUNIT_ASSERT_EQUAL(initial - 1, | |
801 | m_watcher->GetWatchedPathsCount()); | |
802 | } | |
803 | ||
804 | void WatchTree(const wxFileName& dir) | |
805 | { | |
806 | CPPUNIT_ASSERT(m_watcher); | |
807 | ||
227dee95 VZ |
808 | size_t treeitems = 1; // the trunk |
809 | #ifndef __WINDOWS__ | |
810 | // When there's no file mask, wxMSW sets a single watch | |
811 | // on the trunk which is implemented recursively. | |
6eef5763 VZ |
812 | // wxGTK always sets an additional watch for each subdir |
813 | treeitems += subdirs + 1; // +1 for 'child' | |
227dee95 | 814 | #endif // __WINDOWS__ |
2b6d227c VZ |
815 | |
816 | // Store the initial count; there may already be some watches | |
817 | const int initial = m_watcher->GetWatchedPathsCount(); | |
818 | ||
46770031 | 819 | GrowTree(dir); |
2b6d227c VZ |
820 | |
821 | m_watcher->AddTree(dir); | |
822 | const int plustree = m_watcher->GetWatchedPathsCount(); | |
823 | ||
824 | CPPUNIT_ASSERT_EQUAL(initial + treeitems, plustree); | |
825 | ||
826 | m_watcher->RemoveTree(dir); | |
827 | CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount()); | |
76cfd1bf VZ |
828 | |
829 | // Now test the refcount mechanism by watching items more than once | |
830 | wxFileName child(dir); | |
831 | child.AppendDir("child"); | |
832 | m_watcher->AddTree(child); | |
833 | // Check some watches were added; we don't care about the number | |
834 | CPPUNIT_ASSERT(initial < m_watcher->GetWatchedPathsCount()); | |
835 | // Now watch the whole tree and check that the count is the same | |
836 | // as it was the first time, despite also adding 'child' separately | |
837 | // Except that in wxMSW this isn't true: each watch will be a | |
838 | // single, recursive dir; so fudge the count | |
839 | size_t fudge = 0; | |
840 | #ifdef __WINDOWS__ | |
841 | fudge = 1; | |
842 | #endif // __WINDOWS__ | |
843 | m_watcher->AddTree(dir); | |
844 | CPPUNIT_ASSERT_EQUAL(plustree + fudge, m_watcher->GetWatchedPathsCount()); | |
845 | m_watcher->RemoveTree(child); | |
846 | CPPUNIT_ASSERT(initial < m_watcher->GetWatchedPathsCount()); | |
847 | m_watcher->RemoveTree(dir); | |
848 | CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount()); | |
0fccda2c VZ |
849 | #if defined(__UNIX__) |
850 | // Finally, test a tree containing internal symlinks | |
851 | RmDir(dir); | |
852 | GrowTree(dir, true /* test symlinks */); | |
853 | ||
854 | // Without the DontFollowLink() call AddTree() would now assert | |
855 | // (and without the assert, it would infinitely loop) | |
856 | wxFileName fn = dir; | |
857 | fn.DontFollowLink(); | |
858 | CPPUNIT_ASSERT(m_watcher->AddTree(fn)); | |
859 | CPPUNIT_ASSERT(m_watcher->RemoveTree(fn)); | |
860 | ||
861 | // Regrow the tree without symlinks, ready for the next test | |
862 | RmDir(dir); | |
863 | GrowTree(dir, false); | |
864 | #endif // __UNIX__ | |
2b6d227c VZ |
865 | } |
866 | ||
227dee95 VZ |
867 | void WatchTreeWithFilespec(const wxFileName& dir) |
868 | { | |
869 | CPPUNIT_ASSERT(m_watcher); | |
870 | CPPUNIT_ASSERT(dir.DirExists()); // Was built in WatchTree() | |
871 | ||
872 | // Store the initial count; there may already be some watches | |
873 | const int initial = m_watcher->GetWatchedPathsCount(); | |
874 | ||
875 | // When we use a filter, both wxMSW and wxGTK implementations set | |
6eef5763 VZ |
876 | // an additional watch for each subdir (+1 for the root dir itself |
877 | // and another +1 for "child"). | |
878 | const size_t treeitems = subdirs + 2; | |
227dee95 VZ |
879 | m_watcher->AddTree(dir, wxFSW_EVENT_ALL, "*.txt"); |
880 | ||
881 | const int plustree = m_watcher->GetWatchedPathsCount(); | |
882 | CPPUNIT_ASSERT_EQUAL(initial + treeitems, plustree); | |
883 | ||
884 | // RemoveTree should try to remove only those files that were added | |
885 | m_watcher->RemoveTree(dir); | |
886 | CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount()); | |
887 | } | |
888 | ||
2b6d227c VZ |
889 | void RemoveAllWatches() |
890 | { | |
891 | CPPUNIT_ASSERT(m_watcher); | |
892 | ||
893 | m_watcher->RemoveAll(); | |
894 | CPPUNIT_ASSERT_EQUAL(0, m_watcher->GetWatchedPathsCount()); | |
895 | } | |
896 | ||
897 | virtual void GenerateEvent() | |
898 | { | |
899 | // We don't use this function for events. Just run the tests | |
900 | ||
901 | wxFileName watchdir = EventGenerator::GetWatchDir(); | |
902 | CPPUNIT_ASSERT(watchdir.DirExists()); | |
903 | ||
904 | wxFileName treedir(watchdir); | |
905 | treedir.AppendDir("treetrunk"); | |
906 | CPPUNIT_ASSERT(!treedir.DirExists()); | |
907 | ||
908 | wxFileName singledir(watchdir); | |
909 | singledir.AppendDir("single"); | |
910 | CPPUNIT_ASSERT(!singledir.DirExists()); | |
911 | CPPUNIT_ASSERT(singledir.Mkdir()); | |
912 | ||
913 | WatchDir(singledir); | |
914 | WatchTree(treedir); | |
227dee95 VZ |
915 | // Now test adding and removing a tree using a filespec |
916 | // wxMSW uses the generic method to add matching files; which fails | |
917 | // as it doesn't support adding files :/ So disable the test | |
918 | #ifndef __WINDOWS__ | |
919 | WatchTreeWithFilespec(treedir); | |
920 | #endif // __WINDOWS__ | |
2b6d227c VZ |
921 | |
922 | RemoveSingleWatch(singledir); | |
923 | // Add it back again, ready to test RemoveAll() | |
924 | WatchDir(singledir); | |
925 | ||
926 | RemoveAllWatches(); | |
927 | ||
928 | // Clean up | |
929 | RmDir(singledir); | |
930 | RmDir(treedir); | |
931 | ||
932 | Exit(); | |
933 | } | |
934 | ||
935 | virtual wxFileSystemWatcherEvent ExpectedEvent() | |
936 | { | |
937 | CPPUNIT_FAIL("Shouldn't be called"); | |
938 | ||
939 | return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR); | |
940 | } | |
941 | ||
942 | virtual void CheckResult() | |
943 | { | |
944 | // Do nothing. We override this to prevent receiving events in | |
945 | // ExpectedEvent() | |
946 | } | |
947 | }; | |
948 | ||
949 | TreeTester tester; | |
950 | tester.Run(); | |
951 | } | |
227dee95 VZ |
952 | #endif // !defined(__VISUALC__) || wxCHECK_VISUALC_VERSION(7) |
953 | ||
2b6d227c | 954 | |
d3eec3b6 VZ |
955 | namespace |
956 | { | |
957 | ||
958 | // We can't define this class locally inside TestNoEventsAfterRemove() for some | |
959 | // reason with g++ 4.0 under OS X 10.5, it results in the following mysterious | |
960 | // error: | |
961 | // | |
962 | // /var/tmp//ccTkNCkc.s:unknown:Non-global symbol: | |
963 | // __ZThn80_ZN25FileSystemWatcherTestCase23TestNoEventsAfterRemoveEvEN11EventTester6NotifyEv.eh | |
964 | // can't be a weak_definition | |
965 | // | |
966 | // So define this class outside the function instead. | |
967 | class NoEventsAfterRemoveEventTester : public EventHandler, | |
968 | public wxTimer | |
51fb8678 | 969 | { |
d3eec3b6 VZ |
970 | public: |
971 | NoEventsAfterRemoveEventTester() | |
51fb8678 | 972 | { |
d3eec3b6 VZ |
973 | // We need to use an inactivity timer as we never get any file |
974 | // system events in this test, so we consider that the test is | |
975 | // finished when this 1s timeout expires instead of, as usual, | |
976 | // stopping after getting the file system events. | |
977 | Start(1000, true); | |
978 | } | |
51fb8678 | 979 | |
d3eec3b6 VZ |
980 | virtual void GenerateEvent() |
981 | { | |
982 | m_watcher->Remove(EventGenerator::GetWatchDir()); | |
983 | CPPUNIT_ASSERT(eg.CreateFile()); | |
984 | } | |
51fb8678 | 985 | |
d3eec3b6 VZ |
986 | virtual void CheckResult() |
987 | { | |
988 | CPPUNIT_ASSERT( m_events.empty() ); | |
989 | } | |
51fb8678 | 990 | |
d3eec3b6 VZ |
991 | virtual wxFileSystemWatcherEvent ExpectedEvent() |
992 | { | |
993 | CPPUNIT_FAIL( "Shouldn't be called" ); | |
51fb8678 | 994 | |
d3eec3b6 VZ |
995 | return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR); |
996 | } | |
51fb8678 | 997 | |
d3eec3b6 VZ |
998 | virtual void Notify() |
999 | { | |
1000 | SendIdle(); | |
1001 | } | |
1002 | }; | |
51fb8678 | 1003 | |
d3eec3b6 VZ |
1004 | } // anonymous namespace |
1005 | ||
1006 | void FileSystemWatcherTestCase::TestNoEventsAfterRemove() | |
1007 | { | |
1008 | NoEventsAfterRemoveEventTester tester; | |
51fb8678 VZ |
1009 | tester.Run(); |
1010 | } |