EMMA Coverage Report (generated Sat Oct 08 11:41:37 CEST 2011)
[all classes][net.sf.jomic.tools]

COVERAGE SUMMARY FOR SOURCE FILE [TestTools.java]

nameclass, %method, %block, %line, %
TestTools.java100% (1/1)80%  (41/51)63%  (1236/1970)69%  (258.5/373)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TestTools100% (1/1)80%  (41/51)63%  (1236/1970)69%  (258.5/373)
assertGreaterOrEqual (double, double): void 0%   (0/1)0%   (0/21)0%   (0/2)
assertLessOrEqual (double, double): void 0%   (0/1)0%   (0/21)0%   (0/2)
assertLessThan (double, double): void 0%   (0/1)0%   (0/21)0%   (0/2)
createTempDir (Class, String): File 0%   (0/1)0%   (0/17)0%   (0/2)
createTempFile (Class, String, String): File 0%   (0/1)0%   (0/29)0%   (0/5)
createTempFile (String, String): File 0%   (0/1)0%   (0/26)0%   (0/5)
createTempZipArchive (Class, String, String [], String []): File 0%   (0/1)0%   (0/13)0%   (0/3)
createTestFrame (JComponent, Class, String): JFrame 0%   (0/1)0%   (0/70)0%   (0/15)
getJomicHome (): File 0%   (0/1)0%   (0/12)0%   (0/2)
getTestsBaseDir (): File 0%   (0/1)0%   (0/3)0%   (0/1)
assertFilesEqual (String): void 100% (1/1)35%  (17/48)38%  (5/13)
createImageInputStream (File): ImageInputStream 100% (1/1)41%  (11/27)70%  (3.5/5)
setupLogging (): void 100% (1/1)46%  (38/82)49%  (10.3/21)
assertFilesEqual (File, File): void 100% (1/1)46%  (45/97)67%  (14.8/22)
getTestImage (String): RenderedImage 100% (1/1)49%  (28/57)58%  (7.5/13)
waitSomeTime (): void 100% (1/1)50%  (6/12)60%  (3/5)
getCleanTestOutputFolder (String): File 100% (1/1)56%  (18/32)71%  (5/7)
createZipArchive (File, String [], String []): void 100% (1/1)59%  (91/153)74%  (16.9/23)
TestTools (): void 100% (1/1)62%  (158/254)65%  (30.7/47)
addZipEntry (ZipOutputStream, String, String): void 100% (1/1)65%  (48/74)90%  (15.4/17)
copyTestFile (String, String): void 100% (1/1)73%  (22/30)83%  (5/6)
getTestFiles (String): File [] 100% (1/1)73%  (55/75)83%  (10/12)
createTestZipArchive (File, String [], String []): void 100% (1/1)74%  (71/96)82%  (16.4/20)
getTestFile (String): File 100% (1/1)76%  (39/51)92%  (11/12)
getAtOrNull (String [], int): String 100% (1/1)82%  (18/22)90%  (4.5/5)
assertEquals (Dimension, Dimension): void 100% (1/1)83%  (15/18)80%  (4/5)
assertEquals (String [], String []): void 100% (1/1)85%  (45/53)88%  (7/8)
assertEquals (byte [], byte []): void 100% (1/1)85%  (45/53)88%  (7/8)
assertEquals (int [], int []): void 100% (1/1)85%  (45/53)88%  (7/8)
writeImageFile (File, RenderedImage): void 100% (1/1)89%  (42/47)97%  (9.7/10)
setupTestSettings (Class, String, String []): void 100% (1/1)90%  (105/117)94%  (19.6/21)
<static initializer> 100% (1/1)90%  (27/30)95%  (1.9/2)
getTestFileName (Class, String, String, String): String 100% (1/1)92%  (48/52)94%  (8.5/9)
assertGreaterOrEqual (long, long): void 100% (1/1)95%  (20/21)98%  (2/2)
assertGreaterThan (double, double): void 100% (1/1)95%  (20/21)98%  (2/2)
assertGreaterThan (long, long): void 100% (1/1)95%  (20/21)98%  (2/2)
assertLessOrEqual (long, long): void 100% (1/1)95%  (20/21)98%  (2/2)
assertLessThan (long, long): void 100% (1/1)95%  (20/21)98%  (2/2)
getTestComicFile (): File 100% (1/1)100% (4/4)100% (1/1)
getTestExpectedFile (String): File 100% (1/1)100% (7/7)100% (1/1)
getTestGeneratedInputDir (): File 100% (1/1)100% (3/3)100% (1/1)
getTestGeneratedInputFile (String): File 100% (1/1)100% (7/7)100% (1/1)
getTestImage (): RenderedImage 100% (1/1)100% (4/4)100% (1/1)
getTestImageFile (): File 100% (1/1)100% (4/4)100% (1/1)
getTestInputFile (String): File 100% (1/1)100% (7/7)100% (1/1)
getTestOutputFile (String): File 100% (1/1)100% (7/7)100% (1/1)
getTestTextFile (): File 100% (1/1)100% (4/4)100% (1/1)
instance (): TestTools 100% (1/1)100% (8/8)100% (3/3)
lengthOr0 (Object []): int 100% (1/1)100% (10/10)100% (4/4)
setupAbbotLogging (): void 100% (1/1)100% (13/13)100% (2/2)
setupCache (): void 100% (1/1)100% (21/21)100% (7/7)

