From c869d8ef45f580bcbe5b6732fd3814c691291780 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Thu, 20 Jan 2022 18:20:51 +0300 Subject: [PATCH 01/12] added deps --- build.sbt | 2 +- project/Dependencies.scala | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 88e966d7..7b4feced 100644 --- a/build.sbt +++ b/build.sbt @@ -134,7 +134,7 @@ lazy val analysis = project ) .settings( name := "analysis", - libraryDependencies ++= Dependencies.scalaTest ++ Dependencies.pprint + libraryDependencies ++= Dependencies.munit ++ Dependencies.pprint ) val backendsBaseDirectory: File = file("backends") diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a53ea131..a3a94e7a 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -5,7 +5,7 @@ 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" @@ -18,6 +18,10 @@ object Dependencies { val newTypes = "0.0.1" val eoParser = "0.21.8" val catsEffectScalatest = "1.4.0" + val munit = "0.7.29" + val munitScalacheck = "0.7.29" + val munitScalacheckEffect = "1.0.3" + val munitCatsEffect = "1.0.7" } val cats = Seq( @@ -36,6 +40,13 @@ object Dependencies { "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( "com.github.tarao" %% "nonempty" % V.nonEmpty, ) @@ -75,8 +86,9 @@ object Dependencies { val allCats: Seq[ModuleID] = cats ++ catsEffect - val common: Seq[ModuleID] = allCats ++ scalaTest ++ nonEmpty ++ monocle ++ - newTypes + val common: Seq[ModuleID] = + allCats ++ scalaTest ++ munit ++ nonEmpty ++ monocle ++ + newTypes val utils: Seq[ModuleID] = common ++ fs2io From f9e3df86302f49174dc177b9520e01087b79e812 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Thu, 20 Jan 2022 18:21:17 +0300 Subject: [PATCH 02/12] rewrote analysis tests with munit --- .../odin/analysis/CallGraphTests.scala | 110 ++++----- .../odin/analysis/MutualrecTests.scala | 232 ++++++++---------- 2 files changed, 155 insertions(+), 187 deletions(-) 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 77088514..a2fa25c6 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/CallGraphTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/CallGraphTests.scala @@ -1,15 +1,22 @@ package org.polystat.odin.analysis -import org.scalatest.wordspec.AnyWordSpec - import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ import org.polystat.odin.analysis.mutualrec.advanced.Program._ -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"), @@ -33,63 +40,54 @@ 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(None, "a"), "s"), - MethodName(ObjectName(None, "a"), "b"), - MethodName(ObjectName(None, "a"), "d"), - MethodName(ObjectName(None, "a"), "s") - ), - List( - MethodName(ObjectName(None, "a"), "b"), - MethodName(ObjectName(None, "a"), "d"), - MethodName(ObjectName(None, "a"), "s"), - MethodName(ObjectName(None, "a"), "b") - ), - List( - MethodName(ObjectName(None, "a"), "c"), - MethodName(ObjectName(None, "a"), "b"), - MethodName(ObjectName(None, "a"), "d"), - MethodName(ObjectName(None, "a"), "s"), - MethodName(ObjectName(None, "a"), "b") - ), - List( - MethodName(ObjectName(None, "a"), "d"), - MethodName(ObjectName(None, "a"), "s"), - MethodName(ObjectName(None, "a"), "b"), - MethodName(ObjectName(None, "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) + ) - assert(exampleCgBefore.findCycles.toSet == cycles) - } } - "call chain" should { - "be shift-equivalent" in { - assert(cc1 isShiftOf cc2) - assert(cc2 isShiftOf cc1) - } + test("correctly find cycles") { + val cycles: Set[CallChain] = Set( + List( + MethodName(ObjectName(None, "a"), "s"), + MethodName(ObjectName(None, "a"), "b"), + MethodName(ObjectName(None, "a"), "d"), + MethodName(ObjectName(None, "a"), "s") + ), + List( + MethodName(ObjectName(None, "a"), "b"), + MethodName(ObjectName(None, "a"), "d"), + MethodName(ObjectName(None, "a"), "s"), + MethodName(ObjectName(None, "a"), "b") + ), + List( + MethodName(ObjectName(None, "a"), "c"), + MethodName(ObjectName(None, "a"), "b"), + MethodName(ObjectName(None, "a"), "d"), + MethodName(ObjectName(None, "a"), "s"), + MethodName(ObjectName(None, "a"), "b") + ), + List( + MethodName(ObjectName(None, "a"), "d"), + MethodName(ObjectName(None, "a"), "s"), + MethodName(ObjectName(None, "a"), "b"), + MethodName(ObjectName(None, "a"), "d") + ) + ) + + assertEquals(exampleCgBefore.findCycles.toSet, cycles) } - // } 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 af894494..de1ec3dd 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala @@ -1,155 +1,119 @@ package org.polystat.odin.analysis +import cats.ApplicativeError import cats.data.NonEmptyList -import cats.effect.unsafe.implicits.global import cats.effect.{IO, Sync} -import cats.syntax.functor._ -import org.polystat.odin.utils.files -import org.scalacheck.{Prop, Test} -import org.scalatest.wordspec.AnyWordSpec -import org.scalatestplus.scalacheck.Checkers +import cats.parse.{Parser => P, Parser0 => P0} +import cats.implicits._ +import fs2.io.file.Files +import munit.{CatsEffectSuite, ScalaCheckEffectSuite} +import org.polystat.odin.analysis.mutualrec.advanced.Analyzer +import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ import org.polystat.odin.analysis.gens.MutualRecursionTestGen.genProgram -import pprint.pprintln import org.polystat.odin.analysis.mutualrec.advanced.Program._ -import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ -import org.polystat.odin.analysis.mutualrec.advanced.Analyzer -import org.scalatest.Assertion -import fs2.io.file.Files import org.polystat.odin.parser.eo.Parser -import org.scalacheck.Gen -import cats.parse.{Parser => P, Parser0 => P0} +import org.polystat.odin.utils.files +import org.scalacheck.{Gen, Test} +import org.scalacheck.effect.PropF +import pprint.pprintln -import scala.util.Try +class MutualrecTests extends CatsEffectSuite with ScalaCheckEffectSuite { -class MutualrecTests extends AnyWordSpec with Checkers { + import MutualrecTests.parseCallChains - val params: Test.Parameters = Test + override def scalaCheckTestParameters: Test.Parameters = Test .Parameters .default .withMinSuccessfulTests(1000) .withWorkers(4) - def odinErrors( + def odinErrors[F[_]]( code: String - ): Either[String, List[CallChain]] = { - Parser - .parse(code) - .flatMap( - Analyzer - .produceChains[Either[String, *]](_) - ) - } - - "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) + )(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) - _ <- Right( - Try(if (errors.isEmpty) pprintln(prog, height = 10000)) - .recover(_ => pprintln(prog, height = 10000)) - ) - } yield errors.toSet == prog.findMultiObjectCycles.toSet - - assertion.getOrElse(false) - - } - check(prop, params) - } - - 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)) - } + test("find mutual recursion in auto-generated tests") { + val gen = Gen + .choose(2, 100) + .flatMap(n => + genProgram(n).retryUntil(p => p.findMultiObjectCycles.nonEmpty) + ) - "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, + 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.map(_.show).foreach(println) - 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: Files](path: String)( + check: (String, String) => F[Unit] + ): F[Unit] = + files + .readEoCodeFromResources[F](path) + .map(files => + files.foreach { case (name, code) => + test(name)(check(name, code)) + } + ) + + 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, + ) + + val expectedError = 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 { @@ -188,7 +152,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)) + ) } From e13b14e814b60a37ee80aafa5b1cc3ee5856da4a Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Mon, 24 Jan 2022 17:01:59 +0300 Subject: [PATCH 03/12] rewrote eo parser tests --- .../odin/parser/EOParserTestSuite.scala | 126 ------ .../polystat/odin/parser/eo/ParserTests.scala | 396 +++++++++++------- 2 files changed, 250 insertions(+), 272 deletions(-) delete mode 100644 parser/src/test/scala/org/polystat/odin/parser/EOParserTestSuite.scala 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 010ffe35..00000000 --- a/parser/src/test/scala/org/polystat/odin/parser/EOParserTestSuite.scala +++ /dev/null @@ -1,126 +0,0 @@ -package org.polystat.odin.parser - -import org.scalatest.Assertion -import org.scalatest.wordspec.AnyWordSpec -import TestUtils._ -import org.polystat.odin.core.ast.EOProg -import org.polystat.odin.core.ast.astparams.EOExprOnly -import org.scalacheck.{Gen, Prop, Test} -import org.scalatestplus.scalacheck.Checkers - -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))) - - val mutualRecursionExample: Tests[EOProg[EOExprOnly]] = List( - TestCase( - "Mutual Recursion Example", - MutualRecExample.code, - Some(MutualRecExample.ast) - ) - ) - - "existing programs" should { - runParserTests(programParser, correctTests = examplesFromSources) - runParserTests(programParser, correctTests = mutualRecursionExample) - - } - - "single line application" should { - runParserTests[EOExprOnly]( - singleLineApplicationParser, - correctTests = SingleLineExamples.correct - ) - } - -} 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 4efde4c5..d5e34c21 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,200 +1,304 @@ package org.polystat.odin.parser.eo +import cats.effect.IO +import cats.implicits._ import cats.parse.{Parser => P, Parser0 => P0} 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.{Gens, MutualRecExample, SingleLineExamples} import org.polystat.odin.parser.TestUtils.TestCase -import org.polystat.odin.parser.Gens +import org.polystat.odin.utils.files +import org.scalacheck.{Gen, Prop, Test} +import ParserTests._ -class ParserTests extends EOParserTestSuite { +abstract class ParserTests extends munit.CatsEffectSuite with munit.ScalaCheckSuite { - override type Success[A] = A - override type Error = P.Error + override def scalaCheckTestParameters: Test.Parameters = Test + .Parameters + .default + .withMinSuccessfulTests(numberOfTests) + .withMaxSize(numberOfTests) + .withMinSize(numberOfTests) + .withWorkers(8) + .withTestCallback(new Test.TestCallback { - override def programParser: ParserT[EOProg[EOExprOnly]] = - Left(Parser.program(0, 2)) - - override def singleLineApplicationParser: ParserT[EOExprOnly] = - Right(SingleLine.singleLineApplication) + 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 + ) + } - type ParserT[A] = Either[P0[A], P[A]] + }) + .withLegacyShrinking(false) - 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(_) => () + 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)) + } + ) } - check(parsed) } - "tokens" should { +} - "comments or empty lines" in { - runParserTestsGen( - Left(Tokens.emptyLinesOrComments), - Gens.emptyLinesOrComments - ) - } +class TokenTests extends ParserTests { - "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) + property("comments or empty lines") { + runParserTestGen( + Tokens.emptyLinesOrComments.asLeft, + Gens.emptyLinesOrComments + ) + } - "pass" in { - runParserTestsGen(Right(Tokens.string), Gens.string) - } + property("strings - generated tests") { + Prop.forAllNoShrink(Gens.string) { string => + shouldParse(Tokens.string.asRight, string) } + } - "chars" should { - "pass" in { - runParserTestsGen(Right(Tokens.char), Gens.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('香')), + 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" ) - runParserTests(Right(Tokens.char), charTests) - } + ), + 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("惣流·明日香·兰格雷") + ) + ) - "identifiers" in { - runParserTestsGen(Right(Tokens.identifier), Gens.identifier) - } + runParserTests("strings - ", Tokens.string.asRight, stringTests.pure[IO]) + .unsafeRunSync() - "integers" in { - runParserTestsGen(Right(Tokens.integer), Gens.integer) - } + 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('香')), + ) - "floats" in { - runParserTestsGen(Right(Tokens.float), Gens.float) - } + runParserTests("chars - ", Tokens.char.asRight, charTests.pure[IO]) + + property("chars - generated tests") { + runParserTestGen(Tokens.char.asRight, Gens.char) } - "metas" should { - "package meta" in { - runParserTestsGen(Right(Metas.packageMeta), Gens.packageMeta) + property("identifiers") { + Prop.forAllNoShrink(Gens.identifier) { id => + shouldProduceAST[String](id)(Tokens.identifier.asRight, id) } + } - "alias meta" in { - runParserTestsGen(Right(Metas.aliasMeta), Gens.aliasMeta) + property("integers") { + Prop.forAllNoShrink(Gens.integer) { int => + shouldProduceAST[Int](int.toInt)(Tokens.integer.asRight, int) } + } - "rt meta" in { - runParserTestsGen(Right(Metas.rtMeta), Gens.rtMeta) + property("floats") { + Prop.forAllNoShrink(Gens.float) { float => + shouldProduceAST[Float](float.toFloat)(Tokens.float.asRight, float) } - "all metas" in { - runParserTestsGen(Left(Metas.metas), Gens.metas) - } } - "abstraction params" should { - "pass" in { - runParserTestsGen(Right(SingleLine.params), Gens.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 ...]") - } +} + +class MetasTests extends ParserTests { + + property("package meta") { + runParserTestGen(Metas.aliasMeta.asRight, Gens.aliasMeta) } - "binding name" should { - "pass" in { - runParserTestsGen(Right(Named.name), Gens.bndName) - } + property("alias meta") { + runParserTestGen(Metas.aliasMeta.asRight, Gens.aliasMeta) + } - val incorrectTests = List[TestCase[EONamedBnd]]( - TestCase("incorrect symbol", " < name"), - TestCase("no name", " > !"), - ) + property("rt meta") { + runParserTestGen(Metas.rtMeta.asRight, Gens.rtMeta) + } - runParserTests[EONamedBnd]( - Right(Named.name), - incorrectTests = incorrectTests - ) + property("all metas") { + runParserTestGen(Metas.metas.asLeft, Gens.metas) } - "single line application" should { - "pass" in { - runParserTestsGen( - singleLineApplicationParser, - Gens.singleLineApplication(recDepthMax = 4) - ) - } +} + +class AbstractionParamsTests extends ParserTests { + + property("should parse") { + runParserTestGen(SingleLine.params.asRight, Gens.abstractionParams) } - "object" should { - "pass" in { - runParserTestsGen( - Right(Parser.`object`(0, 4)), - Gens.`object`( - named = true, - indentationStep = 4, - recDepthMax = 2 - ) - ) - } + 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 ...]") + } + +} + +class BindingNameTests extends ParserTests { + + property("pass") { + runParserTestGen(Named.name.asRight, Gens.bndName) } - "program" should { - "pass" in { - runParserTestsGen( - Left(Parser.program(0, indentationStep = 2)), - Gens.program(indentationStep = 2, recDepthMax = 2) + val incorrectTests: List[TestCase[EONamedBnd]] = List[TestCase[EONamedBnd]]( + TestCase("incorrect symbol", " < name"), + TestCase("no name", " > !"), + ) + + runParserTests[EONamedBnd]( + "binding name should fail parsing - ", + Named.name.asRight, + incorrectTests = incorrectTests.pure[IO] + ).unsafeRunSync() + +} + +class ProgramTests extends ParserTests { + + property("single line application - generated tests") { + runParserTestGen( + singleLineApplicationParser, + Gens.singleLineApplication(recDepthMax = 4) + ) + } + + property("object - generated tests") { + runParserTestGen( + Right(Parser.`object`(0, 4)), + Gens.`object`( + named = true, + indentationStep = 4, + recDepthMax = 2 ) - } + ) } + property("program - generated tests") { + runParserTestGen( + Left(Parser.program(0, indentationStep = 2)), + Gens.program(indentationStep = 2, recDepthMax = 2) + ) + } + + runParserTests("hand-crafted examples - ", programParser, correctTests = examplesFromSources) + .unsafeRunSync() + + runParserTests( + "mutual recursion example - ", + programParser, + correctTests = IO.pure(mutualRecursionExample) + ).unsafeRunSync() + + runParserTests( + "single line examples - ", + singleLineApplicationParser, + correctTests = IO.pure(SingleLineExamples.correct) + ).unsafeRunSync() + } object ParserTests { - def main(args: Array[String]): Unit = { - for (_ <- 1 to 1) { - println( - Gens - .program(indentationStep = 4, recDepthMax = 2) - .sample - .get - ) + 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 + .readEoCodeFromResources[IO]("/eo_sources") + .map( + _.map { case (name, code) => + TestCase(label = name, code = code) + } + ) + + val mutualRecursionExample: List[TestCase[EOProg[EOExprOnly]]] = List( + TestCase( + "Mutual Recursion Example", + MutualRecExample.code, + Some(MutualRecExample.ast) + ) + ) } From ddd82f9ca822baff9332b90bed870e7ca078ce91 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Mon, 24 Jan 2022 17:07:52 +0300 Subject: [PATCH 04/12] rewrote xmir parser tests --- .../odin/parser/xmir/ParserTests.scala | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) 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 64efe632..aeeed224 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,19 +1,19 @@ package org.polystat.odin.parser.xmir -import cats.effect.testing.scalatest.AsyncIOSpec +import cats.ApplicativeError import cats.effect.{IO, Sync} import cats.implicits._ -import org.polystat.odin.core.ast.EOBnd import org.polystat.odin.core.ast.astparams.EOExprOnly -import org.polystat.odin.parser.MutualRecExample +import org.polystat.odin.core.ast.EOBnd import org.polystat.odin.parser.EoParser.sourceCodeEoParser -import org.scalatest.Assertion -import org.scalatest.wordspec.AsyncWordSpec +import org.polystat.odin.parser.MutualRecExample 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) } @@ -38,14 +38,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) } } @@ -126,19 +126,17 @@ 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, - "mutual_recursion_example" -> MutualRecExample.code - ) + val tests = List( + "a lot of code" -> code, + "very simple" -> verySimple, + "simple" -> simple, + "division by zero" -> divByZero, + "mutual_recursion_example" -> MutualRecExample.code + ) - tests.foreach { case (label, code) => - registerAsyncTest(label) { - compare[IO](code) - } + tests.foreach { case (label, code) => + test("XMIR parser test - " + label) { + compare[IO](code) } } From 2fb0a5d64ce68b110a53c2e2bc25bacc65aa8a21 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Mon, 24 Jan 2022 17:17:05 +0300 Subject: [PATCH 05/12] updated deps --- project/Dependencies.scala | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a3a94e7a..196e8846 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,7 +7,6 @@ object Dependencies { val catsMtl = "1.2.1" 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" @@ -17,7 +16,6 @@ object Dependencies { val fs2io = "3.2.4" val newTypes = "0.0.1" val eoParser = "0.21.8" - val catsEffectScalatest = "1.4.0" val munit = "0.7.29" val munitScalacheck = "0.7.29" val munitScalacheckEffect = "1.0.3" @@ -31,13 +29,6 @@ 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( @@ -87,7 +78,7 @@ object Dependencies { val allCats: Seq[ModuleID] = cats ++ catsEffect val common: Seq[ModuleID] = - allCats ++ scalaTest ++ munit ++ nonEmpty ++ monocle ++ + allCats ++ munit ++ nonEmpty ++ monocle ++ newTypes val utils: Seq[ModuleID] = common ++ fs2io From 43f1481a9f41909bdcc119118a355e0e3166f1bb Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Mon, 24 Jan 2022 17:43:37 +0300 Subject: [PATCH 06/12] scalafix + scalafmt --- .../polystat/odin/analysis/MutualrecTests.scala | 4 ++-- .../org/polystat/odin/parser/eo/ParserTests.scala | 15 +++++++++++---- .../polystat/odin/parser/xmir/ParserTests.scala | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) 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 de1ec3dd..6bc1d3e3 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala @@ -69,7 +69,7 @@ class MutualrecTests extends CatsEffectSuite with ScalaCheckEffectSuite { } ) - val fileNameToChain = Map( + val fileNameToChain: Map[String, String] = Map( "mutual_rec_somewhere.eo" -> """ |c.g -> b.f -> c.g @@ -92,7 +92,7 @@ class MutualrecTests extends CatsEffectSuite with ScalaCheckEffectSuite { |""".stripMargin, ) - val expectedError = Map( + val expectedError: Map[String, String] = Map( "decoration.eo" -> """Method "f" was called from the object "derived", although it is not defined there!""" ) 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 d5e34c21..fd4c195b 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 @@ -11,7 +11,9 @@ import org.polystat.odin.utils.files import org.scalacheck.{Gen, Prop, Test} import ParserTests._ -abstract class ParserTests extends munit.CatsEffectSuite with munit.ScalaCheckSuite { +abstract class ParserTests + extends munit.CatsEffectSuite + with munit.ScalaCheckSuite { override def scalaCheckTestParameters: Test.Parameters = Test .Parameters @@ -75,7 +77,7 @@ class TokenTests extends ParserTests { } } - val stringTests = List( + val stringTests: List[TestCase[String]] = List( TestCase( label = "basic escapes", code = @@ -103,7 +105,7 @@ class TokenTests extends ParserTests { runParserTests("strings - ", Tokens.string.asRight, stringTests.pure[IO]) .unsafeRunSync() - val charTests = List( + 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')), @@ -221,7 +223,11 @@ class ProgramTests extends ParserTests { ) } - runParserTests("hand-crafted examples - ", programParser, correctTests = examplesFromSources) + runParserTests( + "hand-crafted examples - ", + programParser, + correctTests = examplesFromSources + ) .unsafeRunSync() runParserTests( @@ -301,4 +307,5 @@ object ParserTests { Some(MutualRecExample.ast) ) ) + } 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 aeeed224..4c7401b8 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 @@ -126,7 +126,7 @@ class ParserTests extends munit.CatsEffectSuite { """"hello" > world |""".stripMargin - val tests = List( + val tests: List[(String, String)] = List( "a lot of code" -> code, "very simple" -> verySimple, "simple" -> simple, From 658084867c88d7bb8d0d3a820bfb13f13532aa8a Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Tue, 1 Feb 2022 13:49:28 +0300 Subject: [PATCH 07/12] fixed tests not being run --- .../org/polystat/odin/parser/eo/ParserTests.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 bf53a2c3..fca89552 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 @@ -224,14 +224,14 @@ class ProgramTests extends ParserTests { 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) - assert(expected == actual) - } + 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) } } From 0ce7454a5b946c6bbb52cecc96abbed05f1d9688 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Thu, 17 Feb 2022 16:23:19 +0300 Subject: [PATCH 08/12] using fs2.io to read files from resources --- .../odin/analysis/MutualrecTests.scala | 5 +- .../org/polystat/odin/parser/TestUtils.scala | 18 ------ .../polystat/odin/parser/eo/ParserTests.scala | 2 +- .../scala/org/polystat/odin/utils/files.scala | 57 ++++++------------- 4 files changed, 19 insertions(+), 63 deletions(-) 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 6bc1d3e3..de3567f4 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala @@ -5,7 +5,6 @@ import cats.data.NonEmptyList import cats.effect.{IO, Sync} import cats.parse.{Parser => P, Parser0 => P0} import cats.implicits._ -import fs2.io.file.Files import munit.{CatsEffectSuite, ScalaCheckEffectSuite} import org.polystat.odin.analysis.mutualrec.advanced.Analyzer import org.polystat.odin.analysis.mutualrec.advanced.CallGraph._ @@ -58,11 +57,11 @@ class MutualrecTests extends CatsEffectSuite with ScalaCheckEffectSuite { } } - def runTestsFrom[F[_]: Sync: Files](path: String)( + def runTestsFrom[F[_]: Sync](path: String)( check: (String, String) => F[Unit] ): F[Unit] = files - .readEoCodeFromResources[F](path) + .readEoCodeFromDirectory[F](path) .map(files => files.foreach { case (name, code) => test(name)(check(name, code)) 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 2a9ca7b3..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,9 +3,6 @@ package org.polystat.odin.parser import com.github.tarao.nonempty.collection.NonEmpty import pprint.PPrinter -import java.nio.file.{Files, Paths} -import scala.jdk.CollectionConverters._ - object TestUtils { case class TestCase[AST]( @@ -26,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 fca89552..17b8a6f9 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 @@ -305,7 +305,7 @@ object ParserTests { val examplesFromSources: IO[List[TestCase[EOProg[EOExprOnly]]]] = files - .readEoCodeFromResources[IO]("/eo_sources") + .readEoCodeFromDirectory[IO]("/eo_sources") .map( _.map { case (name, code) => TestCase(label = name, code = code) 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 0c7d1c9b..08768f3a 100644 --- a/utils/src/main/scala/org/polystat/odin/utils/files.scala +++ b/utils/src/main/scala/org/polystat/odin/utils/files.scala @@ -1,55 +1,30 @@ package org.polystat.odin.utils import cats.effect.Sync +import fs2.io.file.Path +import fs2.io.readClassResource import fs2.text -import fs2.Stream -import fs2.io.file.{Files, Path} - -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)]] = { + readClassResource(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 => + readClassResource( + (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 + } } From e2e3fce20e524435434285607138edbda9f917a7 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Thu, 17 Feb 2022 16:23:45 +0300 Subject: [PATCH 09/12] ported inlining tests to munit --- .../analysis/inlining/InliningTests.scala | 64 +++++++++---------- 1 file changed, 30 insertions(+), 34 deletions(-) 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 17d511a9..ace72367 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 @@ -3,49 +3,45 @@ package org.polystat.odin.analysis.inlining 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._ -class InliningTests extends AnyWordSpec { +class InliningTests extends munit.FunSuite { - "setLocators" should { - val locatorTests: List[LocatorTestCase] = List( - vitaliyTestLocators, - nikolayTestLocators, - ) - locatorTests.foreach { case LocatorTestCase(label, before, after) => - registerTest(label) { - val expected: Either[String, String] = Right(after) - val obtained: Either[String, String] = Parser - .parse(before) - .map(Context.setLocators _ andThen (_.toEOPretty)) - assert(expected == obtained) - } + val locatorTests: List[LocatorTestCase] = List( + vitaliyTestLocators, + nikolayTestLocators, + ) + + locatorTests.foreach { case LocatorTestCase(label, before, after) => + test("setLocators - " + label) { + val expected: Either[String, String] = Right(after) + val obtained: Either[String, String] = Parser + .parse(before) + .map(Context.setLocators _ andThen (_.toEOPretty)) + assertEquals(expected, obtained) } } - "inlineCalls" should { - val inliningTests: List[InliningTestCase] = List( - nikolayTestInlining, - vitaliyTestInlining, - fakeCallTest, - looksFakeButRealTest, - factorialTest, - evenOddTest, - ) - - inliningTests.foreach { case InliningTestCase(label, before, after) => - registerTest(label) { - val expected: Either[String, String] = Right(after) - val actual: Either[String, String] = Parser - .parse(before) - .flatMap(Inliner_old.inlineCalls) - .map(_.toEOPretty) - assert(actual == expected) - } + val inliningTests: List[InliningTestCase] = List( + nikolayTestInlining, + vitaliyTestInlining, + fakeCallTest, + looksFakeButRealTest, + factorialTest, + evenOddTest, + ) + inliningTests.foreach { case InliningTestCase(label, before, after) => + test("inlineAllCalls - " + label) { + val expected: Either[String, String] = Right(after) + val obtained: Either[String, String] = Parser + .parse(before) + .flatMap(Inliner_old.inlineCalls) + .map(_.toEOPretty) + assertEquals(obtained, expected) } + } } From 5c50e31a9ef0a510630b8d69dae841ff450058a1 Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Tue, 22 Feb 2022 13:38:17 +0300 Subject: [PATCH 10/12] merged changes from master --- .../analysis/inlining/InliningTests.scala | 111 ++++++------------ 1 file changed, 39 insertions(+), 72 deletions(-) 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 86094dad..37b6346d 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 @@ -3,90 +3,57 @@ package org.polystat.odin.analysis.inlining 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 cats.syntax.either._ import cats.data.{EitherNel, NonEmptyList => Nel} -class InliningTests extends AnyWordSpec { +class InliningTests extends munit.FunSuite { - "setLocators" should { - val locatorTests: List[LocatorTestCase] = List( - vitaliyTestLocators, - nikolayTestLocators, - nonExistentNameTestLocators, - ) - 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(Context.setLocators) - .map(_.toEOPretty) - assert(expected == obtained) - } - } - } - - "inlineCalls" should { - val inliningTests: List[InliningTestCase] = List( - nikolayTestInlining, - vitaliyTestInlining, - fakeCallTest, - looksFakeButRealTest, - factorialTest, - evenOddTest, - average3Test, - average3WithComponentsTest, - notEnoughArgs, - tooManyArgs, - ) ++ - simpleTests - - inliningTests.foreach { case InliningTestCase(label, before, after) => - registerTest(label) { - val expected = after - val actual = Parser - .parse(before) - .leftMap(Nel.one) - .flatMap(Inliner.inlineAllCalls) - .map(_.toEOPretty) + val locatorTests: List[LocatorTestCase] = List( + vitaliyTestLocators, + nikolayTestLocators, + nonExistentNameTestLocators, + ) - assert(actual == expected) - } + 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(Context.setLocators) + .map(_.toEOPretty) + assertEquals(obtained, expected) } } -} - -object InliningTests { - - def main(args: Array[String]): Unit = { - val code = """[] > a - | [self y] > x - | y > @ - | - | [self x y] > f - | self.g self x > h - | [] > @ - | self.g self y > z - | - | [self z] > g - | x > k - | z > l - | [] > @ - | l > a - | k > b - | z > c - | self > d - |""".stripMargin + val inliningTests: List[InliningTestCase] = List( + nikolayTestInlining, + vitaliyTestInlining, + fakeCallTest, + looksFakeButRealTest, + factorialTest, + evenOddTest, + average3Test, + average3WithComponentsTest, + notEnoughArgs, + tooManyArgs, + ) ++ + simpleTests + + inliningTests.foreach { case InliningTestCase(label, before, after) => + test("inlineAllCalls - " + label) { + val expected = after + val obtained = Parser + .parse(before) + .leftMap(Nel.one) + .flatMap(Inliner.inlineAllCalls) + .map(_.toEOPretty) + + assertEquals(obtained, expected) - Parser.parse(code) match { - case Left(value) => println(value) - case Right(value) => pprint.pprintln(value) } } From 2ceeba2c7af664cc95e3fa71eb721412239ff2db Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Tue, 22 Feb 2022 13:44:53 +0300 Subject: [PATCH 11/12] assertions should be more robust to fluctuations in whitespace --- .../analysis/inlining/InliningTests.scala | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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 37b6346d..3987068c 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 @@ -6,7 +6,8 @@ import org.polystat.odin.parser.eo.Parser import SetLocatorsTestCases._ import InlineCallsTestCases._ import cats.syntax.either._ -import cats.data.{EitherNel, NonEmptyList => Nel} +import cats.syntax.foldable._ +import cats.data.{NonEmptyList => Nel} class InliningTests extends munit.FunSuite { @@ -18,14 +19,18 @@ class InliningTests extends munit.FunSuite { locatorTests.foreach { case LocatorTestCase(label, before, after) => test("setLocators - " + label) { - val expected: EitherNel[String, String] = after - val obtained: EitherNel[String, String] = Parser + val expected = after + .leftMap(_.mkString_(util.Properties.lineSeparator)) + .merge + val obtained = Parser .parse(before) .leftMap(Nel.one) .flatMap(Context.setLocators) .map(_.toEOPretty) + .leftMap(_.mkString_(util.Properties.lineSeparator)) + .merge - assertEquals(obtained, expected) + assertNoDiff(obtained, expected) } } @@ -46,13 +51,17 @@ class InliningTests extends munit.FunSuite { inliningTests.foreach { case InliningTestCase(label, before, after) => test("inlineAllCalls - " + label) { val expected = after + .leftMap(_.mkString_(util.Properties.lineSeparator)) + .merge val obtained = Parser .parse(before) .leftMap(Nel.one) .flatMap(Inliner.inlineAllCalls) .map(_.toEOPretty) + .leftMap(_.mkString_(util.Properties.lineSeparator)) + .merge - assertEquals(obtained, expected) + assertNoDiff(obtained, expected) } } From 1a5de7fba87684b7b72b0dc4820e31e78a65037f Mon Sep 17 00:00:00 2001 From: nikololiahim Date: Thu, 3 Mar 2022 12:39:12 +0300 Subject: [PATCH 12/12] fixed reading files --- .../scala/org/polystat/odin/analysis/MutualrecTests.scala | 6 +++--- .../scala/org/polystat/odin/parser/eo/ParserTests.scala | 2 +- utils/src/main/scala/org/polystat/odin/utils/files.scala | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) 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 de3567f4..469f5430 100644 --- a/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala +++ b/analysis/src/test/scala/org/polystat/odin/analysis/MutualrecTests.scala @@ -96,7 +96,7 @@ class MutualrecTests extends CatsEffectSuite with ScalaCheckEffectSuite { """Method "f" was called from the object "derived", although it is not defined there!""" ) - runTestsFrom[IO]("/mutualrec/with_recursion") { (fileName, code) => + runTestsFrom[IO]("mutualrec/with_recursion") { (fileName, code) => for { expectedErrors <- parseCallChains[IO](fileNameToChain(fileName)) actualErrors <- odinErrors[IO](code) @@ -104,11 +104,11 @@ class MutualrecTests extends CatsEffectSuite with ScalaCheckEffectSuite { } yield assertEquals(actualErrors.toSet, expectedErrors.toSet) }.unsafeRunSync() - runTestsFrom[IO]("/mutualrec/no_recursion") { (_, code) => + runTestsFrom[IO]("mutualrec/no_recursion") { (_, code) => odinErrors[IO](code).map(errors => assert(errors.isEmpty)) }.unsafeRunSync() - runTestsFrom[IO]("/mutualrec/failing") { (fileName, code) => + runTestsFrom[IO]("mutualrec/failing") { (fileName, code) => interceptIO[Exception](odinErrors[IO](code)) .map(error => assertEquals(error.getMessage, expectedError(fileName))) }.unsafeRunSync() 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 17b8a6f9..0b07fe45 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 @@ -305,7 +305,7 @@ object ParserTests { val examplesFromSources: IO[List[TestCase[EOProg[EOExprOnly]]]] = files - .readEoCodeFromDirectory[IO]("/eo_sources") + .readEoCodeFromDirectory[IO]("eo_sources") .map( _.map { case (name, code) => TestCase(label = name, code = code) 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 08768f3a..9fdc15e7 100644 --- a/utils/src/main/scala/org/polystat/odin/utils/files.scala +++ b/utils/src/main/scala/org/polystat/odin/utils/files.scala @@ -2,7 +2,7 @@ package org.polystat.odin.utils import cats.effect.Sync import fs2.io.file.Path -import fs2.io.readClassResource +import fs2.io.readClassLoaderResource import fs2.text object files { @@ -10,12 +10,12 @@ object files { def readEoCodeFromDirectory[F[_]: Sync]( dir: String ): F[List[(String, String)]] = { - readClassResource(dir) + readClassLoaderResource(dir) .through(text.utf8.decode) .through(text.lines) .filter(_.nonEmpty) .flatMap(path => - readClassResource( + readClassLoaderResource( (Path(dir) / Path(path)) .toString .replace("\\", "/")