package com.occamlab.te.spi.ctl; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.Seq; import org.apache.jena.vocabulary.DCTerms; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.occamlab.te.spi.vocabulary.CITE; import com.occamlab.te.spi.vocabulary.CONTENT; import com.occamlab.te.spi.vocabulary.EARL; import com.occamlab.te.spi.vocabulary.HTTP; public class CtlEarlReporter { private String langCode = "en"; private Resource testRun; private int resultCount = 0; private Resource assertor; private Resource testSubject; private Model earlModel; private Seq reqs; private int cPassCount; private int cFailCount; private int cSkipCount; private int cContinueCount; private int cBestPracticeCount; private int cNotTestedCount; private int cWarningCount; private int cInheritedFailureCount; public CtlEarlReporter() { this.earlModel = ModelFactory.createDefaultModel(); } public void generateEarlReport(File outputDirectory, File reportFile, String suiteName, String iut) throws UnsupportedEncodingException { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); docFactory.setNamespaceAware(true); docFactory.setXIncludeAware(true); // Source results = null; Document document; try { document = docFactory.newDocumentBuilder().parse(reportFile); } catch (IOException | SAXException | ParserConfigurationException e) { throw new RuntimeException(e); } document.getDocumentElement().normalize(); // Here comes the root node Element root = document.getDocumentElement(); Model model = initializeModel(suiteName, iut); this.reqs = model.createSeq(); NodeList executionList = document.getElementsByTagName("execution"); for (int temp = 0; temp < executionList.getLength(); temp++) { Node executionNode = executionList.item(temp); Element executionElement = (Element) executionNode; NodeList logList = executionElement.getElementsByTagName("log"); Element logElement = (Element) logList.item(0); NodeList starttestList = logElement.getElementsByTagName("starttest"); Element starttestElement = (Element) starttestList.item(0); /* * Get list of the <testcall> element. */ NodeList testcallList = logElement.getElementsByTagName("testcall"); /* * Get the subtest result recursively. */ getSubtestResult(model, testcallList, logList, starttestElement.getAttribute("local-name")); } this.testRun.addProperty(CITE.requirements, this.reqs); this.earlModel.add(model); try { writeModel(this.earlModel, outputDirectory, true); } catch (IOException iox) { throw new RuntimeException("Failed to serialize EARL results to " + outputDirectory.getAbsolutePath(), iox); } } public void getSubtestResult(Model model, NodeList testcallList, NodeList logList, String fTestname) throws UnsupportedEncodingException { String conformanceClass = ""; for (int k = 0; k < testcallList.getLength(); k++) { // Get current testcall element path attribute String testcallPath = ""; Element testcallElement = (Element) testcallList.item(k); testcallPath = testcallElement.getAttribute("path"); // Iterate the log element list. for (int j = 0; j < logList.getLength(); j++) { Element logElements = (Element) logList.item(j); String decodedBaseURL = ""; decodedBaseURL = java.net.URLDecoder.decode(logElements.getAttribute("xml:base"), "UTF-8"); String pattern = Pattern.quote(System.getProperty("file.separator")); String[] decodedBaseURLArr = decodedBaseURL.split(pattern); String output = ""; for (int a = 3; a < decodedBaseURLArr.length; a++) { output = output + "\\" + decodedBaseURLArr[a]; } String logtestcall = output.substring(output.indexOf("\\") + 1, output.lastIndexOf("\\")).replace("\\", "/"); // Check sub-testcall is matching with the <log baseURL=""> if (testcallPath.equals(logtestcall)) { Map<String, String> testinfo = getTestinfo(logElements); if (testinfo.get("isConformanceClass").equals("true")) { System.out.println(" The test '" + testinfo.get("testName") + "' is the conformance class and BASE URL is= " + decodedBaseURL); conformanceClass = testinfo.get("testName"); this.cPassCount = 0; this.cFailCount = 0; this.cSkipCount = 0; this.cContinueCount = 0; this.cBestPracticeCount = 0; this.cNotTestedCount = 0; this.cWarningCount = 0; this.cInheritedFailureCount = 0; addTestRequirements(model, testinfo.get("testName")); } /* * Process Test Result */ processTestResults(model, logElements, logList, logtestcall, conformanceClass); Resource testReq = model.createResource(conformanceClass); testReq.addLiteral(CITE.testsPassed, new Integer(this.cPassCount)); testReq.addLiteral(CITE.testsFailed, new Integer(this.cFailCount)); testReq.addLiteral(CITE.testsSkipped, new Integer(this.cSkipCount)); testReq.addLiteral(CITE.testsContinue, new Integer(this.cContinueCount)); testReq.addLiteral(CITE.testsBestPractice, new Integer(this.cBestPracticeCount)); testReq.addLiteral(CITE.testsNotTested, new Integer(this.cNotTestedCount)); testReq.addLiteral(CITE.testsWarning, new Integer(this.cWarningCount)); testReq.addLiteral(CITE.testsInheritedFailure, new Integer(this.cInheritedFailureCount)); break; } // end of sub-testcall } } } public Map<String, String> getTestinfo(Element logElements) { Map<String, String> attr = new HashMap<String, String>(); NodeList starttestLists = logElements.getElementsByTagName("starttest"); Element starttestElements = (Element) starttestLists.item(0); Element endtestElements = (Element) logElements.getElementsByTagName("endtest").item(0); attr.put("testName", starttestElements.getAttribute("local-name")); attr.put("result", endtestElements.getAttribute("result")); NodeList isConformanceClassList = logElements.getElementsByTagName("conformanceClass"); String isCC = (isConformanceClassList.getLength() > 0) ? "true" : "false"; attr.put("isConformanceClass", isCC); return attr; } Model initializeModel(String suiteName, String iut) { Model model = ModelFactory.createDefaultModel(); Map<String, String> nsBindings = new HashMap<>(); nsBindings.put("earl", EARL.NS_URI); nsBindings.put("dct", DCTerms.NS); nsBindings.put("cite", CITE.NS_URI); nsBindings.put("http", HTTP.NS_URI); nsBindings.put("cnt", CONTENT.NS_URI); model.setNsPrefixes(nsBindings); this.testRun = model.createResource(CITE.TestRun); this.testRun.addProperty(DCTerms.title, suiteName); String nowUTC = ZonedDateTime.now(ZoneId.of("Z")).format(DateTimeFormatter.ISO_INSTANT); this.testRun.addProperty(DCTerms.created, nowUTC); this.assertor = model.createResource("https://github.com/opengeospatial/teamengine", EARL.Assertor); this.assertor.addProperty(DCTerms.title, "OGC TEAM Engine", this.langCode); this.assertor.addProperty(DCTerms.description, "Official test harness of the OGC conformance testing program (CITE).", this.langCode); /* * Map<String, String> params = suite.getXmlSuite().getAllParameters(); * String iut = params.get("iut"); if (null == iut) { // non-default * parameter refers to test subject--use first URI value for * (Map.Entry<String, String> param : params.entrySet()) { try { URI uri * = URI.create(param.getValue()); iut = uri.toString(); } catch * (IllegalArgumentException e) { continue; } } } if (null == iut) { * throw new * NullPointerException("Unable to find URI reference for IUT in test run parameters." * ); } */ this.testSubject = model.createResource(iut, EARL.TestSubject); return model; } /* * Add TestRequirements */ void addTestRequirements(Model earl, String testName) { Resource testReq = earl.createResource(testName.replaceAll("\\s", "-"), EARL.TestRequirement); testReq.addProperty(DCTerms.title, testName); this.reqs.add(testReq); } /* * Process child tests of Conformance Class and call same method recursively * if it has the child tests. * */ public void processTestResults(Model earl, Element logElements, NodeList logList, String logtestcallPath, String conformanceClass) throws UnsupportedEncodingException { NodeList childtestcallList = logElements.getElementsByTagName("testcall"); String testcallPath; Element childlogElements = null; Map<String, String> testDetails; String childLogtestcall = ""; for (int l = 0; l < childtestcallList.getLength(); l++) { Element childtestcallElement = (Element) childtestcallList.item(l); testcallPath = childtestcallElement.getAttribute("path"); for (int m = 0; m < logList.getLength(); m++) { childlogElements = (Element) logList.item(m); String decodedBaseURL = java.net.URLDecoder.decode(childlogElements.getAttribute("xml:base"), "UTF-8"); String pattern = Pattern.quote(System.getProperty("file.separator")); String[] decodedBaseURLArr = decodedBaseURL.split(pattern); String output = ""; for (int a = 3; a < decodedBaseURLArr.length; a++) { output = output + "\\" + decodedBaseURLArr[a]; } childLogtestcall = output.substring(output.indexOf("\\") + 1, output.lastIndexOf("\\")).replace("\\", "/"); if (testcallPath.equals(childLogtestcall)) { break; } } if (!childlogElements.equals(null)) { testDetails = getTestinfo(childlogElements); } else { throw new NullPointerException("Failed to get Test-Info due to null log element."); } // create earl:Assertion GregorianCalendar calTime = new GregorianCalendar(TimeZone.getDefault()); Resource assertion = earl.createResource("assert-" + ++this.resultCount, EARL.Assertion); assertion.addProperty(EARL.mode, EARL.AutomaticMode); assertion.addProperty(EARL.assertedBy, this.assertor); assertion.addProperty(EARL.subject, this.testSubject); // link earl:TestResult to earl:Assertion Resource earlResult = earl.createResource("result-" + this.resultCount, EARL.TestResult); earlResult.addProperty(DCTerms.date, earl.createTypedLiteral(calTime)); int res = Integer.parseInt(testDetails.get("result")); switch (res) { case 0: earlResult.addProperty(EARL.outcome, CITE.Continue); this.cContinueCount++; break; case 2: earlResult.addProperty(EARL.outcome, CITE.Not_Tested); this.cNotTestedCount++; break; case 6: // Fail earlResult.addProperty(EARL.outcome, EARL.Fail); this.cFailCount++; break; case 3: earlResult.addProperty(EARL.outcome, EARL.NotTested); this.cSkipCount++; break; case 4: earlResult.addProperty(EARL.outcome, CITE.Warning); this.cWarningCount++; break; case 5: earlResult.addProperty(EARL.outcome, CITE.Inherited_Failure); this.cWarningCount++; break; default: earlResult.addProperty(EARL.outcome, EARL.Pass); break; } assertion.addProperty(EARL.result, earlResult); // link earl:TestCase to earl:Assertion and earl:TestRequirement String testName = testDetails.get("testName"); StringBuilder testCaseId = new StringBuilder(childLogtestcall); testCaseId.append('#').append(testName); Resource testCase = earl.createResource(testCaseId.toString(), EARL.TestCase); testCase.addProperty(DCTerms.title, testName); testCase.addProperty(DCTerms.description, "Test satisfies the OGC specification"); assertion.addProperty(EARL.test, testCase); earl.createResource(conformanceClass).addProperty(DCTerms.hasPart, testCase); NodeList testcallLists = childtestcallElement.getElementsByTagName("testcall"); if (testcallLists.getLength() > 0) { processTestResults(earl, childtestcallElement, logList, childLogtestcall, conformanceClass); } } } /* * Write CTL result into EARL report. */ private void writeModel(Model earlModel2, File outputDirectory, boolean abbreviated) throws IOException { File outputFile = new File(outputDirectory, "earl-results.rdf"); if (!outputFile.createNewFile()) { outputFile.delete(); outputFile.createNewFile(); } String syntax = (abbreviated) ? "RDF/XML-ABBREV" : "RDF/XML"; String baseUri = new StringBuilder("http://example.org/earl/").append(outputDirectory.getName()).append('/') .toString(); OutputStream outStream = new FileOutputStream(outputFile); try (Writer writer = new OutputStreamWriter(outStream, StandardCharsets.UTF_8)) { earlModel2.write(writer, syntax, baseUri); } } }