de.interactive_instruments.ShapeChange.BasicTest.java Source code

Java tutorial

Introduction

Here is the source code for de.interactive_instruments.ShapeChange.BasicTest.java

Source

/**
 * ShapeChange - processing application schemas for geographic information
 *
 * This file is part of ShapeChange. ShapeChange takes a ISO 19109 
 * Application Schema from a UML model and translates it into a 
 * GML Application Schema or other implementation representations.
 *
 * Additional information about the software can be found at
 * http://shapechange.net/
 *
 * (c) 2002-2012 interactive instruments GmbH, Bonn, Germany
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact:
 * interactive instruments GmbH
 * Trierer Strasse 70-72
 * 53115 Bonn
 * Germany
 */

package de.interactive_instruments.ShapeChange;

import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.SystemUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.HTMLDocumentBuilder;
import org.custommonkey.xmlunit.TolerantSaxDocumentBuilder;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.jaxp13.Validator;
import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.SAXParseException;

import de.interactive_instruments.ShapeChange.Util.ZipHandler;
import de.interactive_instruments.ShapeChange.TestInstance;

/**
 * Basic unit test for ShapeChange
 * 
 * <li>Process without configuration file and verify that no error is reported
 * <li>Process XMI 1.0 test model and verify that no error is reported
 * <li>On Windows, process EA test model and verify that no error is reported
 */
public class BasicTest {

    boolean testTime = false;

