diff options
author | Andreas Fitzek <andreas.fitzek@iaik.tugraz.at> | 2014-08-29 15:09:54 +0200 |
---|---|---|
committer | Andreas Fitzek <andreas.fitzek@iaik.tugraz.at> | 2014-08-29 15:09:54 +0200 |
commit | ffd1e0da6b73e2737f5cad0a6d3e82dbc3de206f (patch) | |
tree | 73a685d2c6becb3a274522fb31b898c8a9b0903c /pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization | |
parent | 7a983c6687e8045efcc918c273bc43798319423b (diff) | |
download | pdf-as-4-ffd1e0da6b73e2737f5cad0a6d3e82dbc3de206f.tar.gz pdf-as-4-ffd1e0da6b73e2737f5cad0a6d3e82dbc3de206f.tar.bz2 pdf-as-4-ffd1e0da6b73e2737f5cad0a6d3e82dbc3de206f.zip |
Integrated PDF-AS Testing library
Diffstat (limited to 'pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization')
7 files changed, 1146 insertions, 0 deletions
diff --git a/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/SerializiationManager.java b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/SerializiationManager.java new file mode 100644 index 00000000..294084eb --- /dev/null +++ b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/SerializiationManager.java @@ -0,0 +1,254 @@ +package at.gv.egiz.param_tests.serialization; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.AssumptionViolatedException; + +import at.gv.egiz.param_tests.provider.BaseSignatureTestData; +import at.gv.egiz.param_tests.testinfo.TestInfo; +import at.gv.egiz.param_tests.testinfo.TestVerdict; + +/** + * Singleton managing the serialization of test data. Generally there is one + * serializer prototype per test type and one serializer clone per test which + * was run. Each serializer clone holds the data of exactly one test and is able + * to serizalize in some format. Additionally to that there is also a + * TestSummaryWriter-object which writes a summary of all test results. + * + * @author mtappler + * + */ +public class SerializiationManager { + + /** + * the singleton instance + */ + private static SerializiationManager instance = null; + + /** + * The singleton accessor method. It is not used synchronized as it is + * expected that the parameterized unit tests won't be run in parallel. + * + * @return the singleton instance + */ + public static SerializiationManager getInstance() { + // implement locking if needed + if (instance == null) { + instance = new SerializiationManager(); + } + return instance; + } + + /** + * a map of serializer prototypes, the key is the canonical name of the + * TestInfo-subclass which the prototype can serializer and the value is the + * prototype itself + */ + private Map<String, TestInfoSerializer<? extends TestInfo>> serializerPrototypes = new HashMap<String, TestInfoSerializer<? extends TestInfo>>(); + + /** + * a list of serializer clones, with one element for each test + */ + private List<TestInfoSerializer<? extends TestInfo>> serializers = new ArrayList<TestInfoSerializer<? extends TestInfo>>(); + /** + * the test summary, which writes the summary of test results all tests + */ + private TestSummaryWriter testSummaryWriter; + + /** + * protected constructor + */ + protected SerializiationManager() { + } + + /** + * This method registers a serializer for a TestInfo-subclass. + * + * @param serializer + * a serializer prototype + * @param testInfoClass + * the class object corresponding to the TestInfo-subclass + */ + public <T extends TestInfo> void registerSerializer( + TestInfoSerializer<T> serializer, Class<T> testInfoClass) { + serializerPrototypes.put(testInfoClass.getCanonicalName(), serializer); + } + + /** + * This methods creates an instance of a TestInfo-subclass and returns. + * Actually a serializer is clone and the clone creates the instance and + * holds it to serialize it later. + * + * @param testInfoClass + * the class of the requested test information object + * @param baseData + * basic test data which is set for all test information object + * @return a test information object + */ + public <T extends TestInfo> T createTestInfo(Class<T> testInfoClass, + BaseSignatureTestData baseData) { + @SuppressWarnings("unchecked") + // unfortunately this cast is necessary + TestInfoSerializer<T> serializer = (TestInfoSerializer<T>) serializerPrototypes + .get(testInfoClass.getCanonicalName()); + // if there is no test info, the test will fail with a null pointer + // exception + // if it tries to access the test info + if (serializer == null) + return null; + TestInfoSerializer<T> serializerClone = serializer.cloneSerializer(); + serializers.add(serializerClone); + T tInfo = serializerClone.createTestInfo(); + tInfo.setBaseTestData(baseData); + + return tInfo; + } + + /** + * Sets a test given its directory name as succeeded. The directory name is + * used, because it uniquely defines the test, as there is only one test per + * directory and it is encoded in display name of the test. + * + * @param directoryName + * the name of the directory of the test + */ + public void setSucceeded(String directoryName) { + TestInfo testInfo = getTestInfoForDirectory(directoryName); + if (testInfo != null) { + testInfo.setVerdict(TestVerdict.SUCCEEDED); + } + } + + /** + * Retrieves test information for a test. + * + * @param directoryName + * the name of the directory of the test + * @return basic test information + */ + private TestInfo getTestInfoForDirectory(String directoryName) { + for (TestInfoSerializer<?> s : serializers) { + if (directoryName.startsWith(s.getBaseTestInfo().getBaseTestData() + .getUniqueUnitTestName())) + return s.getBaseTestInfo(); + } + return null; + } + + /** + * Sets a test given its directory name as failed and sets a + * <code>Throwable</code>-object as fail cause. The directory name is used, + * because it uniquely defines the test, as there is only one test per + * directory and it is encoded in display name of the test. + * + * @param directoryName + * the name of the directory of the test + * @param e + * the cause of the fail + */ + public void setFailed(String directoryName, Throwable e) { + TestInfo testInfo = getTestInfoForDirectory(directoryName); + if (testInfo != null) { + testInfo.setVerdict(TestVerdict.FAILED); + testInfo.setFailCause(e); + } + } + + /** + * Serializes all test results including a summary. After a call to this + * function there will be an index file with the summary in the root test + * directory and a test results file in all directories containing tests. + */ + public void serializeAll() { + testSummaryWriter.init(); + testSummaryWriter.writeHeader(); + Collections.sort(serializers, + new Comparator<TestInfoSerializer<? extends TestInfo>>() { + public int compare( + TestInfoSerializer<? extends TestInfo> o1, + TestInfoSerializer<? extends TestInfo> o2) { + String firstDir = o1.getBaseTestInfo() + .getBaseTestData().getTestDirectory(); + String secondDir = o2.getBaseTestInfo() + .getBaseTestData().getTestDirectory(); + return firstDir.compareTo(secondDir); + } + }); + + for (TestInfoSerializer<?> s : serializers) { + testSummaryWriter.writeSummaryOfTest(s.getBaseTestInfo(), + s.getTestType()); + s.serialize(); + } + testSummaryWriter.writeFooter(); + testSummaryWriter.close(); + } + + /** + * Sets a test summary writer. This method must be call before + * serializeAll() is called, because this method relies on the presence of a + * test summary writer. + * + * @param testSummaryWriter + * a concrete implementation of <code>TestSummaryWriter</code> + */ + public void setTestSummaryWriter(TestSummaryWriter testSummaryWriter) { + this.testSummaryWriter = testSummaryWriter; + } + + /** + * Sets the content which was written to the standard output during a test + * for a test. + * + * @param directoryName + * the directory name of the test + * @param stdOutFromTest + * the standard output content + */ + public void setStdOut(String directoryName, String stdOutFromTest) { + TestInfo testInfo = getTestInfoForDirectory(directoryName); + testInfo.setStdOut(stdOutFromTest); + } + + /** + * Sets the content which was written to the standard error during a test + * for a test. + * + * @param directoryName + * the directory name of the test + * @param stdErrFromTest + * the standard error content + */ + public void setStdErr(String directoryName, String stdErrFromTest) { + TestInfo testInfo = getTestInfoForDirectory(directoryName); + testInfo.setStdErr(stdErrFromTest); + } + + /** + * Sets a test given its directory name as inconclusive and sets a + * <code>AssumptionViolatedException</code>-object as cause for the verdict. + * The directory name is used, because it uniquely defines the test, as + * there is only one test per directory and it is encoded in display name of + * the test. A test may be inconclusive if an assumption is violated,e.g. if + * PDF-A conformance should be checked after signing and the input file does + * not conform to the PDF-A standard then the test cannot perform any + * meaningful checks. + * + * @param directoryName + * the name of the directory of the test + * @param e + * the cause of the fail + */ + public void setInconclusive(String directoryName, + AssumptionViolatedException e) { + TestInfo testInfo = getTestInfoForDirectory(directoryName); + testInfo.setVerdict(TestVerdict.INCONCLUSIVE); + testInfo.setFailCause(e); + } + +} diff --git a/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestInfoSerializer.java b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestInfoSerializer.java new file mode 100644 index 00000000..a3014b86 --- /dev/null +++ b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestInfoSerializer.java @@ -0,0 +1,170 @@ +package at.gv.egiz.param_tests.serialization; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.param_tests.testinfo.TestInfo; + +/** + * Base for all test information serializer. It uses template method pattern, + * fixing an algorithm for serialization, but delegating the actual steps to the + * subclasses. A two-step hierarchy shall be formed, with this class at the + * upper-most level, technology-specific (like HTML) serializer at the next and + * technology- and test-type-specific serializer at the lowest level. + * + * @author mtappler + * + * @param <T> + * the type of TestInfo which instance of this class, respectively + * subclasses of it can serialize + */ +public abstract class TestInfoSerializer<T extends TestInfo> { + + /** + * logger for this class + */ + private static final Logger logger = LoggerFactory + .getLogger(TestInfoSerializer.class); + + /** + * basic test information + */ + protected TestInfo baseTestInfo; + + /** + * Method for creating an of a subclass of <code>TestInfo</code>. The test + * information created using this method shall be retrieved, when + * <code>getBaseTestInfo()</code> is called. + * + * @return instance of a <code>TestInfo</code>-subclass + */ + public abstract T createTestInfo(); + + /** + * Clone method for serializer, it shall create a shallow copy of + * <code>this</code> and return it. It does not use + * <code>Object.clone()</code> for type safety, because this method returns + * an <code>Object</code>-instance. + * + * @return a clone of <code>this</code> + */ + public abstract TestInfoSerializer<T> cloneSerializer(); + + /** + * getter + * + * @return basic test information + */ + public TestInfo getBaseTestInfo() { + return baseTestInfo; + } + + /** + * Main serialization method. This method creates a test result file in the + * test directory and then calls the methods to perform the serialization + * steps. The following parts shall be written: + * <ol> + * <li>header</li> + * <li>test specific parameter</li> + * <li>test result</li> + * <li>test specific data</li> + * <li>footer</li> + * </ol> + */ + public void serialize() { + File outputFile = new File(baseTestInfo.getBaseTestData() + .getTestDirectory() + "/test_result." + fileEnding()); + FileOutputStream os = null; + try { + os = new FileOutputStream(outputFile); + PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "UTF8")); + writeHeader(pw); + writeTestSpecificParameters(pw); + writeTestResult(pw); + writeTestData(pw); + writeFooter(pw); + pw.flush(); + } catch (FileNotFoundException e) { + logger.warn("File not found for test info serialization.", e); + } catch (UnsupportedEncodingException e) { + logger.warn("Use unsupported encoding for serialization.", e); + } finally { + if (os != null) + IOUtils.closeQuietly(os); + } + } + + /** + * This writes the test result in some format like HTML to the provided + * <code>PrintWriter</code>-object. + * + * @param pw + * the <code>PrintWriter</code>-object whill shall be used for + * serialization + */ + protected abstract void writeTestResult(PrintWriter pw); + + /** + * This writes test specific parameters in some format like HTML to the + * provided <code>PrintWriter</code>-object. + * + * @param pw + * the <code>PrintWriter</code>-object whill shall be used for + * serialization + */ + protected abstract void writeTestSpecificParameters(PrintWriter pw); + + /** + * This writes the file header in some format like HTML to the provided + * <code>PrintWriter</code>-object. + * + * @param pw + * the <code>PrintWriter</code>-object whill shall be used for + * serialization + */ + protected abstract void writeHeader(PrintWriter pw); + + /** + * This writes test specific data in some format like HTML to the provided + * <code>PrintWriter</code>-object. + * + * @param pw + * the <code>PrintWriter</code>-object whill shall be used for + * serialization + */ + protected abstract void writeTestData(PrintWriter pw); + + /** + * This writes the file footer in some format like HTML to the provided + * <code>PrintWriter</code>-object. + * + * @param pw + * the <code>PrintWriter</code>-object whill shall be used for + * serialization + */ + protected abstract void writeFooter(PrintWriter pw); + + /** + * This method returns the file ending used for the test result file, like + * "html" or "xml". + * + * @return the file ending string + */ + public abstract String fileEnding(); + + /** + * This method returns a description of the concrete test type, which a + * concrete implementation of this class serializes. + * + * @return a test type description + */ + public abstract String getTestType(); +} diff --git a/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestSummaryWriter.java b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestSummaryWriter.java new file mode 100644 index 00000000..ad0dff7d --- /dev/null +++ b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestSummaryWriter.java @@ -0,0 +1,45 @@ +package at.gv.egiz.param_tests.serialization; + +import at.gv.egiz.param_tests.testinfo.TestInfo; + +/** + * interface defining methods for a test summary writer. This shall be + * implemented in a technology-dependent way, like for HTML. + * + * @author mtappler + * + */ +public interface TestSummaryWriter { + + /** + * This method shall write a header to a file. + */ + public void writeHeader(); + + /** + * This method shall write a short summary of a test to a file. + * + * @param tInfo + * test information for the test + * @param testType + * the type of the test + */ + public void writeSummaryOfTest(TestInfo tInfo, String testType); + + /** + * This method shall write a footer to the file. + */ + public void writeFooter(); + + /** + * This method shall initialize the writing process, e.g. by creating and + * opening a file, to which the summary is written. + */ + public void init(); + + /** + * This method shall terminate the writing process, e.g. by closing the + * summary file. + */ + public void close(); +} diff --git a/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLSerializer.java b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLSerializer.java new file mode 100644 index 00000000..9bbb0497 --- /dev/null +++ b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLSerializer.java @@ -0,0 +1,204 @@ +package at.gv.egiz.param_tests.serialization.html; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Map.Entry; + +import at.gv.egiz.param_tests.provider.BaseSignatureTestData; +import at.gv.egiz.param_tests.serialization.TestInfoSerializer; +import at.gv.egiz.param_tests.testinfo.TestInfo; +import at.gv.egiz.param_tests.testinfo.TestVerdict; + +/** + * A subclass implementing some methods, which can be implemented independent of + * concrete test type. It uses the Twitter-bootstrap framework, which must be + * provided for the files to be correctly displayed in a browser. + * + * @author mtappler + * + * @param <T> + * the type of TestInfo which instance of this class, respectively + * subclasses of it can serialize + */ +public abstract class HTMLSerializer<T extends TestInfo> extends + TestInfoSerializer<T> { + + @Override + public String fileEnding() { + return "html"; + } + + @Override + protected void writeHeader(PrintWriter pw) { + BaseSignatureTestData baseData = baseTestInfo.getBaseTestData(); + pw.println("<!doctype html>"); + pw.println("<html>"); + pw.println("<head>"); + pw.println("<title>Test results for " + baseData.getTestName() + + "</title>"); + pw.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"../css/bootstrap.min.css\">"); + pw.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"../css/bootstrap-theme.min.css\">"); + pw.println("<meta charset=\"UTF-8\">"); + pw.println("</head>"); + pw.println("<body>"); + pw.println("<div class=\"container\">"); + pw.println("<div class=\"page-header\">"); + pw.println("<h1>Detailed test results <small>" + baseData.getTestName() + + "</small></h1>"); + pw.println("</div>"); + String basicTestDataPanel = createBasicTestData(); + writePanel(pw, "Basic test data", basicTestDataPanel); + } + + /** + * Helper method which creates three tabs, one for basic test parameter, one + * for the standard output and one for standard error. + * + * @return HTML-string defining the tabs + */ + protected String createBasicTestData() { + BaseSignatureTestData baseData = baseTestInfo.getBaseTestData(); + String basicParameterBody = createBasicParameterRepresentation(baseData); + StringBuilder basicTestDataPanel = new StringBuilder(); + basicTestDataPanel + .append("<ul id=\"tabs\" class=\"nav nav-tabs\" data-tabs=\"tabs\">"); + basicTestDataPanel + .append("<li class=\"active\"><a href=\"#basic-parameters\" " + + "data-toggle=\"tab\">Basic parameters</a></li>"); + basicTestDataPanel + .append("<li><a href=\"#std-out\" data-toggle=\"tab\">Standard output</a></li>"); + basicTestDataPanel + .append("<li><a href=\"#std-err\" data-toggle=\"tab\">Standard error</a></li>"); + basicTestDataPanel.append("</ul>"); + basicTestDataPanel + .append("<div id=\"my-tab-content\" class=\"tab-content\">"); + basicTestDataPanel + .append("<div class=\"tab-pane active\" id=\"basic-parameters\">"); + basicTestDataPanel.append(basicParameterBody); + basicTestDataPanel.append("</div>"); + basicTestDataPanel.append("<div class=\"tab-pane\" id=\"std-out\">"); + basicTestDataPanel.append("<pre class=\"pre-scrollable\">" + + baseTestInfo.getStdOut() + "</pre>"); + basicTestDataPanel.append("</div>"); + basicTestDataPanel.append("<div class=\"tab-pane\" id=\"std-err\">"); + basicTestDataPanel.append("<pre class=\"pre-scrollable\">" + + baseTestInfo.getStdErr() + "</pre>"); + basicTestDataPanel.append("</div>"); + basicTestDataPanel.append("</div>"); + return basicTestDataPanel.toString(); + } + + /** + * This method creates a definition list for basic parameters of a test. + * + * @param baseData + * basic test parameters + * @return HTML-string defining a definition list + */ + private String createBasicParameterRepresentation( + BaseSignatureTestData baseData) { + StringBuilder sb = new StringBuilder(); + sb.append("<dl>"); + sb.append(createDescription("Input file", baseData.getPdfFile())); + sb.append(createDescription("Output file", baseData.getOutputFile())); + sb.append(createDescription("Signature profile", baseData.getProfilID())); + sb.append(createDescription("Connector Type", baseData + .getConnectorData().getConnectorType())); + if (baseData.getConnectorData().getConnectorParameters().size() > 0) { + StringBuilder connectorParameters = new StringBuilder(); + connectorParameters.append("<dl class=\"dl-horizontal\">"); + for (Entry<String, String> e : baseData.getConnectorData() + .getConnectorParameters().entrySet()) + connectorParameters.append(createDescription(e.getKey(), + e.getValue())); + connectorParameters.append("</dl>"); + sb.append(createDescription("Connector Parameters", + connectorParameters.toString())); + } + sb.append(createDescription("Test Type", getTestType())); + sb.append("</dl>"); + return sb.toString(); + } + + /** + * Helper for writing a bootstrap panel with some title and content. + * + * @param pw + * <code>PrintWriter</code>-object to which the panel should be + * written + * @param panelTitle + * title of the panel + * @param panelBody + * panel content + */ + protected void writePanel(PrintWriter pw, String panelTitle, + String panelBody) { + pw.println("<div class=\"panel panel-default\">"); + pw.println(" <div class=\"panel-heading\">"); + pw.println("<h3 class=\"panel-title\">" + panelTitle + "</h3>"); + pw.println("</div>"); + pw.println("<div class=\"panel-body\">"); + pw.println(panelBody); + pw.println("</div>"); + pw.println("</div>"); + } + + @Override + protected void writeTestResult(PrintWriter pw) { + StringBuilder panelBody = new StringBuilder(); + panelBody.append("<h4>" + + HTMLTestSummaryWriter.verdictToLabel(baseTestInfo + .getVerdict()) + "</h4>"); + if (baseTestInfo.getVerdict().equals(TestVerdict.FAILED) + || baseTestInfo.getVerdict().equals(TestVerdict.INCONCLUSIVE)) { + panelBody.append(createExceptionDataString(baseTestInfo + .getFailCause())); + } + writePanel(pw, "Test result", panelBody.toString()); + + } + + /** + * This method creates a HTML-representation for data contained in a + * throwable. + * + * @param t + * <code>Throwable</code>-object which should be displayed + * @return HTML-string for the throwable + */ + protected String createExceptionDataString(Throwable t) { + StringBuilder exceptionData = new StringBuilder(); + exceptionData.append("<dl class=\"dl-horizontal\">"); + exceptionData.append(createDescription("Cause", t.toString())); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + exceptionData.append(createDescription("Stacktrace", sw.toString())); + exceptionData.append("</dl>"); + return exceptionData.toString(); + } + + /** + * Helper method for creating an item of a definition list. + * + * @param term + * the term of the item (the key) + * @param definition + * the definition of the item (the value) + * @return an HTML-string for the definition list item + */ + protected String createDescription(String term, String definition) { + return String.format("<dt>%s</dt><dd>%s</dd>%n", term, definition); + } + + @Override + protected void writeFooter(PrintWriter pw) { + pw.println("<a href=\"../index.html\">Back to summary</a>"); + pw.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\">" + + "</script>"); + pw.println("<script src=\"../js/bootstrap.min.js\"></script>"); + pw.println("</div>"); + pw.println("</body>"); + pw.println("</html>"); + } +} diff --git a/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLTestSummaryWriter.java b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLTestSummaryWriter.java new file mode 100644 index 00000000..0734a3ec --- /dev/null +++ b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLTestSummaryWriter.java @@ -0,0 +1,150 @@ +package at.gv.egiz.param_tests.serialization.html; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.param_tests.serialization.TestSummaryWriter; +import at.gv.egiz.param_tests.testinfo.TestInfo; +import at.gv.egiz.param_tests.testinfo.TestVerdict; + +/** + * Concrete implementation of the <code>TestSummaryWriter</code>, which creates + * HTML output and uses the Twitter-bootstrap framework. + * + * @author mtappler + * + */ +public class HTMLTestSummaryWriter implements TestSummaryWriter { + + /** + * the location of the test directory + */ + private String testDir; + /** + * the print writer which is used for writing + */ + private PrintWriter pw; + /** + * the logger for this class + */ + private static final Logger logger = LoggerFactory + .getLogger(HTMLTestSummaryWriter.class); + + /** + * Constructor which sets the test directory. + * + * @param testDir + * location of the test directory + */ + public HTMLTestSummaryWriter(String testDir) { + this.testDir = testDir; + } + + public void writeHeader() { + if (pw == null) + return; + pw.println("<!doctype html>"); + pw.println("<html>"); + pw.println("<head>"); + pw.println("<title>Summary of test results</title>"); + pw.println("<meta charset=\"UTF-8\">"); + pw.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"css/bootstrap.min.css\">"); + pw.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"../css/bootstrap-theme.min.css\">"); + pw.println("</head>"); + pw.println("<body>"); + pw.println("<div class=\"container\">"); + pw.println("<div class=\"page-header\">"); + pw.println("<h1>Test result summary</h1>"); + pw.println("</div>"); + pw.println("<table class=\"table table-bordered table-striped\">"); + pw.println("<thead>"); + pw.println("<tr>"); + pw.println("<th>Test name</th>"); + pw.println("<th>Test directory</th>"); + pw.println("<th>Test type</th>"); + pw.println("<th>Verdict</th>"); + pw.println("</tr>"); + pw.println("</thead>"); + + } + + public void writeSummaryOfTest(TestInfo tInfo, String testType) { + if (pw == null) + return; + pw.println("<tr>"); + pw.println(String.format( + "<td><a href=\"%s/test_result.html\">%s</a></td>", tInfo + .getBaseTestData().getTestDirectory(), tInfo + .getBaseTestData().getTestName())); + pw.println(String.format("<td>%s</td>", tInfo.getBaseTestData() + .getTestDirectory())); + pw.println(String.format("<td>%s</td>", testType)); + pw.println(String.format("<td>%s</td>", + verdictToLabel(tInfo.getVerdict()))); + pw.println("</tr>"); + } + + // intentionally package protected + /** + * Static method for creating bootstrap label for a test verdict. Since it + * is technology dependent (HTML + bootstrap) it is defined as package + * protected. + * + * @param verdict + * the verdict of a test + * @return HTML-string for a verdict label + */ + static String verdictToLabel(TestVerdict verdict) { + switch (verdict) { + case FAILED: + return "<span class=\"label label-danger\">Fail</span>"; + case INCONCLUSIVE: + return "<span class=\"label label-warning\">Inconclusive</span>"; + case SUCCEEDED: + return "<span class=\"label label-success\">Success</span>"; + default: + return "<span class=\"label label-default\">Unknown</span>"; + } + } + + public void writeFooter() { + if (pw == null) + return; + + pw.println("</table>"); + pw.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\">" + + "</script>"); + pw.println("<script src=\"js/bootstrap.min.js\"></script>"); + pw.println("</div>"); + pw.println("</body>"); + pw.println("</html>"); + } + + public void init() { + OutputStream os; + try { + os = new FileOutputStream(testDir + "/index.html"); + pw = new PrintWriter(new OutputStreamWriter(os, "UTF8")); + } catch (FileNotFoundException e) { + logger.debug("Could not find output file, not writing any summary", + e); + } catch (UnsupportedEncodingException e) { + logger.debug("Used unsupported encoding for writing summary file.", + e); + } + + } + + public void close() { + if (pw != null) + pw.close(); + } + +} diff --git a/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/PDFAHTMLSerizalier.java b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/PDFAHTMLSerizalier.java new file mode 100644 index 00000000..fdaa80fa --- /dev/null +++ b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/PDFAHTMLSerizalier.java @@ -0,0 +1,147 @@ +package at.gv.egiz.param_tests.serialization.html; + +import java.io.PrintWriter; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.pdfbox.preflight.ValidationResult; +import org.apache.pdfbox.preflight.ValidationResult.ValidationError; + +import at.gv.egiz.param_tests.serialization.TestInfoSerializer; +import at.gv.egiz.param_tests.testinfo.PDFATestInfo; + +/** + * Concrete test information serializer for PDF-A conformance tests using HTML + * and Twitter-bootstrap. + * + * @author mtappler + * + */ +public class PDFAHTMLSerizalier extends HTMLSerializer<PDFATestInfo> { + + /** + * the test information object which is serialized, it is the same as + * <code>TestInfoSerializer.baseTestInfo</code> + */ + private PDFATestInfo testInfo; + + /** + * Default contructor used for registering it as prototype. + */ + public PDFAHTMLSerizalier() { + this(null); + } + + /** + * Package protected constructor used for cloning + * + * @param testInfo + */ + PDFAHTMLSerizalier(PDFATestInfo testInfo) { + this.testInfo = testInfo; + } + + @Override + public PDFATestInfo createTestInfo() { + testInfo = new PDFATestInfo(); + baseTestInfo = testInfo; + return testInfo; + } + + @Override + public TestInfoSerializer<PDFATestInfo> cloneSerializer() { + return new PDFAHTMLSerizalier(testInfo); + } + + @Override + protected void writeTestData(PrintWriter pw) { + pw.println("<h2>Validation status before signing</h2>"); + writeValidationResult(pw, testInfo.getResultBeforeSign()); + if (testInfo.getResultAfterSign() != null) { + pw.println("<h2>Validation status after signing</h2>"); + writeValidationResult(pw, testInfo.getResultAfterSign()); + } + } + + /** + * This method writes the validation result to the given print writer, i.e. + * an information about the success of the validation or an exception which + * was thrown during the validation or a list of validation errors. + * + * @param pw + * the <code>PrintWriter</code>-object to which the HTML-code is + * written + * @param validationResult + * the pair which defines the result of a validation + */ + private void writeValidationResult(PrintWriter pw, + Pair<ValidationResult, Throwable> validationResult) { + if (validationResult.getRight() != null) { + StringBuilder exceptionString = new StringBuilder(); + exceptionString + .append("p>An exception happened during the validation process:</p>"); + exceptionString.append(createExceptionDataString(validationResult + .getRight())); + writePanel(pw, "Validation exception details", + exceptionString.toString()); + } + if (validationResult.getLeft() != null) { + + StringBuilder conformanceString = new StringBuilder(); + if (validationResult.getLeft().isValid()) { + conformanceString + .append("<p>The document conforms to the PDF-A standard.</p>"); + } else { + List<ValidationError> errors = validationResult.getLeft() + .getErrorsList(); + conformanceString.append("<p>With to the PDF-A standard, " + + "the document contains the following errors:</p>"); + conformanceString + .append("<table class=\"table table-bordered table-striped\">"); + conformanceString.append("<thead>"); + conformanceString.append("<tr>"); + conformanceString.append("<th>Error code</th>"); + conformanceString.append("<th>Error details</th>"); + conformanceString.append("<th>Warning</th>"); + conformanceString.append("</tr>"); + conformanceString.append("</thead>"); + for (ValidationError error : errors) { + writeValidationError(conformanceString, error); + } + conformanceString.append("</table>"); + } + writePanel(pw, "PDF-A conformance", conformanceString.toString()); + } + } + + /** + * Helper method for writing a table line for a single validation error. + * + * @param conformanceString + * the string builder to which the table line is appended + * @param error + * the error which should be serialized + */ + private void writeValidationError(StringBuilder conformanceString, + ValidationError error) { + conformanceString.append("<tr>"); + conformanceString.append(String.format("<td>%s</td>", + error.getErrorCode())); + conformanceString.append(String.format("<td>%s</td>", + error.getDetails())); + conformanceString.append(String.format("<td>%s</td>", + error.isWarning() ? "x" : "")); + conformanceString.append("</tr>"); + } + + @Override + public String getTestType() { + return "PDF-A"; + } + + @Override + protected void writeTestSpecificParameters(PrintWriter pw) { + // intentionally left blank, no pdf-a specific parameters + } + +} diff --git a/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/SignaturePositionHTMLSerializer.java b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/SignaturePositionHTMLSerializer.java new file mode 100644 index 00000000..6cebb9ef --- /dev/null +++ b/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/SignaturePositionHTMLSerializer.java @@ -0,0 +1,176 @@ +package at.gv.egiz.param_tests.serialization.html; + +import java.awt.Rectangle; +import java.io.PrintWriter; +import java.util.List; + +import at.gv.egiz.param_tests.serialization.TestInfoSerializer; +import at.gv.egiz.param_tests.testinfo.SignaturePositionTestInfo; +import at.gv.egiz.param_tests.testinfo.SignaturePositionTestInfo.SignaturePositionParameters; + +/** + * Concrete test information serializer for signature position tests using HTML + * and Twitter-bootstrap. + * + * @author mtappler + * + */ +public class SignaturePositionHTMLSerializer extends + HTMLSerializer<SignaturePositionTestInfo> { + /** + * the test information object which is serialized, it is the same as + * <code>TestInfoSerializer.baseTestInfo</code> + */ + private SignaturePositionTestInfo testInfo; + + /** + * Default contructor used for registering it as prototype. + */ + public SignaturePositionHTMLSerializer() { + this(null); + } + + /** + * Package protected constructor used for cloning + * + * @param testInfo + */ + SignaturePositionHTMLSerializer(SignaturePositionTestInfo testInfo) { + this.testInfo = testInfo; + } + + @Override + public SignaturePositionTestInfo createTestInfo() { + testInfo = new SignaturePositionTestInfo(); + baseTestInfo = testInfo; + return testInfo; + } + + @Override + public TestInfoSerializer<SignaturePositionTestInfo> cloneSerializer() { + return new SignaturePositionHTMLSerializer(testInfo); + } + + @Override + protected void writeTestData(PrintWriter pw) { + if (testInfo.getAdditionParameters().isCaptureReferenceImage()) { + writeTestDataCapture(pw); + } else { + writeTestDataTest(pw); + } + } + + /** + * Method for writing test data if only a reference image was captured + * + * @param pw + * object which should be used for writing + */ + private void writeTestDataCapture(PrintWriter pw) { + pw.println("<h2>Captured reference image</h2>"); + writePanel( + pw, + "Reference image", + getImageString(testInfo.getAdditionParameters() + .getRefImageFileName(), "reference image")); + if (testInfo.getRefImageIgnored() != null) { + writePanel( + pw, + "Reference image with ignored areas", + getImageString(testInfo.getRefImageIgnored(), + "reference image (ignored)")); + } + } + + /** + * Method for writing test data if an actual signature position test was + * performed. + * + * @param pw + * object which should be used for writing + */ + private void writeTestDataTest(PrintWriter pw) { + pw.println("<h2>Image data captured during test</h2>"); + writePanel( + pw, + "Reference image with ignored areas", + getImageString(testInfo.getRefImageIgnored(), + "reference image (ignored)")); + writePanel( + pw, + "Signature page image with ignored areas", + getImageString(testInfo.getSigPageImageIgnored(), + "signature page image (ignored)")); + writePanel(pw, "Difference image", + getImageString(testInfo.getDiffImage(), "difference image")); + } + + /** + * This method creates HTML-image-tag for an image file. + * + * @param imageFileName + * location of the image file + * @param altText + * the alternative text + * @return HTML-image-tag + */ + private String getImageString(String imageFileName, String altText) { + String imageString; + if (imageFileName != null) + imageString = String.format( + "<img class=\"img-responsive\" src=\"%s\" alt=\"%s\">", + imageFileName, altText); + else + imageString = "<p>This image was not captured correctly. " + + "The test may have been aborted before because of an IO error.</p>"; + return imageString; + } + + @Override + public String getTestType() { + return "Signature Position"; + } + + @Override + protected void writeTestSpecificParameters(PrintWriter pw) { + + SignaturePositionParameters additionalParameters = testInfo + .getAdditionParameters(); + StringBuilder panelBody = new StringBuilder(); + + panelBody.append("<dl>"); + panelBody.append(createDescription("Signature positioning string", + additionalParameters.getPositionString())); + panelBody.append(createDescription("Signature page number", + Integer.toString(additionalParameters.getSigPageNumber()))); + panelBody.append(createDescription("Reference image file name", + additionalParameters.getRefImageFileName())); + panelBody.append(createDescription("Ignored Areas", + createIgnoredAreasDescription(additionalParameters + .getIgnoredAreas()))); + panelBody.append("</dl>"); + writePanel(pw, "Test specific parameters", panelBody.toString()); + } + + /** + * This method creates an unordered HTML list for the ignored areas for a + * signature position test. + * + * @param ignoredAreas + * list of ignored areas + * @return HTML-string representing the ignored areas + */ + private String createIgnoredAreasDescription(List<Rectangle> ignoredAreas) { + StringBuilder sb = new StringBuilder(); + sb.append("<ul>"); + for (Rectangle r : ignoredAreas) { + sb.append("<li>"); + sb.append(String.format("x:%d y:%d width:%d height:%d", r.x, r.y, + r.width, r.height)); + sb.append("</li>"); + } + sb.append("</ul>"); + return sb.toString(); + } + +} |