aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization
diff options
context:
space:
mode:
authorAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2014-08-29 15:09:54 +0200
committerAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2014-08-29 15:09:54 +0200
commitffd1e0da6b73e2737f5cad0a6d3e82dbc3de206f (patch)
tree73a685d2c6becb3a274522fb31b898c8a9b0903c /pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization
parent7a983c6687e8045efcc918c273bc43798319423b (diff)
downloadpdf-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')
-rw-r--r--pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/SerializiationManager.java254
-rw-r--r--pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestInfoSerializer.java170
-rw-r--r--pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/TestSummaryWriter.java45
-rw-r--r--pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLSerializer.java204
-rw-r--r--pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/HTMLTestSummaryWriter.java150
-rw-r--r--pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/PDFAHTMLSerizalier.java147
-rw-r--r--pdf-as-tests/src/test/java/at/gv/egiz/param_tests/serialization/html/SignaturePositionHTMLSerializer.java176
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();
+ }
+
+}