diff --git a/docs/faqs/directory.conf b/docs/faqs/directory.conf index e62abbeb..22c9873e 100644 --- a/docs/faqs/directory.conf +++ b/docs/faqs/directory.conf @@ -3,4 +3,5 @@ laika.navigationOrder = [ typelevel_stewardship.md ide_faqs.md, other_effects.md + expecty_removal.md ] diff --git a/docs/faqs/expecty_removal.md b/docs/faqs/expecty_removal.md new file mode 100644 index 00000000..03e1fdb6 --- /dev/null +++ b/docs/faqs/expecty_removal.md @@ -0,0 +1,30 @@ +Why are my error messages worse ? +===================================== + +Weaver `0.9.0` reduced the error reporting power of `expect`. + +Prior to `0.9.0`, `expect` would capture the values of its contents on failure. This used the [expecty](https://github.com/eed3si9n/expecty/) macro library to introspect code. While the macro behaved well in most cases, it came with a high maintenance burden. Users reported many bugs in the Scala 3 implementation that were non-trivial to fix. + +As the stewardship of weaver moved to Typelevel, it was decided to cut the cord with expecty in order to eliminate this maintenance burden, and to bring in `clue` (inspired from munit) as a much simpler alternative. + +Expecty's error messages were useful, and this decision may be disappointing for users. However, it will allow maintainers to focus on improving other aspects of weaver. + +## How can I improve my error messages? + +You can rewrite your `expect` assertions into `expect.same` calls, and add `clue` to any remaining assertions. + +We recommend you do this automatically by [applying a scalafix rule](https://scalacenter.github.io/scalafix/docs/rules/external-rules.html). + +Run the `RewriteExpect` rule to rewrite `expect` and `expect.all` into assertions with better failure messages. + +```sh +sbt scalafixAll github:typelevel/weaver-test/RewriteExpect?sha=@VERSION@ +``` + +Run the `AddClueToExpect` rule to add `clue` calls to the remaining `expect` assertions. + +```sh +sbt scalafixAll github:typelevel/weaver-test/AddClueToExpect?sha=@VERSION@ +``` + +Your failure messages will now include the values captured in `clue`. diff --git a/scalafix/readme.md b/scalafix/readme.md index d3418d57..5d60a8c6 100644 --- a/scalafix/readme.md +++ b/scalafix/readme.md @@ -5,3 +5,29 @@ To develop rule: sbt ~tests/test # edit rules/src/main/scala/fix/RenameAssertToExpect.scala ``` + +## Exploring symbols for the `SymbolMatcher` + +Create an input file containing the symbol. + +``` +# create v0_9_0/input/src/main/scala/fix/AddClueToExpectTest.scala +``` + +Compile the code + +``` +sbt compile +``` + +Find the corresponding `.semanticdb` file. + +``` +ls v0_9_0/input/target/jvm-3/meta/META-INF/semanticdb/v0_9_0/input/src/main/scala/fix/AddClueToExpectTest.scala.semanticdb +``` + +Explore the symbols using the [metap](https://scalameta.org/docs/semanticdb/guide.html#installation) tool. + +``` +metap v0_9_0/input/target/jvm-3/meta/META-INF/semanticdb/v0_9_0/input/src/main/scala/fix/AddClueToExpectTest.scala.semanticdb +``` diff --git a/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule index 39b115e0..d146580c 100644 --- a/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule +++ b/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule @@ -1,3 +1,3 @@ fix.RenameAssertToExpect -fix.RenameExpectToExpectSame +fix.RewriteExpect fix.AddClueToExpect diff --git a/scalafix/rules/src/main/scala/fix/AddClueToExpect.scala b/scalafix/rules/src/main/scala/fix/AddClueToExpect.scala index b41a07c7..59cb8eaf 100644 --- a/scalafix/rules/src/main/scala/fix/AddClueToExpect.scala +++ b/scalafix/rules/src/main/scala/fix/AddClueToExpect.scala @@ -8,34 +8,17 @@ class AddClueToExpect extends SemanticRule("AddClueToExpect") { override def fix(implicit doc: SemanticDocument): Patch = { val expectMethod = SymbolMatcher.normalized("weaver/Expectations.Helpers#expect.") + val expectAllMethod = SymbolMatcher.normalized("weaver/ExpectMacro#all().") + doc.tree.collect { - case expectMethod(tree) => - val clues = tree.collect { - case q"clue" => () - } - if (clues.isEmpty) { - tree match { - case q"expect($lhs $op $rhs)" => - Patch.replaceTree( - tree, - s"expect(${makeClue(lhs)} $op ${makeClue(rhs)})") - case q"expect($lhs.$op(...$exprss))" => - val clues = exprss.map(_.map(makeClue)) - Patch.replaceTree( - tree, - q"expect(${makeClue(lhs)}.$op(...$clues))".toString) - case q"expect($op(...$exprss))" => - val clues = exprss.map(_.map(makeClue)) - Patch.replaceTree(tree, q"expect($op(...$clues))".toString) - case q"expect" => - Patch.empty - case other => - println(s"Ignoring $other") - Patch.empty - } - } else { - Patch.empty - } + case Term.Apply.After_4_6_0(expectMethod(_), + Term.ArgClause(List(tree), _)) => + Patch.replaceTree(tree, addClues(tree).toString) + case Term.Apply.After_4_6_0(expectAllMethod(_), + Term.ArgClause(trees, _)) => + trees.map { tree => + Patch.replaceTree(tree, addClues(tree).toString) + }.asPatch }.asPatch } @@ -47,4 +30,37 @@ class AddClueToExpect extends SemanticRule("AddClueToExpect") { case _ => q"clue($expr)" } } + + private def addClues(tree: Tree)(implicit doc: SemanticDocument): Tree = { + val clueSymbol = + SymbolMatcher.normalized("weaver/internals/ClueHelpers#clue().") + val clues = tree.collect { + case clueSymbol(_) => () + } + if (clues.isEmpty) { + tree match { + case q"$lhs $op $rhs" => + q"${makeClue(lhs)} $op ${makeClue(rhs)}" + case q"$lhs.$op(...$exprss)" => + val clues = exprss.map(_.map(makeClue)) + q"${makeClue(lhs)}.$op(...$clues)" + case q"!$lhs.$op(...$exprss)" => + val clues = exprss.map(_.map(makeClue)) + q"!${makeClue(lhs)}.$op(...$clues)" + case q"$expr.$op[..$tpes]" => + q"${makeClue(expr)}.$op[..$tpes]" + case q"!$expr.$op[..$tpes]" => + q"!${makeClue(expr)}.$op[..$tpes]" + case q"!$op(...$exprss)" => + val clues = exprss.map(_.map(makeClue)) + q"!$op(...$clues)" + case q"$op(...$exprss)" => + val clues = exprss.map(_.map(makeClue)) + q"$op(...$clues)" + case other => other + } + } else { + tree + } + } } diff --git a/scalafix/rules/src/main/scala/fix/RenameExpectToExpectSame.scala b/scalafix/rules/src/main/scala/fix/RenameExpectToExpectSame.scala deleted file mode 100644 index ad08abc1..00000000 --- a/scalafix/rules/src/main/scala/fix/RenameExpectToExpectSame.scala +++ /dev/null @@ -1,22 +0,0 @@ -package fix - -import scalafix.v1._ -import scala.meta._ -class RenameExpectToExpectSame - extends SemanticRule("RenameExpectToExpectSame") { - - override def fix(implicit doc: SemanticDocument): Patch = { - val expectMethod = - SymbolMatcher.normalized("weaver/Expectations.Helpers#expect.") - doc.tree.collect { - case expectMethod(tree) => - tree match { - case q"expect($lhs == $rhs)" => - Patch.replaceTree(tree, s"expect.same($lhs, $rhs)") - case _ => - Patch.empty - } - }.asPatch - } - -} diff --git a/scalafix/rules/src/main/scala/fix/RewriteExpect.scala b/scalafix/rules/src/main/scala/fix/RewriteExpect.scala new file mode 100644 index 00000000..0d402725 --- /dev/null +++ b/scalafix/rules/src/main/scala/fix/RewriteExpect.scala @@ -0,0 +1,225 @@ +package fix + +import scalafix.v1._ +import scala.meta._ + +/** + * Rewrites `expect` and `expect.all` calls into `expect.eql` and `expect.same`. + * + * As of weaver `0.9.0`, `expect` and `expect.all` do not capture any + * information on failure. `expect.eql` and `expect.same` have better error + * messages. + * + * This rule can be applied for weaver versions `0.9.x` and above. + */ +class RewriteExpect + extends SemanticRule("RewriteExpect") { + + override def fix(implicit doc: SemanticDocument): Patch = { + val expectMethod = + SymbolMatcher.normalized("weaver/Expectations.Helpers#expect.") + val expectAllMethod = SymbolMatcher.normalized("weaver/ExpectMacro#all().") + doc.tree.collect { + case expectTree @ Term.Apply.After_4_6_0(expectMethod(_), + Term.ArgClause(List(tree), _)) => + // Matched `expect(tree)` + rewrite(tree) match { + case Some(next) => Patch.replaceTree(expectTree, next.toString) + case None => Patch.empty + } + case expectAll @ Term.Apply.After_4_6_0(expectAllMethod(_), + Term.ArgClause(trees, _)) => + // Matched `expect.all(trees)` + val (equalityAssertions, otherAssertions) = partition(trees)(tree => + rewrite(tree) match { + case Some(equality) => Left(equality) + case None => Right(tree) + }) + equalityAssertions match { + case firstEqAssertion :: remainingEqAssertions => + val combinedEqAssertion = + remainingEqAssertions.foldLeft(firstEqAssertion: Term) { + (acc, cur) => + q"$acc.and($cur)" + } + otherAssertions match { + case Nil => + // All assertions were == or ===. Remove the `expect.all` statement. + Patch.replaceTree(expectAll, combinedEqAssertion.toString) + case singleAssertion :: Nil => + // A single assertion is not == or ===. Wrap this in `expect`. + val combinedAssertion = + q"$combinedEqAssertion.and(expect($singleAssertion))" + Patch.replaceTree(expectAll, combinedAssertion.toString) + case _ :: _ :: _ => + // Several assertions are not == or ===. Wrap these in `expect.all`. + val combinedAssertion = + q"$combinedEqAssertion.and(expect.all(..$otherAssertions))" + Patch.replaceTree(expectAll, combinedAssertion.toString) + } + case Nil => + // `expect.all` didn't contain any == or === assertions. + Patch.empty + } + }.asPatch + } + + /** + * Rewrites boolean assertions into `expect.same` and `expect` calls. + * + * For example: + * - `a == b` is rewritten to `expect.same(a, b)` + * - `a && b` is rewritten to `expect(a).and(expect(b))` + * - `if (cond) a else b` is rewritten to + * `if (cond) expect(a) else expect(b)` . + */ + def rewrite(tree: Tree)(implicit doc: SemanticDocument): Option[Term] = { + val catsEqMethod = SymbolMatcher.normalized( + "cats/syntax/EqOps#`===`().") + SymbolMatcher.normalized( + "cats/syntax/EqOps#eqv().") + + tree match { + case q"$lhs == $rhs" if !containsClues(tree) => + val (expected, found) = inferExpectedAndFound(lhs, rhs) + Some(q"expect.same($expected, $found)") + case Term.ApplyInfix.After_4_6_0(lhs, + catsEqMethod(_), + _, + Term.ArgClause(List(rhs), _)) + if !containsClues(tree) => + val (expected, found) = inferExpectedAndFound(lhs, rhs) + Some(q"expect.eql($expected, $found)") + case Term.Apply.After_4_6_0(Term.Select(lhs, catsEqMethod(_)), + Term.ArgClause(List(rhs), _)) + if !containsClues(tree) => + val (expected, found) = inferExpectedAndFound(lhs, rhs) + Some(q"expect.eql($expected, $found)") + case q"$lhs && $rhs" => + val nextLhs = rewrite(lhs).getOrElse(q"expect($lhs)") + val nextRhs = rewrite(rhs).getOrElse(q"expect($rhs)") + Some(q"$nextLhs.and($nextRhs)") + case q"$lhs || $rhs" => + val nextLhs = rewrite(lhs).getOrElse(q"expect($lhs)") + val nextRhs = rewrite(rhs).getOrElse(q"expect($rhs)") + Some(q"$nextLhs.or($nextRhs)") + case q"true" => Some(q"success") + case q"false" => Some(q"""failure("Assertion failed")""") + case q"if ($cond) $lhs else $rhs" if !containsClues(cond) => + val nextLhs = rewrite(lhs).getOrElse(q"expect($lhs)") + val nextRhs = rewrite(rhs).getOrElse(q"expect($rhs)") + Some(q"if ($cond) $nextLhs else $nextRhs") + case q"$expr match { ..case $casesnel }" if !containsClues(expr) => + // Rewrite assertions with `case _ => false` to use `matches` + val wildcardFalse = casesnel.find { + case p"case _ => false" => true + case _ => false + } + wildcardFalse match { + case Some(wildcardCase) if casesnel.size > 1 => + val nextCases = rewriteCases(casesnel.filterNot(_ == wildcardCase)) + Some(q"matches($expr) {..case $nextCases}") + case _ => + val nextCases = rewriteCases(casesnel) + Some(q"$expr match {..case $nextCases }") + } + case _ => + None + } + } + + def rewriteCases(casesnel: List[Case])(implicit + doc: SemanticDocument): List[Case] = { + casesnel.map { caseTree => + val nextExpr = + rewrite(caseTree.body).getOrElse(q"expect(${caseTree.body})") + p"case ${caseTree.pat} if ${caseTree.cond} => $nextExpr" + } + } + + /** + * Checks is an assertion contains `clue(...)`. If so, it should not be + * rewritten. + */ + def containsClues(tree: Tree)(implicit doc: SemanticDocument): Boolean = { + val clueSymbol = + SymbolMatcher.normalized("weaver/internals/ClueHelpers#clue().") + tree.collect { + case clueSymbol(_) => () + }.nonEmpty + + } + + /** + * Infers the order of `expected` and `found` parameters in `expect.same`. + * + * When converting from `expect(a == b)` to `expect.same(a, b)`, we do not + * know which of `a` or `b` is the expected and found value. + * + * The expected value is likely to be: + * - A literal e.g. `"hello-world"` + * - An ADT e.g. `Pet.Cat` + * - An term containing "expected" in its name e.g. `expectedValue` + * - An expression containing literals e.g. `makeId(1)` + */ + def inferExpectedAndFound(left: Term, right: Term)(implicit + doc: SemanticDocument): (Term, Term) = { + def isAlgebraicDataType(tree: Tree): Boolean = tree.symbol.info.exists { + info => + info.isObject && info.isFinal + } + def containsLiterals(term: Term): Boolean = term.collect { + case Lit(_) => () + }.nonEmpty + + def startsWithCapital(term: Term): Boolean = { + val firstLetter = term.syntax.head.toString + firstLetter.capitalize == firstLetter + } + def hasFoundInName(term: Term): Boolean = { + val foundKeywords = List("obtained", "actual", "result", "found") + foundKeywords.exists(term.syntax.contains) + } + def hasExpectedInName(term: Term): Boolean = { + term.syntax.contains("expected") + } + + (left, right) match { + case (Lit(_), _) => (left, right) + case (_, Lit(_)) => (right, left) + case _ if isAlgebraicDataType(right) && !isAlgebraicDataType(left) => + (right, left) + case _ if isAlgebraicDataType(left) && !isAlgebraicDataType(right) => + (left, right) + // Test for expected and found values using naming conventions instead + case _ if hasExpectedInName(right) && !hasExpectedInName(left) => + (right, left) + case _ if hasExpectedInName(left) && !hasExpectedInName(right) => + (left, right) + case _ if hasFoundInName(right) && !hasFoundInName(left) => + (left, right) + case _ if hasFoundInName(left) && !hasFoundInName(right) => + (right, left) + // Assume the symbol is an expected ADT if it starts with a capital + // Symbol information is not present in Scala 3 - see https://github.com/scalacenter/scalafix/issues/2054 + // If the symbol-based ADT test cannot be performed, we perform an additional test based on naming conventions. + case _ if startsWithCapital(right) && !startsWithCapital(left) => + (right, left) + case _ if startsWithCapital(left) && !startsWithCapital(right) => + (left, right) + case _ if containsLiterals(right) && !containsLiterals(left) => + (right, left) + case _ if containsLiterals(left) && !containsLiterals(right) => + (left, right) + case _ => + (left, right) + } + } + + def partition[A, B, C]( + values: List[A])(f: A => Either[B, C]): (List[B], List[C]) = { + val eithers = values.map(f) + val lefts = eithers.collect { case Left(v) => v } + val rights = eithers.collect { case Right(v) => v } + (lefts, rights) + } +} diff --git a/scalafix/v0_9_0/input/src/main/scala/fix/AddClueToExpectTest.scala b/scalafix/v0_9_0/input/src/main/scala/fix/AddClueToExpectTest.scala index bace3e12..5f2ba96e 100644 --- a/scalafix/v0_9_0/input/src/main/scala/fix/AddClueToExpectTest.scala +++ b/scalafix/v0_9_0/input/src/main/scala/fix/AddClueToExpectTest.scala @@ -32,6 +32,18 @@ object AddClueToExpectTest extends SimpleIOSuite { expect(isGreater(a, b)(c)) } + pureTest("negation") { + expect(!a.<(b)) + } + + pureTest("type application") { + expect(a.isInstanceOf[Int]) + } + + pureTest("negated type application") { + expect(!a.isInstanceOf[Int]) + } + pureTest("ignore anonymous functions") { expect(Some(1).fold(true)(_ == 1)) } @@ -47,4 +59,14 @@ object AddClueToExpectTest extends SimpleIOSuite { pureTest("ignore clue") { expect(clue(1) > 0) } + + pureTest("all") { + def isGreater(a: Int, b: Int, c: Int): Boolean = a > b && b > c + expect.all(a == b, Some(2).nonEmpty, isGreater(a, b, c)) + } + + pureTest("ignore clue in expect.all") { + expect.all(clue(1) > 0) + } + } diff --git a/scalafix/v0_9_0/input/src/main/scala/fix/RenameExpectToExpectSameTest.scala b/scalafix/v0_9_0/input/src/main/scala/fix/RenameExpectToExpectSameTest.scala deleted file mode 100644 index 8e16e0c0..00000000 --- a/scalafix/v0_9_0/input/src/main/scala/fix/RenameExpectToExpectSameTest.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* -rule = RenameExpectToExpectSame - */ -package fix -import weaver._ - -object RenameExpectToExpectSameTest extends SimpleIOSuite { - - pureTest("==") { - expect(1 == 1) and expect(2 == 2) && not(expect(1 == 2)) - } - - pureTest("not ==") { - expect(1 > 0) - } - - pureTest("nested ==") { - expect(1 == 1 && 2 == 2) and expect(1 == 1 && 2 >= 2) - } - -} diff --git a/scalafix/v0_9_0/input/src/main/scala/fix/RewriteExpectTest.scala b/scalafix/v0_9_0/input/src/main/scala/fix/RewriteExpectTest.scala new file mode 100644 index 00000000..12a48682 --- /dev/null +++ b/scalafix/v0_9_0/input/src/main/scala/fix/RewriteExpectTest.scala @@ -0,0 +1,116 @@ +/* +rule = RewriteExpect + */ +package fix +import weaver._ +import cats.syntax.all._ + +object RewriteExpectTest extends SimpleIOSuite { + + pureTest("==") { + expect(1 == 1) and expect(2 == 2) && not(expect(1 == 2)) + } + + pureTest("not ==") { + expect(1 > 0) + } + + pureTest("nested ==") { + expect(1 == 1 && 2 == 2) and expect(1 == 1 && 2 >= 2) + } + + pureTest("===") { + expect(1 === 1 && 2.===(2)) + } + + pureTest("eqv") { + expect(1.eqv(1) && (2 eqv 2)) + } + + pureTest("ignore clue") { + expect(clue(1) == 2) + } + + pureTest("|| and &&") { + expect(1 == 2 && 3 > 4 || 4 > 4 && 5 < 5) + } + + pureTest("if then else") { + expect(if (1 == 2) 3 > 4 else 5 == 5) + } + + pureTest("if then else if") { + expect(if (1 == 2) 3 > 4 else if (2 == 3) 5 == 5 else 4 > 3) + } + + pureTest("match") { + val n: Int = 1 + expect(n match { + case 2 => 1 > 2 + case 3 => true + case 4 => false + }) + } + + pureTest("match with wildcard false") { + val n: Int = 1 + expect(n match { + case 2 => 1 > 2 + case _ => false + }) + } + + pureTest("expect.all ==") { + expect.all(1 == 1, 2 == 2, 3 == 3) + } + + pureTest("expect.all ===") { + expect.all(1 === 1, 2 == 2, 3 == 3) + } + + pureTest("expect.all with one not ==") { + expect.all(1 > 0, 2 == 2) + } + + pureTest("expect.all with some not ==") { + expect.all(1 > 0, 2 > 0, 2 == 2) + } + + pureTest("expect.all with || and &&") { + expect.all(1 > 0 && 2 == 2, 2 > 0 || 3 > 4, 2 == 2) + } + + pureTest("expect.all ignore clue") { + expect.all(1 == 1, clue(2) == 2, 3 === clue(3)) + } + + pureTest("infer order with literals") { + val result = 2 + expect(result == 1) + } + + pureTest("infer order with constructors") { + val result = List(2) + expect(result == List(1)) + } + + pureTest("infer order with sealed traits") { + sealed trait Pet + object Pet { + case object Cat extends Pet + case class Dog(friend: Pet) extends Pet + } + val petCat = Pet.Cat + val petDog = Pet.Dog(Pet.Cat) + expect(petCat == Pet.Cat && petDog == Pet.Dog(Pet.Cat)) + } + + pureTest("infer order with common names") { + val expectedId = 1 + val actualId = 2 + val obtainedId = 3 + val result = 4 + expect(result == expectedId && actualId == expectedId && obtainedId == expectedId) + } +} + diff --git a/scalafix/v0_9_0/output/src/main/scala/fix/AddClueToExpectTest.scala b/scalafix/v0_9_0/output/src/main/scala/fix/AddClueToExpectTest.scala index c6170d9c..a08c9aa0 100644 --- a/scalafix/v0_9_0/output/src/main/scala/fix/AddClueToExpectTest.scala +++ b/scalafix/v0_9_0/output/src/main/scala/fix/AddClueToExpectTest.scala @@ -30,6 +30,18 @@ object AddClueToExpectTest extends SimpleIOSuite { expect(isGreater(clue(a), clue(b))(clue(c))) } + pureTest("negation") { + expect(!clue(a).<(clue(b))) + } + + pureTest("type application") { + expect(clue(a).isInstanceOf[Int]) + } + + pureTest("negated type application") { + expect(!clue(a).isInstanceOf[Int]) + } + pureTest("ignore anonymous functions") { expect(clue(Some(1)).fold(true)(_ == 1)) } @@ -45,4 +57,14 @@ object AddClueToExpectTest extends SimpleIOSuite { pureTest("ignore clue") { expect(clue(1) > 0) } + + pureTest("all") { + def isGreater(a: Int, b: Int, c: Int): Boolean = a > b && b > c + expect.all(clue(a) == clue(b), clue(Some(2)).nonEmpty, isGreater(clue(a), clue(b), clue(c))) + } + + pureTest("ignore clue in expect.all") { + expect.all(clue(1) > 0) + } + } diff --git a/scalafix/v0_9_0/output/src/main/scala/fix/RenameExpectToExpectSameTest.scala b/scalafix/v0_9_0/output/src/main/scala/fix/RenameExpectToExpectSameTest.scala deleted file mode 100644 index 7d228550..00000000 --- a/scalafix/v0_9_0/output/src/main/scala/fix/RenameExpectToExpectSameTest.scala +++ /dev/null @@ -1,18 +0,0 @@ -package fix -import weaver._ - -object RenameExpectToExpectSameTest extends SimpleIOSuite { - - pureTest("==") { - expect.same(1, 1) and expect.same(2, 2) && not(expect.same(1, 2)) - } - - pureTest("not ==") { - expect(1 > 0) - } - - pureTest("nested ==") { - expect(1 == 1 && 2 == 2) and expect(1 == 1 && 2 >= 2) - } - -} diff --git a/scalafix/v0_9_0/output/src/main/scala/fix/RewriteExpectTest.scala b/scalafix/v0_9_0/output/src/main/scala/fix/RewriteExpectTest.scala new file mode 100644 index 00000000..494387e7 --- /dev/null +++ b/scalafix/v0_9_0/output/src/main/scala/fix/RewriteExpectTest.scala @@ -0,0 +1,115 @@ +package fix +import weaver._ +import cats.syntax.all._ + +object RewriteExpectTest extends SimpleIOSuite { + + pureTest("==") { + expect.same(1, 1) and expect.same(2, 2) && not(expect.same(1, 2)) + } + + pureTest("not ==") { + expect(1 > 0) + } + + pureTest("nested ==") { + expect.same(1, 1).and(expect.same(2, 2)) and expect.same(1, 1).and(expect(2 >= 2)) + } + + pureTest("===") { + expect.eql(1, 1).and(expect.eql(2, 2)) + } + + pureTest("eqv") { + expect.eql(1, 1).and(expect.eql(2, 2)) + } + + pureTest("ignore clue") { + expect(clue(1) == 2) + } + + pureTest("|| and &&") { + expect.same(1, 2).and(expect(3 > 4)).or(expect(4 > 4).and(expect(5 < 5))) + } + + pureTest("if then else") { + if (1 == 2) expect(3 > 4) else expect.same(5, 5) + } + + pureTest("if then else if") { + if (1 == 2) expect(3 > 4) else if (2 == 3) expect.same(5, 5) else expect(4 > 3) + } + + pureTest("match") { + val n: Int = 1 + n match { + case 2 => + expect(1 > 2) + case 3 => + success + case 4 => + failure("Assertion failed") +} + } + + pureTest("match with wildcard false") { + val n: Int = 1 + matches(n) { + case 2 => + expect(1 > 2) +} + } + + pureTest("expect.all ==") { + expect.same(1, 1).and(expect.same(2, 2)).and(expect.same(3, 3)) + } + + pureTest("expect.all ===") { + expect.eql(1, 1).and(expect.same(2, 2)).and(expect.same(3, 3)) + } + + pureTest("expect.all with one not ==") { + expect.same(2, 2).and(expect(1 > 0)) + } + + pureTest("expect.all with some not ==") { + expect.same(2, 2).and(expect.all(1 > 0, 2 > 0)) + } + + pureTest("expect.all with || and &&") { + expect(1 > 0).and(expect.same(2, 2)).and(expect(2 > 0).or(expect(3 > 4))).and(expect.same(2, 2)) + } + + pureTest("expect.all ignore clue") { + expect.same(1, 1).and(expect.all(clue(2) == 2, 3 === clue(3))) + } + + pureTest("infer order with literals") { + val result = 2 + expect.same(1, result) + } + + pureTest("infer order with constructors") { + val result = List(2) + expect.same(List(1), result) + } + + pureTest("infer order with sealed traits") { + sealed trait Pet + object Pet { + case object Cat extends Pet + case class Dog(friend: Pet) extends Pet + } + val petCat = Pet.Cat + val petDog = Pet.Dog(Pet.Cat) + expect.same(Pet.Cat, petCat).and(expect.same(Pet.Dog(Pet.Cat), petDog)) + } + + pureTest("infer order with common names") { + val expectedId = 1 + val actualId = 2 + val obtainedId = 3 + val result = 4 + expect.same(expectedId, result).and(expect.same(expectedId, actualId)).and(expect.same(expectedId, obtainedId)) + } +}