From a6059c75da339a58ba2547c1416bbd88eb49eb0c Mon Sep 17 00:00:00 2001 From: Antonio Gelameris Date: Mon, 27 May 2024 22:05:25 +0200 Subject: [PATCH] Add contextSize test parameter to enlarge the scope of a diff --- docs/tests.md | 77 +++++++++++++++++++ .../src/main/scala/munit/diff/Diff.scala | 11 ++- .../src/main/scala/munit/diff/Diffs.scala | 23 ++++-- .../src/main/scala/munit/Assertions.scala | 17 +++- .../shared/src/main/scala/munit/Compare.scala | 3 + munit/shared/src/main/scala/munit/Diffs.scala | 3 +- .../src/test/scala/munit/DiffsSuite.scala | 46 ++++++++++- 7 files changed, 163 insertions(+), 17 deletions(-) diff --git a/docs/tests.md b/docs/tests.md index 9d347d23..4e989702 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -196,6 +196,83 @@ List( ... ``` +# Enlarging the diff context + +When a test fails Munit shows a rich difference, to easily spot the difference between the test result and the obtained value. +Understanding where the reported value actually is can sometimes be difficult, specifically with repeated values, as by default Munit shows the diff surrounding it just with a single line of context. + +The context shown in the diff can be enlarged in a specific testing suite by overriding `contextSize`. + +```scala mdoc +import munit.FunSuite + +class CustomContextSizeTest extends FunSuite { + override def contextSize: Int = 10 + + test("contextSize") { + val a = List("a", "a", "a", "a", "a", "a", "a", "a", "a") + val b = List("a", "a", "a", "a", "b", "a", "a", "a", "a") + assertEquals(a,b) + } +} +``` + +will yield + +``` +values are not the same +=> Obtained +List( + "a", + "a", + "a", + "a", + "a", + "a", + "a", + "a", + "a" +) +=> Diff (- obtained, + expected) + List( + "a", + "a", + "a", + "a", ++ "b", + "a", + "a", + "a", +- "a", + "a" + ) +``` + +while by default the "Diff" sections shows a more scoped diff: + +``` +values are not the same +=> Obtained +List( + "a", + "a", + "a", + "a", + "a", + "a", + "a", + "a", + "a" +) +=> Diff (- obtained, + expected) + "a", ++ "b", + "a", + "a", +- "a", + "a" +``` + ## Run tests in parallel MUnit does not support running individual test cases in parallel. However, sbt diff --git a/munit-diff/shared/src/main/scala/munit/diff/Diff.scala b/munit-diff/shared/src/main/scala/munit/diff/Diff.scala index 57996767..139daf4d 100644 --- a/munit-diff/shared/src/main/scala/munit/diff/Diff.scala +++ b/munit-diff/shared/src/main/scala/munit/diff/Diff.scala @@ -5,12 +5,14 @@ import munit.diff.console.AnsiColors import scala.collection.JavaConverters._ -class Diff(val obtained: String, val expected: String) extends Serializable { +class Diff(val obtained: String, val expected: String, val contextSize: Int) + extends Serializable { val obtainedClean: String = AnsiColors.filterAnsi(obtained) val expectedClean: String = AnsiColors.filterAnsi(expected) val obtainedLines: Seq[String] = splitIntoLines(obtainedClean) val expectedLines: Seq[String] = splitIntoLines(expectedClean) - val unifiedDiff: String = createUnifiedDiff(obtainedLines, expectedLines) + val unifiedDiff: String = + createUnifiedDiff(obtainedLines, expectedLines, contextSize) def isEmpty: Boolean = unifiedDiff.isEmpty() def createReport( @@ -72,7 +74,8 @@ class Diff(val obtained: String, val expected: String) extends Serializable { private def createUnifiedDiff( original: Seq[String], - revised: Seq[String] + revised: Seq[String], + contextSize: Int ): String = { val diff = DiffUtils.diff(original.asJava, revised.asJava) val result = @@ -84,7 +87,7 @@ class Diff(val obtained: String, val expected: String) extends Serializable { "expected", original.asJava, diff, - 1 + contextSize ) .asScala .iterator diff --git a/munit-diff/shared/src/main/scala/munit/diff/Diffs.scala b/munit-diff/shared/src/main/scala/munit/diff/Diffs.scala index 828084c7..b1991d03 100644 --- a/munit-diff/shared/src/main/scala/munit/diff/Diffs.scala +++ b/munit-diff/shared/src/main/scala/munit/diff/Diffs.scala @@ -2,27 +2,36 @@ package munit.diff object Diffs { - def create(obtained: String, expected: String): Diff = - new Diff(obtained, expected) + def create(obtained: String, expected: String, contextSize: Int): Diff = + new Diff(obtained, expected, contextSize) def createDiffOnlyReport( obtained: String, - expected: String + expected: String, + contextSize: Int ): String = { - create(obtained, expected).createDiffOnlyReport() + create(obtained, expected, contextSize).createDiffOnlyReport() } def createReport( obtained: String, expected: String, title: String, + contextSize: Int, printObtainedAsStripMargin: Boolean = true ): String = { - create(obtained, expected).createReport(title, printObtainedAsStripMargin) + create(obtained, expected, contextSize).createReport( + title, + printObtainedAsStripMargin + ) } - def unifiedDiff(obtained: String, expected: String): String = { - create(obtained, expected).unifiedDiff + def unifiedDiff( + obtained: String, + expected: String, + contextSize: Int + ): String = { + create(obtained, expected, contextSize).unifiedDiff } } diff --git a/munit/shared/src/main/scala/munit/Assertions.scala b/munit/shared/src/main/scala/munit/Assertions.scala index 54f33aa7..94beebb6 100644 --- a/munit/shared/src/main/scala/munit/Assertions.scala +++ b/munit/shared/src/main/scala/munit/Assertions.scala @@ -57,6 +57,7 @@ trait Assertions extends MacroCompat.CompileErrorMacro { expected, exceptionHandlerFromAssertions(this, Clues.empty), munitPrint(clue), + contextSize, printObtainedAsStripMargin = true ) } @@ -115,7 +116,14 @@ trait Assertions extends MacroCompat.CompileErrorMacro { ) case _ => } - compare.failEqualsComparison(obtained, expected, clue, loc, this) + compare.failEqualsComparison( + obtained, + expected, + clue, + contextSize, + loc, + this + ) } } } @@ -324,6 +332,13 @@ trait Assertions extends MacroCompat.CompileErrorMacro { def printer: Printer = EmptyPrinter + /** + * Lines of context that should be printer around the diff to locate it better + * + * By default is set 1. High values might slow down the results reporting. + */ + def contextSize: Int = 1 + def munitPrint(clue: => Any): String = { clue match { case message: String => message diff --git a/munit/shared/src/main/scala/munit/Compare.scala b/munit/shared/src/main/scala/munit/Compare.scala index 62e6aa95..ffe5f93e 100644 --- a/munit/shared/src/main/scala/munit/Compare.scala +++ b/munit/shared/src/main/scala/munit/Compare.scala @@ -41,6 +41,7 @@ trait Compare[A, B] { obtained: A, expected: B, title: Any, + contextSize: Int, loc: Location, assertions: Assertions ): Nothing = { @@ -64,6 +65,7 @@ trait Compare[A, B] { assertions.munitPrint(expected), diffHandler, title = assertions.munitPrint(title), + contextSize = contextSize, printObtainedAsStripMargin = false )(loc) @@ -74,6 +76,7 @@ trait Compare[A, B] { expected.toString(), diffHandler, title = assertions.munitPrint(title), + contextSize = contextSize, printObtainedAsStripMargin = false )(loc) diff --git a/munit/shared/src/main/scala/munit/Diffs.scala b/munit/shared/src/main/scala/munit/Diffs.scala index c0252ac3..5ef5ab95 100644 --- a/munit/shared/src/main/scala/munit/Diffs.scala +++ b/munit/shared/src/main/scala/munit/Diffs.scala @@ -9,6 +9,7 @@ object Diffs { expected: String, handler: ComparisonFailExceptionHandler, title: String, + contextSize: Int, printObtainedAsStripMargin: Boolean )(implicit loc: Location): Boolean = { if (obtained.isEmpty && !expected.isEmpty) { @@ -18,7 +19,7 @@ object Diffs { |$expected""".stripMargin handler.handle(msg, obtained, expected, loc) } - val diff = new Diff(obtained, expected) + val diff = new Diff(obtained, expected, contextSize) if (diff.isEmpty) true else { handler.handle( diff --git a/tests/shared/src/test/scala/munit/DiffsSuite.scala b/tests/shared/src/test/scala/munit/DiffsSuite.scala index f8287268..50d8700a 100644 --- a/tests/shared/src/test/scala/munit/DiffsSuite.scala +++ b/tests/shared/src/test/scala/munit/DiffsSuite.scala @@ -2,9 +2,9 @@ package munit class DiffsSuite extends FunSuite { self => test("ansi") { - val diff1 = munit.diff.Diffs.unifiedDiff("a", "b") - val diff2 = munit.diff.Diffs.unifiedDiff("a", "c") - val obtained = munit.diff.Diffs.unifiedDiff(diff1, diff2) + val diff1 = munit.diff.Diffs.unifiedDiff("a", "b", 1) + val diff2 = munit.diff.Diffs.unifiedDiff("a", "c", 1) + val obtained = munit.diff.Diffs.unifiedDiff(diff1, diff2, 1) // Asserts that a roundtrip of ANSI color processing still produces // intuitive results. assertNoDiff( @@ -23,7 +23,7 @@ class DiffsSuite extends FunSuite { self => expected: String )(implicit loc: Location): Unit = { test(name) { - val obtained = munit.diff.Diffs.unifiedDiff(a, b) + val obtained = munit.diff.Diffs.unifiedDiff(a, b, 1) assertNoDiff(obtained, expected) } } @@ -45,4 +45,42 @@ class DiffsSuite extends FunSuite { self => "" ) + test("contextSize") { + val a = munit.Assertions.munitPrint( + List("a", "a", "a", "a", "a", "a", "a", "a", "a") + ) + val b = munit.Assertions.munitPrint( + List("a", "a", "a", "a", "b", "a", "a", "a", "a") + ) + val defaultDiff = munit.diff.Diffs.unifiedDiff(a, b, 1) + assertNoDiff( + defaultDiff, + """| "a", + |+ "b", + | "a", + | "a", + |- "a", + | "a" + |""".stripMargin + ) + + val bigDiff = munit.diff.Diffs.unifiedDiff(a, b, 10) + assertNoDiff( + bigDiff, + """|List( + | "a", + | "a", + | "a", + | "a", + |+ "b", + | "a", + | "a", + | "a", + |- "a", + | "a" + | ) + |""".stripMargin + ) + } + }