]> git.saurik.com Git - wxWidgets.git/blame - tests/fswatcher/fswatchertest.cpp
Replace wxComboBox::IsEmpty() with Is{List,Text}Empty().
[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
1c9c8e88
VZ
338 WX_ASSERT_EQUAL_MESSAGE
339 (
340 (
77241feb
VZ
341 "Extra events received, first is of type %x, for path=\"%s\","
342 "last is of type %x, path=\"%s\"",
343 e->GetChangeType(),
344 e->GetPath().GetFullPath(),
1c9c8e88
VZ
345 m_events.back()->GetChangeType(),
346 m_events.back()->GetPath().GetFullPath()
347 ),
348 1, m_events.size()
349 );
350
6b8ef0b3
VZ
351 // this is our "reference event"
352 const wxFileSystemWatcherEvent expected = ExpectedEvent();
353
354 CPPUNIT_ASSERT_EQUAL( expected.GetChangeType(), e->GetChangeType() );
355
356 CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER, e->GetEventType());
357
358 // XXX this needs change
359 CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN, e->GetEventCategory());
360
361 CPPUNIT_ASSERT_EQUAL(expected.GetPath(), e->GetPath());
362 CPPUNIT_ASSERT_EQUAL(expected.GetNewPath(), e->GetNewPath());
6b8ef0b3
VZ
363 }
364
365 virtual void GenerateEvent() = 0;
366
367 virtual wxFileSystemWatcherEvent ExpectedEvent() = 0;
368
369
370protected:
371 EventGenerator& eg;
372 wxEventLoopBase* m_loop; // loop reference
373 int m_count; // idle events count
374
375 wxFileSystemWatcher* m_watcher;
376 bool tested; // indicates, whether we have already passed the test
377
378 #include "wx/arrimpl.cpp"
379 WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent*, wxArrayEvent);
380 wxArrayEvent m_events;
381 wxFileSystemWatcherEvent* m_lastEvent;
382};
383
384
385// ----------------------------------------------------------------------------
386// test class
387// ----------------------------------------------------------------------------
388
389class FileSystemWatcherTestCase : public CppUnit::TestCase
390{
391public:
392 FileSystemWatcherTestCase() { }
393
394 virtual void setUp();
395 virtual void tearDown();
396
397protected:
398 wxEventLoopBase* m_loop;
399
400private:
401 CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase );
402 CPPUNIT_TEST( TestEventCreate );
403 CPPUNIT_TEST( TestEventDelete );
404
405 // kqueue-based implementation doesn't collapse create/delete pairs in
406 // renames and doesn't detect neither modifications nor access to the
407 // files reliably currently so disable these tests
408 //
409 // FIXME: fix the code and reenable them
410#ifndef wxHAS_KQUEUE
411 CPPUNIT_TEST( TestEventRename );
412 CPPUNIT_TEST( TestEventModify );
413
414 // MSW implementation doesn't detect file access events currently
415#ifndef __WXMSW__
416 CPPUNIT_TEST( TestEventAccess );
417#endif // __WXMSW__
418#endif // !wxHAS_KQUEUE
51fb8678
VZ
419
420 CPPUNIT_TEST( TestNoEventsAfterRemove );
6b8ef0b3
VZ
421 CPPUNIT_TEST_SUITE_END();
422
423 void TestEventCreate();
424 void TestEventDelete();
425 void TestEventRename();
426 void TestEventModify();
427 void TestEventAccess();
428
51fb8678
VZ
429 void TestNoEventsAfterRemove();
430
6b8ef0b3
VZ
431 DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase)
432};
433
b66a6888
VZ
434// the test currently hangs under OS X for some reason and this prevents tests
435// ran by buildbot from completing so disable it until someone has time to
436// debug it
437//
438// FIXME: debug and fix this!
439#ifndef __WXOSX__
6b8ef0b3
VZ
440// register in the unnamed registry so that these tests are run by default
441CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase );
b66a6888 442#endif
6b8ef0b3 443
e3778b4d 444// also include in its own registry so that these tests can be run alone
6b8ef0b3
VZ
445CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase,
446 "FileSystemWatcherTestCase" );
447
448void FileSystemWatcherTestCase::setUp()
449{
450 wxLog::AddTraceMask(wxTRACE_FSWATCHER);
451 EventGenerator::Get().GetWatchDir();
452}
453
454void FileSystemWatcherTestCase::tearDown()
455{
456 EventGenerator::Get().RemoveWatchDir();
457}
458
459// ----------------------------------------------------------------------------
460// TestEventCreate
461// ----------------------------------------------------------------------------
462void FileSystemWatcherTestCase::TestEventCreate()
463{
464 wxLogDebug("TestEventCreate()");
465
466 class EventTester : public EventHandler
467 {
468 public:
469 virtual void GenerateEvent()
470 {
471 CPPUNIT_ASSERT(eg.CreateFile());
472 }
473
474 virtual wxFileSystemWatcherEvent ExpectedEvent()
475 {
476 wxFileSystemWatcherEvent event(wxFSW_EVENT_CREATE);
477 event.SetPath(eg.m_file);
478 event.SetNewPath(eg.m_file);
479 return event;
480 }
481 };
482
483 EventTester tester;
484
485 wxLogTrace(wxTRACE_FSWATCHER, "TestEventCreate tester created()");
486
487 tester.Run();
488}
489
490// ----------------------------------------------------------------------------
491// TestEventDelete
492// ----------------------------------------------------------------------------
493void FileSystemWatcherTestCase::TestEventDelete()
494{
495 wxLogDebug("TestEventDelete()");
496
497 class EventTester : public EventHandler
498 {
499 public:
500 virtual void GenerateEvent()
501 {
502 CPPUNIT_ASSERT(eg.DeleteFile());
503 }
504
505 virtual wxFileSystemWatcherEvent ExpectedEvent()
506 {
507 wxFileSystemWatcherEvent event(wxFSW_EVENT_DELETE);
508 event.SetPath(eg.m_old);
509
510 // CHECK maybe new path here could be NULL or sth?
511 event.SetNewPath(eg.m_old);
512 return event;
513 }
514 };
515
516 // we need to create a file now, so we can delete it
517 EventGenerator::Get().CreateFile();
518
519 EventTester tester;
520 tester.Run();
521}
522
523// ----------------------------------------------------------------------------
524// TestEventRename
525// ----------------------------------------------------------------------------
526void FileSystemWatcherTestCase::TestEventRename()
527{
528 wxLogDebug("TestEventRename()");
529
530 class EventTester : public EventHandler
531 {
532 public:
533 virtual void GenerateEvent()
534 {
535 CPPUNIT_ASSERT(eg.RenameFile());
536 }
537
538 virtual wxFileSystemWatcherEvent ExpectedEvent()
539 {
540 wxFileSystemWatcherEvent event(wxFSW_EVENT_RENAME);
541 event.SetPath(eg.m_old);
542 event.SetNewPath(eg.m_file);
543 return event;
544 }
545 };
546
547 // need a file to rename later
548 EventGenerator::Get().CreateFile();
549
550 EventTester tester;
551 tester.Run();
552}
553
554// ----------------------------------------------------------------------------
555// TestEventModify
556// ----------------------------------------------------------------------------
557void FileSystemWatcherTestCase::TestEventModify()
558{
559 wxLogDebug("TestEventModify()");
560
561 class EventTester : public EventHandler
562 {
563 public:
564 virtual void GenerateEvent()
565 {
566 CPPUNIT_ASSERT(eg.ModifyFile());
567 }
568
569 virtual wxFileSystemWatcherEvent ExpectedEvent()
570 {
571 wxFileSystemWatcherEvent event(wxFSW_EVENT_MODIFY);
572 event.SetPath(eg.m_file);
573 event.SetNewPath(eg.m_file);
574 return event;
575 }
576 };
577
578 // we need to create a file to modify
579 EventGenerator::Get().CreateFile();
580
581 EventTester tester;
582 tester.Run();
583}
584
585// ----------------------------------------------------------------------------
586// TestEventAccess
587// ----------------------------------------------------------------------------
588void FileSystemWatcherTestCase::TestEventAccess()
589{
590 wxLogDebug("TestEventAccess()");
591
592 class EventTester : public EventHandler
593 {
594 public:
595 virtual void GenerateEvent()
596 {
597 CPPUNIT_ASSERT(eg.ReadFile());
598 }
599
600 virtual wxFileSystemWatcherEvent ExpectedEvent()
601 {
602 wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS);
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 read from it and write sth to it
610 EventGenerator::Get().CreateFile();
611 EventGenerator::Get().ModifyFile();
612
613 EventTester tester;
614 tester.Run();
615}
51fb8678 616
d3eec3b6
VZ
617namespace
618{
619
620// We can't define this class locally inside TestNoEventsAfterRemove() for some
621// reason with g++ 4.0 under OS X 10.5, it results in the following mysterious
622// error:
623//
624// /var/tmp//ccTkNCkc.s:unknown:Non-global symbol:
625// __ZThn80_ZN25FileSystemWatcherTestCase23TestNoEventsAfterRemoveEvEN11EventTester6NotifyEv.eh
626// can't be a weak_definition
627//
628// So define this class outside the function instead.
629class NoEventsAfterRemoveEventTester : public EventHandler,
630 public wxTimer
51fb8678 631{
d3eec3b6
VZ
632public:
633 NoEventsAfterRemoveEventTester()
51fb8678 634 {
d3eec3b6
VZ
635 // We need to use an inactivity timer as we never get any file
636 // system events in this test, so we consider that the test is
637 // finished when this 1s timeout expires instead of, as usual,
638 // stopping after getting the file system events.
639 Start(1000, true);
640 }
51fb8678 641
d3eec3b6
VZ
642 virtual void GenerateEvent()
643 {
644 m_watcher->Remove(EventGenerator::GetWatchDir());
645 CPPUNIT_ASSERT(eg.CreateFile());
646 }
51fb8678 647
d3eec3b6
VZ
648 virtual void CheckResult()
649 {
650 CPPUNIT_ASSERT( m_events.empty() );
651 }
51fb8678 652
d3eec3b6
VZ
653 virtual wxFileSystemWatcherEvent ExpectedEvent()
654 {
655 CPPUNIT_FAIL( "Shouldn't be called" );
51fb8678 656
d3eec3b6
VZ
657 return wxFileSystemWatcherEvent(wxFSW_EVENT_ERROR);
658 }
51fb8678 659
d3eec3b6
VZ
660 virtual void Notify()
661 {
662 SendIdle();
663 }
664};
51fb8678 665
d3eec3b6
VZ
666} // anonymous namespace
667
668void FileSystemWatcherTestCase::TestNoEventsAfterRemove()
669{
670 NoEventsAfterRemoveEventTester tester;
51fb8678
VZ
671 tester.Run();
672}