Skip to content

Commit

Permalink
Merge pull request #261 from olafurpg/escape-testname
Browse files Browse the repository at this point in the history
Fix #258 - escape "non-visible" characters in test names.
  • Loading branch information
olafurpg authored Nov 18, 2020
2 parents 336c1f3 + 23ad9bc commit 545570d
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 18 deletions.
3 changes: 2 additions & 1 deletion munit/shared/src/main/scala/munit/MUnitRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import scala.concurrent.Future
import scala.concurrent.ExecutionContext
import java.util.concurrent.ExecutionException
import munit.internal.junitinterface.Settings
import munit.internal.console.Printers

class MUnitRunner(val cls: Class[_ <: Suite], newInstance: () => Suite)
extends Runner
Expand Down Expand Up @@ -62,7 +63,7 @@ class MUnitRunner(val cls: Class[_ <: Suite], newInstance: () => Suite)
def createTestDescription(test: suite.Test): Description = {
descriptions.getOrElseUpdate(
test, {
val escapedName = test.name.replace("\n", "\\n")
val escapedName = Printers.escapeNonVisible(test.name)
val testName = munit.internal.Compat.LazyList
.from(0)
.map {
Expand Down
40 changes: 36 additions & 4 deletions munit/shared/src/main/scala/munit/internal/console/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,39 @@ object Printers {
}
}

private def printChar(c: Char, sb: StringBuilder) =
/**
* Pretty-prints this string with non-visible characters escaped.
*
* The exact definition of "non-visible" is fuzzy and is subject to change.
* The original motivation for this method was to fix
* https://github.com/scalameta/munit/issues/258 related to escaping \r in
* test names.
*
* The spirit of this method is to preserve "visible" characters like emojis
* and double quotes and escape "non-visible" characters like newlines and
* ANSI escape codes. A non-goal of this method is to make the output
* copy-pasteable back into source code unlike the `printChar` method, which
* escapes for example double-quote characters.
*/
def escapeNonVisible(string: String): String = {
val out = new StringBuilder()
var i = 0
while (i < string.length()) {
val ch = string.charAt(i)
(ch: @switch) match {
case '"' | '\'' => out.append(ch)
case _ => printChar(ch, out, isEscapeUnicode = false)
}
i += 1
}
out.toString()
}

private def printChar(
c: Char,
sb: StringBuilder,
isEscapeUnicode: Boolean = true
): Unit =
(c: @switch) match {
case '"' => sb.append("\\\"")
case '\\' => sb.append("\\\\")
Expand All @@ -198,9 +230,9 @@ object Printers {
case '\r' => sb.append("\\r")
case '\t' => sb.append("\\t")
case c =>
val isUnicode = false
if (c < ' ' || (c > '~' && isUnicode))
sb.append("\\u%04x" format c.toInt)
val isNonReadableAscii = c < ' ' || (c > '~' && isEscapeUnicode)
if (isNonReadableAscii && !Character.isLetter(c))
sb.append("\\u%04x".format(c.toInt))
else sb.append(c)
}

Expand Down
34 changes: 21 additions & 13 deletions tests/shared/src/main/scala/munit/TestNameFrameworkSuite.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
package munit

class TestNameFrameworkSuite extends FunSuite {
test("basic") {
// pass
}
test("basic") {
// pass
}
test("newline\n") {
// pass
}
test("basic") {}
test("basic") {}
test("newline\n") {}
test("carriage-return\r\n") {}
test("tab\t") {}
test("return\t") {}
test("form-feed\f") {}
test("substitute\u001az") {}
test("emoji😆") {}
test("red" + Console.RED) {}
}

object TestNameFrameworkSuite
extends FrameworkTest(
classOf[TestNameFrameworkSuite],
"""|==> success munit.TestNameFrameworkSuite.basic
|==> success munit.TestNameFrameworkSuite.basic-1
|==> success munit.TestNameFrameworkSuite.newline\n
|""".stripMargin
s"""|==> success munit.TestNameFrameworkSuite.basic
|==> success munit.TestNameFrameworkSuite.basic-1
|==> success munit.TestNameFrameworkSuite.newline\\n
|==> success munit.TestNameFrameworkSuite.carriage-return\\r\\n
|==> success munit.TestNameFrameworkSuite.tab\\t
|==> success munit.TestNameFrameworkSuite.return\\t
|==> success munit.TestNameFrameworkSuite.form-feed\\f
|==> success munit.TestNameFrameworkSuite.substitute\\u001az
|==> success munit.TestNameFrameworkSuite.emoji😆
|==> success munit.TestNameFrameworkSuite.red\\u001b[31m
|""".stripMargin
)

0 comments on commit 545570d

Please sign in to comment.