View Javadoc

1   // Copyright (C) 2004 - 2011 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.engine.agent;
23  
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertFalse;
26  import static org.junit.Assert.assertNotNull;
27  import static org.junit.Assert.assertTrue;
28  import static org.junit.Assert.fail;
29  import static org.mockito.Matchers.contains;
30  import static org.mockito.Matchers.isA;
31  import static org.mockito.Mockito.mock;
32  import static org.mockito.Mockito.times;
33  import static org.mockito.Mockito.verify;
34  import static org.mockito.Mockito.verifyNoMoreInteractions;
35  
36  import java.io.File;
37  import java.io.FileOutputStream;
38  import java.io.OutputStream;
39  import java.util.Random;
40  
41  import net.grinder.communication.CommunicationException;
42  import net.grinder.communication.Message;
43  import net.grinder.communication.MessageDispatchSender;
44  import net.grinder.communication.SimpleMessage;
45  import net.grinder.messages.agent.CacheHighWaterMark;
46  import net.grinder.messages.agent.ClearCacheMessage;
47  import net.grinder.messages.agent.DistributeFileMessage;
48  import net.grinder.messages.agent.DistributionCacheCheckpointMessage;
49  import net.grinder.messages.agent.StubCacheHighWaterMark;
50  import net.grinder.testutility.AbstractJUnit4FileTestCase;
51  import net.grinder.testutility.FileUtilities;
52  import net.grinder.util.Directory;
53  import net.grinder.util.FileContents;
54  
55  import org.junit.Test;
56  import org.slf4j.Logger;
57  
58  
59  /**
60   *  Unit tests for {@code FileStore}.
61   *
62   * @author Philip Aston
63   */
64  public class TestFileStore extends AbstractJUnit4FileTestCase {
65  
66    private static final Random s_random = new Random();
67  
68    @Test public void testConstruction() throws Exception {
69  
70      File.createTempFile("file", "", getDirectory());
71      assertEquals(1, getDirectory().list().length);
72  
73      final FileStore fileStore = new FileStore(getDirectory(), null);
74      final File currentDirectory = fileStore.getDirectory().getFile();
75      assertNotNull(currentDirectory);
76  
77      assertTrue(
78        currentDirectory.getPath().startsWith(getDirectory().getPath()));
79  
80      // No messages have been received, so no physical directories will
81      // have been created yet.
82  
83      assertEquals(1, getDirectory().list().length);
84      assertTrue(!currentDirectory.exists());
85  
86      // Can't use a plain file.
87      final File file1 = File.createTempFile("file", "", getDirectory());
88  
89      try {
90        new FileStore(file1, null);
91        fail("Expected FileStoreException");
92      }
93      catch (FileStore.FileStoreException e) {
94      }
95  
96      // Nor a directory that contains a plain file clashing with one
97      // of the subdirectory names.
98      assertTrue(file1.delete());
99      assertTrue(file1.mkdir());
100     assertTrue(new File(file1, "current").createNewFile());
101 
102     try {
103       new FileStore(file1, null);
104       fail("Expected FileStoreException");
105     }
106     catch (FileStore.FileStoreException e) {
107     }
108 
109     // Can't use a read-only directory.
110     final File readOnlyDirectory = new File(getDirectory(), "directory");
111     assertTrue(readOnlyDirectory.mkdir());
112     assertTrue(readOnlyDirectory.setReadOnly());
113 
114     try {
115       new FileStore(readOnlyDirectory, null);
116       fail("Expected FileStoreException");
117     }
118     catch (FileStore.FileStoreException e) {
119     }
120 
121     // Perfectly fine to create a FileStore around a directory that
122     // doens't yet exist.
123     final File notThere = new File(getDirectory(), "notThere");
124     new FileStore(notThere, null);
125   }
126 
127   @Test public void testSender() throws Exception {
128 
129     final Logger logger = mock(Logger.class);
130 
131     final FileStore fileStore = new FileStore(getDirectory(), logger);
132 
133     final MessageDispatchSender messageDispatcher = new MessageDispatchSender();
134     fileStore.registerMessageHandlers(messageDispatcher);
135 
136     // Other Messages get ignored.
137     final Message message0 = new SimpleMessage();
138     messageDispatcher.send(message0);
139     verifyNoMoreInteractions(logger);
140 
141     // Shutdown does nothing.
142     messageDispatcher.shutdown();
143     verifyNoMoreInteractions(logger);
144 
145     // Test with a good message.
146     final File sourceDirectory = new File(getDirectory(), "source");
147     assertTrue(sourceDirectory.mkdirs());
148 
149     final File file0 = new File(sourceDirectory, "dir/file0");
150     assertTrue(file0.getParentFile().mkdirs());
151     final OutputStream outputStream = new FileOutputStream(file0);
152     final byte[] bytes = new byte[500];
153     s_random.nextBytes(bytes);
154     outputStream.write(bytes);
155     outputStream.close();
156 
157     final FileContents fileContents0 =
158       new FileContents(sourceDirectory, new File("dir/file0"));
159 
160     final File readmeFile = new File(getDirectory(), "README.txt");
161     final File incomingDirectoryFile = new File(getDirectory(), "incoming");
162     final File currentDirectoryFile = new File(getDirectory(), "current");
163     assertEquals(currentDirectoryFile, fileStore.getDirectory().getFile());
164 
165     // Before message sent, none of our files or directories exist.
166     assertTrue(!readmeFile.exists());
167     assertTrue(!incomingDirectoryFile.exists());
168     assertTrue(!currentDirectoryFile.exists());
169 
170     final Message message1 = new DistributeFileMessage(fileContents0);
171 
172     // Can't receive a DFM if the incoming directory can't be created.
173     FileUtilities.setCanAccess(getDirectory(), false);
174 
175     try {
176       messageDispatcher.send(message1);
177       fail("Expected CommunicationException");
178     }
179     catch (CommunicationException e) {
180     }
181 
182     FileUtilities.setCanAccess(getDirectory(), true);
183 
184     verify(logger).error(contains("Could not create directory"));
185 
186     assertFalse(incomingDirectoryFile.delete());
187 
188     messageDispatcher.send(message1);
189     verify(logger).info(contains("Updating file store"),
190                         isA(FileContents.class));
191 
192     // Message has been sent, the incoming directory and the read me exist.
193     assertTrue(readmeFile.exists());
194     assertTrue(incomingDirectoryFile.exists());
195     assertTrue(!currentDirectoryFile.exists());
196 
197     final File targetFile = new File(incomingDirectoryFile, "dir/file0");
198     assertTrue(targetFile.canRead());
199 
200     assertEquals(currentDirectoryFile, fileStore.getDirectory().getFile());
201 
202     // Now getDirectory() has been called, both directories exist.
203     assertTrue(readmeFile.exists());
204     assertTrue(incomingDirectoryFile.exists());
205     assertTrue(currentDirectoryFile.exists());
206 
207     // Frig with currentDirectory so that getDirectory() fails.
208     new Directory(currentDirectoryFile).deleteContents();
209     assertTrue(currentDirectoryFile.delete());
210     assertTrue(currentDirectoryFile.createNewFile());
211 
212     try {
213       fileStore.getDirectory();
214       fail("Expected FileStoreException");
215     }
216     catch (FileStore.FileStoreException e) {
217     }
218 
219     // Put things back again.
220     assertTrue(currentDirectoryFile.delete());
221     fileStore.getDirectory();
222 
223     // Test with a bad message.
224     assertTrue(targetFile.setReadOnly());
225 
226     try {
227       messageDispatcher.send(message1);
228       fail("Expected CommunicationException");
229     }
230     catch (CommunicationException e) {
231     }
232 
233     verify(logger, times(2)).info(contains("Updating file store"),
234                                   isA(FileContents.class));
235 
236     verify(logger).error(contains("Failed to create file"));
237 
238     final Message message2 = new ClearCacheMessage();
239 
240     FileUtilities.setCanAccess(targetFile, false);
241     // UNIX: Permission to remove a file is set on directory.
242     FileUtilities.setCanAccess(targetFile.getParentFile(), false);
243 
244     try {
245       messageDispatcher.send(message2);
246       fail("Expected CommunicationException");
247     }
248     catch (CommunicationException e) {
249     }
250 
251     FileUtilities.setCanAccess(targetFile.getParentFile(), true);
252     FileUtilities.setCanAccess(targetFile, true);
253 
254     verify(logger).info(contains("Clearing file store"));
255     verify(logger).error(contains("Could not delete"));
256 
257     messageDispatcher.send(message2);
258     verify(logger, times(2)).info(contains("Clearing file store"));
259     verifyNoMoreInteractions(logger);
260 
261     assertTrue(!targetFile.canRead());
262 
263     assertEquals(currentDirectoryFile, fileStore.getDirectory().getFile());
264   }
265 
266   @Test public void testFileStoreException() throws Exception {
267     final Exception nested = new Exception("");
268     final FileStore.FileStoreException e =
269       new FileStore.FileStoreException("bite me", nested);
270 
271     assertEquals(nested, e.getCause());
272   }
273 
274   @Test public void testDistributionCheckpointMessage() throws Exception {
275     final FileStore fileStore = new FileStore(getDirectory(), null);
276 
277     final CacheHighWaterMark outOfDateCacheHighWaterMark =
278       fileStore.getCacheHighWaterMark();
279     assertEquals(-1, outOfDateCacheHighWaterMark.getTime());
280     assertFalse(outOfDateCacheHighWaterMark.isForSameCache(
281                   outOfDateCacheHighWaterMark));
282 
283     final MessageDispatchSender messageDispatcher = new MessageDispatchSender();
284     fileStore.registerMessageHandlers(messageDispatcher);
285 
286     final CacheHighWaterMark cacheHighWaterMark =
287       new StubCacheHighWaterMark("", 123);
288     final Message message =
289       new DistributionCacheCheckpointMessage(cacheHighWaterMark);
290 
291     messageDispatcher.send(message);
292 
293     assertEquals(cacheHighWaterMark, fileStore.getCacheHighWaterMark());
294   }
295 }