1 /*******************************************************************************
2 
3     Unittest for FileSystemEvent.
4 
5     Copyright:
6         Copyright (c) 2014-2017 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14 *******************************************************************************/
15 
16 module integrationtest.filesystemevent.main;
17 
18 import ocean.meta.types.Qualifiers;
19 
20 import ocean.core.Enforce;
21 
22 import ocean.io.device.File;
23 
24 import ocean.io.device.TempFile;
25 
26 import ocean.io.select.client.TimerEvent;
27 
28 import ocean.io.select.client.FileSystemEvent;
29 
30 import ocean.io.select.EpollSelectDispatcher;
31 
32 import ocean.io.model.IConduit: ISelectable;
33 
34 import ocean.io.FilePath;
35 
36 import ocean.core.Test;
37 
38 import ocean.util.test.DirectorySandbox;
39 
40 import ocean.task.Task;
41 
42 import ocean.task.Scheduler;
43 
44 import ocean.task.util.Event;
45 
46 import ocean.text.convert.Formatter;
47 
48 import ocean.sys.Environment;
49 
50 
51 class FileCreationTestTask: Task
52 {
53     /***************************************************************************
54 
55         File operations to be checked
56 
57     ***************************************************************************/
58 
59     private bool created;
60     private bool modified = false;
61     private bool deleted  = false;
62     private bool closed   = false;
63 
64     /***************************************************************************
65 
66         Name of the created file.
67 
68     ***************************************************************************/
69 
70     private cstring created_name;
71 
72     /***************************************************************************
73 
74         Path to the monitored file.
75 
76     ***************************************************************************/
77 
78     private FilePath temp_path;
79 
80     /***************************************************************************
81 
82         Variable to control/test the order of the file operations
83 
84     ***************************************************************************/
85 
86     private int operation_order = 0;
87 
88     /***************************************************************************
89 
90         Path to the monitored directory.
91 
92     ***************************************************************************/
93 
94     private cstring watched_path;
95 
96     /***************************************************************************
97 
98         Tested FileSystemEvent instance.
99 
100     ***************************************************************************/
101 
102     private FileSystemEvent inotifier;
103 
104     /***************************************************************************
105 
106         TaskEvent to suspend/resume the task.
107 
108     ***************************************************************************/
109 
110     private TaskEvent task_event;
111 
112     /***************************************************************************
113 
114         Test that tests monitoring a directory and watching for the file
115         creation.
116 
117     ***************************************************************************/
118 
119     private void testFileCreation ( )
120     {
121         inotifier.watch(this.watched_path.dup,
122                FileEventsEnum.IN_CREATE);
123 
124         theScheduler.epoll.register(inotifier);
125 
126         auto file_name = "test_file";
127         File.set(file_name, "".dup);
128 
129         this.task_event.wait();
130 
131         theScheduler.epoll.unregister(inotifier);
132         inotifier.unwatch(this.watched_path.dup);
133 
134         test(this.created);
135         test!("==")(this.created_name, file_name);
136     }
137 
138     /***************************************************************************
139 
140         Test that tests modifications/closing/deleting performed on individual
141         file (not a directory)
142 
143     ***************************************************************************/
144 
145     private void testFileModification ( )
146     {
147         auto temp_file = new File("./testfile_modification", File.WriteCreate);
148         this.temp_path = FilePath(temp_file.toString());
149 
150         inotifier.watch(cast(char[]) temp_file.toString(),
151                        FileEventsEnum.IN_MODIFY | FileEventsEnum.IN_DELETE_SELF
152                      | FileEventsEnum.IN_CLOSE_WRITE );
153 
154         theScheduler.epoll.register(inotifier);
155 
156         temp_file.write("something");
157         temp_file.close;
158         temp_path.remove();
159 
160         this.task_event.wait();
161 
162         theScheduler.epoll.unregister(inotifier);
163 
164         test(this.modified);
165         test(this.closed);
166         test(this.deleted);
167     }
168 
169     /***************************************************************************
170 
171         Test entry point. Prepares environment and tests the FileSystemEvent.
172 
173     ***************************************************************************/
174 
175     override public void run ( )
176     {
177         auto makd_tmpdir = Environment.get("MAKD_TMPDIR");
178         mstring path_template;
179         sformat(path_template, "{}/Dunittests-XXXXXXXX",
180                 makd_tmpdir.length? makd_tmpdir : "/tmp");
181 
182         auto sandbox = DirectorySandbox.create(null, path_template);
183         scope (exit)
184             sandbox.exitSandbox();
185 
186         this.watched_path = sandbox.path;
187 
188         this.inotifier  = new FileSystemEvent(&this.fileSystemHandler);
189 
190         this.testFileCreation();
191         this.testFileModification();
192     }
193 
194     /**********************************************************************
195 
196         File System handler: called anytime a File System event occurs.
197 
198         Params:
199             path   = monitored path
200             name = name of the file
201             event  = Inotify event (see FileEventsEnum)
202 
203     **********************************************************************/
204 
205     private void fileSystemHandler ( FileSystemEvent.RaisedEvent raised_event )
206     {
207         with (raised_event.Active) switch (raised_event.active)
208         {
209         case directory_file_event:
210             auto event = raised_event.directory_file_event;
211 
212             if ( this.watched_path == event.path )
213             {
214                 switch ( event.event )
215                 {
216                     case FileEventsEnum.IN_CREATE:
217                         this.created = true;
218                         this.created_name = event.name.dup;
219                         this.task_event.trigger();
220                         break;
221                     default:
222                         test(false, "Unexpected file system event notification.");
223                 }
224             }
225             break;
226 
227         case file_event:
228             auto event = raised_event.file_event;
229 
230             if ( this.temp_path == event.path )
231             {
232                 this.operation_order++;
233 
234                 switch ( event.event )
235                 {
236                     case FileEventsEnum.IN_MODIFY:
237 
238                         if ( this.operation_order == 1 )
239                         {
240                             this.modified = true;
241                         }
242                         break;
243 
244                     case FileEventsEnum.IN_CLOSE_WRITE:
245 
246                         if ( this.operation_order == 2 )
247                         {
248                             this.closed = true;
249                         }
250                         break;
251 
252                     case FileEventsEnum.IN_DELETE_SELF:
253 
254                         if ( this.operation_order == 3 )
255                         {
256                             this.deleted = true;
257                             this.task_event.trigger();
258                         }
259                         break;
260 
261                     case FileEventsEnum.IN_IGNORED:
262                         enforce(this.deleted);
263                         break;
264 
265                     default:
266                         test(false, "Unexpected file system event notification.");
267                 }
268             }
269             break;
270 
271         default:
272             assert(false);
273         }
274     }
275 }
276 
277 
278 /*******************************************************************************
279 
280     Main test
281 
282 *******************************************************************************/
283 
284 version (unittest) {} else
285 void main ( )
286 {
287     initScheduler(SchedulerConfiguration.init);
288     theScheduler.exception_handler = (Task t, Exception e) {
289         throw e;
290     };
291 
292     auto dir_test_task = new FileCreationTestTask;
293     theScheduler.schedule(dir_test_task);
294     theScheduler.eventLoop();
295 }