    @Test
    public void test() {
        initialise();

        /*
         * Invoke without parameters, if we are connected
         */
        String[] xsdTest = { "test" };
        try {
            URL url = new URL("http://shapechange.net/resources/config/minimal.xml");
            InputStream configStream = url.openStream();
            if (configStream != null) {
                xsdTest(null, xsdTest, null, ".", "src/test/resources/reference/xsd");
            }
        } catch (Exception e) {
        }

        /*
         * Process the XMI 1.0 test model
         */
        xsdTest("src/test/resources/config/testXMI.xml", xsdTest, null, "testResults/xmi/INPUT",
                "src/test/resources/reference/xsd");

        /*
         * On Windows process also the EA test models
         */
        if (SystemUtils.IS_OS_WINDOWS) {

            /*
             * First the same test model as the XMI 1.0 test model
             */
            xsdTest("src/test/resources/config/testEA.xml", xsdTest, null, "testResults/ea/INPUT",
                    "src/test/resources/reference/xsd");

            /*
             * Now with some replacement values
             */
            HashMap<String, String> replace = new HashMap<String, String>();
            replace.put("$eap$", "src/test/resources/test.eap");
            replace.put("$log$", "testResults/ea/log.xml");
            replace.put("$out$", "testResults/ea");
            xsdTest("src/test/resources/config/testEA_x.xml", xsdTest, null, replace, "testResults/ea/INPUT",
                    "src/test/resources/reference/xsd");

            /*
             * Test property options: ordered, uniqueness and inline/byReference
             */
            multiTest("src/test/resources/config/testEA_prop.xml", new String[] { "xsd", "xml", "html" },
                    "testResults/ea/prop/INPUT", "src/test/resources/reference/prop");

            /*
             * Test multiple tagged values and stereotypes
             */
            HashMap<String, String> replace2 = new HashMap<String, String>();
            replace2.put("$tvimpl$", "");
            replace2.put("$logout$", "testResults/multipleTaggedValuesAndStereotypes/tv_map_impl/log.xml");
            replace2.put("$xsdout$", "testResults/multipleTaggedValuesAndStereotypes/tv_map_impl");
            xsdTest("src/test/resources/config/testEA_multipleTaggedValuesAndStereotypes.xml",
                    new String[] { "multTvAndSt" }, null, replace2,
                    "testResults/multipleTaggedValuesAndStereotypes/tv_map_impl/INPUT",
                    "src/test/resources/reference/xsd/multipleTaggedValuesAndStereotypes/INPUT");

            /*
             * Test multiple tagged values and stereotypes - with array
             * implementation for tagged values.
             */
            replace2 = new HashMap<String, String>();
            replace2.put("$tvimpl$", "array");
            replace2.put("$logout$", "testResults/multipleTaggedValuesAndStereotypes/tv_array_impl/log.xml");
            replace2.put("$xsdout$", "testResults/multipleTaggedValuesAndStereotypes/tv_array_impl");
            xsdTest("src/test/resources/config/testEA_multipleTaggedValuesAndStereotypes.xml",
                    new String[] { "multTvAndSt" }, null, replace2,
                    "testResults/multipleTaggedValuesAndStereotypes/tv_array_impl/INPUT",
                    "src/test/resources/reference/xsd/multipleTaggedValuesAndStereotypes/INPUT");

            /*
             * Test the descriptor functionality
             */
            multiTest("src/test/resources/config/testEA_descriptors_fc_en.xml", new String[] { "xml", "html" },
                    "testResults/html/descriptors/INPUT", "src/test/resources/reference/html/descriptors");

            multiTest("src/test/resources/config/testEA_descriptors_fc_de.xml", new String[] { "xml", "html" },
                    "testResults/html/descriptors/INPUT", "src/test/resources/reference/html/descriptors");

            multiTest("src/test/resources/config/testEA_descriptors_inspire.xml", new String[] { "xml", "html" },
                    "testResults/html/descriptors/INPUT", "src/test/resources/reference/html/descriptors");

            multiTest("src/test/resources/config/testEA_descriptors_aaa.xml", new String[] { "xml", "html" },
                    "testResults/html/descriptors/INPUT", "src/test/resources/reference/html/descriptors");

            multiTest("src/test/resources/config/testEA_descriptors_bbr.xml", new String[] { "xml", "html" },
                    "testResults/html/descriptors/INPUT", "src/test/resources/reference/html/descriptors");

            /*
             * Test derivation of application schema metadata.
             */
            multiTest("src/test/resources/config/testEA_schema_metadata.xml", new String[] { "xml" },
                    "testResults/schema_metadata/INPUT", "src/test/resources/reference/schema_metadata/INPUT");

            /*
             * Test creation of an HTML feature catalogue with
             * inheritedProperties=true and noAlphabeticSortingForProperties =
             * true
             */
            multiTest("src/test/resources/config/testEA_fc_inheritedProperties.xml", new String[] { "xml", "html" },
                    "testResults/html/inheritedProperties/INPUT",
                    "src/test/resources/reference/html/inheritedProperties/INPUT");

            /*
             * Test derivation of application schema differences (output as
             * single page HTML feature catalogue).
             */
            multiTest("src/test/resources/config/testEA_model_diff.xml", new String[] { "xml", "html" },
                    "testResults/html/diff/INPUT", "src/test/resources/reference/html/diff/INPUT");

            /*
             * SQL - basic text
             */
            multiTest("src/test/resources/config/testEA_sql.xml", new String[] { "sql" }, "testResults/sql/basic",
                    "src/test/resources/reference/sql/basic");

            /*
             * SQL - associative tables tests
             */
            multiTest("src/test/resources/config/testEA_sqlAssociativeTables.xml", new String[] { "sql" },
                    "testResults/sql/associativeTables", "src/test/resources/reference/sql/associativeTables");

            /*
             * SQL - geometry parameters test
             */
            multiTest("src/test/resources/config/testEA_sqlGeometryParameters.xml", new String[] { "sql" },
                    "testResults/sql/geometryParameters", "src/test/resources/reference/sql/geometryParameters");

            /*
             * Test XML Schema creation for Application Schema with multiple
             * packages that shall be output as individual xsd files.
             */
            multiTest("src/test/resources/config/testEA_packageIncludes.xml", new String[] { "xsd" },
                    "testResults/xsd/packageIncludes", "src/test/resources/reference/xsd/packageIncludes");

            /*
             * Test XML Schema creation for two Application Schema, with
             * identity transformation.
             */
            multiTest("src/test/resources/config/testEA_MultipleAppSchema.xml", new String[] { "xsd" },
                    "testResults/xsd/multiAppSchema", "src/test/resources/reference/xsd/multiAppSchema");

            /*
             * A simple model to test the creation of a single-file html feature
             * catalogue
             */
            htmlTest("src/test/resources/config/testEA_Html.xml", new String[] { "test" }, "testResults/html/INPUT",
                    "src/test/resources/reference/html");

            /*
             * A simple model to test the localization functionality
             */
            htmlTest("src/test/resources/config/testEA_HtmlLocalization.xml", new String[] { "test" },
                    "testResults/html/localization/INPUT", "src/test/resources/reference/html/localization");

            /*
             * A simple model to test the creation of a docx feature catalogue
             */
            docxTest("src/test/resources/config/testEA_Docx.xml", new String[] { "test" },
                    "testResults/docx/myInputId", "src/test/resources/reference/docx");

            /*
             * A simple model to test the creation of a docx feature catalogue
             * that includes UML diagrams
             */
            // TODO image file names and sizes not stable
            // docxTest("src/test/resources/config/testEA_Docx_FC_with_images.xml",
            // new String[]{"test_featurecatalog_with_images"},
            // "testResults/docx_with_images/myInputId",
            // "src/test/resources/reference/docx");

            /*
             * A test model where documentation is retrieved from classifiers
             * that are suppliers of a dependency (feature, attribute and value
             * concepts)
             */
            xsdTest("src/test/resources/config/testEA_dep.xml", xsdTest, null, "testResults/ea/INPUT",
                    "src/test/resources/reference/xsd");

            /*
             * A simple CityGML Application Domain Extension (ADE)
             */
            String[] xsdADE = { "ade" };
            xsdTest("src/test/resources/config/testEA_ADE.xml", xsdADE, null, "testResults/ea/INPUT",
                    "src/test/resources/reference/xsd");

            /*
             * Qualified associations as well as array and list properties. Note
             * that there are errors reported during the conversion (on purpose)
             * unlike in most other tests.
             */
            String[] xsdaaask = { "testaaask" };
            xsdTest("src/test/resources/config/testEA_aaa-sk.xml", xsdaaask, null, "testResults/ea/INPUT",
                    "src/test/resources/reference/xsd", false);

            /*
             * An association class and the GML 3.3 code list values
             */
            String[] xsdgml33 = { "testgml33" };
            xsdTest("src/test/resources/config/testEA_gml33.xml", xsdgml33, xsdgml33, "testResults/ea/INPUT",
                    "src/test/resources/reference/xsd");

            /*
             * Test the mixin options
             */
            String[] xsdmixin = { "testgroupmixin" };
            xsdTest("src/test/resources/config/testEA_groupmixin.xml", xsdmixin, null, "testResults/ea/INPUT",
                    "src/test/resources/reference/xsd");

            /*
             * A simple 19115 metadata profile and Schematron tests
             */
            String[] xsdmd = { "testbasetypes", "testprofile", "testlet" };
            String[] schmd = { "testprofile", "testlet" };
            xsdTest("src/test/resources/config/testEA_md.xml", xsdmd, schmd, "testResults/ea/md/INPUT",
                    "src/test/resources/reference/xsd");

            /*
             * The SWE Common 2.0 encoding
             */
            String[] xsdswe = { "advanced_encodings", "basic_types", "block_components", "choice_components",
                    "record_components", "simple_components", "simple_encodings", "swe" };
            xsdTest("src/test/resources/config/testEA_swe.xml", xsdswe, null, "testResults/ea/swe/INPUT",
                    "src/test/resources/reference/xsd/swe");

            // TODO add INSPIRE, OKSTRA, no GML, more Schematron tests

            /*
             * JSON encoding with geoservices encoding rule
             */
            String[] typenamesGsr = { "FeatureType1", "FeatureType2" };
            jsonTest("src/test/resources/config/testEA_JsonGsr.xml", typenamesGsr,
                    "testResults/ea/json/geoservices/INPUT", "src/test/resources/reference/json/geoservices");

            /*
             * JSON encoding with extended geoservices encoding rule
             */
            String[] typenamesGsrExtended = { "DataType", "DataType2", "FeatureType1", "FeatureType2", "NilUnion",
                    "Union" };
            jsonTest("src/test/resources/config/testEA_JsonGsrExtended.xml", typenamesGsrExtended,
                    "testResults/ea/json/geoservices_extended/INPUT",
                    "src/test/resources/reference/json/geoservices_extended");

            /*
             * SKOS codelists
             */
            String[] rdfskos = { "Codelists" };
            rdfTest("src/test/resources/config/testEA_skos.xml", rdfskos, "testResults/ea/skos/INPUT",
                    "src/test/resources/reference/rdf/skos");

            /*
             * OWL 19150-2 ontologies
             */
            String[] rdfowl = { "SchemaA/SchemaA", "SchemaB/SchemaB" };
            // FIXME text comparison does not work, order changes each time,
            // compare ttl files on a triple level (with Jena?)
            // ttlTest("src/test/resources/config/testEA_owliso19150_default.xml",
            // rdfowl, "testResults/ea/owl/default/INPUT",
            // "src/test/resources/reference/rdf/owliso19150/default");
            // ttlTest("src/test/resources/config/testEA_owliso19150_extensions.xml",
            // rdfowl, "testResults/ea/owl/extensions/INPUT",
            // "src/test/resources/reference/rdf/owliso19150/extensions");

            /*
             * Flattening transformation
             */
            multiTest("src/test/resources/config/testEA_Flattening.xml", new String[] { "xsd" },
                    "testResults/flattening/xsd", "src/test/resources/reference/flattening/xsd");

            /*
             * Flattening transformation - only homogeneous geometries
             */
            multiTest("src/test/resources/config/testEA_Flattening_homogeneousGeometries.xml",
                    new String[] { "xsd" }, "testResults/flattening/homogeneousGeometry_core/",
                    "src/test/resources/reference/flattening/homogeneousGeometry");

            /*
             * Flattening transformation - only homogeneous geometries - Test1
             * (handling of associations)
             */
            multiTest("src/test/resources/config/testEA_Flattening_homogeneousGeometries_test1.xml",
                    new String[] { "xsd" }, "testResults/flattening/homogeneousGeometry_test1/",
                    "src/test/resources/reference/flattening/homogeneousGeometry_test1");

            /*
             * Flattening transformation - only inheritance
             */
            multiTest("src/test/resources/config/testEA_Flattening_inheritance.xml", new String[] { "xsd" },
                    "testResults/flattening/inheritance/", "src/test/resources/reference/flattening/inheritance");

            /*
             * Flattening transformation - cycles (and isFlatTarget setting)
             */
            multiTest("src/test/resources/config/testEA_Flattening_cycles.xml", new String[] { "xsd" },
                    "testResults/flattening/xsd/cycles_step1",
                    "src/test/resources/reference/flattening/xsd/cycles_step1");

            /*
             * Flattening transformation - remove feature-2-feature
             * relationships
             */
            multiTest("src/test/resources/config/testEA_Flattening_removeFeatureTypeRelationships.xml",
                    new String[] { "xsd" }, "testResults/flattening/removeFeatureTypeRelationships",
                    "src/test/resources/reference/flattening/removeFeatureTypeRelationships");

            /*
             * Flattening transformation - remove object-2-feature relationships
             * for specific object types
             */
            multiTest("src/test/resources/config/testEA_Flattening_removeObjectToFeatureTypeNavigability.xml",
                    new String[] { "xsd" }, "testResults/flattening/removeObjectToFeatureTypeNavigability",
                    "src/test/resources/reference/flattening/removeObjectToFeatureTypeNavigability");

            /*
             * Replication schema target
             */
            multiTest("src/test/resources/config/testEA_repSchema.xml", new String[] { "xsd" },
                    "testResults/repSchema/repXsd", "src/test/resources/reference/xsd/replicationSchema");

            /*
             * Attribute creation transformer
             */
            multiTest("src/test/resources/config/testEA_attributeCreation.xml", new String[] { "xsd" },
                    "testResults/attribute_creation/xsd", "src/test/resources/reference/xsd/attributeCreation");

            /*
             * Test the profiling functionality
             */

            multiTest("src/test/resources/config/testEA_Profiling.xml", new String[] { "xsd", "xml" },
                    "testResults/profiling/xsd", "src/test/resources/reference/profiling/xsd");

            /*
             * Test the profiling functionality - with explicit profile settings
             * behavior
             */
            multiTest("src/test/resources/config/testEA_Profiling_explicitProfileSettings.xml",
                    new String[] { "xsd", "xml" }, "testResults/profiling_explicitProfileSettings/xsd",
                    "src/test/resources/reference/profiling_explicitProfileSettings/xsd");

            // /*
            // * Test the creation of a frame-based html feature catalogue
            // */
            // multiTest("src/test/resources/config/testEA_HtmlFrame.xml",
            // new String[] { "html" }, "testResults/html/frame/INPUT",
            // "src/test/resources/reference/html/frame/INPUT");

            /*
             * Schematron derived from SBVR (with intermediate translation to
             * FOL)
             */
            multiTest("src/test/resources/config/testEA_Sbvr.xml", new String[] { "xml" },
                    "testResults/fol/fromSbvr/sch/step3", "src/test/resources/reference/sch/fromSbvr");
        }
    }

