/* * Copyright 2013-16 Skynav, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.skynav.ttpe.app; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; //import org.junit.Ignore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.xmlunit.builder.DiffBuilder; import org.xmlunit.builder.Input; import org.xmlunit.diff.Diff; import com.skynav.ttv.app.TimedTextVerifier; import com.skynav.ttv.util.IOUtil; import com.skynav.ttv.util.TextReporter; public class PresenterTestDriver { protected void performPresentationTest(String resourceName, int expectedErrors, int expectedWarnings) { performPresentationTest(resourceName, expectedErrors, expectedWarnings, null); } protected void performPresentationTest(String resourceName, int expectedErrors, int expectedWarnings, String[] additionalOptions) { URL url = getClass().getResource(resourceName); if (url == null) fail("Can't find test resource: " + resourceName + "."); String urlString = url.toString(); URI input; try { input = new URI(urlString); } catch (URISyntaxException e) { fail("Bad test resource syntax: " + urlString + "."); return; } List<String> args = new java.util.ArrayList<String>(); args.add("-v"); args.add("--warn-on"); args.add("all"); if (expectedErrors >= 0) { args.add("--expect-errors"); args.add(Integer.toString(expectedErrors)); } if (expectedWarnings >= 0) { args.add("--expect-warnings"); args.add(Integer.toString(expectedWarnings)); } maybeAddConfiguration(args, input); maybeAddFontDirectory(args, input); maybeAddOutputDirectory(args); if (additionalOptions != null) { args.addAll(java.util.Arrays.asList(additionalOptions)); } args.add(urlString); Presenter ttpe = new Presenter(); URI output = ttpe.present(args, new TextReporter()); assertNotNull("Presentation failed, no output produced.", output); maybeCheckDifferences(output, input); TimedTextVerifier.Results r = ttpe.getResults(urlString); int resultCode = r.getCode(); int resultFlags = r.getFlags(); if (resultCode == TimedTextVerifier.RV_PASS) { if ((resultFlags & TimedTextVerifier.RV_FLAG_ERROR_EXPECTED_MATCH) != 0) { fail("Unexpected success with expected error(s) match."); } if ((resultFlags & TimedTextVerifier.RV_FLAG_WARNING_UNEXPECTED) != 0) { fail("Unexpected success with unexpected warning(s)."); } if ((resultFlags & TimedTextVerifier.RV_FLAG_WARNING_EXPECTED_MISMATCH) != 0) { fail("Unexpected success with expected warning(s) mismatch."); } } else if (resultCode == TimedTextVerifier.RV_FAIL) { if ((resultFlags & TimedTextVerifier.RV_FLAG_ERROR_UNEXPECTED) != 0) { fail("Unexpected failure with unexpected error(s)."); } if ((resultFlags & TimedTextVerifier.RV_FLAG_ERROR_EXPECTED_MISMATCH) != 0) { fail("Unexpected failure with expected error(s) mismatch."); } } else fail("Unexpected result code " + resultCode + "."); } private void maybeAddConfiguration(List<String> args, URI input) { String[] components = getComponents(input); if (hasFileScheme(components)) { File f = getConfiguration(components); if (f != null) { try { String p = f.getCanonicalPath(); args.add("--config"); args.add(p); } catch (IOException e) { } } } } private File getConfiguration(String[] components) { File f1 = new File(joinComponents(components, ".config.xml")); if (f1.exists()) return f1; File f2 = new File(joinComponents(components, "test", ".config.xml")); if (f2.exists()) return f2; return null; } private void maybeAddFontDirectory(List<String> args, URI input) { String[] components = getComponents(input); if (hasFileScheme(components)) { File f = getFontDirectory(components); if (f != null) { try { String p = f.getCanonicalPath(); args.add("--font-directory"); args.add(p); } catch (IOException e) { } } } } private File getFontDirectory(String[] components) { File f1 = new File(components[1], "fonts"); if (f1.exists()) return f1; File f2 = new File(".", "src/test/fonts"); if (f2.exists()) return f2; File f3 = new File(".", "fonts"); if (f3.exists()) return f3; return null; } private void maybeAddOutputDirectory(List<String> args) { String reportsDirectory = System.getProperty("surefire.reportsDirectory"); if (reportsDirectory != null) { maybeCreateOutputDirectory(reportsDirectory); File f = new File(reportsDirectory); if (f.exists()) { args.add("--output-directory"); args.add(reportsDirectory); } } } private boolean maybeCreateOutputDirectory(String outputDirectory) { File f = new File(outputDirectory); if (!f.exists()) return f.mkdir(); else return false; } private void maybeCheckDifferences(URI output, URI input) { String[] components = getComponents(input); if (hasFileScheme(components)) { File control = new File(joinComponents(components, ".expected.zip")); if (control.exists()) { checkDifferences(control.toURI(), output); } } } private void checkDifferences(URI uri1, URI uri2) { assert hasFileScheme(uri1); assert hasFileScheme(uri2); BufferedInputStream bis1 = null; BufferedInputStream bis2 = null; ZipInputStream zis1 = null; ZipInputStream zis2 = null; try { File[] retFile1 = new File[1]; File[] retFile2 = new File[2]; if ((bis1 = getArchiveInputStream(uri1, retFile1)) != null) { if ((bis2 = getArchiveInputStream(uri2, retFile2)) != null) { File f1 = retFile1[0]; File f2 = retFile2[0]; zis1 = new ZipInputStream(bis1); zis2 = new ZipInputStream(bis2); checkDifferences(f1, zis1, f2, zis2); } } } catch (IOException e) { fail(e.getMessage()); } finally { IOUtil.closeSafely(zis2); IOUtil.closeSafely(bis2); IOUtil.closeSafely(zis1); IOUtil.closeSafely(bis1); } } private void checkDifferences(File f1, ZipInputStream zi1, File f2, ZipInputStream zi2) throws IOException { boolean done = false; while (!done) { ZipEntry e1 = zi1.getNextEntry(); ZipEntry e2 = zi2.getNextEntry(); if (e1 != null) { if (e2 != null) { checkDifferences(f1, zi1, e1, f2, zi2, e2); } else { fail("Archive entry count mismatch, extra entry in " + f1 + "."); done = true; } } else if (e2 != null) { fail("Archive entry count mismatch, extra entry in " + f2 + "."); done = true; } else { done = true; } } } private void checkDifferences(File f1, InputStream is1, ZipEntry e1, File f2, InputStream is2, ZipEntry e2) { String n1 = e1.getName(); String n2 = e2.getName(); assertEquals("Archive entry name mismatch.", n1, n2); InputStream eis1 = null; InputStream eis2 = null; try { eis1 = readEntryData(e1, is1); eis2 = readEntryData(e2, is2); checkDifferences(n1, eis1, n2, eis2); } catch (IOException e) { String m = ""; if (eis1 == null) m = "Can't read data for entry '" + n1 + "': "; else if (eis2 == null) m = "Can't read data for entry '" + n1 + "': "; fail(m + e.getMessage() + "."); } finally { IOUtil.closeSafely(eis1); IOUtil.closeSafely(eis2); } } private InputStream readEntryData(ZipEntry e, InputStream is) throws IOException { ByteArrayOutputStream bos = null; try { long limit = e.getSize(); assertTrue("Entry data size exceeds 4GB limit.", limit <= (long) Integer.MAX_VALUE); long consumed = 0; byte[] buffer = new byte[4096]; bos = new ByteArrayOutputStream(limit > 0 ? (int) limit : buffer.length); for (int nb; (nb = is.read(buffer)) >= 0;) { if (nb > 0) { bos.write(buffer, 0, nb); consumed += nb; } else Thread.yield(); } assertTrue("Unable to read all entry data, got " + consumed + " bytes, expected " + limit + " bytes.", (limit < 0) || (consumed == limit)); return new ByteArrayInputStream(bos.toByteArray()); } finally { IOUtil.closeSafely(bos); } } private void checkDifferences(String n1, InputStream is1, String n2, InputStream is2) { assert n1.equals(n2); if (n1.endsWith(".xml")) checkDifferencesXML(n1, is1, n2, is2); else if (n1.endsWith(".svg")) checkDifferencesXML(n1, is1, n2, is2); else if (n2.endsWith(".png")) checkDifferencesPNG(n1, is1, n2, is2); else checkDifferencesOther(n1, is1, n2, is2); } private void checkDifferencesXML(String n1, InputStream is1, String n2, InputStream is2) { Diff diff = DiffBuilder .compare(Input.fromStream(is1).build()) .withTest(Input.fromStream(is2).build()) .ignoreWhitespace() .build(); assertFalse(diff.toString(), diff.hasDifferences()); } private void checkDifferencesPNG(String n1, InputStream is1, String n2, InputStream is2) { } private void checkDifferencesOther(String n1, InputStream is1, String n2, InputStream is2) { } private BufferedInputStream getArchiveInputStream(URI uri, File[] retFile) throws IOException { File f = new File(uri.getPath()); if (retFile != null) retFile[0] = f; return new BufferedInputStream(new FileInputStream(f)); } private String[] getComponents(URI uri) { assert uri != null; String s = uri.getScheme(); String p = uri.getPath(); String n, x; int i = p.lastIndexOf('/'); if (i >= 0) { n = p.substring(i + 1); p = p.substring(0, i + 1); } else n = null; int j = n.lastIndexOf('.'); if (j >= 0) { x = n.substring(j); n = n.substring(0, j); } else { x = null; } return new String[] { s, p, n, x }; } private boolean hasFileScheme(URI uri) { return hasFileScheme(getComponents(uri)); } private boolean hasFileScheme(String[] components) { return (components != null) && (components[0] != null) && components[0].equals("file"); } private String joinComponents(String[] components, String extension) { assert components != null; return joinComponents(components, components[2], extension); } private String joinComponents(String[] components, String name, String extension) { assert components != null; assert components[1] != null; assert name != null; assert extension != null; StringBuffer sb = new StringBuffer(); sb.append(components[1]); sb.append('/'); sb.append(name); sb.append(extension); return sb.toString(); } }