View Javadoc

1   // Copyright (C) 2005 - 2009 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.console.distribution;
23  
24  import java.io.File;
25  import java.io.FileFilter;
26  import java.util.regex.Pattern;
27  
28  import net.grinder.console.communication.DistributionControl;
29  import net.grinder.console.communication.ProcessControl;
30  import net.grinder.console.distribution.FileChangeWatcher.FileChangedListener;
31  import net.grinder.testutility.AbstractFileTestCase;
32  import net.grinder.testutility.AssertUtilities;
33  import net.grinder.testutility.CallData;
34  import net.grinder.testutility.FileUtilities;
35  import net.grinder.testutility.RandomStubFactory;
36  import net.grinder.util.Directory;
37  
38  
39  /**
40   * Unit test for {@link FileDistributionImplementation}.
41   *
42   * @author Philip Aston
43   */
44  public class TestFileDistribution extends AbstractFileTestCase {
45  
46    private final Pattern m_matchIgnoredPattern =
47      Pattern.compile("^.grinder/$");
48    private final Pattern m_matchAllPattern = Pattern.compile(".*");
49  
50    private final RandomStubFactory<ProcessControl> m_processControlStubFactory =
51      RandomStubFactory.create(ProcessControl.class);
52    private final ProcessControl m_processControl =
53      m_processControlStubFactory.getStub();
54  
55    public void testGetHandler() throws Exception {
56      final RandomStubFactory<DistributionControl> distributionControlStubFactory =
57        RandomStubFactory.create(DistributionControl.class);
58      final DistributionControl distributionControl =
59        distributionControlStubFactory.getStub();
60  
61      final Directory directory1 = new Directory(getDirectory());
62  
63      final FileDistributionImplementation fileDistribution =
64        new FileDistributionImplementation(distributionControl,
65                                           m_processControl,
66                                           directory1,
67                                           m_matchIgnoredPattern);
68  
69      distributionControlStubFactory.assertNoMoreCalls();
70  
71      assertNotNull(fileDistribution.getAgentCacheState());
72  
73      final File anotherFile = new File(getDirectory(), "foo");
74      assertTrue(anotherFile.mkdir());
75      final Directory directory2 = new Directory(anotherFile);
76  
77      final FileDistributionHandler fileDistributionHandler1 =
78        fileDistribution.getHandler();
79  
80      distributionControlStubFactory.assertNoMoreCalls();
81  
82      // Test with same directory.
83      final FileDistributionHandler fileDistributionHandler2 =
84        fileDistribution.getHandler();
85  
86      assertNotSame(fileDistributionHandler1, fileDistributionHandler2);
87      distributionControlStubFactory.assertNoMoreCalls();
88  
89      // Test with a different directory.
90      fileDistribution.setDirectory(directory2);
91  
92      final FileDistributionHandler fileDistributionHandler3 =
93        fileDistribution.getHandler();
94  
95      assertNotSame(fileDistributionHandler1, fileDistributionHandler3);
96      assertNotSame(fileDistributionHandler2, fileDistributionHandler3);
97  
98      distributionControlStubFactory.assertNoMoreCalls();
99    }
100 
101   public void testScanDistributionFiles() throws Exception {
102     final RandomStubFactory<DistributionControl>
103       distributionControlStubFactory =
104         RandomStubFactory.create(DistributionControl.class);
105 
106     final UpdateableAgentCacheStateStubFactory
107       agentCacheStateStubFactory =
108         new UpdateableAgentCacheStateStubFactory();
109 
110     final UpdateableAgentCacheState agentCacheState =
111       agentCacheStateStubFactory.getStub();
112 
113     final RandomStubFactory<FileChangedListener> fileListenerStubFactory =
114       RandomStubFactory.create(FileChangedListener.class);
115 
116     final Directory directory = new Directory(getDirectory());
117     agentCacheStateStubFactory.override_setDirectory(null, directory);
118     agentCacheStateStubFactory.override_setFileFilterPattern(
119       null, m_matchIgnoredPattern);
120 
121     final FileDistributionImplementation fileDistribution =
122       new FileDistributionImplementation(
123         distributionControlStubFactory.getStub(),
124         agentCacheState);
125     fileDistribution.addFileChangedListener(fileListenerStubFactory.getStub());
126 
127     fileDistribution.scanDistributionFiles();
128 
129     final CallData filesChangedCall =
130       fileListenerStubFactory.assertSuccess("filesChanged", File[].class);
131     final File[] changedFiles = (File[])(filesChangedCall.getParameters()[0]);
132     assertEquals(1, changedFiles.length);
133     assertTrue(changedFiles[0].equals(directory.getFile()));
134 
135     fileListenerStubFactory.assertNoMoreCalls();
136 
137     final File file1 = new File(getDirectory(), "file1");
138     assertTrue(file1.createNewFile());
139     final File file2 = new File(getDirectory(), "file2");
140     assertTrue(file2.createNewFile());
141     final File oldFile = new File(getDirectory(), "file3");
142     assertTrue(oldFile.createNewFile());
143     assertTrue(oldFile.setLastModified(0));
144     assertTrue(file2.setLastModified(file1.lastModified() + 5000));
145 
146     assertTrue(file1.delete());
147     assertTrue(file1.createNewFile());
148     assertTrue(file2.delete());
149     assertTrue(file2.createNewFile());
150     assertTrue(file2.setLastModified(file1.lastModified() + 5000));
151 
152     fileDistribution.scanDistributionFiles();
153     assertEquals(file1.lastModified(),
154                  agentCacheStateStubFactory.getEarliestOutOfDateTime());
155 
156     final CallData filesChangedCall2 =
157       fileListenerStubFactory.assertSuccess("filesChanged", File[].class);
158     final File[] changedFiles2 = (File[])(filesChangedCall2.getParameters()[0]);
159     assertEquals(3, changedFiles2.length);
160     AssertUtilities.assertArrayContainsAll(
161       changedFiles2,
162       new File[] { directory.getFile(), file1, file2 } );
163 
164     fileListenerStubFactory.assertNoMoreCalls();
165 
166     // Even if the cache has older out of date times, we only scan from the
167     // last scan time.
168     final File file4 = new File(getDirectory(), "file4");
169     assertTrue(file4.createNewFile());
170     agentCacheStateStubFactory.resetOutOfDate();
171     fileDistribution.scanDistributionFiles();
172     assertEquals(file4.lastModified(),
173                  agentCacheStateStubFactory.getEarliestOutOfDateTime());
174     fileListenerStubFactory.resetCallHistory();
175 
176     fileDistribution.setDirectory(directory);
177     fileDistribution.setFileFilterPattern(m_matchAllPattern);
178     fileDistribution.scanDistributionFiles();
179     fileListenerStubFactory.assertNoMoreCalls();
180 
181     // Do some checks with directories
182     agentCacheStateStubFactory.resetOutOfDate();
183     final File testDirectory = new File(getDirectory(), "test");
184     assertTrue(testDirectory.mkdir());
185     fileDistribution.setDirectory(new Directory(testDirectory));
186 
187     // Set the fileDistribution scan time to now.
188     fileDistribution.setFileFilterPattern(m_matchIgnoredPattern);
189     fileDistribution.scanDistributionFiles();
190     fileListenerStubFactory.resetCallHistory();
191 
192     assertTrue(testDirectory.setLastModified(
193       testDirectory.lastModified() + 5000));
194 
195     final File directory1 = new File(getDirectory(), "test/dir1");
196     assertTrue(directory1.mkdir());
197     final File oldDirectory = new File(getDirectory(), "test/dir3");
198     assertTrue(oldDirectory.mkdir());
199     final File directory2 = new File(getDirectory(), "test/dir3/dir2");
200     assertTrue(directory2.mkdir());
201     assertTrue(oldDirectory.setLastModified(0));
202     assertTrue(file2.setLastModified(file1.lastModified() + 5000));
203 
204     fileDistribution.scanDistributionFiles();
205     // Directories no longer affect cache.
206     assertEquals(Long.MAX_VALUE,
207                  agentCacheStateStubFactory.getEarliestOutOfDateTime());
208 
209     final CallData directoriesChangedCall =
210       fileListenerStubFactory.assertSuccess("filesChanged", File[].class);
211     final File[] changedDirectories =
212       (File[])(directoriesChangedCall.getParameters()[0]);
213     assertEquals(3, changedDirectories.length);
214     AssertUtilities.assertArrayContainsAll(changedDirectories,
215       new File[] { testDirectory, directory1, directory2 } );
216 
217     fileListenerStubFactory.assertNoMoreCalls();
218 
219     // If the cache has been reset, we scan the lot.
220     fileDistribution.setDirectory(directory);
221     fileDistribution.scanDistributionFiles();
222     assertEquals(0, agentCacheStateStubFactory.getEarliestOutOfDateTime());
223     fileListenerStubFactory.resetCallHistory();
224 
225     // Test with r/o directory, just for coverage's sake.
226     final Directory subdirectory =
227       new Directory(new File(getDirectory(), "subdirectory"));
228     subdirectory.create();
229     final File f1 = new File(subdirectory.getFile(), "file");
230     assertTrue(f1.createNewFile());
231 
232     FileUtilities.setCanAccess(subdirectory.getFile(), false);
233 
234     fileDistribution.setDirectory(subdirectory);
235     fileDistribution.scanDistributionFiles();
236 
237     assertEquals(f1.lastModified(),
238                  agentCacheStateStubFactory.getEarliestOutOfDateTime());
239 
240     FileUtilities.setCanAccess(subdirectory.getFile(), true);
241   }
242 
243   public static class UpdateableAgentCacheStateStubFactory
244     extends RandomStubFactory<UpdateableAgentCacheState> {
245 
246     private long m_earliestOutOfDateTime = Long.MAX_VALUE;
247     private Directory m_directory;
248     private Pattern m_pattern;
249 
250     public UpdateableAgentCacheStateStubFactory() {
251       super(UpdateableAgentCacheState.class);
252     }
253 
254     public long getEarliestOutOfDateTime() {
255       return m_earliestOutOfDateTime;
256     }
257 
258     public void override_setNewFileTime(Object proxy, long t) {
259       if (t < m_earliestOutOfDateTime) {
260         m_earliestOutOfDateTime = t;
261       }
262     }
263 
264     public void resetOutOfDate() {
265       m_earliestOutOfDateTime = Long.MAX_VALUE;
266     }
267 
268     public void override_setDirectory(Object proxy, Directory directory) {
269        m_directory = directory;
270     }
271 
272     public void override_setFileFilterPattern(Object proxy,
273                                               Pattern fileFilterPattern) {
274       m_pattern = fileFilterPattern;
275     }
276 
277     public CacheParameters override_getCacheParameters(Object proxy) {
278       return new CacheParametersImplementation(m_directory,
279                                                m_pattern);
280     }
281   }
282 
283   public void testFilter() throws Exception {
284     final Pattern pattern = Pattern.compile("^a.*[^/]$|.*exclude.*|.*b/$");
285 
286     final FileFilter filter =
287       new FileDistributionImplementation.FixedPatternFileFilter(10000L,
288                                                                 pattern);
289 
290     final String[] acceptableFilenames = new String[] {
291       "DoesntStartWithA.acceptable",
292       "blah blah blah",
293       "blah-file-store",
294     };
295 
296     for (int i = 0; i < acceptableFilenames.length; ++i) {
297       final File f = new File(getDirectory(), acceptableFilenames[i]);
298       assertTrue(f.createNewFile());
299       assertTrue(f.getPath() + " is acceptable", filter.accept(f));
300     }
301 
302     final String[] unacceptableFileNames = new String[] {
303       "exclude me",
304       "a file beginning with a",
305       "a directory ending with b",
306     };
307 
308     for (int i = 0; i < unacceptableFileNames.length; ++i) {
309       final File f = new File(getDirectory(), unacceptableFileNames[i]);
310       assertTrue(f.createNewFile());
311 
312       assertTrue(f.getPath() + " is unacceptable", !filter.accept(f));
313     }
314 
315     final File timeFile = new File(getDirectory(), "time file");
316     assertTrue(timeFile.createNewFile());
317     assertTrue(timeFile.getPath() + " is acceptable", filter.accept(timeFile));
318     assertTrue(timeFile.setLastModified(123L));
319     assertTrue(timeFile.getPath() + " is unacceptable",
320                !filter.accept(timeFile));
321 
322     // Add an error margin, as Linux does not support setting the modification
323     // date with millisecond precision.
324     assertTrue(timeFile.setLastModified(101001L));
325     assertTrue(timeFile.getPath() + " is acceptable", filter.accept(timeFile));
326 
327     final String[] acceptableDirectoryNames = new String[] {
328       "a directory ending with b.not",
329       "include me",
330     };
331 
332     for (int i = 0; i < acceptableDirectoryNames.length; ++i) {
333       final File f = new File(getDirectory(), acceptableDirectoryNames[i]);
334       assertTrue(f.mkdir());
335       assertTrue(f.getPath() + " is acceptable", filter.accept(f));
336     }
337 
338     final String[] unacceptableDirectoryNames = new String[] {
339       "a directory ending with b",
340       "exclude me",
341     };
342 
343     for (int i = 0; i < unacceptableDirectoryNames.length; ++i) {
344       final File f = new File(getDirectory(), unacceptableDirectoryNames[i]);
345       assertTrue(f.getPath() + " is unacceptable", !filter.accept(f));
346     }
347 
348     final File timeDirectory = new File(getDirectory(), "time directory");
349     assertTrue(timeDirectory.mkdir());
350     assertTrue(timeDirectory.getPath() + " is acceptable",
351                filter.accept(timeDirectory));
352     assertTrue(timeDirectory.setLastModified(123L));
353     assertTrue(timeDirectory.getPath() + " is acceptable",
354                filter.accept(timeDirectory));
355 
356     final File fileStoreDirectory = new File(getDirectory(), "foo-file-store");
357     assertTrue(fileStoreDirectory.mkdir());
358     assertTrue(fileStoreDirectory.getPath() + " is acceptable",
359                filter.accept(fileStoreDirectory));
360 
361     final File readMeFile = new File(fileStoreDirectory, "README.txt");
362     assertTrue(readMeFile.createNewFile());
363     assertTrue(fileStoreDirectory.getPath() + " is unacceptable",
364                !filter.accept(fileStoreDirectory));
365 
366     assertTrue(readMeFile.delete());
367     assertTrue(fileStoreDirectory.getPath() + " is acceptable",
368                filter.accept(fileStoreDirectory));
369   }
370 
371   public void testIsDistributableFile() throws Exception {
372     final Pattern pattern = Pattern.compile("^a.*[^/]$|.*exclude.*|.*b/$");
373 
374     final FileDistributionImplementation fileDistribution =
375       new FileDistributionImplementation(
376         null, m_processControl, new Directory(getDirectory()), pattern);
377     final FileFilter filter = fileDistribution.getDistributionFileFilter();
378 
379     final String[] acceptableFilenames = new String[] {
380       "DoesntStartWithA.acceptable",
381       "blah blah blah",
382       "blah-file-store",
383     };
384 
385     for (int i = 0; i < acceptableFilenames.length; ++i) {
386       final File f = new File(getDirectory(), acceptableFilenames[i]);
387       assertTrue(f.createNewFile());
388       assertTrue(f.getPath() + " is distributable", filter.accept(f));
389     }
390 
391     final String[] unacceptableFileNames = new String[] {
392       "exclude me",
393       "a file beginning with a",
394       "a directory ending with b",
395     };
396 
397     for (int i = 0; i < unacceptableFileNames.length; ++i) {
398       final File f = new File(getDirectory(), unacceptableFileNames[i]);
399       assertTrue(f.createNewFile());
400 
401       assertTrue(f.getPath() + " is not distributable", !filter.accept(f));
402     }
403 
404     // filter should still be valid if pattern changes.
405     fileDistribution.setFileFilterPattern(Pattern.compile(".*exclude.*"));
406 
407     assertTrue(!filter.accept(new File(getDirectory(), "exclude me")));
408     assertTrue(
409       filter.accept(new File(getDirectory(), "a file begining with a")));
410 
411   }
412 }