1// Jomic - a viewer for comic book archives.
2// Copyright (C) 2004-2011 Thomas Aglassinger
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16package net.sf.jomic.tools;
17 
18import java.awt.Dimension;
19import java.awt.image.RenderedImage;
20import java.io.File;
21import java.io.FileFilter;
22import java.io.FileInputStream;
23import java.io.FileNotFoundException;
24import java.io.FileOutputStream;
25import java.io.IOException;
26import java.io.InputStream;
27import java.util.Arrays;
28import java.util.Iterator;
29import java.util.LinkedList;
30import java.util.List;
31import java.util.Properties;
32import java.util.zip.ZipEntry;
33import java.util.zip.ZipOutputStream;
34 
35import javax.imageio.ImageIO;
36import javax.imageio.ImageReader;
37import javax.imageio.ImageWriter;
38import javax.imageio.stream.ImageInputStream;
39import javax.imageio.stream.ImageOutputStream;
40import javax.media.jai.JAI;
41import javax.media.jai.PlanarImage;
42import javax.swing.JComponent;
43import javax.swing.JFrame;
44 
45import junit.framework.Assert;
46import net.sf.jomic.comic.ComicCache;
47import net.sf.jomic.common.JomicConfigurator;
48import net.sf.jomic.common.PropertyConstants;
49import net.sf.jomic.common.Settings;
50import net.sf.jomic.common.StartupTools;
51import org.apache.commons.logging.Log;
52import org.apache.commons.logging.LogFactory;
53 
54import org.apache.log4j.Level;
55 
56/**
57 *  Utility class to simplify testing.
58 *
59 * @author    Thomas Aglassinger
60 */
61public final class TestTools
62{
63    public static final String BROKEN_IMAGE_INCOMPLETE_CBZ = "broken_image_incomplete.cbz";
64    public static final String BROKEN_NO_IMAGES_PDF = "broken_no_images.pdf";
65    public static final String DISGUISED_RAR = "disguised_rar.cbz";
66    public static final String DISGUISED_ZIP = "disguised_zip.cbr";
67    public static final String[] EMPTY_IMAGE_NAMES = new String[]{"empty.gif", "empty.jpg", "empty.png"};
68    public static final int FRAME_HEIGHT = 300;
69    public static final int FRAME_WIDTH = 500;
70    public static final int HUGE_PAGE_COUNT = 150;
71    public static final String HUGO_COMIC = "huge.cbz";
72    public static final String IMAGES_WITH_WRONG_SUFFIX_COMIC = "images_with_wrong_suffix.cbz";
73    public static final String SINGLE_PAGE_COMIC = "1page.cbz";
74    public static final String TEST_COMIC_CBR = "test.cbr";
75    public static final String TEST_COMIC_CBZ = "test.cbz";
76    public static final String TEST_COMIC_FILE_NAME = TEST_COMIC_CBZ;
77    public static final String TEST_COMIC_INTERNAL_ERROR = "broken_comic_internal_zip_error.cbz";
78    public static final String TEST_COMIC_LANDSCAPE_ONLY = "test_landscape_only.cbz";
79    public static final String TEST_COMIC_PDF = "test.pdf";
80    public static final String TEST_COMIC_WITH_LOGO_TITLE = "test_with_logo_title.cbz";
81    public static final String TEST_EMPTY_IMAGES_CBZ = "test_empty_images.cbz";
82    public static final String TEST_IMAGE_1_BIT = "1-bit.png";
83    public static final String TEST_IMAGE_4_BIT = "4-bit.png";
84    public static final String TEST_IMAGE_8_BIT = "01.87a.gif";
85    public static final String TEST_IMAGE_8_BIT_GRAY = "8-bit-gray.jpg";
86    public static final String TEST_IMAGE_BROKEN_BY_CUTTING_NAME = "02-broken-image-by-cutting.png";
87    public static final String TEST_IMAGE_BROKEN_BY_FILLING_NAME = "03-broken-image-by-filling.png";
88    public static final String TEST_IMAGE_BROKEN_EMPTY_NAME = "04-broken-image-empty.png";
89    public static final String TEST_IMAGE_DISGUISED_JPG = "02-jpg.png";
90    public static final String TEST_IMAGE_DISGUISED_PNG = "01-png.jpg";
91    public static final String TEST_IMAGE_FILE_NAME = "01.png";
92    public static final String TEST_IMAGE_GIF87A = "01.87a.gif";
93    public static final String TEST_IMAGE_GIF89A = "01.89a.gif";
94    public static final String TEST_IMAGE_IBM_TIFF = "01.ibm.tiff";
95    public static final String TEST_IMAGE_JP2_NAME = "01.jp2";
96    public static final String TEST_IMAGE_JPG_NAME = "27.jpg";
97    public static final String TEST_IMAGE_LANDSCAPE_NAME = "04+05.png";
98    public static final String TEST_IMAGE_MAC_TIFF = "01.mac.tiff";
99    public static final String TEST_IMAGE_PNG_NAME = "01.png";
100    public static final String TEST_IMAGE_PORTRAIT_NAME = "01.png";
101    public static final String TEST_MORONIC_NUMBERING_CBZ = "test_moronic_numbering.cbz";
102    public static final String TEST_TEXT_NAME = "test.txt";
103    public static final String THREE_PAGE_COMIC = "3pages.cbz";
104    public static final String TWO_PAGE_COMIC = "2pages.cbz";
105 
106    private static final int BUFFER_SIZE = 4096;
107 
108    private static TestTools instance;
109 
110    private int delay;
111    private FileTools fileTools;
112    private File jomicHome;
113    private Log logger;
114    private StringTools stringTools;
115    private File testExpectedDir;
116    private File testGeneratedInputDir;
117    private File testInputDir;
118    private File testOutputDir;
119    private File testsBaseDir;
120    private File testsDataDir;
121 
122    private TestTools() {
123        logger = LogFactory.getLog(TestTools.class);
124        try {
125            setupLogging();
126        } catch (IOException error) {
127            // No point in continuing any testing with a broken logging.
128            IllegalStateException bug = new IllegalStateException("cannot setup logging");
129 
130            bug.initCause(error);
131            throw bug;
132        }
133        setupAbbotLogging();
134 
135        Settings settings = Settings.instance();
136        String propertyJomicHome = PropertyConstants.TEST_JOMIC_HOME;
137 
138        settings.setDefault(PropertyConstants.TEST_JOMIC_HOME, System.getProperty("user.dir"));
139 
140        String home = settings.getProperty(propertyJomicHome);
141 
142        if (home == null) {
143            String propertyJomicHomeFull = PropertyConstants.SYSTEM_PROPERTY_PREFIX + propertyJomicHome;
144 
145            throw new IllegalStateException(
146                    "property " + propertyJomicHomeFull + " must be set"
147                    + " (for example: -D" + propertyJomicHomeFull + "=/Users/me/Programs/jomic)");
148        }
149        jomicHome = new File(home);
150        testsBaseDir = new File(jomicHome, "tests");
151        if (!testsBaseDir.exists()) {
152            String propertyJomicHomeFull = PropertyConstants.SYSTEM_PROPERTY_PREFIX + propertyJomicHome;
153 
154            throw new IllegalStateException(
155                    "folder specified by " + propertyJomicHomeFull + " must exist: " + testsBaseDir);
156        }
157        testGeneratedInputDir = new File(testsBaseDir, "generated");
158        testsDataDir = testGeneratedInputDir;
159        testInputDir = new File(testsBaseDir, "input");
160        testExpectedDir = new File(testsBaseDir, "expected");
161        testOutputDir = new File(testsBaseDir, "output");
162        delay = settings.getIntProperty(PropertyConstants.TEST_DELAY);
163        if (logger.isInfoEnabled()) {
164            logger.info("jomic.home = \"" + jomicHome + "\"");
165            logger.info("jomic.delay = " + delay + " ms");
166            logger.info("testdata = \"" + testsDataDir + "\"");
167        }
168        stringTools = StringTools.instance();
169        fileTools = FileTools.instance();
170        try {
171            fileTools.mkdirs(testGeneratedInputDir);
172            fileTools.mkdirs(testOutputDir);
173        } catch (FileNotFoundException errorToTunnel) {
174            IllegalStateException error = new IllegalStateException("cannot create test data directory");
175 
176            error.initCause(errorToTunnel);
177            throw error;
178        }
179 
180        try {
181            settings.read(settings.getSettingsFile());
182        } catch (IOException errorToTunnel) {
183            IllegalStateException error = new IllegalStateException("cannot read test settings");
184 
185            error.initCause(errorToTunnel);
186            throw error;
187        }
188    }
189 
190    /**
191     *  Setup cache for tests that need it.
192     */
193    public void setupCache()
194        throws IOException {
195        ComicCache cache = ComicCache.instance();
196        Settings settings = Settings.instance();
197        File cacheDir = settings.getCacheDir();
198 
199        cacheDir = new File(cacheDir, "tests");
200        cache.setUp(cacheDir);
201        settings.setFileProperty(PropertyConstants.CACHE_DIR, cacheDir);
202    }
203 
204    /**
205     *  Create a settings file containing <code>keyValuePairs</code> and use it as default settings
206     *  file for <code>Jomic.main()</code>.
207     *
208     * @see                    PropertyConstants#SETTINGS_DIR
209     * @see                    StartupTools#getSettingsFile(String)
210     * @see                    Jomic#main(String[])
211     * @param  testCaseClass   class of TestCase the settings are for
212     * @param  testMethodName  null or name of test method the settings are for
213     * @param  keyValuePairs   array of strings of pattern <code>[key1, value1, key2, value2, ...]</code>
214     *      specifying property keys and values the settings file should contain
215     */
216    public void setupTestSettings(Class testCaseClass, String testMethodName, String[] keyValuePairs)
217        throws IOException {
218        assert testCaseClass != null;
219        assert keyValuePairs != null;
220        assert keyValuePairs.length % 2 == 0;
221 
222        File settingsBaseDir = getTestGeneratedInputDir();
223        StartupTools startupTools = StartupTools.instance();
224        String settingsDirName = testCaseClass.getName();
225 
226        if (testMethodName != null) {
227            settingsDirName += "." + testMethodName;
228        }
229 
230        File settingsDir = new File(settingsBaseDir, settingsDirName);
231 
232        System.setProperty(PropertyConstants.SYSTEM_PROPERTY_PREFIX + PropertyConstants.SETTINGS_DIR,
233                settingsDir.getAbsolutePath());
234 
235        Properties defaultSettings = new Properties();
236 
237        for (int i = 0; i < keyValuePairs.length; i += 2) {
238            String key = keyValuePairs[i];
239            String value = keyValuePairs[i + 1];
240 
241            defaultSettings.setProperty(key, value);
242        }
243 
244        Settings settings = new Settings(defaultSettings);
245 
246        fileTools.mkdirs(settingsDir);
247 
248        File settingsFile = startupTools.getSettingsFile("jomic");
249 
250        logger.info("using settings in \"" + settingsFile + "\"");
251        settings.write(settingsFile);
252    }
253 
254    private void setupAbbotLogging() {
255        abbot.Log.init(new String[]{"--debug", "all"});
256    }
257 
258    /**
259     *  Attempt to read logger settings from file. If the file does not exist, use internal
260     *  defaults.
261     */
262    private void setupLogging()
263        throws IOException {
264        File loggerSettingsFile = new File("settings", "jomic-tests-logging.properties");
265        Properties loggerProperties = new Properties();
266        InputStream loggerSettingsStream = null;
267        boolean readFromFile = false;
268        org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger();
269 
270        root.setLevel(Level.INFO);
271        try {
272            loggerSettingsStream = new FileInputStream(loggerSettingsFile);
273            loggerProperties.load(loggerSettingsStream);
274            readFromFile = true;
275        } catch (FileNotFoundException error) {
276            loggerProperties.clear();
277            loggerProperties.setProperty("log4j.rootLogger", "INFO, stdout");
278            loggerProperties.setProperty("log4j.appender.stdout", "org.apache.log4j.ConsoleAppender");
279        } finally {
280            if (loggerSettingsStream != null) {
281                loggerSettingsStream.close();
282            }
283        }
284        JomicConfigurator.configure();
285        JomicConfigurator.setLevel(loggerProperties);
286        if (!readFromFile) {
287            if (logger.isInfoEnabled()) {
288                logger.info("cannot find logger settings \"" + loggerSettingsFile
289                        + "\"; using internal defaults");
290            }
291        }
292    }
293 
294    /**
295     *  Get a folder where test output can be written to. The folder is created and all possibly
296     *  existing files in it are removed.
297     */
298    public File getCleanTestOutputFolder(String name) {
299        File result = getTestOutputFile(name);
300 
301        fileTools.attemptToDeleteAll(result, logger);
302        try {
303            fileTools.mkdirs(result);
304        } catch (FileNotFoundException error) {
305            throw new TunneledIOException("cannot create clean test output folder: " + result, error);
306        }
307        return result;
308    }
309 
310    /**
311     *  Gets the directory where the local copy of the Jomic CVS is located. In order for this to
312     *  work, you have to set the Java property jomic.home to point to this directory.
313     */
314    public File getJomicHome() {
315        assert jomicHome != null;
316        return jomicHome;
317    }
318 
319    /**
320     *  Gets a file containing a genric test comic archive with a few images.
321     */
322    public File getTestComicFile() {
323        return getTestGeneratedInputFile(TEST_COMIC_FILE_NAME);
324    }
325 
326    /**
327     *  Get a test input file from the testdata directory.
328     */
329    public File getTestExpectedFile(String name) {
330        return new File(testExpectedDir, name);
331    }
332 
333    /**
334     *  Get existing test input file from either "tests/generated" or "tests/input".
335     *
336     * @see    #getTestGeneratedInputFile(String)
337     * @see    #getTestInputFile(String)
338     */
339    public File getTestFile(String name) {
340        File result;
341        File generatedFile = getTestGeneratedInputFile(name);
342        boolean generatedExists = generatedFile.exists();
343        File inputFile = getTestInputFile(name);
344        boolean inputExists = inputFile.exists();
345 
346        if (generatedExists) {
347            if (inputExists) {
348                throw new IllegalStateException("test file must not exist both in \"generated\" and \"input\": "
349                        + name);
350            }
351            result = generatedFile;
352        } else {
353            if (!inputExists) {
354                throw new IllegalStateException("test file must exist either in \"generated\" or \"input\": " + name);
355            }
356            result = inputFile;
357        }
358 
359        return result;
360    }
361 
362    /**
363     *  Get a plain file name for a test file. This does not yield a complete path, use <code>getTest*File()</code>
364     *  to access or store an actual test file.
365     *
366     * @see                    #getTestExpectedFile(String)
367     * @see                    #getTestInputFile(String)
368     * @see                    #getTestOutputFile(String)
369     * @param  testCaseClass   the class where the test case resides that uses the file
370     * @param  testMethodName  null or the name of the method that uses the file
371     * @param  name            null or a short name further describing the file (if the test method
372     *      needs more than one file)
373     * @param  suffix          null or file suffix without dot, for example "png"
374     */
375    public String getTestFileName(Class testCaseClass, String testMethodName, String name, String suffix) {
376        assert testCaseClass != null;
377        String result = testCaseClass.getName();
378 
379        if (testMethodName != null) {
380            result += "." + testMethodName;
381        }
382        if (name != null) {
383            result += "-" + name;
384        }
385        if (suffix != null) {
386            result += "." + suffix;
387        }
388        return result;
389    }
390 
391    /**
392     *  Get files from generated and static test data directory matching a certain pattern.
393     *
394     * @param  pattern  a regular expression describing the names of the files to get
395     */
396    public File[] getTestFiles(String pattern) {
397        List result = new LinkedList();
398        File[] testDirs = new File[]{testGeneratedInputDir, testInputDir};
399        FileFilter filter = new RegExFileFilter(pattern);
400 
401        for (int i = 0; i < testDirs.length; i += 1) {
402            File testDir = testDirs[i];
403            File[] filesToAppend = testDir.listFiles(filter);
404 
405            if (filesToAppend != null) {
406                result.addAll(Arrays.asList(filesToAppend));
407            }
408        }
409 
410        if (result.size() == 0) {
411            String message = "test directories must contain at least one file matching \""
412                    + pattern + "\": " + StringTools.instance().arrayToString(testDirs);
413 
414            throw new IllegalStateException(message);
415        }
416        return (File[]) result.toArray(new File[0]);
417    }
418 
419    /**
420     *  Get the directory where all the test data are stored.
421     */
422    public File getTestGeneratedInputDir() {
423        return testsDataDir;
424    }
425 
426    /**
427     *  Get a generated test input file from the test data directory.
428     */
429    public File getTestGeneratedInputFile(String name) {
430        return new File(testGeneratedInputDir, name);
431    }
432 
433    /**
434     *  Get the test image with the specified <code>fileName</code>.
435     */
436    public RenderedImage getTestImage(String fileName)
437        throws IOException {
438        assert fileName != null;
439        RenderedImage result;
440        File imageFile = getTestFile(fileName);
441        ImageInputStream in = createImageInputStream(imageFile);
442        ImageReader reader = (ImageReader) ImageIO.getImageReaders(in).next();
443 
444        reader.setInput(in);
445        try {
446            result = PlanarImage.wrapRenderedImage(reader.read(0));
447        } catch (Exception error) {
448            logger.warn("cannot read image using ImageIO; reverting to JAI", error);
449            result = JAI.create("fileload", imageFile.getAbsolutePath());
450            if (result == null) {
451                throw new IOException("cannot read image file: " + imageFile);
452            }
453        }
454        return result;
455    }
456 
457    /**
458     *  Get a genric test image representing some dummy comic page.
459     */
460    public RenderedImage getTestImage()
461        throws IOException {
462        return getTestImage(TEST_IMAGE_FILE_NAME);
463    }
464 
465    /**
466     *  Get a file containing a genric test image representing some dummy comic page.
467     */
468    public File getTestImageFile() {
469        return getTestFile(TEST_IMAGE_FILE_NAME);
470    }
471 
472    /**
473     *  Get a test input file from the test data directory.
474     */
475    public File getTestInputFile(String name) {
476        return new File(testInputDir, name);
477    }
478 
479    /**
480     *  Get a test input file from the test data directory.
481     */
482    public File getTestOutputFile(String name) {
483        return new File(testOutputDir, name);
484    }
485 
486    /**
487     *  Get a file containing a genric test text.
488     */
489    public File getTestTextFile() {
490        return getTestFile(TEST_TEXT_NAME);
491    }
492 
493    /**
494     *  Get the base directory where the test source code and stuff is located, typically
495     *  "${build.dir}/tests".
496     */
497    public File getTestsBaseDir() {
498        return testsBaseDir;
499    }
500 
501    /**
502     *  Get <code>data[i]</code> or null in case <code>data</code> is null or smaller than <code>index</code>
503     *  .
504     */
505    private String getAtOrNull(String[] data, int index) {
506        assert index >= 0;
507        String result = null;
508 
509        if ((data != null) && (index < data.length)) {
510            result = data[index];
511        }
512        return result;
513    }
514 
515    /**
516     *  Get accessor.
517     */
518    public static synchronized TestTools instance() {
519        if (instance == null) {
520            instance = new TestTools();
521        }
522        return instance;
523    }
524 
525    public void assertEquals(byte[] expected, byte[] actual) {
526        assert expected != null;
527        assert actual != null;
528        int expectedCount = expected.length;
529        int actualCount = actual.length;
530 
531        for (int i = 0; i < Math.min(expectedCount, actualCount); i += 1) {
532            Assert.assertEquals("data at index " + i + " must be equal", expected[i], actual[i]);
533        }
534        Assert.assertEquals("array length must be equal", expectedCount, actualCount);
535    }
536 
537    public void assertEquals(int[] expected, int[] actual) {
538        assert expected != null;
539        assert actual != null;
540        int expectedCount = expected.length;
541        int actualCount = actual.length;
542 
543        for (int i = 0; i < Math.min(expectedCount, actualCount); i += 1) {
544            Assert.assertEquals("data at index " + i + " must be equal", expected[i], actual[i]);
545        }
546        Assert.assertEquals("array length must be equal", expectedCount, actualCount);
547    }
548 
549    public void assertEquals(String[] expected, String[] actual) {
550        assert expected != null;
551        assert actual != null;
552        int expectedCount = expected.length;
553        int actualCount = actual.length;
554 
555        for (int i = 0; i < Math.min(expectedCount, actualCount); i += 1) {
556            Assert.assertEquals("data at index " + i + " must be equal", expected[i], actual[i]);
557        }
558        Assert.assertEquals("array length must be equal", expectedCount, actualCount);
559    }
560 
561    public void assertEquals(Dimension expected, Dimension actual) {
562        if ((expected == null) || (actual == null)) {
563            Assert.assertEquals(expected, actual);
564        }
565        Assert.assertEquals(expected.width, actual.width);
566        Assert.assertEquals(expected.height, actual.height);
567    }
568 
569    public void assertFilesEqual(String baseName) {
570        File expectedFile = getTestExpectedFile(baseName);
571        File outputFile = getTestOutputFile(baseName);
572 
573        if (expectedFile.exists()) {
574            assertFilesEqual(expectedFile, outputFile);
575        } else {
576            logger.warn("cannot find expected file, creating it: " + expectedFile);
577            try {
578                fileTools.copyFile(outputFile, expectedFile);
579            } catch (IOException error) {
580                String errorMessage = "cannot create expected file";
581                IllegalStateException tunneledError = new IllegalStateException(errorMessage);
582 
583                tunneledError.initCause(error);
584                throw tunneledError;
585            }
586        }
587    }
588 
589    public void assertFilesEqual(File expectedFile, File actualFile) {
590        // TODO: Improve performance by changing read() to read(buffer).
591        // TODO: Improve error message by including hex dump of a few characters surrounding the mismatch.
592        try {
593            InputStream expectedStream = new FileInputStream(expectedFile);
594 
595            try {
596                InputStream actualStream = new FileInputStream(actualFile);
597                long filePosition = 0;
598 
599                try {
600                    int expectedChar = 0;
601                    int actualChar = 0;
602 
603                    while ((expectedChar == actualChar) && (expectedChar >= 0) && (actualChar >= 0)) {
604                        expectedChar = expectedStream.read();
605                        actualChar = actualStream.read();
606                        if (expectedChar != actualChar) {
607                            // We are doing this inside an "if" so the message only has to be
608                            // computed in case anythings wrong. This improves performance.
609                            String message = "character at postion " + filePosition + " in " + expectedFile
610                                    + " must match " + actualFile;
611 
612                            Assert.assertEquals(message, expectedChar, actualChar);
613                        } else {
614                            filePosition += 1;
615                        }
616                    }
617                } finally {
618                    actualStream.close();
619                }
620            } finally {
621                expectedStream.close();
622            }
623        } catch (IOException error) {
624            String errorMessage = "cannot compare files " + expectedFile + " and " + actualFile;
625 
626            throw new TunneledIOException(errorMessage, error);
627        }
628    }
629 
630    public void assertGreaterOrEqual(double actual, double limit) {
631        Assert.assertTrue("" + actual + " >= " + limit, actual >= limit);
632    }
633 
634    public void assertGreaterOrEqual(long actual, long limit) {
635        Assert.assertTrue("" + actual + " >= " + limit, actual >= limit);
636    }
637 
638    public void assertGreaterThan(double actual, double limit) {
639        Assert.assertTrue("" + actual + " > " + limit, actual > limit);
640    }
641 
642    public void assertGreaterThan(long actual, long limit) {
643        Assert.assertTrue("" + actual + " > " + limit, actual > limit);
644    }
645 
646    public void assertLessOrEqual(double actual, double limit) {
647        Assert.assertTrue("" + actual + " <= " + limit, actual <= limit);
648    }
649 
650    public void assertLessOrEqual(long actual, long limit) {
651        Assert.assertTrue("" + actual + " <= " + limit, actual <= limit);
652    }
653 
654    public void assertLessThan(double actual, double limit) {
655        Assert.assertTrue("" + actual + " < " + limit, actual < limit);
656    }
657 
658    public void assertLessThan(long actual, long limit) {
659        Assert.assertTrue("" + actual + " < " + limit, actual < limit);
660    }
661 
662    public void copyTestFile(String sourceName, String targetName)
663        throws IOException {
664        assert sourceName != null;
665        assert targetName != null;
666        File source = getTestFile(sourceName);
667        File target = getTestGeneratedInputFile(targetName);
668 
669        fileTools.copyFile(source, target);
670    }
671 
672    /**
673     *  Same as ImageIO.createImageInputStream, but throws a <code>FileNotFoundException</code> if
674     *  <code>imageFile</code> cannot be found (instead of returning <code>null</code>). Why the
675     *  original does not work that way already is beyond me.
676     */
677    public ImageInputStream createImageInputStream(File imageFile)
678        throws IOException {
679        assert imageFile != null;
680        ImageInputStream result = ImageIO.createImageInputStream(imageFile);
681 
682        if (result == null) {
683            throw new FileNotFoundException("cannot find image file: " + imageFile);
684        }
685        return result;
686    }
687 
688    /**
689     *  Create a temporary test directory.
690     */
691    public File createTempDir(Class clazz, String prefix)
692        throws IOException {
693        File result = fileTools.createTempDir(clazz.getName() + "-" + prefix);
694 
695        // TODO: automatically remove directory when done.
696        return result;
697    }
698 
699    /**
700     *  Create a temporary file that will be deleted on exit unless the system property
701     *  net.sf.jomic.test.keepTempFiles has been set to <code>true</code>.
702     *
703     * @see    File#createTempFile(java.lang.String, java.lang.String)
704     * @see    File#deleteOnExit()
705     */
706    public File createTempFile(String prefix, String suffix)
707        throws IOException {
708        File result = File.createTempFile(prefix, suffix);
709 
710        if (Boolean.getBoolean(PropertyConstants.TEST_KEEP_TEMP_FILES)) {
711            logger.warn("keeping temp file: " + StringTools.instance().sourced(result.getAbsolutePath()));
712        } else {
713            result.deleteOnExit();
714        }
715        return result;
716    }
717 
718    /**
719     *  Create a temporary file that will be deleted on exit unless the system property
720     *  net.sf.jomic.test.keepTempFiles has been set to <code>true</code>. The name of the file is
721     *  prefixed by the name of <code>claszz</code> and <code>prefix</code>, separated by a hyphen
722     *  (-) provided <code>prefix</code> is not <code>null</code>.
723     *
724     * @see    File#createTempFile(java.lang.String, java.lang.String)
725     * @see    File#deleteOnExit()
726     */
727    public File createTempFile(Class clazz, String prefix, String suffix)
728        throws IOException {
729        assert clazz != null;
730        String actualPrefix = clazz.getName();
731 
732        if (prefix != null) {
733            actualPrefix += "-" + prefix;
734        }
735        return createTempFile(actualPrefix, suffix);
736    }
737 
738    /**
739     *  Create a temporary ZIP archive with a file name derived from <code>caller</code> and <code>baseName</code>
740     *  . For <code>inNames</code> and <code>outNames</code> the same things apply as with <code>createTestZipArchive()</code>
741     *  .
742     *
743     * @see    #createTempFile(Class, String, String)
744     * @see    #createTestZipArchive(File, String[], String[])
745     */
746    public File createTempZipArchive(Class caller, String baseName, String[] inNames,
747            String[] outNames)
748        throws IOException {
749        File result = createTempFile(caller, baseName, ".cbz");
750 
751        createTestZipArchive(result, inNames, outNames);
752        return result;
753    }
754 
755    /**
756     *  Create and show a test frame.
757     *
758     * @param  component  the JComponent to show inside the frame
759     * @param  test       the class of the TestCase opening the frame; to be shown as title
760     * @param  method     the method in the TestCase opening the frame, or <code>null</code> if none
761     *      needed to be shown in the title
762     */
763    public JFrame createTestFrame(JComponent component, Class test, String method) {
764        assert test != null;
765        assert component != null;
766        String title = test.getClass().getName();
767 
768        if (method != null) {
769            title += "." + method;
770        }
771        JFrame result = new JFrame(title);
772        boolean ok = false;
773 
774        try {
775            result.getContentPane().add(component);
776            result.setSize(FRAME_WIDTH, FRAME_HEIGHT);
777            result.pack();
778            result.setVisible(true);
779            ok = true;
780        } finally {
781            if (!ok) {
782                result.dispose();
783            }
784        }
785        return result;
786    }
787 
788    /**
789     *  Create a test ZIP archive in <code>zipFile</code>. One of <code>inNames</code> or <code>outNames</code>
790     *  can be null or shorter than the other, in which case the missing entries will be filled with
791     *  names derived from the other names.
792     *
793     * @see              #getTestFile(String)
794     * @param  inNames   relative input file names that will be expaned to full paths using <code>getTestFile()</code>
795     * @param  outNames  file names to be used in archive (with path); missing names will be filled
796     *      with the plain file name (without path) derived from the corresponding entry in <code>inNames</code>
797     *      .
798     */
799    public void createTestZipArchive(File zipFile, String[] inNames,
800            String[] outNames)
801        throws IOException {
802        assert !((inNames == null) && (outNames == null));
803        int inLength = lengthOr0(inNames);
804        int outLength = lengthOr0(outNames);
805 
806        int nameCount = Math.max(inLength, outLength);
807        String[] filledInNames = new String[nameCount];
808        String[] filledOutNames = new String[nameCount];
809 
810        for (int i = 0; i < nameCount; i += 1) {
811            String inName = getAtOrNull(inNames, i);
812            String outName = getAtOrNull(outNames, i);
813 
814            if (inName == null) {
815                assert outName != null;
816                inName = outName;
817            } else if (outName == null) {
818                outName = inName;
819            } else {
820                assert inName != null;
821                assert outName != null;
822            }
823            filledInNames[i] = getTestFile(inName).getAbsolutePath();
824            filledOutNames[i] = outName;
825        }
826 
827        createZipArchive(zipFile, filledInNames, filledOutNames);
828    }
829 
830    public void createZipArchive(File zipFile, String[] inNames,
831            String[] outNames)
832        throws IOException {
833        assert zipFile != null;
834        assert inNames != null;
835        assert inNames.length > 0;
836        assert outNames != null;
837        assert inNames.length == outNames.length :
838                "outNames.length must " + inNames.length + " but is " + outNames.length;
839        boolean done = false;
840 
841        if (logger.isInfoEnabled()) {
842            logger.info("create zip archive: " + zipFile);
843        }
844        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));
845 
846        try {
847            for (int i = 0; i < inNames.length; i += 1) {
848                String inName = inNames[i];
849                String outName = null;
850 
851                assert inName != null;
852                if ((outNames != null) && (outNames.length > i)) {
853                    outName = outNames[i];
854                }
855                if (outName == null) {
856                    outName = inName;
857                }
858                addZipEntry(out, inName, outName);
859                done = true;
860            }
861        } finally {
862            out.close();
863            if (!done) {
864                // Delete incomplete archive.
865                fileTools.deleteOrWarn(zipFile, logger);
866            }
867        }
868    }
869 
870    /**
871     *  Waits some time. The exact time can be specified in the property PROPERTY_JOMIC_DELAY. If
872     *  this is missing, use an internal default value.
873     */
874    public void waitSomeTime() {
875        try {
876            Thread.sleep(delay);
877        } catch (InterruptedException interruption) {
878            logger.warn("interrupted", interruption);
879        }
880    }
881 
882    public void writeImageFile(File targetImageFile, RenderedImage imageToWrite)
883        throws IOException {
884        Iterator writers = ImageIO.getImageWritersByFormatName(fileTools.getSuffix(targetImageFile));
885        ImageWriter writer = (ImageWriter) writers.next();
886 
887        if (logger.isInfoEnabled()) {
888            logger.info("write test image " + stringTools.sourced(targetImageFile.getAbsolutePath()));
889        }
890        ImageOutputStream ios = ImageIO.createImageOutputStream(targetImageFile);
891 
892        try {
893            writer.setOutput(ios);
894            writer.write(imageToWrite);
895        } finally {
896            ios.close();
897        }
898    }
899 
900    private void addZipEntry(ZipOutputStream out, String inName, String outName)
901        throws IOException {
902        byte[] buffer = new byte[BUFFER_SIZE];
903        ZipEntry zipEntry = new ZipEntry(outName);
904 
905        if (logger.isDebugEnabled()) {
906            logger.debug("add " + stringTools.sourced(inName) + " + "
907                    + stringTools.sourced(outName));
908        }
909 
910        out.putNextEntry(zipEntry);
911 
912        FileInputStream in = new FileInputStream(inName);
913        boolean continueReading = true;
914 
915        try {
916            while (continueReading) {
917                int bytesRead = in.read(buffer);
918 
919                continueReading = (bytesRead > 0);
920                if (continueReading) {
921                    out.write(buffer, 0, bytesRead);
922                }
923            }
924        } finally {
925            in.close();
926        }
927        out.closeEntry();
928    }
929 
930    private int lengthOr0(Object[] some) {
931        int result;
932 
933        if (some == null) {
934            result = 0;
935        } else {
936            result = some.length;
937        }
938        return result;
939    }
940}

[all classes][net.sf.jomic.tools]
EMMA 2.0.4217 (C) Vladimir Roubtsov