    private void multiTest(String config, String[] fileFormatsToCheck, String basedirResults,
            String basedirReference) {

        Set<String> fileFormatsToCheckLC = null;

        if (fileFormatsToCheck != null) {

            fileFormatsToCheckLC = new HashSet<String>();

            for (int i = 0; i < fileFormatsToCheck.length; i++) {
                fileFormatsToCheckLC.add(fileFormatsToCheck[i].trim().toLowerCase());
            }
        }

        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config);
        long end = (new Date()).getTime();
        System.out.println("Execution time " + config + ": " + (end - start) + "ms");
        assertTrue("Test model execution failed", test.noError());
        if (testTime)
            assertTrue("Execution time too long", end - start < 90000);

        multiTestInDirs(fileFormatsToCheckLC, basedirResults, basedirReference);
    }

    private void multiTestInDirs(Set<String> fileFormatsToCheck, String dirResults, String dirReference) {

        File resDir = new File(dirResults);

        // we check that the ref files are similar to the result files
        // we determine the files to check by inspecting the result files, then
        // doing the similarity test
        // TBD: this does not check that all reference files are contained by
        // the result
        File[] filesInResDir = resDir.listFiles();

        if (filesInResDir == null) {
            fail("Result directory " + dirResults + " is not a directory or an I/O error occurred.");
        } else {
            for (File fres : filesInResDir) {
                if (fres.isDirectory()) {
                    String pathAdd = File.separator + fres.getName();
                    multiTestInDirs(fileFormatsToCheck, dirResults + pathAdd, dirReference + pathAdd);
                } else {

                    String fresExtension = FilenameUtils.getExtension(fres.getName()).trim()
                            .toLowerCase(Locale.ENGLISH);

                    if (fileFormatsToCheck == null) {

                        if (fresExtension.equals("xsd") || fresExtension.equals("xml")
                                || fresExtension.equals("rdf")) {
                            similar(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName());
                        } else if (fresExtension.equals("html")) {
                            similarHtml(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName());
                        } else if (fresExtension.equals("docx")) {
                            similarDocx(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName());
                        } else if (fresExtension.equals("sql")) {
                            similarTxt(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName(), true, true);
                        } else {
                            // TBD add more similarity tests for further file
                            // formats, or add them to one of the above
                        }
                    } else {
                        if ((fresExtension.equals("xsd") && fileFormatsToCheck.contains("xsd"))
                                || (fresExtension.equals("xml") && fileFormatsToCheck.contains("xml"))
                                || (fresExtension.equals("rdf") && fileFormatsToCheck.contains("rdf"))) {
                            similar(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName());
                        } else if (fresExtension.equals("html") && fileFormatsToCheck.contains("html")) {
                            similarHtml(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName());
                        } else if (fresExtension.equals("docx") && fileFormatsToCheck.contains("docx")) {
                            similarDocx(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName());
                        } else if (fresExtension.equals("sql") && fileFormatsToCheck.contains("sql")) {
                            similarTxt(dirResults + File.separator + fres.getName(),
                                    dirReference + File.separator + fres.getName(), true, true);
                        } else {
                            // TBD add more similarity tests for further file
                            // formats, or add them to one of the above
                        }
                    }
                }
            }
        }
    }

    /**
     * Checks that two text files are similar. They must have the same number of
     * lines. Each line must be similar, which is checked via an equals test on
     * the line content. The content may be trimmed and whitespace normalized
     * before comparing if the parameters are set accordingly.
     * 
     * @param txtFileName
     * @param referenceTxtFileName
     * @param normalizeWhitespace
     *            true if all sequences of whitespace within a line shall be
     *            replaced by a single space character before comparing
     * @param trimEachLine
     *            <code>true</code> if whitespace surrounding lines shall be
     *            trimmed before comparing
     */
    private void similarTxt(String txtFileName, String referenceTxtFileName, boolean normalizeWhitespace,
            boolean trimEachLine) {

        List<String> lines = new ArrayList<String>();
        List<String> linesReference = new ArrayList<String>();

        // read files

        BufferedReader reader = null;
        try {
            InputStream stream = new FileInputStream(new File(txtFileName));
            reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
            String txt;
            while ((txt = reader.readLine()) != null) {
                lines.add(txt);
            }
        } catch (Exception e) {
            fail("Could not read file to compare at " + txtFileName);
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        BufferedReader readerReference = null;
        try {
            InputStream streamReference = new FileInputStream(new File(referenceTxtFileName));

            readerReference = new BufferedReader(new InputStreamReader(streamReference, "UTF-8"));
            String txtReference;
            while ((txtReference = readerReference.readLine()) != null) {
                linesReference.add(txtReference);
            }
        } catch (Exception e) {
            fail("Could not read reference file at " + referenceTxtFileName);
        } finally {
            try {
                if (readerReference != null) {
                    readerReference.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // compare

        if (lines.size() > linesReference.size()) {
            fail("Reference file has " + linesReference.size() + " lines. File to be compared has less lines("
                    + lines.size() + ").");
        } else if (lines.size() < linesReference.size()) {
            fail("Reference file has " + linesReference.size() + " lines. File to be compared has more lines("
                    + lines.size() + ").");
        } else {

            for (int i = 0; i < lines.size(); i++) {

                // get original line content
                String txt = lines.get(i);
                String txtReference = linesReference.get(i);

                /*
                 * copy line content so that modifications may be applied before
                 * comparing, without losing the original text
                 */
                String txtComp = txt;
                String txtCompRef = txtReference;

                if (trimEachLine) {
                    txtComp = txtComp.trim();
                    txtCompRef = txtCompRef.trim();
                }

                if (normalizeWhitespace) {
                    txtComp = txtComp.replaceAll("\\s+", " ");
                    txtCompRef = txtCompRef.replaceAll("\\s+", " ");
                }

                if (!txtComp.equals(txtCompRef)) {
                    fail("Mismatch in line " + (i + 1) + ". Expected: '" + txtReference + "'; found: '" + txt
                            + "'.");
                } else {
                    // fine, continue
                }
            }
        }
    }

    private void xsdTest(String config, String[] xsds, String[] schs, String basedirResults,
            String basedirReference) {
        xsdTest(config, xsds, schs, null, basedirResults, basedirReference, true);
    }

    private void xsdTest(String config, String[] xsds, String[] schs, HashMap<String, String> replacevalues,
            String basedirResults, String basedirReference) {
        xsdTest(config, xsds, schs, replacevalues, basedirResults, basedirReference, true);
    }

    private void xsdTest(String config, String[] xsds, String[] schs, String basedirResults,
            String basedirReference, boolean noErrors) {
        xsdTest(config, xsds, schs, null, basedirResults, basedirReference, noErrors);
    }

    private void xsdTest(String config, String[] xsds, String[] schs, HashMap<String, String> replacevalues,
            String basedirResults, String basedirReference, boolean noErrors) {
        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config, replacevalues);
        long end = (new Date()).getTime();
        System.out.println("Execution time " + config + ": " + (end - start) + "ms");
        if (noErrors)
            assertTrue("Test model execution failed", test.noError());
        if (testTime)
            assertTrue("Execution time too long", end - start < 90000);
        if (xsds != null)
            for (String xsd : xsds) {
                similar(basedirResults + "/" + xsd + ".xsd", basedirReference + "/" + xsd + ".xsd");
            }
        if (schs != null)
            for (String xsd : schs) {
                similar(basedirResults + "/" + xsd + ".xsd_SchematronSchema.xml",
                        basedirReference + "/" + xsd + ".xsd_SchematronSchema.xml");
            }
    }

    private void sqlTest(String config, String[] sqls, String basedirResults, String basedirReference) {
        sqlTest(config, sqls, null, basedirResults, basedirReference, true);
    }

    private void sqlTest(String config, String[] sqls, HashMap<String, String> replacevalues, String basedirResults,
            String basedirReference) {
        sqlTest(config, sqls, replacevalues, basedirResults, basedirReference, true);
    }

    private void sqlTest(String config, String[] sqls, String basedirResults, String basedirReference,
            boolean noErrors) {
        sqlTest(config, sqls, null, basedirResults, basedirReference, noErrors);
    }

    private void sqlTest(String config, String[] sqls, HashMap<String, String> replacevalues, String basedirResults,
            String basedirReference, boolean noErrors) {

        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config, replacevalues);
        long end = (new Date()).getTime();

        System.out.println("Execution time " + config + ": " + (end - start) + "ms");

        if (noErrors)
            assertTrue("Test model execution failed", test.noError());

        if (testTime)
            assertTrue("Execution time too long", end - start < 90000);

        if (sqls != null) {
            for (String sql : sqls) {
                similarTxt(basedirResults + "/" + sql + ".sql", basedirReference + "/" + sql + ".sql", true, true);
            }
        }
    }

    private void jsonTest(String config, String[] typenames, String basedirResults, String basedirReference) {
        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config);
        long end = (new Date()).getTime();
        System.out.println("Execution time " + config + ": " + (end - start) + "ms");
        assertTrue("Test model execution failed", test.noError());
        if (testTime)
            assertTrue("Exceution time too long", end - start < 60000);

        ObjectMapper m = new ObjectMapper();
        for (String typename : typenames) {
            try {
                JsonNode rootNodeResult = m.readValue(new File(basedirResults + "/test/" + typename + ".json"),
                        JsonNode.class);
                JsonNode rootNodeReference = m.readValue(new File(basedirReference + "/test/" + typename + ".json"),
                        JsonNode.class);
                boolean equal = rootNodeResult.equals(rootNodeReference);
                assertTrue("JSON output differs from reference result for type " + typename, equal);
            } catch (JsonParseException e) {
                fail("JSON Parse Exception: " + e.getMessage());
            } catch (JsonMappingException e) {
                fail("JSON Mapping Exception: " + e.getMessage());
            } catch (IOException e) {
                fail("IO Exception: " + e.getMessage());
            }
        }
    }

    private void rdfTest(String config, String[] rdfs, String basedirResults, String basedirReference) {
        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config);
        long end = (new Date()).getTime();
        System.out.println("Execution time " + config + ": " + (end - start) + "ms");
        assertTrue("Test model execution failed", test.noError());
        if (testTime)
            assertTrue("Execution time too long", end - start < 60000);
        if (rdfs != null)
            for (String rdf : rdfs) {
                similar(basedirResults + "/" + rdf + ".rdf", basedirReference + "/" + rdf + ".rdf");
            }
    }

    private void ttlTest(String config, String[] rdfs, String basedirResults, String basedirReference) {
        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config);
        long end = (new Date()).getTime();
        System.out.println("Execution time " + config + ": " + (end - start) + "ms");
        assertTrue("Test model execution failed", test.noError());
        if (testTime)
            assertTrue("Execution time too long", end - start < 60000);
        if (rdfs != null)
            for (String rdf : rdfs) {
                similar(basedirResults + "/" + rdf + ".ttl", basedirReference + "/" + rdf + ".ttl");
            }
    }

    private void htmlTest(String config, String[] htmls, String basedirResults, String basedirReference) {
        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config);
        long end = (new Date()).getTime();
        System.out.println("Execution time " + config + ": " + (end - start) + "ms");
        assertTrue("Test model execution failed", test.noError());
        if (testTime)
            assertTrue("Execution time too long", end - start < 60000);
        if (htmls != null)
            for (String htmlFileName : htmls) {
                similarHtml(basedirResults + "/" + htmlFileName + ".html",
                        basedirReference + "/" + htmlFileName + ".html");
            }
    }

    /**
     * Tests if there are differences in the contents of two html files.
     * 
     * Internally, the html files are converted to XHTML and then an XML diff is
     * performed.
     * 
     * @param htmlFileName
     * @param referenceHtmlFileName
     */
    private void similarHtml(String htmlFileName, String referenceHtmlFileName) {

        String myControlHTML = null;
        String myTestHTML = null;
        try {
            myControlHTML = readFile(referenceHtmlFileName);
        } catch (Exception e) {
            fail("Could not read " + referenceHtmlFileName);
        }
        try {
            myTestHTML = readFile(htmlFileName);
        } catch (Exception e) {
            fail("Could not read " + htmlFileName);
        }

        try {
            TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder = new TolerantSaxDocumentBuilder(
                    XMLUnit.newTestParser());
            HTMLDocumentBuilder htmlDocumentBuilder = new HTMLDocumentBuilder(tolerantSaxDocumentBuilder);

            Document wellFormedControlHTMLDocument = htmlDocumentBuilder.parse(myControlHTML);
            Document wellFormedTestHTMLDocument = htmlDocumentBuilder.parse(myTestHTML);

            Diff myDiff = new Diff(wellFormedControlHTMLDocument, wellFormedTestHTMLDocument);
            assertTrue("HTML: " + htmlFileName + " similar to " + referenceHtmlFileName + " - " + myDiff.toString(),
                    myDiff.similar());
        } catch (Exception e) {
            fail("Could not compare " + htmlFileName + " and " + referenceHtmlFileName);
        }
    }

    private void docxTest(String config, String[] docxs, String basedirResults, String basedirReference) {
        long start = (new Date()).getTime();
        TestInstance test = new TestInstance(config);
        long end = (new Date()).getTime();
        System.out.println("Execution time " + config + ": " + (end - start) + "ms");
        assertTrue("Test model execution failed", test.noError());
        if (testTime)
            assertTrue("Execution time too long", end - start < 60000);
        if (docxs != null)
            for (String docxFileName : docxs) {
                similarDocx(basedirResults + "/" + docxFileName + ".docx",
                        basedirReference + "/" + docxFileName + ".docx");
            }
    }

    /**
     * Tests if there are differences in the contents of two docx files.
     * 
     * Currently only tests the internal word/document.xml.
     * 
     * WARNING: word will most likely modify the content of a docx file
     * generated by ShapeChange when it is saved by word! This means that the
     * reference docx file must be the file generated by ShapeChange, WITHOUT it
     * having been opened in and saved by word.
     * 
     * @param docxFileName
     * @param referenceDocxFileName
     */
    private void similarDocx(String docxFileName, String referenceDocxFileName) {

        ZipHandler zipHandler = new ZipHandler();

        File tmpDir = null;

        try {
            File myControlFile = new File(referenceDocxFileName);
            File myTestFile = new File(docxFileName);

            // create temporary directories
            File myTestFileDir = myTestFile.getParentFile();
            tmpDir = new File(myTestFileDir, "docxUnitTest.tmp");
            File tmpDirUnzippedTestFile = new File(tmpDir, "test");
            File tmpDirUnzippedControlFile = new File(tmpDir, "control");

            // unzip docx files
            zipHandler.unzip(myControlFile, tmpDirUnzippedControlFile);
            zipHandler.unzip(myTestFile, tmpDirUnzippedTestFile);

            // perform diff
            // TODO: at the moment only compares the document.xml - this could
            // also test all docx-internal files
            File testDocument = new File(tmpDirUnzippedTestFile, "word/document.xml");
            File controlDocument = new File(tmpDirUnzippedControlFile, "word/document.xml");

            Reader testFileReader = new FileReader(testDocument);
            Reader controlFileReader = new FileReader(controlDocument);

            Diff myDiff = new Diff(controlFileReader, testFileReader);
            assertTrue("DOCX: " + docxFileName + " similar to " + referenceDocxFileName + " - " + myDiff.toString(),
                    myDiff.similar());

            // finally, delete the temporary directory
            // -> handled in 'finally' clause

        } catch (Exception e) {
            fail("Could not compare " + docxFileName + " and " + referenceDocxFileName);
        } finally {
            if (tmpDir != null && tmpDir.exists()) {
                try {
                    FileUtils.deleteDirectory(tmpDir);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void similar(String xsdFileName, String referenceXsdFileName) {
        String myControlXML = null;
        String myTestXML = null;
        try {
            myControlXML = readFile(referenceXsdFileName);
        } catch (Exception e) {
            fail("Could not read " + referenceXsdFileName);
        }
        try {
            myTestXML = readFile(xsdFileName);
        } catch (Exception e) {
            fail("Could not read " + xsdFileName);
        }
        try {
            Diff myDiff = new Diff(myControlXML, myTestXML);
            assertTrue("XML: " + xsdFileName + " similar to " + referenceXsdFileName + " - " + myDiff.toString(),
                    myDiff.similar());
        } catch (Exception e) {
            fail("Could not compare " + xsdFileName + " and " + referenceXsdFileName);
        }
    }

    private String readFile(String fileName) throws Exception {
        InputStream stream = new FileInputStream(new File(fileName));

        Writer writer = new StringWriter();
        char[] buffer = new char[1024];
        Reader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
            int n;
            while ((n = reader.read(buffer)) != -1) {
                writer.write(buffer, 0, n);
            }
        } finally {
            if (reader != null)
                reader.close();
        }
        return writer.toString();
    }

    @SuppressWarnings("unused")
    private void validate(String xmlFileName, String xsdFileName) {
        Validator v = new Validator();

        v.addSchemaSource(new StreamSource(new File(xsdFileName)));
        if (!v.isSchemaValid()) {
            for (Object o : v.getSchemaErrors()) {
                SAXParseException error = (SAXParseException) o;
                if (error != null)
                    fail("Validation of " + xsdFileName + " failed. First error: " + error.getMessage());
            }
        } else {
            StreamSource is = new StreamSource(new File(xmlFileName));
            if (!v.isInstanceValid(is)) {
                for (Object o : v.getInstanceErrors(is)) {
                    SAXParseException error = (SAXParseException) o;
                    if (error != null)
                        fail("Validation of " + xmlFileName + " failed against " + xsdFileName + ". First error: "
                                + error.getMessage());
                }
            }
        }
    }

    private void initialise() {
        System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
                "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
        System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.xerces.jaxp.SAXParserFactoryImpl");
        System.setProperty("javax.xml.transform.TransformerFactory",
                "org.apache.xalan.processor.TransformerFactoryImpl");

        XMLUnit.setIgnoreComments(true);
        XMLUnit.setNormalizeWhitespace(true);
    }

}