From dd62bd7475f3a4f4d1399bdca8e9dd2361e36255 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 17:15:07 +0300 Subject: [PATCH 1/6] failing tests --- .../utxo/BasicOpsSpecification.scala | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 79701d6e07..87a3697462 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -3,6 +3,7 @@ package sigmastate.utxo import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ import sigma.Extensions.ArrayOps +import sigma.VersionContext import sigma.ast.SCollection.SByteArray import sigma.ast.SType.AnyOps import sigma.data.{AvlTreeData, CAnyValue, CSigmaDslBuilder} @@ -157,6 +158,47 @@ class BasicOpsSpecification extends CompilerTestingCommons ) } + property("Lazy evaluation of default in Option.getOrElse") { + val customExt = Map ( + 1.toByte -> IntConstant(5) + ).toSeq + def optTest() = test("getOrElse", env, customExt, + """{ + | getVar[Int](1).getOrElse(getVar[Int](44).get) > 0 + |} + | + |""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + an[Exception] shouldBe thrownBy(optTest()) + } + } + + property("Lazy evaluation of default in Coll.getOrElse") { + val customExt = Map ( + 1.toByte -> IntConstant(5) + ).toSeq + def optTest() = test("getOrElse", env, customExt, + """{ + | val c = Coll[Int](1) + | c.getOrElse(0, getVar[Int](44).get) > 0 + |} + | + |""".stripMargin, + null + ) + + if(VersionContext.current.isV6SoftForkActivated) { + optTest() + } else { + an[Exception] shouldBe thrownBy(optTest()) + } + } + property("Relation operations") { test("R1", env, ext, "{ allOf(Coll(getVar[Boolean](trueVar).get, true, true)) }", From aaef77e76f8b4be4c97520a56cc6b1492bb70791 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 11 Jun 2024 17:46:20 +0300 Subject: [PATCH 2/6] close #906: lazy evaluation of default in Option.getOrElse and Coll.getOrElse --- .../main/scala/sigma/ast/transformers.scala | 43 ++++++++++++++----- .../utxo/BasicOpsSpecification.scala | 10 ++--- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/data/shared/src/main/scala/sigma/ast/transformers.scala b/data/shared/src/main/scala/sigma/ast/transformers.scala index 939da79d98..8d7e689a18 100644 --- a/data/shared/src/main/scala/sigma/ast/transformers.scala +++ b/data/shared/src/main/scala/sigma/ast/transformers.scala @@ -10,7 +10,7 @@ import sigma.eval.ErgoTreeEvaluator.DataEnv import sigma.serialization.CoreByteWriter.ArgInfo import sigma.serialization.OpCodes import sigma.serialization.ValueCodes.OpCode -import sigma.{Box, Coll, Evaluation} +import sigma.{Box, Coll, Evaluation, VersionContext} // TODO refactor: remove this trait as it doesn't have semantic meaning @@ -258,10 +258,22 @@ case class ByIndex[V <: SType](input: Value[SCollection[V]], val indexV = index.evalTo[Int](env) default match { case Some(d) => - val dV = d.evalTo[V#WrappedType](env) - Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased - addCost(ByIndex.costKind) - inputV.getOrElse(indexV, dV) + if (VersionContext.current.isV6SoftForkActivated) { + // lazy evaluation of default in 6.0 + addCost(ByIndex.costKind) + if (inputV.isDefinedAt(indexV)) { + inputV.apply(indexV) + } else { + val dV = d.evalTo[V#WrappedType](env) + Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased + inputV.getOrElse(indexV, dV) + } + } else { + val dV = d.evalTo[V#WrappedType](env) + Value.checkType(d, dV) // necessary because cast to V#WrappedType is erased + addCost(ByIndex.costKind) + inputV.getOrElse(indexV, dV) + } case _ => addCost(ByIndex.costKind) inputV.apply(indexV) @@ -613,11 +625,22 @@ case class OptionGetOrElse[V <: SType](input: Value[SOption[V]], default: Value[ override val opType = SFunc(IndexedSeq(input.tpe, tpe), tpe) override def tpe: V = input.tpe.elemType protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - val inputV = input.evalTo[Option[V#WrappedType]](env) - val dV = default.evalTo[V#WrappedType](env) // TODO v6.0: execute lazily (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/906) - Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased - addCost(OptionGetOrElse.costKind) - inputV.getOrElse(dV) + if(VersionContext.current.isV6SoftForkActivated) { + // lazy evaluation of default in 6.0 + val inputV = input.evalTo[Option[V#WrappedType]](env) + addCost(OptionGetOrElse.costKind) + inputV.getOrElse { + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + dV + } + } else { + val inputV = input.evalTo[Option[V#WrappedType]](env) + val dV = default.evalTo[V#WrappedType](env) + Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased + addCost(OptionGetOrElse.costKind) + inputV.getOrElse(dV) + } } } object OptionGetOrElse extends ValueCompanion with FixedCostValueCompanion { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 87a3697462..c1489355e6 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -166,7 +166,6 @@ class BasicOpsSpecification extends CompilerTestingCommons """{ | getVar[Int](1).getOrElse(getVar[Int](44).get) > 0 |} - | |""".stripMargin, null ) @@ -179,15 +178,12 @@ class BasicOpsSpecification extends CompilerTestingCommons } property("Lazy evaluation of default in Coll.getOrElse") { - val customExt = Map ( - 1.toByte -> IntConstant(5) - ).toSeq - def optTest() = test("getOrElse", env, customExt, + def optTest() = test("getOrElse", env, ext, """{ | val c = Coll[Int](1) - | c.getOrElse(0, getVar[Int](44).get) > 0 + | c.getOrElse(0, getVar[Int](44).get) > 0 && + | c.getOrElse(1, c.getOrElse(0, getVar[Int](44).get)) > 0 |} - | |""".stripMargin, null ) From 4c63a60e981743fd10c29a4b87b8e996ed5b749d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 30 Jul 2024 19:30:54 +0300 Subject: [PATCH 3/6] LSV5 & LSV6 tests --- .../scala/sigma/LanguageSpecificationV5.scala | 146 +++++++++--------- .../scala/sigma/LanguageSpecificationV6.scala | 60 ++++++- 2 files changed, 134 insertions(+), 72 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala index 700b48fd13..347a7b90f4 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV5.scala @@ -7957,77 +7957,81 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => ) ) ) - verifyCases( - // (coll, (index, default)) - { - def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) - Seq( - ((Coll[Int](), (0, default)), success(default)), - ((Coll[Int](), (-1, default)), success(default)), - ((Coll[Int](1), (0, default)), success(1)), - ((Coll[Int](1), (1, default)), success(default)), - ((Coll[Int](1), (-1, default)), success(default)), - ((Coll[Int](1, 2), (0, default)), success(1)), - ((Coll[Int](1, 2), (1, default)), success(2)), - ((Coll[Int](1, 2), (2, default)), success(default)), - ((Coll[Int](1, 2), (-1, default)), success(default)) - ) - }, - existingFeature((x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2), - "{ (x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2) }", - if (lowerMethodCallsInTests) - FuncValue( - Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Vector( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte + + if(!VersionContext.current.isV6SoftForkActivated) { + verifyCases( + // (coll, (index, default)) + { + def success[T](v: T) = Expected(Success(v), 1773, costDetails, 1773) + + Seq( + ((Coll[Int](), (0, default)), success(default)), + ((Coll[Int](), (-1, default)), success(default)), + ((Coll[Int](1), (0, default)), success(1)), + ((Coll[Int](1), (1, default)), success(default)), + ((Coll[Int](1), (-1, default)), success(default)), + ((Coll[Int](1, 2), (0, default)), success(1)), + ((Coll[Int](1, 2), (1, default)), success(2)), + ((Coll[Int](1, 2), (2, default)), success(default)), + ((Coll[Int](1, 2), (-1, default)), success(default)) + ) + }, + existingFeature((x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2), + "{ (x: (Coll[Int], (Int, Int))) => x._1.getOrElse(x._2._1, x._2._2) }", + if (lowerMethodCallsInTests) + FuncValue( + Vector((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Vector( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) - ) - ), - ByIndex( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte ), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - Some(SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte)) + ByIndex( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), + Some(SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte)) + ) ) ) - ) - else - FuncValue( - Array((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), - BlockValue( - Array( - ValDef( - 3, - List(), - SelectField.typed[Value[STuple]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 2.toByte + else + FuncValue( + Array((1, SPair(SCollectionType(SInt), SPair(SInt, SInt)))), + BlockValue( + Array( + ValDef( + 3, + List(), + SelectField.typed[Value[STuple]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 2.toByte + ) ) - ) - ), - MethodCall.typed[Value[SInt.type]]( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), - 1.toByte ), - SCollectionMethods.getMethodByName("getOrElse").withConcreteTypes(Map(STypeVar("IV") -> SInt)), - Vector( - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), - SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) - ), - Map() + MethodCall.typed[Value[SInt.type]]( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), + 1.toByte + ), + SCollectionMethods.getMethodByName("getOrElse").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + Vector( + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), + SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) + ), + Map() + ) ) ) - ) - )) + )) + } } property("Tuple size method equivalence") { @@ -8591,13 +8595,15 @@ class LanguageSpecificationV5 extends LanguageSpecificationBase { suite => "{ (x: Option[Long]) => x.isDefined }", FuncValue(Vector((1, SOption(SLong))), OptionIsDefined(ValUse(1, SOption(SLong)))))) - verifyCases( - Seq( - (None -> Expected(Success(1L), 1766, costDetails3, 1766)), - (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), - existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, - "{ (x: Option[Long]) => x.getOrElse(1L) }", - FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + if (!VersionContext.current.isV6SoftForkActivated) { + verifyCases( + Seq( + (None -> Expected(Success(1L), 1766, costDetails3, 1766)), + (Some(10L) -> Expected(Success(10L), 1766, costDetails3, 1766))), + existingFeature({ (x: Option[Long]) => x.getOrElse(1L) }, + "{ (x: Option[Long]) => x.getOrElse(1L) }", + FuncValue(Vector((1, SOption(SLong))), OptionGetOrElse(ValUse(1, SOption(SLong)), LongConstant(1L))))) + } verifyCases( Seq( diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 3b3de407b5..612f0e33c3 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,8 +1,9 @@ package sigma -import sigma.ast.{Apply, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, JitCost, OptionGet, SBigInt, SByte, SInt, SLong, SShort, ValUse} +import sigma.ast.{Apply, ArithOp, BlockValue, ByIndex, CompanionDesc, Constant, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} import sigma.data.{CBigInt, ExactNumeric} -import sigma.eval.SigmaDsl +import sigma.eval.{SigmaDsl, TracedCost} +import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound @@ -168,7 +169,62 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } } + } + + property("Option.getOrElse with lazy default") { + def getOrElse = newFeature( + { (x: Option[Long]) => x.getOrElse(1 / 0L) }, + "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", + FuncValue( + Array((1, SOption(SLong))), + OptionGetOrElse( + ValUse(1, SOption(SLong)), + ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) + ) + ) + ) + if (VersionContext.current.isV6SoftForkActivated) { + forAll { x: Option[Long] => + Seq(getOrElse).map(_.checkEquality(x)) + } + } else { + forAll { x: Option[Long] => + if (x.isEmpty) { + Seq(getOrElse).map(_.checkEquality(x)) + } + } + } + } + + property("Coll getOrElse with lazy default") { + def getOrElse = newFeature( + (x: (Coll[Int], Int)) => x._1.toArray.unapply(x._2).getOrElse(1 / 0), + "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", + FuncValue( + Array((1, SPair(SCollectionType(SInt), SInt))), + ByIndex( + SelectField.typed[Value[SCollection[SInt.type]]]( + ValUse(1, SPair(SCollectionType(SInt), SInt)), + 1.toByte + ), + SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SCollectionType(SInt), SInt)), 2.toByte), + Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) + ) + ) + ) + + if (VersionContext.current.isV6SoftForkActivated) { + forAll { x: (Coll[Int], Int) => + Seq(getOrElse).map(_.checkEquality(x)) + } + } else { + forAll { x: (Coll[Int], Int) => + if (x._1.isEmpty) { + Seq(getOrElse).map(_.checkEquality(x)) + } + } + } } property("BigInt methods equivalence (new features)") { From 61f90cd339b9a79ce20154f45f3db2980be9dc4c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 31 Jul 2024 14:04:30 +0300 Subject: [PATCH 4/6] assertEXceptionThrown --- sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala | 2 +- .../test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 612f0e33c3..b4fb5b12fb 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -199,7 +199,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Coll getOrElse with lazy default") { def getOrElse = newFeature( - (x: (Coll[Int], Int)) => x._1.toArray.unapply(x._2).getOrElse(1 / 0), + (x: (Coll[Int], Int)) => x._1.toArray.toIndexedSeq.unapply(x._2).getOrElse(1 / 0), "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", FuncValue( Array((1, SPair(SCollectionType(SInt), SInt))), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index c1489355e6..3d25326660 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -170,10 +170,10 @@ class BasicOpsSpecification extends CompilerTestingCommons null ) - if(VersionContext.current.isV6SoftForkActivated) { + if (VersionContext.current.isV6SoftForkActivated) { optTest() } else { - an[Exception] shouldBe thrownBy(optTest()) + assertExceptionThrown(optTest(), _.isInstanceOf[NoSuchElementException]) } } From 26809c94f0ed8aca3d56fabb10995fb30e2889e0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 2 Aug 2024 13:58:07 +0300 Subject: [PATCH 5/6] passing but improper changedFeature test --- .gitignore | 1 + .../scala/sigma/LanguageSpecificationV6.scala | 48 +++++++++---------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index df18ce8ab1..d328645cde 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.fdb_latexmk *.gz +yarn.lock *.log docs/spec/out/ test-out/ diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index b4fb5b12fb..9e9827df1f 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,14 +1,14 @@ package sigma -import sigma.ast.{Apply, ArithOp, BlockValue, ByIndex, CompanionDesc, Constant, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} +import sigma.ast.{Apply, ByIndex, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, IntConstant, JitCost, LongConstant, MethodCall, OptionGet, OptionGetOrElse, PerItemCost, SBigInt, SByte, SCollection, SCollectionMethods, SCollectionType, SInt, SLong, SOption, SPair, SShort, STuple, STypeVar, SelectField, ValDef, ValUse, Value} import sigma.data.{CBigInt, ExactNumeric} -import sigma.eval.{SigmaDsl, TracedCost} +import sigma.eval.{CostDetails, SigmaDsl, TracedCost} import sigma.serialization.ValueCodes.OpCode import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound import java.math.BigInteger -import scala.util.Success +import scala.util.{Failure, Success} /** This suite tests all operations for v6.0 version of the language. * The base classes establish the infrastructure for the tests. @@ -172,29 +172,29 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } property("Option.getOrElse with lazy default") { - def getOrElse = newFeature( - { (x: Option[Long]) => x.getOrElse(1 / 0L) }, - "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", - FuncValue( - Array((1, SOption(SLong))), - OptionGetOrElse( - ValUse(1, SOption(SLong)), - ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) - ) + verifyCases( + Seq( + Some(0L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, CostDetails.ZeroCost, 1793, + newVersionedResults = { + Seq(0 -> (ExpectedResult(Success(6L), Some(1793)) -> None)) + } ), + None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6) + ), + changedFeature( + { (x: Option[Long]) => val default = 1 / 0L; x.getOrElse(default) }, + { (x: Option[Long]) => if (VersionContext.current.isV6SoftForkActivated) {x.getOrElse(1 / 0L)} else {val default = 1 / 0L; x.getOrElse(default)} }, + "{ (x: Option[Long]) => x.getOrElse(1 / 0L) }", + FuncValue( + Array((1, SOption(SLong))), + OptionGetOrElse( + ValUse(1, SOption(SLong)), + ArithOp(LongConstant(1L), LongConstant(0L), OpCode @@ (-99.toByte)) + ) + ), + allowNewToSucceed = true, + changedIn = VersionContext.V6SoftForkVersion ) ) - - if (VersionContext.current.isV6SoftForkActivated) { - forAll { x: Option[Long] => - Seq(getOrElse).map(_.checkEquality(x)) - } - } else { - forAll { x: Option[Long] => - if (x.isEmpty) { - Seq(getOrElse).map(_.checkEquality(x)) - } - } - } } property("Coll getOrElse with lazy default") { From 8da35750e295b0fa95e440c21316d147c45b1f1d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 9 Aug 2024 17:33:12 +0300 Subject: [PATCH 6/6] fixed tests in LSV6 --- .../scala/sigma/LanguageSpecificationV6.scala | 79 ++++++++++++------- .../test/scala/sigma/SigmaDslTesting.scala | 13 ++- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index 4e6641984b..d63d6aae65 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -467,7 +467,7 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => property("Option.getOrElse with lazy default") { - val someTrace = TracedCost( + val trace = TracedCost( Array( FixedCostItem(Apply), FixedCostItem(FuncValue), @@ -481,11 +481,11 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => verifyCases( Seq( - Some(2L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, someTrace, 1793, + Some(2L) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, newVersionedResults = { - expectedSuccessForAllTreeVersions(2L, 2015, someTrace) + expectedSuccessForAllTreeVersions(2L, 2015, trace) } ), - // None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6) + None -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) ), changedFeature( changedInVersion = VersionContext.V6SoftForkVersion, @@ -503,35 +503,56 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => ) ) } -/* + property("Coll getOrElse with lazy default") { - def getOrElse = newFeature( - (x: (Coll[Int], Int)) => x._1.toArray.toIndexedSeq.unapply(x._2).getOrElse(1 / 0), - "{ (x: (Coll[Int], Int)) => x._1.getOrElse(x._2, 1 / 0) }", - FuncValue( - Array((1, SPair(SCollectionType(SInt), SInt))), - ByIndex( - SelectField.typed[Value[SCollection[SInt.type]]]( - ValUse(1, SPair(SCollectionType(SInt), SInt)), - 1.toByte - ), - SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SCollectionType(SInt), SInt)), 2.toByte), - Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) - ) + + val trace = TracedCost( + Array( + FixedCostItem(Apply), + FixedCostItem(FuncValue), + FixedCostItem(GetVar), + FixedCostItem(OptionGet), + FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), + FixedCostItem(ValUse), + FixedCostItem(Constant), + FixedCostItem(ByIndex) ) ) - if (VersionContext.current.isV6SoftForkActivated) { - forAll { x: (Coll[Int], Int) => - Seq(getOrElse).map(_.checkEquality(x)) - } - } else { - forAll { x: (Coll[Int], Int) => - if (x._1.isEmpty) { - Seq(getOrElse).map(_.checkEquality(x)) - } - } + def scalaFuncNew(x: Coll[Int]) = { + if (VersionContext.current.isV6SoftForkActivated) { + x.toArray.toIndexedSeq.headOption.getOrElse(1 / 0) + } else scalaFuncOld(x) + } + + def scalaFuncOld(x: Coll[Int]) = { + x.getOrElse(0, 1 / 0) } + + verifyCases( + Seq( + Coll(1) -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793, + newVersionedResults = { + expectedSuccessForAllTreeVersions(1, 2029, trace) + } ), + Coll[Int]() -> Expected(Failure(new java.lang.ArithmeticException("/ by zero")), 6, trace, 1793) + ), + changedFeature( + changedInVersion = VersionContext.V6SoftForkVersion, + scalaFuncOld, + scalaFuncNew, + "{ (x: Coll[Int]) => x.getOrElse(0, 1 / 0) }", + FuncValue( + Array((1, SCollectionType(SInt))), + ByIndex( + ValUse(1, SCollectionType(SInt)), + IntConstant(0), + Some(ArithOp(IntConstant(1), IntConstant(0), OpCode @@ (-99.toByte))) + ) + ), + allowNewToSucceed = true + ) + ) } - */ + } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 58873449b4..cf2fd98ebf 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -261,6 +261,7 @@ class SigmaDslTesting extends AnyPropSpec s"""Should succeed with the same value or fail with the same exception, but was: |First result: $b1 |Second result: $b2 + |Input: $x |Root cause: $cause |""".stripMargin) } @@ -715,11 +716,17 @@ class SigmaDslTesting extends AnyPropSpec override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = { // check the old implementation against Scala semantic function var oldRes: Try[(B, CostDetails)] = null - if (ergoTreeVersionInTests < VersionContext.JitActivationVersion) oldRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { try checkEq(scalaFunc)(oldF)(input) catch { - case e: TestFailedException => throw e + case e: TestFailedException => + if(activatedVersionInTests < changedInVersion) { + throw e + } else { + // old ergoscript may succeed in new version while old scalafunc may fail, + // see e.g. "Option.getOrElse with lazy default" test + Failure(e) + } case t: Throwable => Failure(t) } @@ -764,7 +771,7 @@ class SigmaDslTesting extends AnyPropSpec override def checkExpected(input: A, expected: Expected[B]): Unit = { // check the new implementation with Scala semantic function val newRes = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { - checkEq(scalaFuncNew)(newF)(input) + checkEq(scalaFuncNew)(newF)(input) } if (VersionContext.current.activatedVersion < changedInVersion) {