]> git.saurik.com Git - wxWidgets.git/blob - tests/fswatcher/fswatchertest.cpp
32a9eb26fb3febe4ab460bc3ede1f869162ae265
[wxWidgets.git] / tests / fswatcher / fswatchertest.cpp
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
20 #include "wx/evtloop.h"
21 #include "wx/filename.h"
22 #include "wx/filefn.h"
23 #include "wx/stdpaths.h"
24 #include "wx/fswatcher.h"
25
26 #include "testfile.h"
27
28 // ----------------------------------------------------------------------------
29 // local functions
30 // ----------------------------------------------------------------------------
31
32 // class generating file system events
33 class EventGenerator
34 {
35 public:
36 static EventGenerator& Get()
37 {
38 if (!ms_instance)
39 ms_instance = new EventGenerator(GetWatchDir());
40
41 return *ms_instance;
42 }
43
44 EventGenerator(const wxFileName& path) : m_base(path)
45 {
46 m_old = wxFileName();
47 m_file = RandomName();
48 m_new = RandomName();
49 }
50
51 // operations
52 bool CreateFile()
53 {
54 wxFile file(m_file.GetFullPath(), wxFile::write);
55 return file.IsOpened() && m_file.FileExists();
56 }
57
58 bool RenameFile()
59 {
60 CPPUNIT_ASSERT(m_file.FileExists());
61
62 wxLogDebug("Renaming %s=>%s", m_file.GetFullPath(), m_new.GetFullPath());
63
64 bool ret = wxRenameFile(m_file.GetFullPath(), m_new.GetFullPath());
65 if (ret)
66 {
67 m_old = m_file;
68 m_file = m_new;
69 m_new = RandomName();
70 }
71
72 return ret;
73 }
74
75 bool DeleteFile()
76 {
77 CPPUNIT_ASSERT(m_file.FileExists());
78
79 bool ret = wxRemoveFile(m_file.GetFullPath());
80 if (ret)
81 {
82 m_old = m_file;
83 m_file = m_new;
84 m_new = RandomName();
85 }
86
87 return ret;
88 }
89
90 bool TouchFile()
91 {
92 return m_file.Touch();
93 }
94
95 bool ReadFile()
96 {
97 wxFile f(m_file.GetFullPath());
98 CPPUNIT_ASSERT(f.IsOpened());
99
100 char buf[1];
101 ssize_t count = f.Read(buf, sizeof(buf));
102 CPPUNIT_ASSERT(count > 0);
103
104 return true;
105 }
106
107 bool ModifyFile()
108 {
109 CPPUNIT_ASSERT(m_file.FileExists());
110
111 wxFile file(m_file.GetFullPath(), wxFile::write_append);
112 CPPUNIT_ASSERT(file.IsOpened());
113
114 CPPUNIT_ASSERT(file.Write("Words of Wisdom, Lloyd. Words of wisdom\n"));
115 return file.Close();
116 }
117
118 // helpers
119 wxFileName RandomName(int length = 10)
120 {
121 return RandomName(m_base, length);
122 }
123
124 // static helpers
125 static const wxFileName& GetWatchDir()
126 {
127 static wxFileName dir;
128
129 if (dir.DirExists())
130 return dir;
131
132 wxString tmp = wxStandardPaths::Get().GetTempDir();
133 dir.AssignDir(tmp);
134
135 // XXX look for more unique name? there is no function to generate
136 // unique filename, the file always get created...
137 dir.AppendDir("fswatcher_test");
138 CPPUNIT_ASSERT(!dir.DirExists());
139 CPPUNIT_ASSERT(dir.Mkdir());
140
141 return dir;
142 }
143
144 static void RemoveWatchDir()
145 {
146 wxFileName dir = GetWatchDir();
147 CPPUNIT_ASSERT(dir.DirExists());
148
149 // just to be really sure we know what we remove
150 CPPUNIT_ASSERT(dir.GetDirs().Last() == "fswatcher_test");
151 CPPUNIT_ASSERT(dir.Rmdir(wxPATH_RMDIR_RECURSIVE));
152 }
153
154 static wxFileName RandomName(const wxFileName& base, int length = 10)
155 {
156 static int ALFA_CNT = 'z' - 'a';
157
158 wxString s;
159 for (int i = 0 ; i < length; ++i)
160 {
161 char c = 'a' + (rand() % ALFA_CNT);
162 s += c;
163 }
164
165 return wxFileName(base.GetFullPath(), s);
166 }
167
168 public:
169 wxFileName m_base; // base dir for doing operations
170 wxFileName m_file; // current file name
171 wxFileName m_old; // previous file name
172 wxFileName m_new; // name after renaming
173
174 protected:
175 static EventGenerator* ms_instance;
176 };
177
178 EventGenerator* EventGenerator::ms_instance = 0;
179
180
181 // custom event handler
182 class EventHandler : public wxEvtHandler
183 {
184 public:
185 const static int WAIT_DURATION = 3;
186
187 EventHandler() :
188 eg(EventGenerator::Get()), m_loop(0), m_count(0), m_watcher(0)
189 {
190 m_loop = new wxEventLoop();
191 Connect(wxEVT_IDLE, wxIdleEventHandler(EventHandler::OnIdle));
192 Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(
193 EventHandler::OnFileSystemEvent));
194 }
195
196 virtual ~EventHandler()
197 {
198 delete m_watcher;
199 if (m_loop)
200 {
201 if (m_loop->IsRunning())
202 m_loop->Exit();
203 delete m_loop;
204 }
205 }
206
207 void Exit()
208 {
209 m_loop->Exit();
210 }
211
212 // sends idle event, so we get called in a moment
213 void SendIdle()
214 {
215 wxIdleEvent* e = new wxIdleEvent();
216 QueueEvent(e);
217 }
218
219 void Run()
220 {
221 SendIdle();
222 m_loop->Run();
223 }
224
225 void OnIdle(wxIdleEvent& /*evt*/)
226 {
227 bool more = Action();
228 m_count++;
229
230 if (more)
231 {
232 SendIdle();
233 }
234 }
235
236 // returns whether we should produce more idle events
237 virtual bool Action()
238 {
239 switch (m_count)
240 {
241 case 0:
242 CPPUNIT_ASSERT(Init());
243 break;
244 case 1:
245 GenerateEvent();
246 break;
247 case 2:
248 // actual test
249 CPPUNIT_ASSERT(CheckResult());
250 Exit();
251 break;
252
253 // TODO a mechanism that will break the loop in case we
254 // don't receive a file system event
255 // this below doesn't quite work, so all tests must pass :-)
256 #if 0
257 case 2:
258 m_loop.Yield();
259 m_loop.WakeUp();
260 CPPUNIT_ASSERT(KeepWaiting());
261 m_loop.Yield();
262 break;
263 case 3:
264 break;
265 case 4:
266 CPPUNIT_ASSERT(AfterWait());
267 break;
268 #endif
269 } // switch (m_count)
270
271 return m_count <= 0;
272 }
273
274 virtual bool Init()
275 {
276 // test we're good to go
277 CPPUNIT_ASSERT(wxEventLoopBase::GetActive());
278
279 // XXX only now can we construct Watcher, because we need
280 // active loop here
281 m_watcher = new wxFileSystemWatcher();
282 m_watcher->SetOwner(this);
283
284 // add dir to be watched
285 wxFileName dir = EventGenerator::GetWatchDir();
286 CPPUNIT_ASSERT(m_watcher->Add(dir, wxFSW_EVENT_ALL));
287
288 return true;
289 }
290
291 virtual bool KeepWaiting()
292 {
293 // did we receive event already?
294 if (!tested)
295 {
296 // well, lets wait a bit more
297 wxSleep(WAIT_DURATION);
298 }
299
300 return true;
301 }
302
303 virtual bool AfterWait()
304 {
305 // fail if still no events
306 if (!tested)
307 {
308 wxString s;
309 s.Printf("No events from watcher during %d seconds!",
310 WAIT_DURATION);
311 CPPUNIT_FAIL((const char*)s);
312 }
313
314 return true;
315 }
316
317 virtual void OnFileSystemEvent(wxFileSystemWatcherEvent& evt)
318 {
319 wxLogDebug("--- %s ---", evt.ToString());
320 m_lastEvent = wxDynamicCast(evt.Clone(), wxFileSystemWatcherEvent);
321 m_events.Add(m_lastEvent);
322
323 // test finished
324 SendIdle();
325 tested = true;
326 }
327
328 virtual bool CheckResult()
329 {
330 CPPUNIT_ASSERT_EQUAL( 1, m_events.size() );
331 const wxFileSystemWatcherEvent * const e = m_events.front();
332
333 // this is our "reference event"
334 const wxFileSystemWatcherEvent expected = ExpectedEvent();
335
336 CPPUNIT_ASSERT_EQUAL( expected.GetChangeType(), e->GetChangeType() );
337
338 CPPUNIT_ASSERT_EQUAL((int)wxEVT_FSWATCHER, e->GetEventType());
339
340 // XXX this needs change
341 CPPUNIT_ASSERT_EQUAL(wxEVT_CATEGORY_UNKNOWN, e->GetEventCategory());
342
343 CPPUNIT_ASSERT_EQUAL(expected.GetPath(), e->GetPath());
344 CPPUNIT_ASSERT_EQUAL(expected.GetNewPath(), e->GetNewPath());
345 CPPUNIT_ASSERT_EQUAL(expected.GetChangeType(), e->GetChangeType());
346
347 return true;
348 }
349
350 virtual void GenerateEvent() = 0;
351
352 virtual wxFileSystemWatcherEvent ExpectedEvent() = 0;
353
354
355 protected:
356 EventGenerator& eg;
357 wxEventLoopBase* m_loop; // loop reference
358 int m_count; // idle events count
359
360 wxFileSystemWatcher* m_watcher;
361 bool tested; // indicates, whether we have already passed the test
362
363 #include "wx/arrimpl.cpp"
364 WX_DEFINE_ARRAY_PTR(wxFileSystemWatcherEvent*, wxArrayEvent);
365 wxArrayEvent m_events;
366 wxFileSystemWatcherEvent* m_lastEvent;
367 };
368
369
370 // ----------------------------------------------------------------------------
371 // test class
372 // ----------------------------------------------------------------------------
373
374 class FileSystemWatcherTestCase : public CppUnit::TestCase
375 {
376 public:
377 FileSystemWatcherTestCase() { }
378
379 virtual void setUp();
380 virtual void tearDown();
381
382 protected:
383 wxEventLoopBase* m_loop;
384
385 private:
386 CPPUNIT_TEST_SUITE( FileSystemWatcherTestCase );
387 CPPUNIT_TEST( TestEventCreate );
388 CPPUNIT_TEST( TestEventDelete );
389
390 // kqueue-based implementation doesn't collapse create/delete pairs in
391 // renames and doesn't detect neither modifications nor access to the
392 // files reliably currently so disable these tests
393 //
394 // FIXME: fix the code and reenable them
395 #ifndef wxHAS_KQUEUE
396 CPPUNIT_TEST( TestEventRename );
397 CPPUNIT_TEST( TestEventModify );
398
399 // MSW implementation doesn't detect file access events currently
400 #ifndef __WXMSW__
401 CPPUNIT_TEST( TestEventAccess );
402 #endif // __WXMSW__
403 #endif // !wxHAS_KQUEUE
404 CPPUNIT_TEST_SUITE_END();
405
406 void TestEventCreate();
407 void TestEventDelete();
408 void TestEventRename();
409 void TestEventModify();
410 void TestEventAccess();
411
412 DECLARE_NO_COPY_CLASS(FileSystemWatcherTestCase)
413 };
414
415 // register in the unnamed registry so that these tests are run by default
416 CPPUNIT_TEST_SUITE_REGISTRATION( FileSystemWatcherTestCase );
417
418 // also include in it's own registry so that these tests can be run alone
419 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileSystemWatcherTestCase,
420 "FileSystemWatcherTestCase" );
421
422 void FileSystemWatcherTestCase::setUp()
423 {
424 wxLog::AddTraceMask(wxTRACE_FSWATCHER);
425 EventGenerator::Get().GetWatchDir();
426 }
427
428 void FileSystemWatcherTestCase::tearDown()
429 {
430 EventGenerator::Get().RemoveWatchDir();
431 }
432
433 // ----------------------------------------------------------------------------
434 // TestEventCreate
435 // ----------------------------------------------------------------------------
436 void FileSystemWatcherTestCase::TestEventCreate()
437 {
438 wxLogDebug("TestEventCreate()");
439
440 class EventTester : public EventHandler
441 {
442 public:
443 virtual void GenerateEvent()
444 {
445 CPPUNIT_ASSERT(eg.CreateFile());
446 }
447
448 virtual wxFileSystemWatcherEvent ExpectedEvent()
449 {
450 wxFileSystemWatcherEvent event(wxFSW_EVENT_CREATE);
451 event.SetPath(eg.m_file);
452 event.SetNewPath(eg.m_file);
453 return event;
454 }
455 };
456
457 EventTester tester;
458
459 wxLogTrace(wxTRACE_FSWATCHER, "TestEventCreate tester created()");
460
461 tester.Run();
462 }
463
464 // ----------------------------------------------------------------------------
465 // TestEventDelete
466 // ----------------------------------------------------------------------------
467 void FileSystemWatcherTestCase::TestEventDelete()
468 {
469 wxLogDebug("TestEventDelete()");
470
471 class EventTester : public EventHandler
472 {
473 public:
474 virtual void GenerateEvent()
475 {
476 CPPUNIT_ASSERT(eg.DeleteFile());
477 }
478
479 virtual wxFileSystemWatcherEvent ExpectedEvent()
480 {
481 wxFileSystemWatcherEvent event(wxFSW_EVENT_DELETE);
482 event.SetPath(eg.m_old);
483
484 // CHECK maybe new path here could be NULL or sth?
485 event.SetNewPath(eg.m_old);
486 return event;
487 }
488 };
489
490 // we need to create a file now, so we can delete it
491 EventGenerator::Get().CreateFile();
492
493 EventTester tester;
494 tester.Run();
495 }
496
497 // ----------------------------------------------------------------------------
498 // TestEventRename
499 // ----------------------------------------------------------------------------
500 void FileSystemWatcherTestCase::TestEventRename()
501 {
502 wxLogDebug("TestEventRename()");
503
504 class EventTester : public EventHandler
505 {
506 public:
507 virtual void GenerateEvent()
508 {
509 CPPUNIT_ASSERT(eg.RenameFile());
510 }
511
512 virtual wxFileSystemWatcherEvent ExpectedEvent()
513 {
514 wxFileSystemWatcherEvent event(wxFSW_EVENT_RENAME);
515 event.SetPath(eg.m_old);
516 event.SetNewPath(eg.m_file);
517 return event;
518 }
519 };
520
521 // need a file to rename later
522 EventGenerator::Get().CreateFile();
523
524 EventTester tester;
525 tester.Run();
526 }
527
528 // ----------------------------------------------------------------------------
529 // TestEventModify
530 // ----------------------------------------------------------------------------
531 void FileSystemWatcherTestCase::TestEventModify()
532 {
533 wxLogDebug("TestEventModify()");
534
535 class EventTester : public EventHandler
536 {
537 public:
538 virtual void GenerateEvent()
539 {
540 CPPUNIT_ASSERT(eg.ModifyFile());
541 }
542
543 virtual wxFileSystemWatcherEvent ExpectedEvent()
544 {
545 wxFileSystemWatcherEvent event(wxFSW_EVENT_MODIFY);
546 event.SetPath(eg.m_file);
547 event.SetNewPath(eg.m_file);
548 return event;
549 }
550 };
551
552 // we need to create a file to modify
553 EventGenerator::Get().CreateFile();
554
555 EventTester tester;
556 tester.Run();
557 }
558
559 // ----------------------------------------------------------------------------
560 // TestEventAccess
561 // ----------------------------------------------------------------------------
562 void FileSystemWatcherTestCase::TestEventAccess()
563 {
564 wxLogDebug("TestEventAccess()");
565
566 class EventTester : public EventHandler
567 {
568 public:
569 virtual void GenerateEvent()
570 {
571 CPPUNIT_ASSERT(eg.ReadFile());
572 }
573
574 virtual wxFileSystemWatcherEvent ExpectedEvent()
575 {
576 wxFileSystemWatcherEvent event(wxFSW_EVENT_ACCESS);
577 event.SetPath(eg.m_file);
578 event.SetNewPath(eg.m_file);
579 return event;
580 }
581 };
582
583 // we need to create a file to read from it and write sth to it
584 EventGenerator::Get().CreateFile();
585 EventGenerator::Get().ModifyFile();
586
587 EventTester tester;
588 tester.Run();
589 }