diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/CallGraphTests.scala b/analysis/src/test/scala/org/polystat/odin/analysis/CallGraphTests.scala index 1a90e8b7..f87e471d 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/CallGraphTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/CallGraphTests.scala @@ -2,13 +2,21 @@ package org.polystat.odin.analysis import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ import org.polystat.odin.analysis.mutualrec.advanced.Program._ -import org.scalatest.wordspec.AnyWordSpec -class CallGraphTests extends AnyWordSpec { +class CallChainTests extends munit.FunSuite { val cc1: CallChain = List("a" % "s", "a" % "b", "a" % "d") val cc2: CallChain = List("a" % "b", "a" % "d", "a" % "s") + test("be shift-equivalent") { + assert(cc1 isShiftOf cc2) + assert(cc2 isShiftOf cc1) + } + +} + +class CallGraphTests extends munit.FunSuite { + val exampleCgBefore: CallGraph = Map( "a" % "s" -> Set("a" % "b"), "a" % "b" -> Set("a" % "d"), @@ -32,63 +40,65 @@ class CallGraphTests extends AnyWordSpec { val exampleNoCycles: CallGraph = exampleCgBefore.updated("a" % "d", Set("a" % "f")) - "call graph" should { - "correctly detect cycles" in { - assert(!exampleNoCycles.containsCycles) - assert(exampleCgBefore.containsCycles) - assert(!exampleCgExtends.containsCycles) - assert(exampleCgAfter.containsCycles) - } - - "correctly extend another call graph" in { - - assert(exampleCgBefore.extendWith(exampleCgExtends).size == 5) - assert(exampleCgBefore.extendWith(exampleCgExtends) == exampleCgAfter) - assert( - exampleCgBefore.extendWith(exampleCgExtends) != - exampleCgExtends.extendWith(exampleCgBefore) - ) + test("correctly detect cycles") { + assert(!exampleNoCycles.containsCycles) + assert(exampleCgBefore.containsCycles) + assert(!exampleCgExtends.containsCycles) + assert(exampleCgAfter.containsCycles) + } - } - "correctly find cycles" in { - val cycles: Set[CallChain] = Set( - List( - MethodName(ObjectName("a"), "s"), - MethodName(ObjectName("a"), "b"), - MethodName(ObjectName("a"), "d"), - MethodName(ObjectName("a"), "s") - ), - List( - MethodName(ObjectName("a"), "b"), - MethodName(ObjectName("a"), "d"), - MethodName(ObjectName("a"), "s"), - MethodName(ObjectName("a"), "b") - ), - List( - MethodName(ObjectName("a"), "c"), - MethodName(ObjectName("a"), "b"), - MethodName(ObjectName("a"), "d"), - MethodName(ObjectName("a"), "s"), - MethodName(ObjectName("a"), "b") - ), - List( - MethodName(ObjectName("a"), "d"), - MethodName(ObjectName("a"), "s"), - MethodName(ObjectName("a"), "b"), - MethodName(ObjectName("a"), "d") - ) + test("correctly extend another call graph") { + + assert(exampleCgBefore.extendWith(exampleCgExtends).size == 5) + assert(exampleCgBefore.extendWith(exampleCgExtends) == exampleCgAfter) + assert( + exampleCgBefore.extendWith(exampleCgExtends) != + exampleCgExtends.extendWith(exampleCgBefore) + ) + + } + + test("correctly find cycles") { + val cycles: Set[CallChain] = Set( + List( + MethodName(ObjectName("a"), "s"), + MethodName(ObjectName("a"), "b"), + MethodName(ObjectName("a"), "d"), + MethodName(ObjectName("a"), "s") + ), + List( + MethodName(ObjectName("a"), "b"), + MethodName(ObjectName("a"), "d"), + MethodName(ObjectName("a"), "s"), + MethodName(ObjectName("a"), "b") + ), + List( + MethodName(ObjectName("a"), "c"), + MethodName(ObjectName("a"), "b"), + MethodName(ObjectName("a"), "d"), + MethodName(ObjectName("a"), "s"), + MethodName(ObjectName("a"), "b") + ), + List( + MethodName(ObjectName("a"), "d"), + MethodName(ObjectName("a"), "s"), + MethodName(ObjectName("a"), "b"), + MethodName(ObjectName("a"), "d") ) + ) - assert(exampleCgBefore.findCycles.toSet == cycles) - } + assert(exampleCgBefore.findCycles.toSet == cycles) } - "call chain" should { - "be shift-equivalent" in { - assert(cc1 isShiftOf cc2) - assert(cc2 isShiftOf cc1) - } + test("correctly extend another call graph") { + + assert(exampleCgBefore.extendWith(exampleCgExtends).size == 5) + assert(exampleCgBefore.extendWith(exampleCgExtends) == exampleCgAfter) + assert( + exampleCgBefore.extendWith(exampleCgExtends) != + exampleCgExtends.extendWith(exampleCgBefore) + ) + } - // } diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/DetectStateAccessTests.scala b/analysis/src/test/scala/org/polystat/odin/analysis/DetectStateAccessTests.scala index afef963f..43a2fdbf 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/DetectStateAccessTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/DetectStateAccessTests.scala @@ -1,14 +1,12 @@ package org.polystat.odin.analysis import cats.effect._ -import cats.effect.unsafe.implicits.global import org.polystat.odin.analysis.EOOdinAnalyzer.directStateAccessAnalyzer import org.polystat.odin.parser.EoParser.sourceCodeEoParser -import org.scalatest.wordspec.AnyWordSpec import EOOdinAnalyzer.OdinAnalysisResult._ -class DetectStateAccessTests extends AnyWordSpec { +class DetectStateAccessTests extends munit.CatsEffectSuite { case class TestCase(label: String, code: String, expected: List[String]) def analyze(code: String): IO[List[String]] = EOOdinAnalyzer @@ -382,23 +380,16 @@ class DetectStateAccessTests extends AnyWordSpec { ) ) - def runTests(tests: List[TestCase]): Unit = + def runTests(prefix: String)(tests: List[TestCase]): Unit = tests.foreach { case TestCase(label, code, expected) => - registerTest(label) { - val obtained = analyze(code).unsafeRunSync() - assert(obtained == expected) + test(prefix + label) { + val obtained = analyze(code) + assertIO(obtained, expected) } } - "analyzer" should { - "find errors" should { - runTests(testsWithDefect) - } - - "not find errors" should { - runTests(testsWithoutDefect) - } + runTests("find errors")(testsWithDefect) - } + runTests("not find errors")(testsWithoutDefect) } diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/LiskovPrincipleTests.scala b/analysis/src/test/scala/org/polystat/odin/analysis/LiskovPrincipleTests.scala index 05325446..a178fd20 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/LiskovPrincipleTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/LiskovPrincipleTests.scala @@ -1,14 +1,12 @@ package org.polystat.odin.analysis import cats.effect._ -import cats.effect.unsafe.implicits.global import org.polystat.odin.analysis.EOOdinAnalyzer.OdinAnalysisResult._ import org.polystat.odin.parser.EoParser.sourceCodeEoParser -import org.scalatest.wordspec.AnyWordSpec import EOOdinAnalyzer.liskovPrincipleViolationAnalyzer -class LiskovPrincipleTests extends AnyWordSpec { +class LiskovPrincipleTests extends munit.CatsEffectSuite { case class TestCase(label: String, code: String, expected: List[String]) @@ -249,23 +247,17 @@ class LiskovPrincipleTests extends AnyWordSpec { ) ) - def runTests(tests: List[TestCase]): Unit = + def runTests(prefix: String)(tests: List[TestCase]): Unit = tests.foreach { case TestCase(label, code, expected) => - registerTest(label) { - val obtained = analyze(code).unsafeRunSync() - assert(obtained == expected) + test(prefix + label) { + val obtained = analyze(code) + assertIO(obtained, expected) } } - "analyzer" should { - "find errors" should { - runTests(testCasesWithErrors) - } + runTests("find errors - ")(testCasesWithErrors) - "not find errors" should { - runTests(testCasesWithoutErrors) - } - } + runTests("not find errors - ")(testCasesWithoutErrors) } diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala index 2277e2d7..4dbb1af2 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala @@ -1,14 +1,14 @@ package org.polystat.odin.analysis +import cats.ApplicativeError import cats.data.NonEmptyList import cats.effect.IO import cats.effect.Sync -import cats.effect.unsafe.implicits.global +import cats.implicits._ import cats.parse.{Parser => P} import cats.parse.{Parser0 => P0} -import cats.syntax.foldable._ -import cats.syntax.functor._ -import fs2.io.file.Files +import munit.CatsEffectSuite +import munit.ScalaCheckEffectSuite import org.polystat.odin.analysis.gens.MutualRecursionTestGen.genProgram import org.polystat.odin.analysis.mutualrec.advanced.Analyzer import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ @@ -16,138 +16,112 @@ import org.polystat.odin.analysis.mutualrec.advanced.Program._ import org.polystat.odin.parser.eo.Parser import org.polystat.odin.utils.files import org.scalacheck.Gen -import org.scalacheck.Prop import org.scalacheck.Test -import org.scalatest.Assertion -import org.scalatest.wordspec.AnyWordSpec -import org.scalatestplus.scalacheck.Checkers +import org.scalacheck.effect.PropF +import pprint.pprintln -class MutualrecTests extends AnyWordSpec with Checkers { +class MutualrecTests extends CatsEffectSuite with ScalaCheckEffectSuite { - val params: Test.Parameters = - Test - .Parameters - .default - .withMinSuccessfulTests(1000) - .withWorkers(4) + import MutualrecTests.parseCallChains - def odinErrors( - code: String - ): Either[String, List[CallChain]] = { - Parser - .parse(code) - .flatMap( - Analyzer - .produceChains[Either[String, *]](_) - ) - } + override def scalaCheckTestParameters: Test.Parameters = Test + .Parameters + .default + .withMinSuccessfulTests(1000) + .withWorkers(4) - "odin" should { - "find mutual recursion in auto-generated tests" in { - val gen = Gen - .choose(2, 100) - .flatMap(n => - genProgram(n).retryUntil(p => p.findMultiObjectCycles.nonEmpty) + def odinErrors[F[_]]( + code: String + )(implicit F: ApplicativeError[F, Throwable]): F[List[CallChain]] = { + F.fromEither( + Parser + .parse(code) + .flatMap( + Analyzer + .produceChains[Either[String, *]](_) ) + .leftMap(new Exception(_)) + ) + } - val prop = Prop - .forAllNoShrink(gen) { prog => - val code = prog.toEO + "\n" - val assertion = for { - errors <- odinErrors(code) - } yield errors.toSet == prog.findMultiObjectCycles.toSet - assertion.getOrElse(false) - } - check(prop, params) - } + test("find mutual recursion in auto-generated tests") { + val gen = Gen + .choose(2, 100) + .flatMap(n => + genProgram(n).retryUntil(p => p.findMultiObjectCycles.nonEmpty) + ) - def runTestsFrom[F[_]: Sync: Files]( - path: String, - check: (String, String) => Assertion, - ): F[Unit] = - for { - files <- files.readEoCodeFromResources[F](path) - } yield files.foreach { case (name, code) => - registerTest(name)(check(name, code)) - } - - "manual tests" should { - - "find mutual recursion in" should { - val fileNameToChain = Map( - "mutual_rec_somewhere.eo" -> - """ - |c.g -> b.f -> c.g - |b.f -> c.g -> b.f - |""".stripMargin, - "nested_eo.eo" -> - """ - |nestedA.a.g -> nestedB.f -> nestedA.a.g - |nestedB.f -> nestedA.a.g -> nestedB.f - |""".stripMargin, - "nested_objects.eo" -> - """ - |nested_objects.abstractions.base.f -> nested_objects.implementations.derived.g -> nested_objects.abstractions.base.f - |nested_objects.implementations.derived.g -> nested_objects.abstractions.base.f -> nested_objects.implementations.derived.g - |""".stripMargin, - "realistic.eo" -> - """ - |c.new.g -> a.new.f -> c.new.g - |a.new.f -> c.new.g -> a.new.f - |""".stripMargin, - "3rd_defect.eo" -> - """ - |test.parent.g -> test.child.h -> test.parent.g - |test.child.h -> test.parent.g -> test.child.h - |""".stripMargin + PropF.forAllNoShrinkF(gen) { prog => + val code = prog.toEO + "\n" + odinErrors[IO](code) + .map(errors => + assertEquals(errors.toSet, prog.findMultiObjectCycles.toSet) ) - - runTestsFrom[IO]( - "/mutualrec/with_recursion", - (fileName, code) => { - val passes = - for { - expectedErrors <- - MutualrecTests.parseCallChains(fileNameToChain(fileName)) - actualErrors <- odinErrors(code) - } yield actualErrors.toSet == expectedErrors.toSet - - assert(passes.getOrElse(false)) - } - ).unsafeRunSync() - } - - "not find mutual recursion" should { - runTestsFrom[IO]( - "/mutualrec/no_recursion", - (_, code) => { - assert( - odinErrors(code) - .map(errors => errors.isEmpty) - .getOrElse(false) - ) - } - ).unsafeRunSync() - } - - "fail" should { - runTestsFrom[IO]( - "/mutualrec/failing", - (fileName, code) => { - val expectedError = Map( - "decoration.eo" -> Left( - """Method "f" was called from the object "derived", although it is not defined there!""" - ) - ) - assert(odinErrors(code) == expectedError(fileName)) - } - ).unsafeRunSync() - } - + .onError(_ => IO.delay(pprintln(prog, height = 10000))) } - } + def runTestsFrom[F[_]: Sync](path: String)( + check: (String, String) => F[Unit] + ): F[Unit] = + files + .readEoCodeFromDirectory[F](path) + .map(files => + files.foreach { case (name, code) => + test(name)(check(name, code)) + } + ) + + val fileNameToChain: Map[String, String] = Map( + "mutual_rec_somewhere.eo" -> + """ + |c.g -> b.f -> c.g + |b.f -> c.g -> b.f + |""".stripMargin, + "nested_eo.eo" -> + """ + |nestedA.a.g -> nestedB.f -> nestedA.a.g + |nestedB.f -> nestedA.a.g -> nestedB.f + |""".stripMargin, + "nested_objects.eo" -> + """ + |nested_objects.abstractions.base.f -> nested_objects.implementations.derived.g -> nested_objects.abstractions.base.f + |nested_objects.implementations.derived.g -> nested_objects.abstractions.base.f -> nested_objects.implementations.derived.g + |""".stripMargin, + "realistic.eo" -> + """ + |c.new.g -> a.new.f -> c.new.g + |a.new.f -> c.new.g -> a.new.f + |""".stripMargin, + "3rd_defect.eo" -> + """ + |test.parent.g -> test.child.h -> test.parent.g + |test.child.h -> test.parent.g -> test.child.h + |""".stripMargin + ) + + val expectedError: Map[String, String] = Map( + "decoration.eo" -> + """Method "f" was called from the object "derived", although it is not defined there!""" + ) + + runTestsFrom[IO]("mutualrec/with_recursion") { (fileName, code) => + for { + expectedErrors <- parseCallChains[IO](fileNameToChain(fileName)) + actualErrors <- odinErrors[IO](code) + // _ <- actualErrors.traverse_(e => IO.println(e.show)) + } yield assertEquals(actualErrors.toSet, expectedErrors.toSet) + }.unsafeRunSync() + + runTestsFrom[IO]("mutualrec/no_recursion") { (_, code) => + odinErrors[IO](code).map(errors => assert(errors.isEmpty)) + }.unsafeRunSync() + + runTestsFrom[IO]("mutualrec/failing") { (fileName, code) => + interceptIO[Exception](odinErrors[IO](code)) + .map(error => assertEquals(error.getMessage, expectedError(fileName))) + }.unsafeRunSync() + } object MutualrecTests { @@ -182,7 +156,13 @@ object MutualrecTests { val ccs: P[List[CallChain]] = eol.?.with1 *> (cc.repSep(eol).map(_.toList) <* eol.?) - def parseCallChains(str: String): Either[P.Error, List[CallChain]] = - ccs.parseAll(str) + def parseCallChains[F[_]]( + str: String + )(implicit F: ApplicativeError[F, Throwable]): F[List[CallChain]] = + F.fromEither( + ccs + .parseAll(str) + .leftMap(error => new Exception(error.toString)) + ) } diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/UnjustifiedAssumptionTests.scala b/analysis/src/test/scala/org/polystat/odin/analysis/UnjustifiedAssumptionTests.scala index c7fa1408..bf2b64d9 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/UnjustifiedAssumptionTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/UnjustifiedAssumptionTests.scala @@ -1,14 +1,12 @@ package org.polystat.odin.analysis import cats.effect._ -import cats.effect.unsafe.implicits.global import org.polystat.odin.analysis.EOOdinAnalyzer.OdinAnalysisResult._ import org.polystat.odin.parser.EoParser.sourceCodeEoParser -import org.scalatest.wordspec.AnyWordSpec import EOOdinAnalyzer.unjustifiedAssumptionAnalyzer -class UnjustifiedAssumptionTests extends AnyWordSpec { +class UnjustifiedAssumptionTests extends munit.CatsEffectSuite { case class TestCase(label: String, code: String, expected: List[String]) @@ -258,23 +256,17 @@ class UnjustifiedAssumptionTests extends AnyWordSpec { ) ) - def runTests(tests: List[TestCase]): Unit = + def runTests(prefix: String)(tests: List[TestCase]): Unit = tests.foreach { case TestCase(label, code, expected) => - registerTest(label) { - val obtained = analyze(code).unsafeRunSync() - assert(obtained == expected) + test(prefix + label) { + val obtained = analyze(code) + assertIO(obtained, expected) } } - "analyzer" should { - "find errors" should { - runTests(testCasesWithErrors) - } + runTests("find errors - ")(testCasesWithErrors) - "not find errors" should { - runTests(testCasesWithoutErrors) - } - } + runTests("not find errors - ")(testCasesWithoutErrors) } diff --git a/analysis/src/test/scala/org/polystat/odin/analysis/inlining/InliningTests.scala b/analysis/src/test/scala/org/polystat/odin/analysis/inlining/InliningTests.scala index 538e9432..32c47be1 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/inlining/InliningTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/inlining/InliningTests.scala @@ -8,123 +8,60 @@ import org.polystat.odin.analysis.utils.inlining.LocatorContext import org.polystat.odin.backend.eolang.ToEO.instances._ import org.polystat.odin.backend.eolang.ToEO.ops._ import org.polystat.odin.parser.eo.Parser -import org.scalatest.wordspec.AnyWordSpec import SetLocatorsTestCases._ import InlineCallsTestCases._ // import org.polystat.odin.core.ast._ -class InliningTests extends AnyWordSpec { - - "setLocators" should { - val locatorTests: List[LocatorTestCase] = List( - vitaliyTestLocators, - nikolayTestLocators, - nonExistentNameTestLocators, - builtinObjects, - autoGenerated, - withAliases, - withPredef - ) - locatorTests.foreach { case LocatorTestCase(label, before, after) => - registerTest(label) { - val expected: EitherNel[String, String] = after - val obtained: EitherNel[String, String] = Parser - .parse(before) - .leftMap(Nel.one) - .flatMap(LocatorContext.setLocators) - .map(_.toEOPretty) - assert(expected == obtained) - } +class InliningTests extends munit.FunSuite { + + val locatorTests: List[LocatorTestCase] = List( + vitaliyTestLocators, + nikolayTestLocators, + nonExistentNameTestLocators, + builtinObjects, + autoGenerated, + withAliases, + withPredef + ) + + locatorTests.foreach { case LocatorTestCase(label, before, after) => + test("setLocators - " + label) { + val expected: EitherNel[String, String] = after + val obtained: EitherNel[String, String] = Parser + .parse(before) + .leftMap(Nel.one) + .flatMap(LocatorContext.setLocators) + .map(_.toEOPretty) + assertEquals(obtained, expected) } } - "inlineCalls" should { - val inliningTests: List[InliningTestCase] = List( - nikolayTestInlining, - vitaliyTestInlining, - fakeCallTest, - looksFakeButRealTest, - factorialTest, - evenOddTest, - average3Test, - average3WithComponentsTest, - notEnoughArgs, - tooManyArgs, - withInheritance, - ) - .concat(simpleTests) - - inliningTests.foreach { case InliningTestCase(label, before, after) => - registerTest(label) { - val expected = after - val obtained = Parser - .parse(before) - .leftMap(Nel.one) - .flatMap(Inliner.inlineAllCalls) - .map(_.toEOPretty) - assert(obtained == expected) - } - + val inliningTests: List[InliningTestCase] = List( + nikolayTestInlining, + vitaliyTestInlining, + fakeCallTest, + looksFakeButRealTest, + factorialTest, + evenOddTest, + average3Test, + average3WithComponentsTest, + notEnoughArgs, + tooManyArgs, + withInheritance, + ) + .concat(simpleTests) + + inliningTests.foreach { case InliningTestCase(label, before, after) => + test("inlineCalls - " + label) { + val expected = after + val obtained = Parser + .parse(before) + .leftMap(Nel.one) + .flatMap(Inliner.inlineAllCalls) + .map(_.toEOPretty) + assertEquals(obtained, expected) } } } - -object InliningTests { - - def main(args: Array[String]): Unit = { - val code = """ - |[] > obj - | [self y] > g - | 3.div y > @ - | - | [self z] > method - | self.g self z > x - | x > @ - | - | - | [] > bebra - | ^.zhepa > @ - | [self] > aboba - | 12 > @ - | [] > zhepa - | opa > @ - | [self] > aboba - | 1 > @ - | [self] > indirect - | 2 > @ - | [] > opa - | [self] > even-more-indirect - | 3 > @ - | - | - | - |[] > derived - | obj > @ - | [self y] > g - | 3.div (y.add 1) > @ - | [] > kukozh - | ^.^.obj.bebra > @ - | - |[] > am - | derived > @ - |""".stripMargin - - val parsed = Parser.parse(code) - -// val tree = parsed -// .leftMap(Nel.one) -// .flatMap(Inliner.createObjectTree) - -// val newTree = tree -// .flatMap(Inliner.resolveParents) - - parsed - .map(Inliner.zipMethodsWithTheirInlinedVersionsFromParent) - .bimap(pprint.pprintln(_), pprint.pprintln(_)) - .merge - - } - -} diff --git a/parser/src/test/scala/org/polystat/odin/parser/EOParserTestSuite.scala b/parser/src/test/scala/org/polystat/odin/parser/EOParserTestSuite.scala deleted file mode 100644 index 4868622a..00000000 --- a/parser/src/test/scala/org/polystat/odin/parser/EOParserTestSuite.scala +++ /dev/null @@ -1,123 +0,0 @@ -package org.polystat.odin.parser - -import org.polystat.odin.core.ast.EOProg -import org.polystat.odin.core.ast.astparams.EOExprOnly -import org.polystat.odin.parser.ast_tests.FullProgramExamples -import org.polystat.odin.parser.ast_tests.SingleLineExamples -import org.scalacheck.Gen -import org.scalacheck.Prop -import org.scalacheck.Test -import org.scalatest.Assertion -import org.scalatest.wordspec.AnyWordSpec -import org.scalatestplus.scalacheck.Checkers - -import TestUtils._ - -trait EOParserTestSuite extends AnyWordSpec with Checkers { - - private val numberOfTests = 10000 - - val scalacheckParams: Test.Parameters = Test - .Parameters - .default - .withMinSuccessfulTests(numberOfTests) - .withMaxSize(numberOfTests) - .withMinSize(numberOfTests) - .withWorkers(8) - .withTestCallback(new Test.TestCallback { - - override def onTestResult(name: String, result: Test.Result): Unit = { - println( - s""" - |Finished with - |Status: ${result.status} - |Tests passed: ${result.succeeded} - |Tests discarded: ${result.discarded} - |Time: ${result.time}ms - |""".stripMargin - ) - } - - }) - .withLegacyShrinking(false) - - implicit def bool2Assertion(b: Boolean): Assertion = { - assert(b) - } - - type ParserT[A] - type Success[A] - type Error - type ParserResultT[A] = Either[Error, Success[A]] - - def checkParser[A]( - check: ParserResultT[A] => Boolean - )(parser: ParserT[A], input: String): Boolean - - def shouldFailParsing[A]: (ParserT[A], String) => Boolean = - checkParser(_.isLeft) - - def shouldParse[A]: (ParserT[A], String) => Boolean = checkParser(_.isRight) - - def shouldProduceAST[AST](ast: AST): (ParserT[AST], String) => Boolean = - checkParser { - case Left(_) => false - case Right(value) => value == ast - } - - def programParser: ParserT[EOProg[EOExprOnly]] - - def singleLineApplicationParser: ParserT[EOExprOnly] - - type Tests[A] = List[TestCase[A]] - - def runParserTests[A]( - parser: ParserT[A], - correctTests: Tests[A] = Nil, - incorrectTests: Tests[A] = Nil - ): Unit = { - ( - (correctTests, shouldParse[A]) :: - (incorrectTests, shouldFailParsing[A]) :: - Nil - ).foreach { case (examples, check) => - examples.foreach { - case TestCase(label, code, Some(ast)) => - registerTest(label) { - shouldProduceAST[A](ast)(parser, code) - } - case TestCase(label, code, None) => - registerTest(label) { - check(parser, code) - } - } - } - } - - def runParserTestsGen[A](p: ParserT[A], gen: Gen[String]): Assertion = { - check( - Prop.forAll(gen) { generated => - shouldParse(p, generated) - }, - scalacheckParams - ) - } - - val examplesFromSources: Tests[EOProg[EOExprOnly]] = getListOfFiles( - "/eo_sources" - ).map(filename => TestCase(fileNameOf(filename), readCodeFrom(filename))) - - "full programs" should { - runParserTests(programParser, correctTests = examplesFromSources) - runParserTests(programParser, correctTests = FullProgramExamples.correct) - - } - - "single line application" should { - runParserTests[EOExprOnly]( - singleLineApplicationParser, - correctTests = SingleLineExamples.correct - ) - } - -} diff --git a/parser/src/test/scala/org/polystat/odin/parser/TestUtils.scala b/parser/src/test/scala/org/polystat/odin/parser/TestUtils.scala index 11e16360..3b28e730 100644 --- a/parser/src/test/scala/org/polystat/odin/parser/TestUtils.scala +++ b/parser/src/test/scala/org/polystat/odin/parser/TestUtils.scala @@ -3,10 +3,6 @@ package org.polystat.odin.parser import com.github.tarao.nonempty.collection.NonEmpty import pprint.PPrinter -import java.nio.file.Files -import java.nio.file.Paths -import scala.jdk.CollectionConverters._ - object TestUtils { case class TestCase[AST]( @@ -27,19 +23,4 @@ object TestUtils { } ) - def fileNameOf(source: String): String = { - Paths.get(source).getFileName.toString - } - - def readCodeFrom(fileName: String): String = { - val code = io.Source.fromFile(fileName) - try code.mkString - finally code.close() - } - - def getListOfFiles(dir: String): List[String] = { - val path = getClass.getResource(dir).toURI - Files.list(Paths.get(path)).iterator().asScala.map(_.toString).toList - } - } diff --git a/parser/src/test/scala/org/polystat/odin/parser/eo/ParserTests.scala b/parser/src/test/scala/org/polystat/odin/parser/eo/ParserTests.scala index 649ab6ae..e85d5e54 100644 --- a/parser/src/test/scala/org/polystat/odin/parser/eo/ParserTests.scala +++ b/parser/src/test/scala/org/polystat/odin/parser/eo/ParserTests.scala @@ -1,235 +1,319 @@ package org.polystat.odin.parser.eo +import cats.effect.IO import cats.parse.{Parser => P} import cats.parse.{Parser0 => P0} +import cats.syntax.all._ import org.polystat.odin.backend.eolang.ToEO.instances._ import org.polystat.odin.backend.eolang.ToEO.ops._ import org.polystat.odin.core.ast._ import org.polystat.odin.core.ast.astparams.EOExprOnly -import org.polystat.odin.parser.EOParserTestSuite import org.polystat.odin.parser.TestUtils.TestCase +import org.polystat.odin.parser.ast_tests._ import org.polystat.odin.parser.gens._ +import org.polystat.odin.utils.files +import org.scalacheck.Gen +import org.scalacheck.Prop +import org.scalacheck.Test + +import ParserTests._ + +abstract class ParserTests + extends munit.CatsEffectSuite + with munit.ScalaCheckSuite { + + override def scalaCheckTestParameters: Test.Parameters = Test + .Parameters + .default + .withMinSuccessfulTests(numberOfTests) + .withMaxSize(numberOfTests) + .withMinSize(numberOfTests) + .withWorkers(8) + // .withTestCallback(new Test.TestCallback { + + // override def onTestResult(name: String, result: Test.Result): Unit = { + // println( + // s""" + // |Finished with + // |Status: ${result.status} + // |Tests passed: ${result.succeeded} + // |Tests discarded: ${result.discarded} + // |Time: ${result.time}ms + // |""".stripMargin + // ) + // } + + // }) + .withLegacyShrinking(false) + + def runParserTests[A]( + prefix: String, + parser: ParserT[A], + correctTests: IO[List[TestCase[A]]] = IO.pure(List()), + incorrectTests: IO[List[TestCase[A]]] = IO.pure(List()), + ): IO[Unit] = { + List( + (correctTests, shouldParse[A]), + (incorrectTests, shouldFailParsing[A]), + ).traverse_ { case (examples, check) => + examples.map(cases => + cases.foreach { + case TestCase(label, code, Some(ast)) => + test(prefix + label)(shouldProduceAST[A](ast)(parser, code)) + case TestCase(label, code, None) => + test(prefix + label)(check(parser, code)) + } + ) + } + } -class ParserTests extends EOParserTestSuite { - - override type Success[A] = A - override type Error = P.Error - - override def programParser: ParserT[EOProg[EOExprOnly]] = - Left(Parser.program(0, 2)) +} - override def singleLineApplicationParser: ParserT[EOExprOnly] = - Right(SingleLine.singleLineApplication) +class TokenTests extends ParserTests { - type ParserT[A] = Either[P0[A], P[A]] + property("comments or empty lines") { + runParserTestGen( + Tokens.emptyLinesOrComments.asLeft, + eo.emptyLinesOrComments + ) + } - def checkParser[A]( - check: ParserResultT[A] => Boolean - )(parser: ParserT[A], input: String): Boolean = { - val parsed = parser match { - case Left(value) => value.parseAll(input) - case Right(value) => value.parseAll(input) - } - parsed match { - case Left(value) => - println(new Prettyprint(input = input).prettyprint(value)) - case Right(_) => () + property("strings - generated tests") { + Prop.forAllNoShrink(eo.string) { string => + shouldParse(Tokens.string.asRight, string) } - check(parsed) } - "tokens" should { - - "comments or empty lines" in { - runParserTestsGen( - Left(Tokens.emptyLinesOrComments), - eo.emptyLinesOrComments + val stringTests: List[TestCase[String]] = List( + TestCase( + label = "basic escapes", + code = + """"\nHello,\n\r\tthis is a 'char'\n\t\b\tand this is a \"string\"\n"""", + ast = Some( + "\nHello,\n\r\tthis is a 'char'\n\t\b\tand this is a \"string\"\n" ) - } + ), + TestCase( + label = "russian unicode", + code = + "\"\\u043F\\u0440\\u0438\\u0432\\u0435\\u0442\\u002C\\u0020\\u044F\\u0020\\u0027\\u0441\\u0438\\u043C" + + "\\u0432\\u043E\\u043B\\u0027\\u002C\\u0020\\u0430\\u0020\\u044D\\u0442\\u043E\\u0020\\u0022\\u0441\\u0442" + + "\\u0440\\u043E\\u0447\\u043A\\u0430\\u0022\"", + ast = Some("привет, я 'символ', а это \"строчка\"") + ), + TestCase( + label = "japanese unicode", + code = + "\"\\u60e3\\u6d41\\u00b7\\u660e\\u65e5\\u9999\\u00b7\\u5170\\u683c\\u96f7\"", + ast = Some("惣流·明日香·兰格雷") + ) + ) - "strings" should { - val stringTests = List( - TestCase( - label = "basic escapes", - code = - """"\nHello,\n\r\tthis is a 'char'\n\t\b\tand this is a \"string\"\n"""", - ast = Some( - "\nHello,\n\r\tthis is a 'char'\n\t\b\tand this is a \"string\"\n" - ) - ), - TestCase( - label = "russian unicode", - code = - "\"\\u043F\\u0440\\u0438\\u0432\\u0435\\u0442\\u002C\\u0020\\u044F\\u0020\\u0027\\u0441\\u0438\\u043C" + - "\\u0432\\u043E\\u043B\\u0027\\u002C\\u0020\\u0430\\u0020\\u044D\\u0442\\u043E\\u0020\\u0022\\u0441\\u0442" + - "\\u0440\\u043E\\u0447\\u043A\\u0430\\u0022\"", - ast = Some("привет, я 'символ', а это \"строчка\"") - ), - TestCase( - label = "japanese unicode", - code = - "\"\\u60e3\\u6d41\\u00b7\\u660e\\u65e5\\u9999\\u00b7\\u5170\\u683c\\u96f7\"", - ast = Some("惣流·明日香·兰格雷") - ) - ) - runParserTests(Right(Tokens.string), stringTests) + runParserTests("strings - ", Tokens.string.asRight, stringTests.pure[IO]) + .unsafeRunSync() - "pass" in { - runParserTestsGen(Right(Tokens.string), eo.string) - } - } + val charTests: List[TestCase[Char]] = List( + TestCase(label = "new line", code = "\'\\n\'", Some('\n')), + TestCase(label = "tab", code = "\'\\t\'", Some('\t')), + TestCase(label = "A", code = "'A'", Some('A')), + TestCase(label = "Ж (escaped)", code = "\'\\u0416\'", Some('Ж')), + TestCase(label = "Ж (literal)", code = "'Ж'", Some('Ж')), + TestCase(label = "香 (escaped)", code = "\'\\u9999\'", Some('香')), + ) - "chars" should { - "pass" in { - runParserTestsGen(Right(Tokens.char), eo.char) - } - val charTests = List( - TestCase(label = "new line", code = "\'\\n\'", Some('\n')), - TestCase(label = "tab", code = "\'\\t\'", Some('\t')), - TestCase(label = "A", code = "'A'", Some('A')), - TestCase(label = "Ж (escaped)", code = "\'\\u0416\'", Some('Ж')), - TestCase(label = "Ж (literal)", code = "'Ж'", Some('Ж')), - TestCase(label = "香 (escaped)", code = "\'\\u9999\'", Some('香')), - ) - runParserTests(Right(Tokens.char), charTests) - } + runParserTests("chars - ", Tokens.char.asRight, charTests.pure[IO]) - "identifiers" in { - runParserTestsGen(Right(Tokens.identifier), eo.identifier) - } + property("chars - generated tests") { + runParserTestGen(Tokens.char.asRight, eo.char) + } - "integers" in { - runParserTestsGen(Right(Tokens.integer), eo.integer) + property("identifiers") { + Prop.forAllNoShrink(eo.identifier) { id => + shouldProduceAST[String](id)(Tokens.identifier.asRight, id) } + } - "floats" in { - runParserTestsGen(Right(Tokens.float), eo.float) + property("integers") { + Prop.forAllNoShrink(eo.integer) { int => + shouldProduceAST[Int](int.toInt)(Tokens.integer.asRight, int) } } - "metas" should { - "package meta" in { - runParserTestsGen(Right(Metas.packageMeta), eo.packageMeta) + property("floats") { + Prop.forAllNoShrink(eo.float) { float => + shouldProduceAST[Float](float.toFloat)(Tokens.float.asRight, float) } - "alias meta" in { - runParserTestsGen(Right(Metas.aliasMeta), eo.aliasMeta) - } + } - "rt meta" in { - runParserTestsGen(Right(Metas.rtMeta), eo.rtMeta) - } +} - "all metas" in { - runParserTestsGen(Left(Metas.metas), eo.metas) - } +class MetasTests extends ParserTests { + + property("package meta") { + runParserTestGen(Metas.aliasMeta.asRight, eo.aliasMeta) } - "abstraction params" should { - "pass" in { - runParserTestsGen(Right(SingleLine.params), eo.abstractionParams) - } - "fail" in { - shouldFailParsing(Right(SingleLine.params), "[...]") - shouldFailParsing(Right(SingleLine.params), "[") - shouldFailParsing(Right(SingleLine.params), "[a..]") - shouldFailParsing(Right(SingleLine.params), "[a a a a a a a..]") - shouldFailParsing(Right(SingleLine.params), "[a ...]") - } + property("alias meta") { + runParserTestGen(Metas.aliasMeta.asRight, eo.aliasMeta) } - "binding name" should { - "pass" in { - runParserTestsGen(Right(SingleLine.bndName), eo.bndName) - } + property("rt meta") { + runParserTestGen(Metas.rtMeta.asRight, eo.rtMeta) + } - val incorrectTests = List[TestCase[EONamedBnd]]( - TestCase("incorrect symbol", " < name"), - TestCase("no name", " > !"), - ) + property("all metas") { + runParserTestGen(Metas.metas.asLeft, eo.metas) + } - runParserTests[EONamedBnd]( - Right(SingleLine.bndName), - incorrectTests = incorrectTests - ) +} + +class AbstractionParamsTests extends ParserTests { + + property("should parse") { + runParserTestGen(SingleLine.params.asRight, eo.abstractionParams) } - "single line application" should { - "pass" in { - runParserTestsGen( - singleLineApplicationParser, - eo.singleLineApplication(maxDepth = 4) - ) - } + test("should fail") { + shouldFailParsing(Right(SingleLine.params), "[...]") + shouldFailParsing(Right(SingleLine.params), "[") + shouldFailParsing(Right(SingleLine.params), "[a..]") + shouldFailParsing(Right(SingleLine.params), "[a a a a a a a..]") + shouldFailParsing(Right(SingleLine.params), "[a ...]") } - "object" should { - "pass" in { - runParserTestsGen( - Right(Parser.`object`(0, 4)), - eo.`object`( - named = true, - indentationStep = 4, - maxDepth = 4 - ) - ) - } +} + +class BindingNameTests extends ParserTests { + + property("pass") { + runParserTestGen(SingleLine.bndName.asRight, eo.bndName) } - "program" should { - "pass auto-generated programs" in { - runParserTestsGen( - Left(Parser.program(0, indentationStep = 2)), - eo.program(indentationStep = 2, maxDepth = 4) - ) - } + val incorrectTests: List[TestCase[EONamedBnd]] = List[TestCase[EONamedBnd]]( + TestCase("incorrect symbol", " < name"), + TestCase("no name", " > !"), + ) + + runParserTests[EONamedBnd]( + "binding name should fail parsing - ", + SingleLine.bndName.asRight, + incorrectTests = incorrectTests.pure[IO] + ).unsafeRunSync() + +} - "prog->pretty == prog->pretty->parsed->pretty" in { - import org.scalacheck.Prop - check( - Prop.forAll(ast.eoProg(4)) { prog => - val expected: Either[String, String] = Right(prog.toEOPretty) - val actual: Either[String, String] = - Parser.parse(prog.toEOPretty).map(_.toEOPretty) - expected == actual - }, - scalacheckParams +class ProgramTests extends ParserTests { + + property("single line application - generated tests") { + runParserTestGen( + singleLineApplicationParser, + eo.singleLineApplication(maxDepth = 4) + ) + } + + property("object - generated tests") { + runParserTestGen( + Right(Parser.`object`(0, 4)), + eo.`object`( + named = true, + indentationStep = 4, + maxDepth = 2 ) + ) + } + + property("program - generated tests") { + runParserTestGen( + Left(Parser.program(0, indentationStep = 2)), + eo.program(indentationStep = 2, maxDepth = 2) + ) + } + + property("program - prog->pretty == prog->pretty->parsed->pretty") { + Prop.forAll(ast.eoProg(4)) { prog => + val expected: Either[String, String] = Right(prog.toEOPretty) + val actual: Either[String, String] = + Parser.parse(prog.toEOPretty).map(_.toEOPretty) + assertEquals(actual, expected) } } + runParserTests( + "hand-crafted examples - ", + programParser, + correctTests = examplesFromSources + ) + .unsafeRunSync() + + runParserTests( + "mutual recursion example - ", + programParser, + correctTests = IO.pure(FullProgramExamples.correct) + ).unsafeRunSync() + + runParserTests( + "single line examples - ", + singleLineApplicationParser, + correctTests = IO.pure(SingleLineExamples.correct) + ).unsafeRunSync() + } object ParserTests { - def main(args: Array[String]): Unit = { - val code = - """ - |[a b c] (123 > a) (123 > a) - |[a v v] > a - | 1 > a - | 2 > v - | 3 > a - |^.^.^.aboba - |$.self - |$ - |^ - | - | - |""".stripMargin - - val parsed = Parser.parse(code) - - parsed.foreach(pprint.pprintln(_)) - - parsed match { - case Left(value) => println(value) - case Right(value) => { - println(value.toEOPretty) - println(Parser.parse(value.toEOPretty).map(_ == value)) - } + private val numberOfTests = 10000 + + type Success[A] = A + type Error = P.Error + type ParserT[A] = Either[P0[A], P[A]] + type ParserResultT[A] = Either[Error, Success[A]] + + def programParser: ParserT[EOProg[EOExprOnly]] = + Left(Parser.program(0, 2)) + + def singleLineApplicationParser: ParserT[EOExprOnly] = + Right(SingleLine.singleLineApplication) + + def checkParser[A]( + check: ParserResultT[A] => Boolean + )(parser: ParserT[A], input: String): Boolean = { + val parsed = parser match { + case Left(value) => value.parseAll(input) + case Right(value) => value.parseAll(input) } + // parsed match { + // case Left(value) => + // println(new Prettyprint(input = input).prettyprint(value)) + // case Right(_) => () + // } + check(parsed) + } + def runParserTestGen[A](parser: ParserT[A], gen: Gen[String]): Prop = { + Prop.forAllNoShrink(gen) { it => + shouldParse(parser, it) + } } + def shouldFailParsing[A]: (ParserT[A], String) => Boolean = + checkParser(_.isLeft) + + def shouldParse[A]: (ParserT[A], String) => Boolean = checkParser(_.isRight) + + def shouldProduceAST[AST](ast: AST): (ParserT[AST], String) => Boolean = + checkParser { + case Left(_) => false + case Right(value) => value == ast + } + + val examplesFromSources: IO[List[TestCase[EOProg[EOExprOnly]]]] = + files + .readEoCodeFromDirectory[IO]("eo_sources") + .map( + _.map { case (name, code) => + TestCase(label = name, code = code) + } + ) + } diff --git a/parser/src/test/scala/org/polystat/odin/parser/xmir/ParserTests.scala b/parser/src/test/scala/org/polystat/odin/parser/xmir/ParserTests.scala index 38b21218..7a4729ed 100644 --- a/parser/src/test/scala/org/polystat/odin/parser/xmir/ParserTests.scala +++ b/parser/src/test/scala/org/polystat/odin/parser/xmir/ParserTests.scala @@ -1,21 +1,21 @@ package org.polystat.odin.parser.xmir +import cats.ApplicativeError import cats.effect.IO import cats.effect.Sync -import cats.effect.testing.scalatest.AsyncIOSpec import cats.implicits._ import org.polystat.odin.core.ast.EOBnd import org.polystat.odin.core.ast.astparams.EOExprOnly import org.polystat.odin.parser.EoParser.sourceCodeEoParser import org.polystat.odin.parser.ast_tests.FullProgramExamples -import org.scalatest.Assertion -import org.scalatest.wordspec.AsyncWordSpec import scala.xml.Elem -class ParserTests extends AsyncWordSpec with AsyncIOSpec { +class ParserTests extends munit.CatsEffectSuite { - def parseEO[F[_]: Sync](code: String): F[Vector[EOBnd[EOExprOnly]]] = { + def parseEO[F[_]: ApplicativeError[*[_], Throwable]]( + code: String + ): F[Vector[EOBnd[EOExprOnly]]] = { sourceCodeEoParser[F]().parse(code).map(_.bnds) } @@ -40,14 +40,14 @@ class ParserTests extends AsyncWordSpec with AsyncIOSpec { } yield parsed } - def compare[F[_]: Sync](code: String): F[Assertion] = { + def compare[F[_]: Sync](code: String): F[Unit] = { for { - parsedSeq <- parseXMIRFromSeq(code) - parsedString <- parseXMIRFromString(code) - parsedEO <- parseEO(code) + parsedSeq <- parseXMIRFromSeq[F](code) + parsedString <- parseXMIRFromString[F](code) + parsedEO <- parseEO[F](code) } yield { - assert(parsedEO == parsedSeq) - assert(parsedEO == parsedString) + assertEquals(parsedSeq, parsedEO) + assertEquals(parsedString, parsedEO) } } @@ -139,28 +139,27 @@ class ParserTests extends AsyncWordSpec with AsyncIOSpec { """"hello" > world |""".stripMargin - "XMIR parser" should { - val tests = List( - "a lot of code" -> code, - "very simple" -> verySimple, - "simple" -> simple, - "division by zero" -> divByZero, - "some arrays" -> arrays, - ).appendedAll( - FullProgramExamples - .correct - // "dir walk" test is not run - // because the single-line abstraction syntax - // is supported incorrectly by the XMIR parser - // https://github.com/cqfn/eo/issues/612 - // TODO: remove .init when it is supported correctly - .init - .map(tc => (tc.label, tc.code)) - ) - tests.foreach { case (label, code) => - registerAsyncTest(label) { - compare[IO](code) - } + val tests: List[(String, String)] = List( + "a lot of code" -> code, + "very simple" -> verySimple, + "simple" -> simple, + "division by zero" -> divByZero, + "some arrays" -> arrays, + ).appendedAll( + FullProgramExamples + .correct + // "dir walk" test is not run + // because the single-line abstraction syntax + // is supported incorrectly by the XMIR parser + // https://github.com/cqfn/eo/issues/612 + // TODO: remove .init when it is supported correctly + .init + .map(tc => (tc.label, tc.code)) + ) + + tests.foreach { case (label, code) => + test("XMIR parser test - " + label) { + compare[IO](code) } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index c8adc1aa..17a57857 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -5,9 +5,8 @@ object Dependencies { object V { val cats = "2.7.0" val catsMtl = "1.2.1" - val catsEffect = "3.3.0" + val catsEffect = "3.3.4" val catsParse = "0.3.6" - val scalaTest = "3.2.9" val scalaCheck = "3.2.9.0" val nonEmpty = "0.2.0" val monocle = "3.1.0" @@ -16,8 +15,11 @@ object Dependencies { val fs2 = "3.2.4" val fs2io = "3.2.4" val newTypes = "0.0.1" + val munit = "0.7.29" + val munitScalacheck = "0.7.29" + val munitScalacheckEffect = "1.0.4" + val munitCatsEffect = "1.0.7" val eoParser = "0.22.2" - val catsEffectScalatest = "1.4.0" val smtlib = "0.2.1-42-gc68dbaa" val princess = "2021-11-15" } @@ -29,13 +31,13 @@ object Dependencies { val catsEffect = Seq( "org.typelevel" %% "cats-effect" % V.catsEffect, - "org.typelevel" %% "cats-effect-testing-scalatest" % V.catsEffectScalatest % Test, ) - val scalaTest = Seq( - "org.scalactic" %% "scalactic" % V.scalaTest % Test, - "org.scalatest" %% "scalatest" % V.scalaTest % Test, - "org.scalatestplus" %% "scalacheck-1-15" % V.scalaCheck % Test, + val munit = Seq( + "org.scalameta" %% "munit" % V.munit % Test, + "org.scalameta" %% "munit-scalacheck" % V.munitScalacheck % Test, + "org.typelevel" %% "scalacheck-effect-munit" % V.munitScalacheckEffect % Test, + "org.typelevel" %% "munit-cats-effect-3" % V.munitCatsEffect % Test ) val nonEmpty = Seq( @@ -82,8 +84,9 @@ object Dependencies { val allCats: Seq[ModuleID] = cats ++ catsEffect - val common: Seq[ModuleID] = allCats ++ scalaTest ++ nonEmpty ++ monocle ++ - newTypes + val common: Seq[ModuleID] = + allCats ++ munit ++ nonEmpty ++ monocle ++ + newTypes val analysis: Seq[ModuleID] = common ++ smtlib ++ pprint diff --git a/utils/src/main/scala/org/polystat/odin/utils/files.scala b/utils/src/main/scala/org/polystat/odin/utils/files.scala index 3a94a3b7..9fdc15e7 100644 --- a/utils/src/main/scala/org/polystat/odin/utils/files.scala +++ b/utils/src/main/scala/org/polystat/odin/utils/files.scala @@ -1,56 +1,30 @@ package org.polystat.odin.utils import cats.effect.Sync -import fs2.Stream -import fs2.io.file.Files import fs2.io.file.Path +import fs2.io.readClassLoaderResource import fs2.text -import java.io.FileNotFoundException -import java.nio.file.Paths - object files { - def readCodeFrom[F[_]: Sync: Files](fileName: String): F[String] = { - readCodeFrom(Path(fileName)) - } - - def readCodeStreamFrom[F[_]: Files](path: Path): Stream[F, String] = { - Files[F] - .readAll(path) + def readEoCodeFromDirectory[F[_]: Sync]( + dir: String + ): F[List[(String, String)]] = { + readClassLoaderResource(dir) .through(text.utf8.decode) - } - - def readCodeFrom[F[_]: Sync: Files](path: Path): F[String] = { - readCodeStreamFrom(path) - .compile - .string - } - - def resourceAsPath[F[_]: Sync]( - classPathResource: String - ): Stream[F, Path] = - Stream.fromEither[F]( - Option(getClass.getResource(classPathResource)) - .map(p => Path.fromNioPath(Paths.get(p.toURI))) - .toRight( - new FileNotFoundException( - s"No file '$classPathResource' in test resources for package ${getClass.getPackage.getName}" - ) + .through(text.lines) + .filter(_.nonEmpty) + .flatMap(path => + readClassLoaderResource( + (Path(dir) / Path(path)) + .toString + .replace("\\", "/") ) - ) - - def readEoCodeFromResources[F[_]: Sync: Files]( - dir: String - ): F[List[(String, String)]] = - files - .resourceAsPath[F](dir) - .flatMap(Files[F].walk) - .filter(p => p.extName == ".eo") - .flatMap(p => - readCodeStreamFrom(p).map(code => (p.fileName.toString, code)) + .through(text.utf8.decode) + .map(code => (path, code)) ) .compile .toList + } }