/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2015, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotoolkit.jdbc.html; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import javax.net.ssl.HttpsURLConnection; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.sis.test.DependsOnMethod; import org.apache.sis.test.TestCase; import org.apache.sis.util.logging.Logging; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Test dom utility methods from {@link HtmlBuilder}. * * @author Alexis Manin (Geomatys) */ public class HtmlBuilderTest extends TestCase { private static URL W3C_VALIDATOR; private static Path TEMP_DIR; @BeforeClass public static void init() throws IOException { W3C_VALIDATOR = new URL("https://validator.w3.org/nu/?out=gnu&level=error"); TEMP_DIR = Files.createTempDirectory("HtmlBuilderTest"); } @AfterClass public static void deleteTempDir() { try { Files.walkFileTree(TEMP_DIR, new SimpleFileVisitor<Path>() { @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); return super.postVisitDirectory(dir, exc); } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return super.visitFile(file, attrs); } }); } catch (IOException e) { Logging.getLogger("org.geotoolkit.tests").log(Level.WARNING, "Cannot delete temporary directory used for tests.", e); } } @Test public void testAppendChild() throws Exception { final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); final String tagName = "bouh"; HtmlBuilder.appendChild(doc, doc, tagName); Assert.assertEquals("Node has not been added to target document.", 1, doc.getElementsByTagName(tagName).getLength()); } @Test @DependsOnMethod("testAppendChild") public void testNewHtmlDocument() throws Exception { final String title = "myHtml"; Document htmlDoc = HtmlBuilder.newHtmlDocument(DocumentBuilderFactory.newInstance().newDocumentBuilder(), title); assertValidHtmlDocument(htmlDoc, title); } @Test @DependsOnMethod("testNewHtmlDocument") public void testWriteDocument() throws Exception { final String title = "TEST"; final DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); final Path tmpFile = Files.createTempFile(TEMP_DIR, "test", ".html"); HtmlBuilder.write( HtmlBuilder.newHtmlDocument(docBuilder, title), tmpFile, HtmlBuilder.createHtmlTransformer()); validateHtml(tmpFile); } @Test public void testNameComparator() throws Exception { final ArrayList<String> var = new ArrayList<>(); var.add("verts"); var.add(HtmlBuilder.DEFAULT_NAME); var.add("allez"); var.add("les"); Collections.sort(var, new HtmlBuilder.NameComparator()); final StringBuilder result = new StringBuilder(); result.append(var.get(0)); for (int i = 1 ; i < var.size() ; i++) { result.append(' ').append(var.get(i)); } Assert.assertEquals("Input list has not been sorted alphabetically !", "allez les verts "+HtmlBuilder.DEFAULT_NAME, result.toString()); } @Test public void testWrongParameters() throws Exception { final Path notDir = Files.createTempFile(TEMP_DIR, "notDir", "html"); final HtmlBuilder builder = new HtmlBuilder(); try { builder.setOutput(notDir); Assert.fail("Html builder should not accept concrete files as output."); } catch (IOException e) { // good behavior } builder.setSource(createMockConnection()); final String badFilter = "unexisting"; try { builder.setCatalog(badFilter); Assert.fail("Html builder should not accept a catalog which is not present in target database."); } catch (IllegalArgumentException e) { // good behavior } try { builder.setSchema(badFilter); Assert.fail("Html builder should not accept a schema which is not present in target database."); } catch (IllegalArgumentException e) { // good behavior } } @Test @DependsOnMethod("testWriteDocument") public void testBuild() throws Exception { final Path myDir = Files.createTempDirectory(TEMP_DIR, "myDear"); final Connection con = createMockConnection(); // populate database try (final PreparedStatement statement = con.prepareStatement("CREATE TABLE TOTO (" + "ID INT PRIMARY KEY NOT NULL," + " A_NUMBER INT DEFAULT 0)"); ) { statement.execute(); } try (final PreparedStatement statement = con.prepareStatement("CREATE TABLE TATA (" + "NAME VARCHAR(65) NOT NULL," + " BIRTH DATE NOT NULL," + " NUMBER INT DEFAULT 0," + " TOTO_ID INT," + " CONSTRAINT FK_TOTO FOREIGN KEY (TOTO_ID) REFERENCES TOTO(ID)," + " PRIMARY KEY (NAME, BIRTH))")) { statement.execute(); } Path result = new HtmlBuilder().setSource(con).setOutput(myDir).build(); final AtomicBoolean totoFound = new AtomicBoolean(false); final AtomicBoolean tataFound = new AtomicBoolean(false); Files.walkFileTree(result.getParent(), new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { final String[] name = file.getFileName().toString().split("\\."); if (name[1].equalsIgnoreCase("html")) { // If we found an html document, we ensure it's a valid one. try { validateHtml(file); } catch (MalformedURLException ex) { throw new IllegalStateException(ex); } switch (name[0]) { case "TOTO": totoFound.set(true); break; case "TATA": tataFound.set(true); break; } } return super.visitFile(file, attrs); } }); Assert.assertTrue("No html created for table TOTO", totoFound.get()); Assert.assertTrue("No html created for table TATA", tataFound.get()); } private static void validateHtml(final Path htmlDoc) throws MalformedURLException, IOException { HttpsURLConnection con = (HttpsURLConnection) W3C_VALIDATOR.openConnection(); con.setRequestMethod("POST"); con.addRequestProperty("Content-type", "text/html; charset=UTF-8"); con.setDoOutput(true); try (final OutputStream stream = con.getOutputStream()) { stream.write(Files.readAllBytes(htmlDoc)); } catch (IOException e) { Assume.assumeNoException("Cannot reach W3C validator.", e); } final StringBuilder builder = new StringBuilder(); int read; try (final InputStream stream = con.getInputStream()) { while ((read = stream.read()) >= 0) builder.append((char)read); } catch (IOException e) { Assume.assumeNoException("Cannot reach W3C validator.", e); } if (builder.length() > 0) { builder.insert(0, System.lineSeparator()) .insert(0, htmlDoc.toString()) .insert(0, "An error has been detected on a document : "); Assert.fail(builder.toString()); } } private static void assertValidHtmlDocument(final Document htmlDoc, final String title) throws Exception { // Check generic structure Assert.assertEquals("Generated doc should have a single <html> markup.", 1, htmlDoc.getElementsByTagName("html").getLength()); Assert.assertEquals("Generated doc should have a single <head> markup.", 1, htmlDoc.getElementsByTagName("head").getLength()); Assert.assertEquals("Generated doc should have a single <body> markup.", 1, htmlDoc.getElementsByTagName("body").getLength()); // Ensure title has been put final NodeList titles = htmlDoc.getElementsByTagName("title"); Assert.assertEquals("Generated doc should have a single <title> markup.", 1, titles.getLength()); final Node titleNode = titles.item(0); Assert.assertEquals("Title markup has not been set correctly !", title, titleNode.getTextContent()); final Node headNode = titleNode.getParentNode(); Assert.assertNotNull("Title has not been put in <head> markup !", headNode); Assert.assertEquals("Title has not been put in <head> markup !", "head", headNode.getNodeName()); // Now that we have head node, we ensure it is into html markup. final Node htmlNode = headNode.getParentNode(); Assert.assertNotNull("Head has not been put in <html> markup !", htmlNode); Assert.assertEquals("Head has not been put in <html> markup !", "html", htmlNode.getNodeName()); // And now, we can ensure body is the second child of html node Assert.assertTrue("Html document incomplete !", htmlNode.getChildNodes().getLength() > 1); Node body = htmlNode.getChildNodes().item(1); Assert.assertEquals("Body has not been put in <html> markup !", "body", body.getNodeName()); } /** * Note : See MetadataWriter test to open a memory connection on derby. * @return A simple connection pointing on an empty database in memory, to test db analysis. */ private static Connection createMockConnection() throws SQLException { return DriverManager.getConnection("jdbc:derby:memory:Test;create=true"); } }