View Javadoc
1   //
2   // $Id$
3   //
4   // jupload - A file upload applet.
5   //
6   // Copyright 2010 The JUpload Team
7   //
8   // Created: 27 janv. 2010
9   // Creator: etienne_sf
10  // Last modified: $Date$
11  //
12  // This program is free software; you can redistribute it and/or modify
13  // it under the terms of the GNU General Public License as published by
14  // the Free Software Foundation; either version 2 of the License, or
15  // (at your option) any later version.
16  //
17  // This program is distributed in the hope that it will be useful,
18  // but WITHOUT ANY WARRANTY; without even the implied warranty of
19  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  // GNU General Public License for more details.
21  //
22  // You should have received a copy of the GNU General Public License
23  // along with this program; if not, write to the Free Software
24  // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  
26  package wjhk.jupload2.upload;
27  
28  import java.io.File;
29  import java.io.IOException;
30  import java.lang.reflect.InvocationTargetException;
31  import java.lang.reflect.Method;
32  import java.net.Socket;
33  import java.net.URL;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.Queue;
37  import java.util.StringTokenizer;
38  import java.util.concurrent.ArrayBlockingQueue;
39  import java.util.concurrent.BlockingQueue;
40  
41  import org.apache.log4j.Logger;
42  import org.junit.After;
43  import org.junit.Assert;
44  import org.junit.Before;
45  
46  import wjhk.jupload2.JUploadDaemon;
47  import wjhk.jupload2.context.JUploadContext;
48  import wjhk.jupload2.exception.JUploadException;
49  import wjhk.jupload2.filedata.FileData;
50  import wjhk.jupload2.gui.JUploadPanel;
51  import wjhk.jupload2.gui.filepanel.FilePanel;
52  import wjhk.jupload2.gui.filepanel.FilePanelFlatDataModel2;
53  import wjhk.jupload2.gui.filepanel.treeview.FileDataTreeViewModel;
54  import wjhk.jupload2.policies.UploadPolicy;
55  import wjhk.jupload2.testhelpers.FileDataTestHelper;
56  import wjhk.jupload2.testhelpers.FilePanelTestHelper;
57  import wjhk.jupload2.testhelpers.FileUploadManagerThreadTestHelper;
58  import wjhk.jupload2.testhelpers.FileUploadThreadTestHelper;
59  import wjhk.jupload2.testhelpers.JUploadContextTestHelper;
60  import wjhk.jupload2.testhelpers.JUploadPanelTestHelper;
61  import wjhk.jupload2.testhelpers.UploadPolicyTestHelper;
62  
63  /**
64   * This class is the superclass of all test classes in this package. It creates all common objects (upload policy,
65   * queues...)
66   * 
67   * @author etienne_sf
68   */
69  public class AbstractJUploadTestHelper {
70      /** Logger for this class */
71      protected final Logger logger = Logger.getLogger(AbstractJUploadTestHelper.class);
72  
73      final static String DEFAULT_LOCAL_POST_URL = "http://localhost/index.html";
74  
75      final static String DEFAULT_POST_URL = "http://jupload.sourceforge.net/upload_dummy.html";
76  
77      /** Maximum time to wait for a thread to finish its work, in milliseconds */
78      final static int MAX_WAIT_FOR_THREAD = 10000;
79  
80      /**
81       * Indicates whether the postURL system property has been set for the current unit test execution.
82       */
83      public static boolean postURLHasBeenSet = false;
84  
85      /** Interval of time, before checking if a thread has finished its work */
86      final static int INTERVAL_BEFORE_CHECKING_THREAD = 1000;
87  
88      /** A default {@link JUploadDaemon} */
89      public JUploadDaemon juploadDaemon;
90  
91      /** A default {@link JUploadContext} */
92      public JUploadContext juploadContext = null;
93  
94      /** A default {@link FilePanel} */
95      public FilePanel filePanel;
96  
97      public FilePanelFlatDataModel2 filePanelFlatDataModel2 = null;
98  
99      public FileDataTreeViewModel fileDataTreeViewModel = null;
100 
101     /** A default {@link JUploadPanel} */
102     public JUploadPanel juploadPanel;
103 
104     /** A default {@link UploadPolicy} */
105     public UploadPolicy uploadPolicy = null;
106 
107     /** The root for the file to upload */
108     public File fileroot = null;
109 
110     /**
111      * The list of files that will be loaded. Initialized in {@link #setupFileList(int)}.
112      */
113     public List<FileData> filesToUpload = null;
114 
115     /** A default {@link FilePreparationThread} */
116     public FilePreparationThread filePreparationThread = null;
117 
118     /** A default {@link PacketConstructionThread} */
119     public PacketConstructionThread packetConstructionThread = null;
120 
121     /** A default {@link FileUploadThread} */
122     public FileUploadThread fileUploadThread = null;
123 
124     /** A default {@link FileUploadManagerThread} */
125     public FileUploadManagerThread fileUploadManagerThread = null;
126 
127     /** The actual start of this test */
128     public long uploadStartTime = -1;
129 
130     /** A default queue, for the prepared files */
131     public BlockingQueue<UploadFileData> preparedFileQueue = new ArrayBlockingQueue<UploadFileData>(100);
132 
133     /** A default queue, for the packets to upload */
134     public BlockingQueue<UploadFilePacket> packetQueue = new ArrayBlockingQueue<UploadFilePacket>(100);
135 
136     /**
137      * Indicates the number of subfolders, for the src/test/resources/files. For instance, if the project is in
138      * H:\data\eclipse-luna\jupload, the files will be in the H:\data\eclipse-luna\jupload\target\test-classes\files\
139      * folder, then nbSubfoldersForSrcTestResources contains 7. This is calculated, as it depends on the project
140      * location, on the local hard drive.
141      */
142     public int nbSubfoldersForSrcTestResourcesFiles = 0;
143 
144     /**
145      * Constructs the UploadPolicy, and all threads to simulate a real upload. Maybe too long for real unit testing.
146      * 
147      * @throws Exception
148      */
149     @Before
150     public void setupFullUploadEnvironment() throws Exception {
151         // Set the postURL for the current unit test, according to the local network access.
152         setPostURL();
153 
154         this.juploadDaemon = new JUploadDaemon();
155         this.filePanel = new FilePanelTestHelper(this.filesToUpload);
156         this.juploadPanel = new JUploadPanelTestHelper(this.filePanel);
157         this.uploadPolicy = new UploadPolicyTestHelper(this.juploadPanel);
158         this.filePanelFlatDataModel2 = new FilePanelFlatDataModel2(this.uploadPolicy);
159         this.fileDataTreeViewModel = new FileDataTreeViewModel(uploadPolicy, this.filePanelFlatDataModel2);
160         // FolderNode visibleRoot =this.fileDataTreeViewModel.getTreePath(item)
161         // this.fileDataTreeViewModel.setRoot(new RootNode(uploadPolicy, this.fileDataTreeViewModel));
162 
163         this.juploadContext = this.uploadPolicy.getContext();
164 
165         this.fileUploadThread = new FileUploadThreadTestHelper(this.packetQueue);
166         this.fileUploadManagerThread = new FileUploadManagerThreadTestHelper();
167         ((JUploadPanelTestHelper) this.juploadPanel).fileUploadManagerThread = this.fileUploadManagerThread;
168 
169         // Set up the file data, for the simulated upload.
170         this.fileroot = new File(JUploadContextTestHelper.TEST_FILES_FOLDER);
171 
172         setupFileList(1);
173 
174         // Calculation of nbSubfoldersForSrcTestResources, which depends on the project location, on the local hard
175         // drive.
176         String path = getTestFilesRootPath().replaceAll("/", "\\");
177         nbSubfoldersForSrcTestResourcesFiles = new StringTokenizer(path, "\\").countTokens();
178 
179         // Let's note the current system time. It should be almost the upload
180         // start time.
181         this.uploadStartTime = System.currentTimeMillis();
182     }
183 
184     /**
185      * This method tries to determine if the current computer can access to jupload.sourceforge.net. If yes, the
186      * http://jupload.sourceforge.net/upload_dummy.html URL is used. <BR>
187      * If no, the http://localhost/index.html URL is used for test postURL.<BR>
188      * <BR>
189      * The reason for this test, is that I often work unconnected, in the train. And I still want the unit tests to work
190      * properly.
191      */
192     protected void setPostURL() {
193         // If the system property has not been set yet, let's determine if
194         // we're connected to the network.
195         if (!postURLHasBeenSet) {
196             String postURL;
197             Socket socket = null;
198             try {
199                 URL url = new URL(DEFAULT_POST_URL);
200                 socket = new Socket(url.getHost(), url.getPort());
201                 // The given host is valid. We use this URL.
202                 postURL = DEFAULT_POST_URL;
203             } catch (Exception e) {
204                 logger.warn(e.getClass().getName() + " when creating URL from " + DEFAULT_POST_URL + " (will use "
205                         + DEFAULT_LOCAL_POST_URL + " instead");
206                 //
207                 postURL = DEFAULT_LOCAL_POST_URL;
208             } finally {
209                 if (socket != null) {
210                     try {
211                         socket.close();
212                     } catch (IOException e) {
213                         logger.error("Error while closing the socket", e);
214                     }
215                 }
216             }
217 
218             System.setProperty(UploadPolicy.PROP_POST_URL, postURL);
219             postURLHasBeenSet = true;
220         }
221     }
222 
223     /**
224      * Create a specific list of files, for tests, then set this list into the filePanel. If any file exists before, it
225      * is removed before adding these files.
226      * 
227      * @param nbFiles
228      */
229     public void setupFileList(int nbFiles) {
230 
231         this.filesToUpload = new ArrayList<FileData>(nbFiles);
232         File[] fArray = new File[nbFiles];
233 
234         for (int i = 0; i < nbFiles; i += 1) {
235             FileData fileData = new FileDataTestHelper(i, uploadPolicy);
236             // We must be able to load the file. Otherwise, it's useless to
237             // start. And there seems to be problem with user dir, depending on
238             // the java tool used.
239             Assert.assertTrue(fileData.getFileName() + " must be readable !", fileData.canRead());
240             this.filesToUpload.add(fileData);
241         }
242 
243         // Let's add these files to the FilePanel
244         if (this.filePanel instanceof FilePanelTestHelper) {
245             ((FilePanelTestHelper) this.filePanel).filesToUpload = this.filesToUpload;
246         } else {
247             // We first clear the list, to be sure of the final content.
248             this.filePanel.removeAll();
249             this.filePanel.addFiles(fArray);
250         }
251     }
252 
253     /**
254      * Call the beforeUpload method for all files.
255      * 
256      * @throws JUploadException
257      */
258     void prepareFileList() throws JUploadException {
259         for (FileData fileData : this.filesToUpload) {
260             fileData.beforeUpload(null);
261         }
262     }
263 
264     /**
265      * Wait for a queue to be emptied by a consuming thread. This will wait {@link #MAX_WAIT_FOR_THREAD} at most, and
266      * check this every {@link INTERVAL_BEFORE_CHECKING_THREAD} ms.
267      */
268     void waitForQueueToBeEmpty(Queue<UploadFileData> queue, String queueName) {
269         int nbLoop = MAX_WAIT_FOR_THREAD / INTERVAL_BEFORE_CHECKING_THREAD;
270         try {
271             for (int i = 0; i < nbLoop; i += 1) {
272                 if (queue.isEmpty()) {
273                     logger.info("The queue " + queueName + " has been emptied");
274                     return;
275                 }
276                 Thread.sleep(10);
277             }
278         } catch (InterruptedException e) {
279             logger.warn("waitForQueueToBeEmpty got interrupted");
280         }
281         logger.warn("The queue " + queueName + " was not emptied");
282     }
283 
284     /**
285      * Wait for a thread to finish normally. This will wait {@link #MAX_WAIT_FOR_THREAD} at most, and check this every
286      * {@link INTERVAL_BEFORE_CHECKING_THREAD} ms.
287      */
288     void waitForThreadToFinish(Thread thread, String threadName) {
289         if (thread.isAlive()) {
290             // Let's wait a little for this thread to finish...
291             try {
292                 (thread).join(MAX_WAIT_FOR_THREAD);
293             } catch (InterruptedException e) {
294                 Assert.fail("Was interrupted during the join: the thread " + threadName + " did not finish on time ");
295             }
296         }
297 
298         // Is the thread finished now ?
299         if (thread.isAlive()) {
300             logger.warn("The thread " + threadName + " did not finished alone: let's interrupt it");
301             thread.interrupt();
302         } else {
303             logger.info("The thread " + threadName + " finished alone (no interruption needed)");
304         }
305     }
306 
307     /** */
308     @After
309     public void cleanQueues() {
310         logger.debug("Finishing the test: interrupting the threads, and cleaning the queues");
311 
312         if (this.fileUploadThread != null) {
313             if (this.fileUploadThread.isAlive()) {
314                 this.fileUploadThread.interrupt();
315                 this.fileUploadThread = null;
316             }
317         }
318         if (this.fileUploadManagerThread != null) {
319             if (this.fileUploadManagerThread.isAlive()) {
320                 this.fileUploadManagerThread.interrupt();
321                 this.fileUploadManagerThread = null;
322             }
323         }
324         if (this.packetConstructionThread != null) {
325             if (this.packetConstructionThread.isAlive()) {
326                 this.packetConstructionThread.interrupt();
327                 this.packetConstructionThread = null;
328             }
329         }
330 
331         if (this.preparedFileQueue != null) {
332             while (!this.preparedFileQueue.isEmpty()) {
333                 this.preparedFileQueue.poll();
334             }
335             this.preparedFileQueue = null;
336         }
337 
338         if (this.packetQueue != null) {
339             while (!this.packetQueue.isEmpty()) {
340                 this.packetQueue.poll();
341             }
342             this.packetQueue = null;
343         }
344     }
345 
346     /**
347      * This method calls a given method onto a given object. It is used to execute the call of the callback registered
348      * by the last call to juploadContext.registerUnload.<BR>
349      * This works only if juploadContext is an instance of {@link JUploadContextTestHelper}
350      * 
351      * @param expectedClass Contains the class to which the lastRegisterUnloadObject should belong to.
352      * @throws InvocationTargetException
353      * @throws IllegalAccessException
354      * @throws IllegalArgumentException
355      * @throws NoSuchMethodException
356      * @throws SecurityException
357      */
358     @SuppressWarnings("rawtypes")
359     public void testLastRegisteredUnload(Class expectedClass) throws IllegalArgumentException, IllegalAccessException,
360             InvocationTargetException, SecurityException, NoSuchMethodException {
361         Assert.assertTrue("juploadContext must be an instance of JUploadContextTestHelper",
362                 this.juploadContext instanceof JUploadContextTestHelper);
363         Assert.assertTrue("The lastRegisterUnloadObject should be an instance of " + expectedClass,
364                 expectedClass.isInstance(((JUploadContextTestHelper) this.juploadContext).lastRegisterUnloadObject));
365         testUnload(((JUploadContextTestHelper) this.juploadContext).lastRegisterUnloadObject,
366                 ((JUploadContextTestHelper) this.juploadContext).lastRegisterUnloadMethod);
367     }
368 
369     /**
370      * This method calls a given method onto a given object.
371      * 
372      * @param object
373      * @param methodName
374      * @throws InvocationTargetException
375      * @throws IllegalAccessException
376      * @throws IllegalArgumentException
377      * @throws NoSuchMethodException
378      * @throws SecurityException
379      */
380     public void testUnload(Object object, String methodName) throws IllegalArgumentException, IllegalAccessException,
381             InvocationTargetException, SecurityException, NoSuchMethodException {
382         // Let's find and call this method. This method must have no argument.
383         Method method = object.getClass().getMethod(methodName);
384         Assert.assertNotNull("The method '" + methodName + "' must exist (and have no argument)", method);
385         method.invoke(object);
386     }
387 
388     /**
389      * This static class computes create a file instance for the given relative path.<BR>
390      * This method is taken from <A HREF= "http://kozelka.net/blog/generating-temporary-files-in-junit-tests">Petr
391      * Kozelka's blog</A>
392      * 
393      * @param relativeFilePath The path of the file, starting from src/test/resources.
394      * @return The File for the file, whose relative path to tests folder is given in argument. This method is
395      *         compatible with whatever tool is used to execute the tests: it can be from maven, from eclipse, or from
396      *         any other tool.
397      */
398     public static File getTestFile(String relativeFilePath) {
399         final String clsUri = AbstractJUploadTestHelper.class.getName().replace('.', File.separatorChar) + ".class";
400         final URL url = AbstractJUploadTestHelper.class.getClassLoader().getResource(clsUri);
401         final String clsPath = url.getPath().replaceAll("%20", " ").replaceAll("%5c", ".");
402         final File root = new File(clsPath.substring(0, clsPath.length() - clsUri.length()));
403         return new File(root, relativeFilePath);
404     }
405 
406     /**
407      * This method returns the absolute path for the given file. It is based on the {@link #getTestFile(String)} method.
408      * 
409      * @param relativeFilePath The path of the file, starting from src/test/resources.
410      * @return The absolute path for the given file.
411      */
412     public static String getTestFilePath(String relativeFilePath) {
413         return getTestFile(relativeFilePath).getAbsolutePath();
414     }
415 
416     /**
417      * Get the root for the tests files, whatever is the current tool used to run JUnit test: maven, eclipse, any IDE...
418      * 
419      * @return The absolute path for the test files root, ending with the file separator character.
420      */
421     public static String getTestFilesRootPath() {
422         return getTestFilePath("files") + File.separator;
423     }
424 }