1 /* 2 * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jaxp.library; 24 25 import static org.testng.Assert.fail; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.StringWriter; 32 import java.nio.ByteBuffer; 33 import java.nio.ByteOrder; 34 import java.nio.charset.Charset; 35 import java.nio.charset.StandardCharsets; 36 import java.nio.charset.UnsupportedCharsetException; 37 import java.nio.file.Files; 38 import java.nio.file.Paths; 39 import java.security.Permission; 40 import java.util.ArrayList; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Optional; 45 import java.util.concurrent.Callable; 46 import java.util.concurrent.ConcurrentHashMap; 47 import java.util.function.Supplier; 48 import java.util.regex.Pattern; 49 import java.util.stream.Collectors; 50 51 import javax.xml.parsers.DocumentBuilder; 52 import javax.xml.parsers.DocumentBuilderFactory; 53 import javax.xml.parsers.ParserConfigurationException; 54 import javax.xml.transform.Transformer; 55 import javax.xml.transform.TransformerException; 56 import javax.xml.transform.TransformerFactory; 57 import javax.xml.transform.dom.DOMSource; 58 import javax.xml.transform.stream.StreamResult; 59 60 import org.w3c.dom.Document; 61 import org.w3c.dom.Node; 62 import org.xml.sax.SAXException; 63 64 /** 65 * This is an interface provide basic support for JAXP functional test. 66 */ 67 public class JAXPTestUtilities { 68 /** 69 * Prefix for error message. 70 */ 71 public static final String ERROR_MSG_HEADER = "Unexcepted exception thrown:"; 72 73 /** 74 * Prefix for error message on clean up block. 75 */ 76 public static final String ERROR_MSG_CLEANUP = "Clean up failed on %s"; 77 78 /** 79 * Force using slash as File separator as we always use cygwin to test in 80 * Windows platform. 81 */ 82 public static final String FILE_SEP = "/"; 83 84 /** 85 * A map storing every test's current test file pointer. File number should 86 * be incremental and it's a thread-safe reading on this file number. 87 */ 88 private static final ConcurrentHashMap<Class<?>, Integer> currentFileNumber 89 = new ConcurrentHashMap<>(); 90 91 /** 92 * BOM table for storing BOM header. 93 */ 94 private final static Map<String, byte[]> bom = new HashMap<>(); 95 96 /** 97 * Initialize all BOM headers. 98 */ 99 static { 100 bom.put("UTF-8", new byte[]{(byte)0xEF, (byte) 0xBB, (byte) 0xBF}); 101 bom.put("UTF-16BE", new byte[]{(byte)0xFE, (byte)0xFF}); 102 bom.put("UTF-16LE", new byte[]{(byte)0xFF, (byte)0xFE}); 103 bom.put("UTF-32BE", new byte[]{(byte)0x00, (byte)0x00, (byte)0xFE, (byte)0xFF}); 104 bom.put("UTF-32LE", new byte[]{(byte)0xFF, (byte)0xFE, (byte)0x00, (byte)0x00}); 105 } 106 107 /** 108 * Compare contents of golden file with test output file line by line. 109 * return true if they're identical. 110 * @param goldfile Golden output file name 111 * @param outputfile Test output file name 112 * @return true if two files are identical. 113 * false if two files are not identical. 114 * @throws IOException if an I/O error occurs reading from the file or a 115 * malformed or unmappable byte sequence is read. 116 */ 117 public static boolean compareWithGold(String goldfile, String outputfile) 118 throws IOException { 119 return compareWithGold(goldfile, outputfile, StandardCharsets.UTF_8); 120 } 121 122 /** 123 * Compare contents of golden file with test output file line by line. 124 * return true if they're identical. 125 * @param goldfile Golden output file name. 126 * @param outputfile Test output file name. 127 * @param cs the charset to use for decoding. 128 * @return true if two files are identical. 129 * false if two files are not identical. 130 * @throws IOException if an I/O error occurs reading from the file or a 131 * malformed or unmappable byte sequence is read. 132 */ 133 public static boolean compareWithGold(String goldfile, String outputfile, 134 Charset cs) throws IOException { 135 boolean isSame = Files.readAllLines(Paths.get(goldfile)). 136 equals(Files.readAllLines(Paths.get(outputfile), cs)); 137 if (!isSame) { 138 System.err.println("Golden file " + goldfile + " :"); 139 Files.readAllLines(Paths.get(goldfile)).forEach(System.err::println); 140 System.err.println("Output file " + outputfile + " :"); 141 Files.readAllLines(Paths.get(outputfile), cs).forEach(System.err::println); 142 } 143 return isSame; 144 } 145 146 /** 147 * Compare contents of golden file with test output list line by line. 148 * return true if they're identical. 149 * @param goldfile Golden output file name. 150 * @param lines test output list. 151 * @return true if file's content is identical to given list. 152 * false if file's content is not identical to given list. 153 * @throws IOException if an I/O error occurs reading from the file or a 154 * malformed or unmappable byte sequence is read 155 */ 156 public static boolean compareLinesWithGold(String goldfile, List<String> lines) 157 throws IOException { 158 return Files.readAllLines(Paths.get(goldfile)).equals(lines); 159 } 160 161 /** 162 * Compare contents of golden file with a test output string. 163 * return true if they're identical. 164 * @param goldfile Golden output file name. 165 * @param string test string. 166 * @return true if file's content is identical to given string. 167 * false if file's content is not identical to given string. 168 * @throws IOException if an I/O error occurs reading from the file or a 169 * malformed or unmappable byte sequence is read 170 */ 171 public static boolean compareStringWithGold(String goldfile, String string) 172 throws IOException { 173 return Files.readAllLines(Paths.get(goldfile)).stream().collect( 174 Collectors.joining(System.getProperty("line.separator"))) 175 .equals(string); 176 } 177 178 /** 179 * Compare contents of golden file with test output file by their document 180 * representation. 181 * Here we ignore the white space and comments. return true if they're 182 * lexical identical. 183 * @param goldfile Golden output file name. 184 * @param resultFile Test output file name. 185 * @return true if two file's document representation are identical. 186 * false if two file's document representation are not identical. 187 * @throws javax.xml.parsers.ParserConfigurationException if the 188 * implementation is not available or cannot be instantiated. 189 * @throws SAXException If any parse errors occur. 190 * @throws IOException if an I/O error occurs reading from the file or a 191 * malformed or unmappable byte sequence is read . 192 */ 193 public static boolean compareDocumentWithGold(String goldfile, String resultFile) 194 throws ParserConfigurationException, SAXException, IOException { 195 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 196 factory.setNamespaceAware(true); 197 factory.setCoalescing(true); 198 factory.setIgnoringElementContentWhitespace(true); 199 factory.setIgnoringComments(true); 200 DocumentBuilder db = factory.newDocumentBuilder(); 201 202 Document goldD = db.parse(Paths.get(goldfile).toFile()); 203 goldD.normalizeDocument(); 204 Document resultD = db.parse(Paths.get(resultFile).toFile()); 205 resultD.normalizeDocument(); 206 return goldD.isEqualNode(resultD); 207 } 208 209 /** 210 * Compare contents of golden file with the serialization represent by given 211 * DOM node. 212 * Here we ignore the white space and comments. return true if they're 213 * lexical identical. 214 * @param goldfile Golden output file name. 215 * @param node A DOM node instance. 216 * @return true if file's content is identical to given node's serialization 217 * represent. 218 * false if file's content is not identical to given node's 219 * serialization represent. 220 * @throws TransformerException If an unrecoverable error occurs during the 221 * course of the transformation.. 222 * @throws IOException if an I/O error occurs reading from the file or a 223 * malformed or unmappable byte sequence is read . 224 */ 225 public static boolean compareSerializeDOMWithGold(String goldfile, Node node) 226 throws TransformerException, IOException { 227 TransformerFactory factory = TransformerFactory.newInstance(); 228 // Use identity transformer to serialize 229 Transformer identityTransformer = factory.newTransformer(); 230 StringWriter sw = new StringWriter(); 231 StreamResult streamResult = new StreamResult(sw); 232 DOMSource nodeSource = new DOMSource(node); 233 identityTransformer.transform(nodeSource, streamResult); 234 return compareStringWithGold(goldfile, sw.toString()); 235 } 236 237 /** 238 * Convert stream to ByteArrayInputStream by given character set. 239 * @param charset target character set. 240 * @param file a file that contains no BOM head content. 241 * @return a ByteArrayInputStream contains BOM heads and bytes in original 242 * stream 243 * @throws IOException I/O operation failed or unsupported character set. 244 */ 245 public static InputStream bomStream(String charset, String file) 246 throws IOException { 247 String localCharset = charset; 248 if (charset.equals("UTF-16") || charset.equals("UTF-32")) { 249 localCharset 250 += ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? "BE" : "LE"; 251 } 252 if (!bom.containsKey(localCharset)) 253 throw new UnsupportedCharsetException("Charset:" + localCharset); 254 255 byte[] content = Files.readAllLines(Paths.get(file)).stream(). 256 collect(Collectors.joining()).getBytes(localCharset); 257 byte[] head = bom.get(localCharset); 258 ByteBuffer bb = ByteBuffer.allocate(content.length + head.length); 259 bb.put(head); 260 bb.put(content); 261 return new ByteArrayInputStream(bb.array()); 262 } 263 264 /** 265 * Worker method to detect common absolute URLs. 266 * 267 * @param s String path\filename or URL (or any, really) 268 * @return true if s starts with a common URI scheme (namely 269 * the ones found in the examples of RFC2396); false otherwise 270 */ 271 protected static boolean isCommonURL(String s) { 272 if (null == s) 273 return false; 274 return Pattern.compile("^(file:|http:|ftp:|gopher:|mailto:|news:|telnet:)") 275 .matcher(s).matches(); 276 } 277 278 /** 279 * Utility method to translate a String filename to URL. 280 * 281 * If the name starts with a common URI scheme (namely the ones 282 * found in the examples of RFC2396), then simply return the 283 * name as-is (the assumption is that it's already a URL). 284 * Otherwise we attempt (cheaply) to convert to a file:/ URL. 285 * 286 * @param filename local path/filename of a file. 287 * @return a file:/ URL if filename represent a file, the same string if 288 * it appears to already be a URL. 289 */ 290 public static String filenameToURL(String filename) { 291 return Paths.get(filename).toUri().toASCIIString(); 292 } 293 294 /** 295 * Prints error message if an exception is thrown 296 * @param ex The exception is thrown by test. 297 */ 298 public static void failUnexpected(Throwable ex) { 299 fail(ERROR_MSG_HEADER, ex); 300 } 301 302 /** 303 * Prints error message if an exception is thrown when clean up a file. 304 * @param ex The exception is thrown in cleaning up a file. 305 * @param name Cleaning up file name. 306 */ 307 public static void failCleanup(IOException ex, String name) { 308 fail(String.format(ERROR_MSG_CLEANUP, name), ex); 309 } 310 311 /** 312 * Retrieve next test output file name. This method is a thread-safe method. 313 * @param clazz test class. 314 * @return next test output file name. 315 */ 316 public static String getNextFile(Class<?> clazz) { 317 int nextNumber = currentFileNumber.contains(clazz) 318 ? currentFileNumber.get(clazz) + 1 : 1; 319 Integer i = currentFileNumber.putIfAbsent(clazz, nextNumber); 320 if (i != null) { 321 do { 322 nextNumber = currentFileNumber.get(clazz) + 1; 323 } while (!currentFileNumber.replace(clazz, nextNumber - 1, nextNumber)); 324 } 325 return USER_DIR + clazz.getName() + nextNumber + ".out"; 326 } 327 328 /** 329 * Acquire a full path string by given class name and relative path string. 330 * @param clazz Class name for the test. 331 * @param relativeDir relative path between java source file and expected 332 * path. 333 * @return a string represents the full path of accessing path. 334 */ 335 public static String getPathByClassName(Class<?> clazz, String relativeDir) { 336 String javaSourcePath = System.getProperty("test.src").replaceAll("\\" + File.separator, FILE_SEP); 337 String normalizedPath = Paths.get(javaSourcePath, relativeDir).normalize(). 338 toAbsolutePath().toString(); 339 return normalizedPath.replace("\\", FILE_SEP) + FILE_SEP; 340 } 341 342 343 /** 344 * Run the supplier with all permissions. This won't impact global policy. 345 * 346 * @param s 347 * Supplier to run 348 */ 349 public static <T> T runWithAllPerm(Supplier<T> s) { 350 Optional<JAXPPolicyManager> policyManager = Optional.ofNullable(JAXPPolicyManager 351 .getJAXPPolicyManager(false)); 352 policyManager.ifPresent(manager -> manager.setAllowAll(true)); 353 try { 354 return s.get(); 355 } finally { 356 policyManager.ifPresent(manager -> manager.setAllowAll(false)); 357 } 358 } 359 360 /** 361 * Run the supplier with all permissions. This won't impact global policy. 362 * 363 * @param s 364 * Supplier to run 365 */ 366 public static <T> T tryRunWithAllPerm(Callable<T> c) throws Exception { 367 Optional<JAXPPolicyManager> policyManager = Optional.ofNullable(JAXPPolicyManager 368 .getJAXPPolicyManager(false)); 369 policyManager.ifPresent(manager -> manager.setAllowAll(true)); 370 try { 371 return c.call(); 372 } finally { 373 policyManager.ifPresent(manager -> manager.setAllowAll(false)); 374 } 375 } 376 377 /** 378 * Run the Runnable with all permissions. This won't impact global policy. 379 * 380 * @param s 381 * Supplier to run 382 */ 383 public static void runWithAllPerm(Runnable r) { 384 Optional<JAXPPolicyManager> policyManager = Optional.ofNullable(JAXPPolicyManager 385 .getJAXPPolicyManager(false)); 386 policyManager.ifPresent(manager -> manager.setAllowAll(true)); 387 try { 388 r.run(); 389 } finally { 390 policyManager.ifPresent(manager -> manager.setAllowAll(false)); 391 } 392 } 393 394 /** 395 * Acquire a system property. 396 * 397 * @param name 398 * System property name to be acquired. 399 * @return property value 400 */ 401 public static String getSystemProperty(String name) { 402 return runWithAllPerm(() -> System.getProperty(name)); 403 } 404 405 /** 406 * Set a system property by given system value. 407 * 408 * @param name 409 * System property name to be set. 410 * @param value 411 * System property value to be set. 412 */ 413 public static void setSystemProperty(String name, String value) { 414 runWithAllPerm(() -> System.setProperty(name, value)); 415 } 416 417 /** 418 * Clear a system property. 419 * 420 * @param name 421 * System property name to be cleared. 422 */ 423 public static void clearSystemProperty(String name) { 424 runWithAllPerm(() -> System.clearProperty(name)); 425 } 426 427 /** 428 * Run the runnable with assigning temporary permissions. This won't impact 429 * global policy. 430 * 431 * @param r 432 * Runnable to run 433 * @param ps 434 * assigning permissions to add. 435 */ 436 public static void runWithTmpPermission(Runnable r, Permission... ps) { 437 JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false); 438 List<Integer> tmpPermissionIndexes = new ArrayList<>(); 439 if (policyManager != null) { 440 for (Permission p : ps) 441 tmpPermissionIndexes.add(policyManager.addTmpPermission(p)); 442 } 443 try { 444 r.run(); 445 } finally { 446 for (int index: tmpPermissionIndexes) 447 policyManager.removeTmpPermission(index); 448 } 449 } 450 451 /** 452 * Run the supplier with assigning temporary permissions. This won't impact 453 * global policy. 454 * 455 * @param s 456 * Supplier to run 457 * @param ps 458 * assigning permissions to add. 459 */ 460 public static <T> T runWithTmpPermission(Supplier<T> s, Permission... ps) { 461 JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false); 462 List<Integer> tmpPermissionIndexes = new ArrayList<>(); 463 if (policyManager != null) { 464 for (Permission p : ps) 465 tmpPermissionIndexes.add(policyManager.addTmpPermission(p)); 466 } 467 try { 468 return s.get(); 469 } finally { 470 for (int index: tmpPermissionIndexes) 471 policyManager.removeTmpPermission(index); 472 } 473 } 474 475 /** 476 * Run the RunnableWithException with assigning temporary permissions. This 477 * won't impact global policy. 478 * 479 * @param r 480 * RunnableWithException to execute 481 * @param ps 482 * assigning permissions to add. 483 */ 484 public static void tryRunWithTmpPermission(RunnableWithException r, Permission... ps) throws Exception { 485 JAXPPolicyManager policyManager = JAXPPolicyManager.getJAXPPolicyManager(false); 486 List<Integer> tmpPermissionIndexes = new ArrayList<>(); 487 if (policyManager != null) { 488 for (Permission p : ps) 489 tmpPermissionIndexes.add(policyManager.addTmpPermission(p)); 490 } 491 try { 492 r.run(); 493 } finally { 494 for (int index: tmpPermissionIndexes) 495 policyManager.removeTmpPermission(index); 496 } 497 } 498 499 @FunctionalInterface 500 public interface RunnableWithException { 501 void run() throws Exception; 502 } 503 504 /** 505 * Current test directory. 506 */ 507 public static final String USER_DIR = getSystemProperty("user.dir") + FILE_SEP;; 508 509 }