| /* |
| * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code 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 |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package jaxp.library; |
| |
| import static org.testng.Assert.fail; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.StringWriter; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.charset.Charset; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.charset.UnsupportedCharsetException; |
| import java.nio.file.Files; |
| import java.nio.file.Paths; |
| import java.security.Permission; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.function.Supplier; |
| import java.util.regex.Pattern; |
| import java.util.stream.Collectors; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Node; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * This is an interface provide basic support for JAXP functional test. |
| */ |
| public class JAXPTestUtilities { |
| /** |
| * Prefix for error message. |
| */ |
| public static final String ERROR_MSG_HEADER = "Unexcepted exception thrown:"; |
| |
| /** |
| * Prefix for error message on clean up block. |
| */ |
| public static final String ERROR_MSG_CLEANUP = "Clean up failed on %s"; |
| |
| /** |
| * Force using slash as File separator as we always use cygwin to test in |
| * Windows platform. |
| */ |
| public static final String FILE_SEP = "/"; |
| |
| /** |
| * A map storing every test's current test file pointer. File number should |
| * be incremental and it's a thread-safe reading on this file number. |
| */ |
| private static final ConcurrentHashMap<Class<?>, Integer> currentFileNumber |
| = new ConcurrentHashMap<>(); |
| |
| /** |
| * BOM table for storing BOM header. |
| */ |
| private final static Map<String, byte[]> bom = new HashMap<>(); |
| |
| /** |
| * Initialize all BOM headers. |
| */ |
| static { |
| bom.put("UTF-8", new byte[]{(byte)0xEF, (byte) 0xBB, (byte) 0xBF}); |
| bom.put("UTF-16BE", new byte[]{(byte)0xFE, (byte)0xFF}); |
| bom.put("UTF-16LE", new byte[]{(byte)0xFF, (byte)0xFE}); |
| bom.put("UTF-32BE", new byte[]{(byte)0x00, (byte)0x00, (byte)0xFE, (byte)0xFF}); |
| bom.put("UTF-32LE", new byte[]{(byte)0xFF, (byte)0xFE, (byte)0x00, (byte)0x00}); |
| } |
| |
| /** |
| * Compare contents of golden file with test output file line by line. |
| * return true if they're identical. |
| * @param goldfile Golden output file name |
| * @param outputfile Test output file name |
| * @return true if two files are identical. |
| * false if two files are not identical. |
| * @throws IOException if an I/O error occurs reading from the file or a |
| * malformed or unmappable byte sequence is read. |
| */ |
| public static boolean compareWithGold(String goldfile, String outputfile) |
| throws IOException { |
| return compareWithGold(goldfile, outputfile, StandardCharsets.UTF_8); |
| } |
| |
| /** |
| * Compare contents of golden file with test output file line by line. |
| * return true if they're identical. |
| * @param goldfile Golden output file name. |
| * @param outputfile Test output file name. |
| * @param cs the charset to use for decoding. |
| * @return true if two files are identical. |
| * false if two files are not identical. |
| * @throws IOException if an I/O error occurs reading from the file or a |
| * malformed or unmappable byte sequence is read. |
| */ |
| public static boolean compareWithGold(String goldfile, String outputfile, |
| Charset cs) throws IOException { |
| boolean isSame = Files.readAllLines(Paths.get(goldfile)). |
| equals(Files.readAllLines(Paths.get(outputfile), cs)); |
| if (!isSame) { |
| System.err.println("Golden file " + goldfile + " :"); |
| Files.readAllLines(Paths.get(goldfile)).forEach(System.err::println); |
| System.err.println("Output file " + outputfile + " :"); |
| Files.readAllLines(Paths.get(outputfile), cs).forEach(System.err::println); |
| } |
| return isSame; |
| } |
| |
| /** |
| * Compare contents of golden file with test output list line by line. |
| * return true if they're identical. |
| * @param goldfile Golden output file name. |
| * @param lines test output list. |
| * @return true if file's content is identical to given list. |
| * false if file's content is not identical to given list. |
| * @throws IOException if an I/O error occurs reading from the file or a |
| * malformed or unmappable byte sequence is read |
| */ |
| public static boolean compareLinesWithGold(String goldfile, List<String> lines) |
| throws IOException { |
| return Files.readAllLines(Paths.get(goldfile)).equals(lines); |
| } |
| |
| /** |
| * Compare contents of golden file with a test output string. |
| * return true if they're identical. |
| * @param goldfile Golden output file name. |
| * @param string test string. |
| * @return true if file's content is identical to given string. |
| * false if file's content is not identical to given string. |
| * @throws IOException if an I/O error occurs reading from the file or a |
| * malformed or unmappable byte sequence is read |
| */ |
| public static boolean compareStringWithGold(String goldfile, String string) |
| throws IOException { |
| return Files.readAllLines(Paths.get(goldfile)).stream().collect( |
| Collectors.joining(System.getProperty("line.separator"))) |
| .equals(string); |
| } |
| |
| /** |
| * Compare contents of golden file with test output file by their document |
| * representation. |
| * Here we ignore the white space and comments. return true if they're |
| * lexical identical. |
| * @param goldfile Golden output file name. |
| * @param resultFile Test output file name. |
| * @return true if two file's document representation are identical. |
| * false if two file's document representation are not identical. |
| * @throws javax.xml.parsers.ParserConfigurationException if the |
| * implementation is not available or cannot be instantiated. |
| * @throws SAXException If any parse errors occur. |
| * @throws IOException if an I/O error occurs reading from the file or a |
| * malformed or unmappable byte sequence is read . |
| */ |
| public static boolean compareDocumentWithGold(String goldfile, String resultFile) |
| throws ParserConfigurationException, SAXException, IOException { |
| DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| factory.setNamespaceAware(true); |
| factory.setCoalescing(true); |
| factory.setIgnoringElementContentWhitespace(true); |
| factory.setIgnoringComments(true); |
| DocumentBuilder db = factory.newDocumentBuilder(); |
| |
| Document goldD = db.parse(Paths.get(goldfile).toFile()); |
| goldD.normalizeDocument(); |
| Document resultD = db.parse(Paths.get(resultFile).toFile()); |
| resultD.normalizeDocument(); |
| return goldD.isEqualNode(resultD); |
| } |
| |
| /** |
| * Compare contents of golden file with the serialization represent by given |
| * DOM node. |
| * Here we ignore the white space and comments. return true if they're |
| * lexical identical. |
| * @param goldfile Golden output file name. |
| * @param node A DOM node instance. |
| * @return true if file's content is identical to given node's serialization |
| * represent. |
| * false if file's content is not identical to given node's |
| * serialization represent. |
| * @throws TransformerException If an unrecoverable error occurs during the |
| * course of the transformation.. |
| * @throws IOException if an I/O error occurs reading from the file or a |
| * malformed or unmappable byte sequence is read . |
| */ |
| public static boolean compareSerializeDOMWithGold(String goldfile, Node node) |
| throws TransformerException, IOException { |
| TransformerFactory factory = TransformerFactory.newInstance(); |
| // Use identity transformer to serialize |
| Transformer identityTransformer = factory.newTransformer(); |
| StringWriter sw = new StringWriter(); |
| StreamResult streamResult = new StreamResult(sw); |
| DOMSource nodeSource = new DOMSource(node); |
| identityTransformer.transform(nodeSource, streamResult); |
| return compareStringWithGold(goldfile, sw.toString()); |
| } |
| |
| /** |
| * Convert stream to ByteArrayInputStream by given character set. |
| * @param charset target character set. |
| * @param file a file that contains no BOM head content. |
| * @return a ByteArrayInputStream contains BOM heads and bytes in original |
| * stream |
| * @throws IOException I/O operation failed or unsupported character set. |
| */ |
| public static InputStream bomStream(String charset, String file) |
| throws IOException { |
| String localCharset = charset; |
| if (charset.equals("UTF-16") || charset.equals("UTF-32")) { |
| localCharset |
| += ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? "BE" : "LE"; |
| } |
| if (!bom.containsKey(localCharset)) |
| throw new UnsupportedCharsetException("Charset:" + localCharset); |
| |
| byte[] content = Files.readAllLines(Paths.get(file)).stream(). |
| collect(Collectors.joining()).getBytes(localCharset); |
| byte[] head = bom.get(localCharset); |
| ByteBuffer bb = ByteBuffer.allocate(content.length + head.length); |
| bb.put(head); |
| bb.put(content); |
| return new ByteArrayInputStream(bb.array()); |
| } |
| |
| /** |
| * Worker method to detect common absolute URLs. |
| * |
| * @param s String path\filename or URL (or any, really) |
| * @return true if s starts with a common URI scheme (namely |
| * the ones found in the examples of RFC2396); false otherwise |
| */ |
| protected static boolean isCommonURL(String s) { |
| if (null == s) |
| return false; |
| return Pattern.compile("^(file:|http:|ftp:|gopher:|mailto:|news:|telnet:)") |
| .matcher(s).matches(); |
| } |
| |
| /** |
| * Utility method to translate a String filename to URL. |
| * |
| * If the name starts with a common URI scheme (namely the ones |
| * found in the examples of RFC2396), then simply return the |
| * name as-is (the assumption is that it's already a URL). |
| * Otherwise we attempt (cheaply) to convert to a file:/ URL. |
| * |
| * @param filename local path/filename of a file. |
| * @return a file:/ URL if filename represent a file, the same string if |
| * it appears to already be a URL. |
| */ |
| public static String filenameToURL(String filename) { |
| return Paths.get(filename).toUri().toASCIIString(); |
| } |
| |
| /** |
| * Prints error message if an exception is thrown |
| * @param ex The exception is thrown by test. |
| */ |
| public static void failUnexpected(Throwable ex) { |
| fail(ERROR_MSG_HEADER, ex); |
| } |
| |
| /** |
| * Prints error message if an exception is thrown when clean up a file. |
| * @param ex The exception is thrown in cleaning up a file. |
| * @param name Cleaning up file name. |
| */ |
| public static void failCleanup(IOException ex, String name) { |
| fail(String.format(ERROR_MSG_CLEANUP, name), ex); |
| } |
| |
| /** |
| * Retrieve next test output file name. This method is a thread-safe method. |
| * @param clazz test class. |
| * @return next test output file name. |
| */ |
| public static String getNextFile(Class<?> clazz) { |
| int nextNumber = currentFileNumber.contains(clazz) |
| ? currentFileNumber.get(clazz) + 1 : 1; |
| Integer i = currentFileNumber.putIfAbsent(clazz, nextNumber); |
| if (i != null) { |
| do { |
| nextNumber = currentFileNumber.get(clazz) + 1; |
| } while (!currentFileNumber.replace(clazz, nextNumber - 1, nextNumber)); |
| } |
| return USER_DIR + clazz.getName() + nextNumber + ".out"; |
| } |
| |
| /** |
| * Acquire a full path string by given class name and relative path string. |
| * @param clazz Class name for the test. |
| * @param relativeDir relative path between java source file and expected |
| * path. |
| * @return a string represents the full path of accessing path. |
| */ |
| public static String getPathByClassName(Class<?> clazz, String relativeDir) { |
| String javaSourcePath = System.getProperty("test.src").replaceAll("\\" + File.separator, FILE_SEP); |
| String normalizedPath = Paths.get(javaSourcePath, relativeDir).normalize(). |
| toAbsolutePath().toString(); |
| return normalizedPath.replace("\\", FILE_SEP) + FILE_SEP; |
| } |
| |
| |
| /** |
| * Run the supplier with all permissions. This won't impact global policy. |
| * |
| * @param s |
| * Supplier to run |
| */ |
| public static <T> T runWithAllPerm(Supplier<T> s) { |
| Optional<JAXPPolicyManager> policyManager = Optional.ofNullable(JAXPPolicyManager |
| .getJAXPPolicyManager(false)); |
| policyManager.ifPresent(manager -> manager.setAllowAll(true)); |
| try { |
| return s.get(); |
| } finally { |
| policyManager.ifPresent(manager -> manager.setAllowAll(false)); |
| } |
| } |
| |
| /** |
| * Run the supplier with all permissions. This won't impact global policy. |
| * |
| * @param s |
| * Supplier to run |
| */ |
| public static <T> T tryRunWithAllPerm(Callable<T> c) throws Exception { |
| Optional<JAXPPolicyManager> policyManager = Optional.ofNullable(JAXPPolicyManager |
| .getJAXPPolicyManager(false)); |
| policyManager.ifPresent(manager -> manager.setAllowAll(true)); |
| try { |
| return c.call(); |
| } finally { |
| policyManager.ifPresent(manager -> manager.setAllowAll(false)); |
| } |
| } |
| |
| /** |
| * Run the Runnable with all permissions. This won't impact global policy. |
| * |
| * @param s |
| * Supplier to run |
| */ |
| public static void runWithAllPerm(Runnable r) { |
| Optional<JAXPPolicyManager> policyManager = Optional.ofNullable(JAXPPolicyManager |
| .getJAXPPolicyManager(false)); |
| policyManager.ifPresent(manager -> manager.setAllowAll(true)); |
| try { |
| r.run(); |
| } finally { |
| policyManager.ifPresent(manager -> manager.setAllowAll(false)); |
| } |
| } |
| |
| /** |
| * Acquire a system property. |
| * |
| * @param name |
| * System property name to be acquired. |
| * @return property value |
| */ |
| public static String getSystemProperty(String name) { |
| return runWithAllPerm(() -> System.getProperty(name)); |
| } |
| |
| /** |
| * Set a system property by given system value. |
| * |
| * @param name |
| * System property name to be set. |
| * @param value |
| * System property value to be set. |
| */ |
| public static void setSystemProperty(String name, String value) { |
| runWithAllPerm(() -> System.setProperty(name, value)); |
| } |
| |
| /** |
| * Clear a system property. |
| * |
| * @param name |
| * System property name to be cleared. |
| */ |
| public static void clearSystemProperty(String name) { |
| runWithAllPerm(() -> System.clearProperty(name)); |
| } |
| |
| /** |
| * Run the runnable with assigning temporary permissions. This won't impact |
| * global policy. |
| * |
| * @param r |
| * Runnable to run |
| * @param ps |
| * assigning permissions to add. |
| */ |
| public static void runWithTmpPermission(Runnable r, Permission... ps) { |
| JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false); |
| List<Integer> tmpPermissionIndexes = new ArrayList<>(); |
| if (policyManager != null) { |
| for (Permission p : ps) |
| tmpPermissionIndexes.add(policyManager.addTmpPermission(p)); |
| } |
| try { |
| r.run(); |
| } finally { |
| for (int index: tmpPermissionIndexes) |
| policyManager.removeTmpPermission(index); |
| } |
| } |
| |
| /** |
| * Run the supplier with assigning temporary permissions. This won't impact |
| * global policy. |
| * |
| * @param s |
| * Supplier to run |
| * @param ps |
| * assigning permissions to add. |
| */ |
| public static <T> T runWithTmpPermission(Supplier<T> s, Permission... ps) { |
| JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false); |
| List<Integer> tmpPermissionIndexes = new ArrayList<>(); |
| if (policyManager != null) { |
| for (Permission p : ps) |
| tmpPermissionIndexes.add(policyManager.addTmpPermission(p)); |
| } |
| try { |
| return s.get(); |
| } finally { |
| for (int index: tmpPermissionIndexes) |
| policyManager.removeTmpPermission(index); |
| } |
| } |
| |
| /** |
| * Run the RunnableWithException with assigning temporary permissions. This |
| * won't impact global policy. |
| * |
| * @param r |
| * RunnableWithException to execute |
| * @param ps |
| * assigning permissions to add. |
| */ |
| public static void tryRunWithTmpPermission(RunnableWithException r, Permission... ps) throws Exception { |
| JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false); |
| List<Integer> tmpPermissionIndexes = new ArrayList<>(); |
| if (policyManager != null) { |
| for (Permission p : ps) |
| tmpPermissionIndexes.add(policyManager.addTmpPermission(p)); |
| } |
| try { |
| r.run(); |
| } finally { |
| for (int index: tmpPermissionIndexes) |
| policyManager.removeTmpPermission(index); |
| } |
| } |
| |
| @FunctionalInterface |
| public interface RunnableWithException { |
| void run() throws Exception; |
| } |
| |
| /** |
| * Current test directory. |
| */ |
| public static final String USER_DIR = getSystemProperty("user.dir") + FILE_SEP;; |
| |
| } |