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 java.io.ByteArrayInputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 import java.nio.charset.UnsupportedCharsetException; 31 import java.nio.file.Files; 32 import java.nio.file.Paths; 33 import java.util.HashMap; 34 import java.util.Map; 35 import java.util.stream.Collectors; 36 import javax.xml.parsers.DocumentBuilder; 37 import javax.xml.parsers.DocumentBuilderFactory; 38 import javax.xml.parsers.ParserConfigurationException; 39 import static org.testng.Assert.fail; 40 import org.w3c.dom.Document; 41 import org.xml.sax.SAXException; 42 43 /** 44 * This is an interface provide basic support for JAXP functional test. 45 */ 46 public class JAXPTestUtilities { 47 /** 48 * Prefix for error message. 49 */ 50 public static final String ERROR_MSG_HEADER = "Unexcepted exception thrown:"; 51 52 /** 53 * Prefix for error message on clean up block. 54 */ 55 public static final String ERROR_MSG_CLEANUP = "Clean up failed on %s"; 56 57 /** 58 * Force using slash as File separator as we always use cygwin to test in 59 * Windows platform. 60 */ 61 public static final String FILE_SEP = "/"; 62 63 /** 64 * User home. 65 */ 66 public static final String USER_DIR = System.getProperty("user.dir", "."); 67 68 /** 69 * TEMP file directory. 70 */ 71 public static final String TEMP_DIR = System.getProperty("java.io.tmpdir", "."); 72 73 /** 74 * BOM table for storing BOM header. 75 */ 76 private final static Map<String, byte[]> bom = new HashMap(); 77 78 /** 79 * Initialize all BOM headers. 80 */ 81 static { 82 bom.put("UTF-8", new byte[]{(byte)0xEF, (byte) 0xBB, (byte) 0xBF}); 83 bom.put("UTF-16BE", new byte[]{(byte)0xFE, (byte)0xFF}); 84 bom.put("UTF-16LE", new byte[]{(byte)0xFF, (byte)0xFE}); 85 bom.put("UTF-32BE", new byte[]{(byte)0x00, (byte)0x00, (byte)0xFE, (byte)0xFF}); 86 bom.put("UTF-32LE", new byte[]{(byte)0xFF, (byte)0xFE, (byte)0x00, (byte)0x00}); 87 } 88 89 /** 90 * Compare contents of golden file with test output file line by line. 91 * return true if they're identical. 92 * @param goldfile Golden output file name 93 * @param outputfile Test output file name 94 * @return true if two files are identical. 95 * false if two files are not identical. 96 * @throws IOException if an I/O error occurs reading from the file or a 97 * malformed or unmappable byte sequence is read 98 */ 99 public static boolean compareWithGold(String goldfile, String outputfile) 100 throws IOException { 101 return Files.readAllLines(Paths.get(goldfile)). 102 equals(Files.readAllLines(Paths.get(outputfile))); 103 } 104 105 /** 106 * Compare contents of golden file with test output file by their document 107 * representation. 108 * Here we ignore the white space and comments. return true if they're 109 * lexical identical. 110 * @param goldfile Golden output file name. 111 * @param resultFile Test output file name. 112 * @return true if two file's document representation are identical. 113 * false if two file's document representation are not identical. 114 * @throws javax.xml.parsers.ParserConfigurationException if the 115 * implementation is not available or cannot be instantiated. 116 * @throws SAXException If any parse errors occur. 117 * @throws IOException if an I/O error occurs reading from the file or a 118 * malformed or unmappable byte sequence is read . 119 */ 120 public static boolean compareDocumentWithGold(String goldfile, String resultFile) 121 throws ParserConfigurationException, SAXException, IOException { 122 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 123 factory.setNamespaceAware(true); 124 factory.setCoalescing(true); 125 factory.setIgnoringElementContentWhitespace(true); 126 factory.setIgnoringComments(true); 127 DocumentBuilder db = factory.newDocumentBuilder(); 128 129 Document goldD = db.parse(Paths.get(goldfile).toFile()); 130 goldD.normalizeDocument(); 131 Document resultD = db.parse(Paths.get(resultFile).toFile()); 132 resultD.normalizeDocument(); 133 return goldD.isEqualNode(resultD); 134 } 135 /** 136 * Convert stream to ByteArrayInputStream by given character set. 137 * @param charset target character set. 138 * @param file a file that contains no BOM head content. 139 * @return a ByteArrayInputStream contains BOM heads and bytes in original 140 * stream 141 * @throws IOException I/O operation failed or unsupported character set. 142 */ 143 public static InputStream bomStream(String charset, String file) 144 throws IOException { 145 String localCharset = charset; 146 if (charset.equals("UTF-16") || charset.equals("UTF-32")) { 147 localCharset 148 += ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? "BE" : "LE"; 149 } 150 if (!bom.containsKey(localCharset)) 151 throw new UnsupportedCharsetException("Charset:" + localCharset); 152 153 byte[] content = Files.readAllLines(Paths.get(file)).stream(). 154 collect(Collectors.joining()).getBytes(localCharset); 155 byte[] head = bom.get(localCharset); 156 ByteBuffer bb = ByteBuffer.allocate(content.length + head.length); 157 bb.put(head); 158 bb.put(content); 159 return new ByteArrayInputStream(bb.array()); 160 } 161 162 /** 163 * Prints error message if an exception is thrown 164 * @param ex The exception is thrown by test. 165 */ 166 public static void failUnexpected(Throwable ex) { 167 fail(ERROR_MSG_HEADER, ex); 168 } 169 170 /** 171 * Prints error message if an exception is thrown when clean up a file. 172 * @param ex The exception is thrown in cleaning up a file. 173 * @param name Cleaning up file name. 174 */ 175 public static void failCleanup(IOException ex, String name) { 176 fail(String.format(ERROR_MSG_CLEANUP, name), ex); 177 } 178 } | 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 java.io.ByteArrayInputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.StringWriter; 29 import java.nio.ByteBuffer; 30 import java.nio.ByteOrder; 31 import java.nio.charset.Charset; 32 import java.nio.charset.StandardCharsets; 33 import java.nio.charset.UnsupportedCharsetException; 34 import java.nio.file.Files; 35 import java.nio.file.Paths; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.concurrent.ConcurrentHashMap; 40 import java.util.regex.Pattern; 41 import java.util.stream.Collectors; 42 import javax.xml.parsers.DocumentBuilder; 43 import javax.xml.parsers.DocumentBuilderFactory; 44 import javax.xml.parsers.ParserConfigurationException; 45 import javax.xml.transform.Transformer; 46 import javax.xml.transform.TransformerException; 47 import javax.xml.transform.TransformerFactory; 48 import javax.xml.transform.dom.DOMSource; 49 import javax.xml.transform.stream.StreamResult; 50 import static org.testng.Assert.fail; 51 import org.w3c.dom.Document; 52 import org.w3c.dom.Node; 53 import org.xml.sax.SAXException; 54 55 /** 56 * This is an interface provide basic support for JAXP functional test. 57 */ 58 public class JAXPTestUtilities { 59 /** 60 * Prefix for error message. 61 */ 62 public static final String ERROR_MSG_HEADER = "Unexcepted exception thrown:"; 63 64 /** 65 * Prefix for error message on clean up block. 66 */ 67 public static final String ERROR_MSG_CLEANUP = "Clean up failed on %s"; 68 69 /** 70 * Force using slash as File separator as we always use cygwin to test in 71 * Windows platform. 72 */ 73 public static final String FILE_SEP = "/"; 74 75 /** 76 * Current test directory. 77 */ 78 public static final String CLASS_DIR 79 = System.getProperty("test.classes", ".") + FILE_SEP; 80 81 /** 82 * A map storing every test's current test file pointer. File number should 83 * be incremental and it's a thread-safe reading on this file number. 84 */ 85 private static final ConcurrentHashMap<Class, Integer> currentFileNumber 86 = new ConcurrentHashMap<>(); 87 88 /** 89 * BOM table for storing BOM header. 90 */ 91 private final static Map<String, byte[]> bom = new HashMap(); 92 93 /** 94 * Initialize all BOM headers. 95 */ 96 static { 97 bom.put("UTF-8", new byte[]{(byte)0xEF, (byte) 0xBB, (byte) 0xBF}); 98 bom.put("UTF-16BE", new byte[]{(byte)0xFE, (byte)0xFF}); 99 bom.put("UTF-16LE", new byte[]{(byte)0xFF, (byte)0xFE}); 100 bom.put("UTF-32BE", new byte[]{(byte)0x00, (byte)0x00, (byte)0xFE, (byte)0xFF}); 101 bom.put("UTF-32LE", new byte[]{(byte)0xFF, (byte)0xFE, (byte)0x00, (byte)0x00}); 102 } 103 104 /** 105 * Compare contents of golden file with test output file line by line. 106 * return true if they're identical. 107 * @param goldfile Golden output file name 108 * @param outputfile Test output file name 109 * @return true if two files are identical. 110 * false if two files are not identical. 111 * @throws IOException if an I/O error occurs reading from the file or a 112 * malformed or unmappable byte sequence is read. 113 */ 114 public static boolean compareWithGold(String goldfile, String outputfile) 115 throws IOException { 116 return compareWithGold(goldfile, outputfile, StandardCharsets.UTF_8); 117 } 118 119 /** 120 * Compare contents of golden file with test output file line by line. 121 * return true if they're identical. 122 * @param goldfile Golden output file name. 123 * @param outputfile Test output file name. 124 * @param cs the charset to use for decoding. 125 * @return true if two files are identical. 126 * false if two files are not identical. 127 * @throws IOException if an I/O error occurs reading from the file or a 128 * malformed or unmappable byte sequence is read. 129 */ 130 public static boolean compareWithGold(String goldfile, String outputfile, 131 Charset cs) throws IOException { 132 return Files.readAllLines(Paths.get(goldfile)). 133 equals(Files.readAllLines(Paths.get(outputfile), cs)); 134 } 135 136 /** 137 * Compare contents of golden file with test output list line by line. 138 * return true if they're identical. 139 * @param goldfile Golden output file name. 140 * @param lines test output list. 141 * @return true if file's content is identical to given list. 142 * false if file's content is not identical to given list. 143 * @throws IOException if an I/O error occurs reading from the file or a 144 * malformed or unmappable byte sequence is read 145 */ 146 public static boolean compareLinesWithGold(String goldfile, List<String> lines) 147 throws IOException { 148 return Files.readAllLines(Paths.get(goldfile)).equals(lines); 149 } 150 151 /** 152 * Compare contents of golden file with a test output string. 153 * return true if they're identical. 154 * @param goldfile Golden output file name. 155 * @param string test string. 156 * @return true if file's content is identical to given string. 157 * false if file's content is not identical to given string. 158 * @throws IOException if an I/O error occurs reading from the file or a 159 * malformed or unmappable byte sequence is read 160 */ 161 public static boolean compareStringWithGold(String goldfile, String string) 162 throws IOException { 163 return Files.readAllLines(Paths.get(goldfile)).stream().collect( 164 Collectors.joining(System.getProperty("line.separator"))) 165 .equals(string); 166 } 167 168 /** 169 * Compare contents of golden file with test output file by their document 170 * representation. 171 * Here we ignore the white space and comments. return true if they're 172 * lexical identical. 173 * @param goldfile Golden output file name. 174 * @param resultFile Test output file name. 175 * @return true if two file's document representation are identical. 176 * false if two file's document representation are not identical. 177 * @throws javax.xml.parsers.ParserConfigurationException if the 178 * implementation is not available or cannot be instantiated. 179 * @throws SAXException If any parse errors occur. 180 * @throws IOException if an I/O error occurs reading from the file or a 181 * malformed or unmappable byte sequence is read . 182 */ 183 public static boolean compareDocumentWithGold(String goldfile, String resultFile) 184 throws ParserConfigurationException, SAXException, IOException { 185 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 186 factory.setNamespaceAware(true); 187 factory.setCoalescing(true); 188 factory.setIgnoringElementContentWhitespace(true); 189 factory.setIgnoringComments(true); 190 DocumentBuilder db = factory.newDocumentBuilder(); 191 192 Document goldD = db.parse(Paths.get(goldfile).toFile()); 193 goldD.normalizeDocument(); 194 Document resultD = db.parse(Paths.get(resultFile).toFile()); 195 resultD.normalizeDocument(); 196 return goldD.isEqualNode(resultD); 197 } 198 199 /** 200 * Compare contents of golden file with the serialization represent by given 201 * DOM node. 202 * Here we ignore the white space and comments. return true if they're 203 * lexical identical. 204 * @param goldfile Golden output file name. 205 * @param node A DOM node instance. 206 * @return true if file's content is identical to given node's serialization 207 * represent. 208 * false if file's content is not identical to given node's 209 * serialization represent. 210 * @throws TransformerException If an unrecoverable error occurs during the 211 * course of the transformation.. 212 * @throws IOException if an I/O error occurs reading from the file or a 213 * malformed or unmappable byte sequence is read . 214 */ 215 public static boolean compareSerializeDOMWithGold(String goldfile, Node node) 216 throws TransformerException, IOException { 217 TransformerFactory factory = TransformerFactory.newInstance(); 218 // Use identity transformer to serialize 219 Transformer identityTransformer = factory.newTransformer(); 220 StringWriter sw = new StringWriter(); 221 StreamResult streamResult = new StreamResult(sw); 222 DOMSource nodeSource = new DOMSource(node); 223 identityTransformer.transform(nodeSource, streamResult); 224 return compareStringWithGold(goldfile, sw.toString()); 225 } 226 227 /** 228 * Convert stream to ByteArrayInputStream by given character set. 229 * @param charset target character set. 230 * @param file a file that contains no BOM head content. 231 * @return a ByteArrayInputStream contains BOM heads and bytes in original 232 * stream 233 * @throws IOException I/O operation failed or unsupported character set. 234 */ 235 public static InputStream bomStream(String charset, String file) 236 throws IOException { 237 String localCharset = charset; 238 if (charset.equals("UTF-16") || charset.equals("UTF-32")) { 239 localCharset 240 += ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? "BE" : "LE"; 241 } 242 if (!bom.containsKey(localCharset)) 243 throw new UnsupportedCharsetException("Charset:" + localCharset); 244 245 byte[] content = Files.readAllLines(Paths.get(file)).stream(). 246 collect(Collectors.joining()).getBytes(localCharset); 247 byte[] head = bom.get(localCharset); 248 ByteBuffer bb = ByteBuffer.allocate(content.length + head.length); 249 bb.put(head); 250 bb.put(content); 251 return new ByteArrayInputStream(bb.array()); 252 } 253 254 /** 255 * Worker method to detect common absolute URLs. 256 * 257 * @param s String path\filename or URL (or any, really) 258 * @return true if s starts with a common URI scheme (namely 259 * the ones found in the examples of RFC2396); false otherwise 260 */ 261 protected static boolean isCommonURL(String s) { 262 if (null == s) 263 return false; 264 return Pattern.compile("^(file:|http:|ftp:|gopher:|mailto:|news:|telnet:)") 265 .matcher(s).matches(); 266 } 267 268 /** 269 * Utility method to translate a String filename to URL. 270 * 271 * If the name starts with a common URI scheme (namely the ones 272 * found in the examples of RFC2396), then simply return the 273 * name as-is (the assumption is that it's already a URL). 274 * Otherwise we attempt (cheaply) to convert to a file:/ URL. 275 * 276 * @param filename local path/filename of a file. 277 * @return a file:/ URL if filename represent a file, the same string if 278 * it appears to already be a URL. 279 */ 280 public static String filenameToURL(String filename) { 281 return Paths.get(filename).toUri().toASCIIString(); 282 } 283 284 /** 285 * Prints error message if an exception is thrown 286 * @param ex The exception is thrown by test. 287 */ 288 public static void failUnexpected(Throwable ex) { 289 fail(ERROR_MSG_HEADER, ex); 290 } 291 292 /** 293 * Prints error message if an exception is thrown when clean up a file. 294 * @param ex The exception is thrown in cleaning up a file. 295 * @param name Cleaning up file name. 296 */ 297 public static void failCleanup(IOException ex, String name) { 298 fail(String.format(ERROR_MSG_CLEANUP, name), ex); 299 } 300 301 /** 302 * Retrieve next test output file name. This method is a thread-safe method. 303 * @param clazz test class. 304 * @return next test output file name. 305 */ 306 public static String getNextFile(Class clazz) { 307 int nextNumber = currentFileNumber.contains(clazz) 308 ? currentFileNumber.get(clazz) + 1 : 1; 309 Integer i = currentFileNumber.putIfAbsent(clazz, nextNumber); 310 if (i != null && i != nextNumber) { 311 do { 312 nextNumber = currentFileNumber.get(clazz) + 1; 313 } while (currentFileNumber.replace(clazz, nextNumber -1, nextNumber)); 314 } 315 return CLASS_DIR + clazz.getName() + nextNumber + ".out"; 316 } 317 } |