diff --git a/shared/src/test/scala/scala/xml/UtilityTest.scala b/shared/src/test/scala/scala/xml/UtilityTest.scala index 263740266..b365294c9 100644 --- a/shared/src/test/scala/scala/xml/UtilityTest.scala +++ b/shared/src/test/scala/scala/xml/UtilityTest.scala @@ -3,6 +3,7 @@ package scala.xml import org.junit.Test import org.junit.Assert.assertTrue import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals class UtilityTest { @@ -63,4 +64,133 @@ class UtilityTest { assertEquals("", Utility.serialize(x, stripComments = false).toString) } + val printableAscii: Seq[Char] = { + (' ' to '/') ++ // Punctuation + ('0' to '9') ++ // Digits + (':' to '@') ++ // Punctuation (cont.) + ('A' to 'Z') ++ // Uppercase + ('[' to '`') ++ // Punctuation (cont.) + ('a' to 'z') ++ // Lowercase + ('{' to '~') // Punctuation (cont.) + } + + val escapedChars: Seq[Char] = + Utility.Escapes.escMap.keys.toSeq + + @Test + def escapePrintablesTest: Unit = { + for { + char <- (printableAscii.diff(escapedChars)) + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeEscapablesTest: Unit = { + for { + char <- escapedChars + } yield { + assertNotEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeAsciiControlCharsTest: Unit = { + + /* Escapes that Scala (Java) doesn't support. + * \u0007 -> \a (bell) + * \u001B -> \e (escape) + * \u000B -> \v (vertical tab) + * \u007F -> DEL (delete) + */ + val input = " \u0007\b\u001B\f\n\r\t\u000B\u007F" + + val expect = " \n\r\t\u007F" + + val result = Utility.escape(input) + + assertEquals(printfc(expect), printfc(result)) // Pretty, + assertEquals(expect, result) // but verify. + } + + @Test + def escapeUnicodeExtendedControlCodesTest: Unit = { + for { + char <- ('\u0080' to '\u009f') // Extended control codes (C1) + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeUnicodeTwoBytesTest: Unit = { + for { + char <- ('\u00A0' to '\u07FF') // Two bytes (cont.) + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + @Test + def escapeUnicodeThreeBytesTest: Unit = { + for { + char <- ('\u0800' to '\uFFFF') // Three bytes + } yield { + assertEquals(char.toString, Utility.escape(char.toString)) + } + } + + /** + * Human-readable character printing + * + * Think of `od -c` of unix od(1) command. + * + * Or think of `printf("%c", i)` in C, but a little better. + */ + def printfc(str: String) = { + str.map(prettyChar).mkString + } + + /** + * Visual representation of characters that enhances output of + * failed test assertions. + */ + val prettyChar: Map[Char,String] = Map( + '\u0000' -> "\\0", // Null + '\u0001' -> "^A", // Start of header + '\u0002' -> "^B", // Start of text + '\u0003' -> "^C", // End of text + '\u0004' -> "^D", // End of transmission + '\u0005' -> "^E", // Enquiry + '\u0006' -> "^F", // Acknowledgment + '\u0007' -> "\\a", // Bell (^G) + '\b' -> "\\b", // Backspace (^H) + '\t' -> "\\t", // Tab (^I) + '\n' -> "\\n", // Newline (^J) + '\u000B' -> "\\v", // Vertical tab (^K) + '\f' -> "\\f", // Form feed (^L) + '\r' -> "\\r", // Carriage return (^M) + '\u000E' -> "^N", // Shift out + '\u000F' -> "^O", // Shift in + '\u0010' -> "^P", // Data link escape + '\u0011' -> "^Q", // DC1 (XON) + '\u0012' -> "^R", // DC2 + '\u0013' -> "^S", // DC3 (XOFF) + '\u0014' -> "^T", // DC4 + '\u0015' -> "^U", // Negative acknowledgment + '\u0016' -> "^V", // Synchronous idle + '\u0017' -> "^W", // End of transmission block + '\u0018' -> "^X", // Cancel + '\u0019' -> "^Y", // End of medium + '\u001A' -> "^Z", // Substitute + '\u001B' -> "\\e", // Escape + '\u001C' -> "^\\", // File separator + '\u001D' -> "^]", // Group separator + '\u001E' -> "^^", // Record separator + '\u001F' -> "^_", // Unit separator + '\u007F' -> "^?" // Delete + ).toMap.withDefault { + key: Char => key.toString + } }