1/*
2 * Copyright 2010 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.mockftpserver.fake.filesystem
17
18import org.mockftpserver.test.AbstractGroovyTestCase
19
20/**
21 * Abstract superclass for tests of FileSystem implementation classes. Contains common
22 * tests and test infrastructure.
23 *
24 * @version $Revision$ - $Date$
25 *
26 * @author Chris Mair
27 */
28abstract class AbstractFileSystemTestCase extends AbstractGroovyTestCase {
29
30    public static final FILENAME1 = "File1.txt"
31    public static final FILENAME2 = "file2.txt"
32    public static final DIR1 = "dir1"
33    public static final NEW_DIRNAME = "testDir"
34    public static final ILLEGAL_FILE = "xx/yy////z!<>?*z.txt"
35    public static final EXISTING_FILE_CONTENTS = "abc 123 %^& xxx"
36    public static final DATE = new Date()
37
38    // These must be set by the concrete subclass (in its constructor)
39    protected String NEW_DIR = null
40    protected String NEW_FILE = null
41    protected String EXISTING_DIR = null
42    protected String EXISTING_FILE = null
43    protected NO_SUCH_DIR = null
44    protected NO_SUCH_FILE = null
45
46    protected FileSystem fileSystem
47
48    //-------------------------------------------------------------------------
49    // Common Tests
50    //-------------------------------------------------------------------------
51
52    void testExists() {
53        assert !fileSystem.exists(NEW_FILE)
54        assert !fileSystem.exists(NEW_DIR)
55        assert !fileSystem.exists(ILLEGAL_FILE)
56        assert fileSystem.exists(EXISTING_FILE)
57        assert fileSystem.exists(EXISTING_DIR)
58
59        shouldFailWithMessageContaining("path") { fileSystem.exists(null) }
60    }
61
62    void testIsDirectory() {
63        assert fileSystem.isDirectory(EXISTING_DIR)
64        assert !fileSystem.isDirectory(EXISTING_FILE)
65        assert !fileSystem.isDirectory(NO_SUCH_DIR)
66        assert !fileSystem.isDirectory(NO_SUCH_FILE)
67        assert !fileSystem.isDirectory(ILLEGAL_FILE)
68
69        shouldFailWithMessageContaining("path") { fileSystem.isDirectory(null) }
70    }
71
72    void testIsFile() {
73        assert fileSystem.isFile(EXISTING_FILE)
74        assert !fileSystem.isFile(EXISTING_DIR)
75        assert !fileSystem.isFile(NO_SUCH_DIR)
76        assert !fileSystem.isFile(NO_SUCH_FILE)
77        assert !fileSystem.isFile(ILLEGAL_FILE)
78
79        shouldFailWithMessageContaining("path") { fileSystem.isFile(null) }
80    }
81
82    void testAdd_Directory() {
83        assert !fileSystem.exists(NEW_DIR), "Before createDirectory"
84        fileSystem.add(new DirectoryEntry(NEW_DIR))
85        assert fileSystem.exists(NEW_DIR), "After createDirectory"
86
87        // Duplicate directory
88        shouldThrowFileSystemExceptionWithMessageKey('filesystem.pathAlreadyExists') {
89            fileSystem.add(new DirectoryEntry(NEW_DIR))
90        }
91
92        // The parent of the path does not exist
93        shouldThrowFileSystemExceptionWithMessageKey('filesystem.parentDirectoryDoesNotExist') {
94            fileSystem.add(new DirectoryEntry(NEW_DIR + "/abc/def"))
95        }
96
97        shouldFail(InvalidFilenameException) { fileSystem.add(new DirectoryEntry(ILLEGAL_FILE)) }
98        shouldFailWithMessageContaining("path") { fileSystem.add(new DirectoryEntry(null)) }
99    }
100
101    void testAdd_File() {
102        assert !fileSystem.exists(NEW_FILE), "Before createFile"
103        fileSystem.add(new FileEntry(NEW_FILE))
104        assert fileSystem.exists(NEW_FILE), "After createFile"
105
106        // File already exists
107        shouldThrowFileSystemExceptionWithMessageKey('filesystem.pathAlreadyExists') {
108            fileSystem.add(new FileEntry(NEW_FILE))
109        }
110
111        // The parent of the path does not exist
112        shouldThrowFileSystemExceptionWithMessageKey('filesystem.parentDirectoryDoesNotExist') {
113            fileSystem.add(new FileEntry(NEW_DIR + "/abc/def"))
114        }
115
116        shouldThrowFileSystemExceptionWithMessageKey('filesystem.parentDirectoryDoesNotExist') {
117            fileSystem.add(new FileEntry(NO_SUCH_DIR))
118        }
119
120        shouldFail(InvalidFilenameException) { fileSystem.add(new FileEntry(ILLEGAL_FILE)) }
121
122        shouldFailWithMessageContaining("path") { fileSystem.add(new FileEntry(null)) }
123    }
124
125    void testRename_NullFromPath() {
126        shouldFailWithMessageContaining("fromPath") { fileSystem.rename(null, FILENAME1) }
127    }
128
129    void testRename_NullToPath() {
130        shouldFailWithMessageContaining("toPath") { fileSystem.rename(FILENAME1, null) }
131    }
132
133    void testListNames() {
134        fileSystem.add(new DirectoryEntry(NEW_DIR))
135        assert fileSystem.listNames(NEW_DIR) == []
136
137        fileSystem.add(new FileEntry(p(NEW_DIR, FILENAME1)))
138        fileSystem.add(new FileEntry(p(NEW_DIR, FILENAME2)))
139        fileSystem.add(new DirectoryEntry(p(NEW_DIR, DIR1)))
140        fileSystem.add(new FileEntry(p(NEW_DIR, DIR1, "/abc.def")))
141
142        List filenames = fileSystem.listNames(NEW_DIR)
143        LOG.info("filenames=" + filenames)
144        assertSameIgnoringOrder(filenames, [FILENAME1, FILENAME2, DIR1])
145
146        // Specify a filename instead of a directory name
147        assert [FILENAME1] == fileSystem.listNames(p(NEW_DIR, FILENAME1))
148
149        assert [] == fileSystem.listNames(NO_SUCH_DIR)
150
151        shouldFailWithMessageContaining("path") { fileSystem.listNames(null) }
152    }
153
154    void testListNames_Wildcards() {
155        fileSystem.add(new DirectoryEntry(NEW_DIR))
156        fileSystem.add(new FileEntry(p(NEW_DIR, 'abc.txt')))
157        fileSystem.add(new FileEntry(p(NEW_DIR, 'def.txt')))
158
159        assertSameIgnoringOrder(fileSystem.listNames(p(NEW_DIR, '*.txt')), ['abc.txt', 'def.txt'])
160        assertSameIgnoringOrder(fileSystem.listNames(p(NEW_DIR, '*')), ['abc.txt', 'def.txt'])
161        assertSameIgnoringOrder(fileSystem.listNames(p(NEW_DIR, '???.???')), ['abc.txt', 'def.txt'])
162        assertSameIgnoringOrder(fileSystem.listNames(p(NEW_DIR, '*.exe')), [])
163        assertSameIgnoringOrder(fileSystem.listNames(p(NEW_DIR, 'abc.???')), ['abc.txt'])
164        assertSameIgnoringOrder(fileSystem.listNames(p(NEW_DIR, 'a?c.?xt')), ['abc.txt'])
165        assertSameIgnoringOrder(fileSystem.listNames(p(NEW_DIR, 'd?f.*')), ['def.txt'])
166    }
167
168    void testListFiles() {
169        fileSystem.add(new DirectoryEntry(NEW_DIR))
170        assert [] == fileSystem.listFiles(NEW_DIR)
171
172        def path1 = p(NEW_DIR, FILENAME1)
173        def fileEntry1 = new FileEntry(path1)
174        fileSystem.add(fileEntry1)
175        assert fileSystem.listFiles(NEW_DIR) == [fileEntry1]
176
177        // Specify a filename instead of a directory name
178        assert fileSystem.listFiles(p(NEW_DIR, FILENAME1)) == [fileEntry1]
179
180        def fileEntry2 = new FileEntry(p(NEW_DIR, FILENAME2))
181        fileSystem.add(fileEntry2)
182        assert fileSystem.listFiles(NEW_DIR) as Set == [fileEntry1, fileEntry2] as Set
183
184        // Write to the file to get a non-zero length
185        final byte[] CONTENTS = "1234567890".getBytes()
186        OutputStream out = fileEntry1.createOutputStream(false)
187        out.write(CONTENTS)
188        out.close()
189        assert fileSystem.listFiles(NEW_DIR) as Set == [fileEntry1, fileEntry2] as Set
190
191        def dirEntry3 = new DirectoryEntry(p(NEW_DIR, DIR1))
192        fileSystem.add(dirEntry3)
193        assert fileSystem.listFiles(NEW_DIR) as Set == [fileEntry1, fileEntry2, dirEntry3] as Set
194
195        assert fileSystem.listFiles(NO_SUCH_DIR) == []
196
197        shouldFailWithMessageContaining("path") { fileSystem.listFiles(null) }
198    }
199
200    void testListFiles_Wildcards() {
201        def dirEntry = new DirectoryEntry(NEW_DIR)
202        def fileEntry1 = new FileEntry(p(NEW_DIR, 'abc.txt'))
203        def fileEntry2 = new FileEntry(p(NEW_DIR, 'def.txt'))
204
205        fileSystem.add(dirEntry)
206        fileSystem.add(fileEntry1)
207        fileSystem.add(fileEntry2)
208
209        assert fileSystem.listFiles(p(NEW_DIR, '*.txt')) as Set == [fileEntry1, fileEntry2] as Set
210        assert fileSystem.listFiles(p(NEW_DIR, '*')) as Set == [fileEntry1, fileEntry2] as Set
211        assert fileSystem.listFiles(p(NEW_DIR, '???.???')) as Set == [fileEntry1, fileEntry2] as Set
212        assert fileSystem.listFiles(p(NEW_DIR, '*.exe')) as Set == [] as Set
213        assert fileSystem.listFiles(p(NEW_DIR, 'abc.???')) as Set == [fileEntry1] as Set
214        assert fileSystem.listFiles(p(NEW_DIR, 'a?c.?xt')) as Set == [fileEntry1] as Set
215        assert fileSystem.listFiles(p(NEW_DIR, 'd?f.*')) as Set == [fileEntry2] as Set
216    }
217
218    void testDelete() {
219        fileSystem.add(new FileEntry(NEW_FILE))
220        assert fileSystem.delete(NEW_FILE)
221        assert !fileSystem.exists(NEW_FILE)
222
223        assert !fileSystem.delete(NO_SUCH_FILE)
224
225        fileSystem.add(new DirectoryEntry(NEW_DIR))
226        assert fileSystem.delete(NEW_DIR)
227        assert !fileSystem.exists(NEW_DIR)
228
229        fileSystem.add(new DirectoryEntry(NEW_DIR))
230        fileSystem.add(new FileEntry(NEW_DIR + "/abc.txt"))
231
232        assert !fileSystem.delete(NEW_DIR), "Directory containing files"
233        assert fileSystem.exists(NEW_DIR)
234
235        shouldFailWithMessageContaining("path") { fileSystem.delete(null) }
236    }
237
238    void testRename() {
239        final FROM_FILE = NEW_FILE + "2"
240        fileSystem.add(new FileEntry(FROM_FILE))
241
242        fileSystem.rename(FROM_FILE, NEW_FILE)
243        assert fileSystem.exists(NEW_FILE)
244
245        fileSystem.add(new DirectoryEntry(NEW_DIR))
246
247        // Rename existing directory
248        final String TO_DIR = NEW_DIR + "2"
249        fileSystem.rename(NEW_DIR, TO_DIR)
250        assert !fileSystem.exists(NEW_DIR)
251        assert fileSystem.exists(TO_DIR)
252    }
253
254    void testRename_ToPathFileAlreadyExists() {
255        final FROM_FILE = EXISTING_FILE
256        final String TO_FILE = NEW_FILE
257        fileSystem.add(new FileEntry(TO_FILE))
258         shouldThrowFileSystemExceptionWithMessageKey('filesystem.alreadyExists') {
259             fileSystem.rename(FROM_FILE, TO_FILE)
260         }
261    }
262
263    void testRename_FromPathDoesNotExist() {
264        final TO_FILE2 = NEW_FILE + "2"
265        shouldThrowFileSystemExceptionWithMessageKey('filesystem.doesNotExist') {
266            fileSystem.rename(NO_SUCH_FILE, TO_FILE2)
267        }
268        assert !fileSystem.exists(TO_FILE2), "After failed rename"
269    }
270
271    void testRename_ToPathIsChildOfFromPath() {
272        final FROM_DIR = NEW_DIR
273        final TO_DIR = FROM_DIR + "/child"
274        fileSystem.add(new DirectoryEntry(FROM_DIR))
275        shouldThrowFileSystemExceptionWithMessageKey('filesystem.renameFailed') {
276            fileSystem.rename(FROM_DIR, TO_DIR)
277        }
278        assert !fileSystem.exists(TO_DIR), "After failed rename"
279    }
280
281    void testRename_EmptyDirectory() {
282        final FROM_DIR = NEW_DIR
283        final TO_DIR = FROM_DIR + "2"
284        fileSystem.add(new DirectoryEntry(FROM_DIR))
285        fileSystem.rename(FROM_DIR, TO_DIR)
286        assert !fileSystem.exists(FROM_DIR)
287        assert fileSystem.exists(TO_DIR)
288    }
289
290    void testRename_DirectoryContainsFiles() {
291        fileSystem.add(new DirectoryEntry(NEW_DIR))
292        fileSystem.add(new FileEntry(NEW_DIR + "/a.txt"))
293        fileSystem.add(new FileEntry(NEW_DIR + "/b.txt"))
294        fileSystem.add(new DirectoryEntry(NEW_DIR + "/subdir"))
295
296        final String TO_DIR = NEW_DIR + "2"
297        fileSystem.rename(NEW_DIR, TO_DIR)
298        assert !fileSystem.exists(NEW_DIR)
299        assert !fileSystem.exists(NEW_DIR + "/a.txt")
300        assert !fileSystem.exists(NEW_DIR + "/b.txt")
301        assert !fileSystem.exists(NEW_DIR + "/subdir")
302
303        assert fileSystem.exists(TO_DIR)
304        assert fileSystem.exists(TO_DIR + "/a.txt")
305        assert fileSystem.exists(TO_DIR + "/b.txt")
306        assert fileSystem.exists(TO_DIR + "/subdir")
307    }
308
309    void testRename_ParentOfToPathDoesNotExist() {
310        final String FROM_FILE = NEW_FILE
311        final String TO_FILE = fileSystem.path(NO_SUCH_DIR, "abc")
312        fileSystem.add(new FileEntry(FROM_FILE))
313
314        shouldThrowFileSystemExceptionWithMessageKey('filesystem.parentDirectoryDoesNotExist') {
315            fileSystem.rename(FROM_FILE, TO_FILE)
316        }
317        assert fileSystem.exists(FROM_FILE)
318        assert !fileSystem.exists(TO_FILE)
319    }
320
321    void testGetParent_Null() {
322        shouldFailWithMessageContaining("path") { fileSystem.getParent(null) }
323    }
324
325    //-------------------------------------------------------------------------
326    // Test setup
327    //-------------------------------------------------------------------------
328
329    void setUp() {
330        super.setUp()
331        fileSystem = createFileSystem()
332    }
333
334    //-------------------------------------------------------------------------
335    // Helper Methods
336    //-------------------------------------------------------------------------
337
338    protected void shouldThrowFileSystemExceptionWithMessageKey(String messageKey, Closure closure) {
339        def e = shouldThrow(FileSystemException, closure)
340        assert e.messageKey == messageKey, "Expected message key [$messageKey], but was [${e.messageKey}]"
341    }
342
343    private verifyEntries(List expected, List actual) {
344        expected.eachWithIndex {entry, index ->
345            def entryStr = entry.toString()
346            LOG.info("expected=$entryStr")
347            assert actual.find {actualEntry -> actualEntry.toString() == entryStr }
348        }
349    }
350
351    protected void assertSameIgnoringOrder(list1, list2) {
352        LOG.info("Comparing $list1 to $list2")
353        assert list1 as Set == list2 as Set, "list1=$list1  list2=$list2"
354    }
355
356    /**
357     * Return a new instance of the FileSystem implementation class under test
358     * @return a new FileSystem instance
359     * @throws Exception
360     */
361    protected abstract FileSystem createFileSystem()
362
363    /**
364     * Verify the contents of the file at the specified path read from its InputSteam
365     *
366     * @param fileSystem - the FileSystem instance
367     * @param expectedContents - the expected contents
368     * @throws IOException
369     */
370    protected abstract void verifyFileContents(FileSystem fileSystem, String path, String contents) throws Exception
371
372}