From 5fa4e724c103ceba2a9fe886533aadb39aefe734 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Thu, 28 Jul 2022 19:10:28 +0200 Subject: [PATCH] [orx-keyframer] Add support for backticked identifier names, replace spek tests --- orx-jvm/orx-keyframer/build.gradle | 8 +- .../src/main/antlr/KeyLangLexer.g4 | 38 +--- .../src/main/antlr/KeyLangParser.g4 | 2 +- .../src/main/kotlin/Expressions.kt | 51 +++-- .../src/test/kotlin/TestExpressionErrors.kt | 67 +++--- ...TestFunctionCall.kt => TestExpressions.kt} | 84 +++++-- .../src/test/kotlin/TestKeyframerChannel.kt | 33 ++- .../src/test/kotlin/TestKeyframerErrors.kt | 206 ++++++++---------- .../src/test/kotlin/TestOperators.kt | 38 +++- 9 files changed, 273 insertions(+), 254 deletions(-) rename orx-jvm/orx-keyframer/src/test/kotlin/{TestFunctionCall.kt => TestExpressions.kt} (59%) diff --git a/orx-jvm/orx-keyframer/build.gradle b/orx-jvm/orx-keyframer/build.gradle index c1a9e6fdb..ff3ca2645 100644 --- a/orx-jvm/orx-keyframer/build.gradle +++ b/orx-jvm/orx-keyframer/build.gradle @@ -38,6 +38,8 @@ dependencies { implementation libs.gson implementation(libs.kotlin.reflect) + testImplementation(libs.kotlin.test) + demoImplementation(project(":orx-camera")) demoImplementation(project(":orx-jvm:orx-panel")) @@ -50,4 +52,8 @@ dependencies { } tasks.getByName("compileKotlin").dependsOn("generateGrammarSource") -tasks.getByName("compileTestKotlin").dependsOn("generateTestGrammarSource") \ No newline at end of file +tasks.getByName("compileTestKotlin").dependsOn("generateTestGrammarSource") + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/main/antlr/KeyLangLexer.g4 b/orx-jvm/orx-keyframer/src/main/antlr/KeyLangLexer.g4 index a88aa2c43..f9fa14cca 100644 --- a/orx-jvm/orx-keyframer/src/main/antlr/KeyLangLexer.g4 +++ b/orx-jvm/orx-keyframer/src/main/antlr/KeyLangLexer.g4 @@ -20,7 +20,7 @@ DECIMAL : 'Decimal'; STRING : 'String'; // Identifiers -ID : [$_]*[a-zA-Z][A-Za-z0-9_]* ; +ID : [$_]*[a-zA-Z][A-Za-z0-9_]* | '`'[$_]*[A-Za-z0-9_-]*'`'; FUNCTION_ID : [$_]*[a-z][A-Za-z0-9_]* ; // Literals @@ -38,6 +38,7 @@ ASSIGN : '=' ; LPAREN : '(' ; RPAREN : ')' ; + COMMA : ',' ; STRING_OPEN : '"' -> pushMode(MODE_IN_STRING); @@ -51,40 +52,5 @@ ESCAPE_SLASH : '\\\\' ; ESCAPE_NEWLINE : '\\n' ; ESCAPE_SHARP : '\\#' ; STRING_CLOSE : '"' -> popMode ; -INTERPOLATION_OPEN : '#{' -> pushMode(MODE_IN_INTERPOLATION) ; STRING_CONTENT : ~["\n\r\t\\#]+ ; -STR_UNMATCHED : . -> type(UNMATCHED) ; - -mode MODE_IN_INTERPOLATION; - -INTERPOLATION_CLOSE : '}' -> popMode ; - -INTERP_WS : [\t ]+ -> channel(WHITESPACE), type(WS) ; - -// Keywords -INTERP_AS : 'as'-> type(AS) ; -INTERP_INT : 'Int'-> type(INT) ; -INTERP_DECIMAL : 'Decimal'-> type(DECIMAL) ; -INTERP_STRING : 'String'-> type(STRING) ; - -// Literals -INTERP_INTLIT : ('0'|[1-9][0-9]*) -> type(INTLIT) ; -INTERP_DECLIT : ('0'|[1-9][0-9]*) '.' [0-9]+ -> type(DECLIT) ; - -// Operators -INTERP_PLUS : '+' -> type(PLUS) ; -INTERP_MINUS : '-' -> type(MINUS) ; -INTERP_ASTERISK : '*' -> type(ASTERISK) ; -INTERP_DIVISION : '/' -> type(DIVISION) ; -INTERP_PERCENTAGE : '%' -> type(PERCENTAGE) ; -INTERP_ASSIGN : '=' -> type(ASSIGN) ; -INTERP_LPAREN : '(' -> type(LPAREN) ; -INTERP_RPAREN : ')' -> type(RPAREN) ; - -// Identifiers -INTERP_ID : [_]*[a-z][A-Za-z0-9_]* -> type(ID); - -INTERP_STRING_OPEN : '"' -> type(STRING_OPEN), pushMode(MODE_IN_STRING); - -INTERP_UNMATCHED : . -> type(UNMATCHED) ; diff --git a/orx-jvm/orx-keyframer/src/main/antlr/KeyLangParser.g4 b/orx-jvm/orx-keyframer/src/main/antlr/KeyLangParser.g4 index 93d5acb6b..3f02a1fbe 100644 --- a/orx-jvm/orx-keyframer/src/main/antlr/KeyLangParser.g4 +++ b/orx-jvm/orx-keyframer/src/main/antlr/KeyLangParser.g4 @@ -7,7 +7,7 @@ package org.openrndr.extra.keyframer.antlr; options { tokenVocab=KeyLangLexer; } -miniCalcFile : lines=line+ ; +keyLangFile : lines=line+ ; line : statement (NEWLINE | EOF) ; diff --git a/orx-jvm/orx-keyframer/src/main/kotlin/Expressions.kt b/orx-jvm/orx-keyframer/src/main/kotlin/Expressions.kt index d23cd48de..f8a5c476a 100644 --- a/orx-jvm/orx-keyframer/src/main/kotlin/Expressions.kt +++ b/orx-jvm/orx-keyframer/src/main/kotlin/Expressions.kt @@ -248,11 +248,11 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx doubleStack.push(node.text.toDouble()) } if (type == KeyLangParser.ID) { - + val name = node.text.replace("`","") @Suppress("DIVISION_BY_ZERO") when (val idType = idTypeStack.pop()) { IDType.VARIABLE -> doubleStack.push( - when (val name = node.text) { + when (name) { "PI" -> PI else -> variables[name] ?: errorValue("unresolved variable: '${name}'", 0.0 / 0.0) } @@ -260,11 +260,11 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx IDType.FUNCTION0 -> { val function: (DoubleArray) -> Double = - when (val candidate = node.text) { + when (name) { "random" -> { _ -> Double.uniform(0.0, 1.0) } - else -> functions.functions0[candidate]?.let { { _: DoubleArray -> it.invoke() } } + else -> functions.functions0[name]?.let { { _: DoubleArray -> it.invoke() } } ?: errorValue( - "unresolved function: '${candidate}()'" + "unresolved function: '${name}()'" ) { _ -> error("this is the error function") } } functionStack.push(function) @@ -272,7 +272,7 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx IDType.FUNCTION1 -> { val function: (DoubleArray) -> Double = - when (val candidate = node.text) { + when (name) { "sqrt" -> { x -> sqrt(x[0]) } "radians" -> { x -> Math.toRadians(x[0]) } "degrees" -> { x -> Math.toDegrees(x[0]) } @@ -287,48 +287,54 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx "floor" -> { x -> floor(x[0]) } "ceil" -> { x -> ceil(x[0]) } "saturate" -> { x -> x[0].coerceIn(0.0, 1.0) } - else -> functions.functions1[candidate]?.let { { x: DoubleArray -> it.invoke(x[0]) } } + else -> functions.functions1[name]?.let { { x: DoubleArray -> it.invoke(x[0]) } } ?: errorValue( - "unresolved function: '${candidate}(x0)'" + "unresolved function: '${name}(x0)'" ) { _ -> error("this is the error function") } } functionStack.push(function) } IDType.FUNCTION2 -> { val function: (DoubleArray) -> Double = - when (val candidate = node.text) { + when (name) { "max" -> { x -> max(x[0], x[1]) } "min" -> { x -> min(x[0], x[1]) } "pow" -> { x -> x[0].pow(x[1]) } "atan2" -> { x -> atan2(x[0], x[1]) } "random" -> { x -> Double.uniform(x[0], x[1]) } "length" -> { x -> Vector2(x[0], x[1]).length } - else -> functions.functions2[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1]) } } + else -> functions.functions2[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1]) } } ?: errorValue( - "unresolved function: '${candidate}(x0, x1)'" + "unresolved function: '${name}(x0, x1)'" ) { _ -> error("this is the error function") } } functionStack.push(function) } IDType.FUNCTION3 -> { val function: (DoubleArray) -> Double = - when (val candidate = node.text) { + when (name) { "mix" -> { x -> mix(x[0], x[1], x[2]) } + "min" -> { x -> x.minOrNull()!! } + "max" -> { x -> x.maxOrNull()!! } + "sum" -> { x -> x.sum() } "smoothstep" -> { x -> smoothstep(x[0], x[1], x[2]) } "length" -> { x -> Vector3(x[0], x[1], x[2]).length } - else -> functions.functions3[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2]) } } + else -> functions.functions3[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2]) } } ?: errorValue( - "unresolved function: '${candidate}(x0, x1, x2)'" + "unresolved function: '${name}(x0, x1, x2)'" ) { _ -> error("this is the error function") } } functionStack.push(function) } IDType.FUNCTION4 -> { val function: (DoubleArray) -> Double = - when (val candidate = node.text) { - else -> functions.functions4[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3]) } } + when (name) { + "min" -> { x -> x.minOrNull()!! } + "max" -> { x -> x.maxOrNull()!! } + "sum" -> { x -> x.sum() } + else -> functions.functions4[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3]) } } ?: errorValue( - "unresolved function: '${candidate}(x0, x1, x2, x3)'" + "unresolved function: '${name}(x0, x1, x2, x3)'" ) { _ -> error("this is the error function") } } functionStack.push(function) @@ -336,11 +342,14 @@ internal class ExpressionListener(val functions: FunctionExtensions = FunctionEx IDType.FUNCTION5 -> { val function: (DoubleArray) -> Double = - when (val candidate = node.text) { + when (name) { + "min" -> { x -> x.minOrNull()!! } + "max" -> { x -> x.maxOrNull()!! } + "sum" -> { x -> x.sum() } "map" -> { x -> map(x[0], x[1], x[2], x[3], x[4]) } - else -> functions.functions5[candidate]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3], x[4]) } } + else -> functions.functions5[name]?.let { { x: DoubleArray -> it.invoke(x[0], x[1], x[2], x[3], x[4]) } } ?: errorValue( - "unresolved function: '${candidate}(x0, x1, x2, x3, x4)'" + "unresolved function: '${name}(x0, x1, x2, x3, x4)'" ) { _ -> error("this is the error function") } } functionStack.push(function) @@ -374,7 +383,7 @@ fun evaluateExpression( } }) - val root = parser.miniCalcFile() + val root = parser.keyLangFile() val listener = ExpressionListener(functions) listener.variables.putAll(variables) try { diff --git a/orx-jvm/orx-keyframer/src/test/kotlin/TestExpressionErrors.kt b/orx-jvm/orx-keyframer/src/test/kotlin/TestExpressionErrors.kt index 5dd689196..07e5817c8 100644 --- a/orx-jvm/orx-keyframer/src/test/kotlin/TestExpressionErrors.kt +++ b/orx-jvm/orx-keyframer/src/test/kotlin/TestExpressionErrors.kt @@ -3,55 +3,50 @@ import org.amshove.kluent.`with message` import org.amshove.kluent.invoking import org.openrndr.extra.keyframer.ExpressionException import org.openrndr.extra.keyframer.evaluateExpression -import org.spekframework.spek2.Spek -import org.spekframework.spek2.style.specification.describe -import java.lang.IllegalStateException +import kotlin.test.Test -object TestExpressionErrors : Spek({ +class TestExpressionErrors { - describe("an expression with non-sensible writing") { + @Test + fun `an expression with non-sensible writing`() { val expression = ")(" - it("should cause an exception to be thrown when evaluated") { - invoking { - evaluateExpression(expression) - } `should throw` ExpressionException::class `with message` "parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]" - } + invoking { + evaluateExpression(expression) + } `should throw` ExpressionException::class `with message` "parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]" + } - describe("an expression with equality instead of assign") { + @Test + fun `an expression with equality instead of assign`() { val expression = "a == 5" - it("should cause an exception to be thrown when evaluated") { - invoking { - evaluateExpression(expression) - } `should throw` ExpressionException::class `with message` "parser error in expression: 'a == 5'; [line: 1, character: 3 , near: [@3,3:3='=',<19>,1:3] ]" - } + invoking { + evaluateExpression(expression) + } `should throw` ExpressionException::class `with message` "parser error in expression: 'a == 5'; [line: 1, character: 3 , near: [@3,3:3='=',<19>,1:3] ]" + } - describe("an expression trying to reassign a number") { + @Test + fun `an expression trying to reassign a number`() { val expression = "3 = 5" - it("should cause an exception to be thrown when evaluated") { - invoking { - evaluateExpression(expression) - } `should throw` ExpressionException::class `with message` "parser error in expression: '3 = 5'; [line: 1, character: 2 , near: [@2,2:2='=',<19>,1:2] ]" - } + invoking { + evaluateExpression(expression) + } `should throw` ExpressionException::class `with message` "parser error in expression: '3 = 5'; [line: 1, character: 2 , near: [@2,2:2='=',<19>,1:2] ]" } - describe("an expression that uses non-existing functions") { + @Test + fun `an expression that uses non-existing functions`() { val expression = "notExisting(5)" - it("should cause an exception to be thrown when evaluated") { - invoking { - evaluateExpression(expression) - } `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting(5)': unresolved function: 'notExisting(x0)'" - } + invoking { + evaluateExpression(expression) + } `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting(5)': unresolved function: 'notExisting(x0)'" + } - describe("an expression that uses non-existing variables") { + @Test + fun `an expression that uses non-existing variables`() { val expression = "notExisting + 4" - it("should cause an exception to be thrown when evaluated") { - invoking { - evaluateExpression(expression) - } `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting+4': unresolved variable: 'notExisting'" - } + invoking { + evaluateExpression(expression) + } `should throw` ExpressionException::class `with message` "error in evaluation of 'notExisting+4': unresolved variable: 'notExisting'" } - -}) +} diff --git a/orx-jvm/orx-keyframer/src/test/kotlin/TestFunctionCall.kt b/orx-jvm/orx-keyframer/src/test/kotlin/TestExpressions.kt similarity index 59% rename from orx-jvm/orx-keyframer/src/test/kotlin/TestFunctionCall.kt rename to orx-jvm/orx-keyframer/src/test/kotlin/TestExpressions.kt index 2bfb6455c..06914e025 100644 --- a/orx-jvm/orx-keyframer/src/test/kotlin/TestFunctionCall.kt +++ b/orx-jvm/orx-keyframer/src/test/kotlin/TestExpressions.kt @@ -1,92 +1,138 @@ +import org.amshove.kluent.shouldBe +import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeNear import org.openrndr.extra.keyframer.FunctionExtensions import org.openrndr.extra.keyframer.evaluateExpression -import org.spekframework.spek2.Spek -import org.spekframework.spek2.style.specification.describe +import org.openrndr.math.map -object TestFunctionCall : Spek({ - describe("a function call") { +import kotlin.test.Test +class TestExpressions { + + + @Test + fun `a value reference`() { + val expression = "someValue" + val result = evaluateExpression(expression, variables= mapOf("someValue" to 5.0)) + result?.shouldBeEqualTo(5.0) + } + + @Test + fun `a backticked value reference`() { + val expression = "`some-value`" + val result = evaluateExpression(expression, variables= mapOf("some-value" to 5.0)) + result?.shouldBeEqualTo(5.0) + } + + + @Test + fun `a function call`() { val expression = "sqrt(4.0)" val result = evaluateExpression(expression) result?.shouldBeNear(2.0, 10E-6) } - describe("two function calls") { + @Test + fun `a function call with the name in backticks`() { + val expression = "`sqrt`(4.0)" + val result = evaluateExpression(expression) + result?.shouldBeNear(2.0, 10E-6) + } + + @Test + fun `two function calls`() { val expression = "sqrt(4.0) * sqrt(4.0)" val result = evaluateExpression(expression) result?.shouldBeNear(4.0, 10E-6) } - describe("two argument function call") { + @Test + fun `two argument max function call`() { val expression = "max(0.0, 4.0)" val result = evaluateExpression(expression) result?.shouldBeNear(4.0, 10E-6) } - describe("two argument function call") { + @Test + fun `two argument min function call`() { val expression = "min(8.0, 4.0)" val result = evaluateExpression(expression) result?.shouldBeNear(4.0, 10E-6) } - describe("three argument function call") { + @Test + fun `three argument function call`() { val expression = "mix(8.0, 4.0, 0.5)" val result = evaluateExpression(expression) result?.shouldBeNear(6.0, 10E-6) } - describe("five argument function call") { + @Test + fun `five argument function call`() { val expression = "map(0.0, 1.0, 0.0, 8.0, 0.5)" val result = evaluateExpression(expression) result?.shouldBeNear(4.0, 10E-6) } - describe("two argument function call, where argument order matters") { + @Test + fun `two argument function call, where argument order matters`() { val expression = "pow(2.0, 3.0)" val result = evaluateExpression(expression) result?.shouldBeNear(8.0, 10E-6) } - describe("nested function call") { + @Test + fun `nested function call`() { val expression = "sqrt(min(8.0, 4.0))" val result = evaluateExpression(expression) result?.shouldBeNear(2.0, 10E-6) } - describe("extension function0 call") { + @Test + fun `extension function0 call`() { val expression = "extension()" val result = evaluateExpression(expression, functions = FunctionExtensions(functions0 = mapOf("extension" to { 2.0 }))) result?.shouldBeNear(2.0, 10E-6) } - describe("extension function1 call") { + @Test + fun `extension function1 call`(){ val expression = "extension(1.0)" val result = evaluateExpression(expression, functions = FunctionExtensions(functions1 = mapOf("extension" to { x -> x * 2.0 }))) result?.shouldBeNear(2.0, 10E-6) } - describe("extension function2 call") { + @Test + fun `extension function1 call with dashed name in backticks`(){ + val expression = "`extension-function`(1.0)" + val result = evaluateExpression(expression, functions = FunctionExtensions(functions1 = mapOf("extension-function" to { x -> x * 2.0 }))) + result?.shouldBeNear(2.0, 10E-6) + } + + @Test + fun `extension function2 call`() { val expression = "extension(1.0, 1.0)" val result = evaluateExpression(expression, functions = FunctionExtensions(functions2 = mapOf("extension" to { x, y -> x + y }))) result?.shouldBeNear(2.0, 10E-6) } - describe("extension function3 call") { + @Test + fun `extension function3 call`() { val expression = "extension(1.0, 1.0, 1.0)" val result = evaluateExpression(expression, functions = FunctionExtensions(functions3 = mapOf("extension" to { x, y, z -> x + y + z}))) result?.shouldBeNear(3.0, 10E-6) } - describe("extension function4 call") { + @Test + fun `extension function4 call`() { val expression = "extension(1.0, 1.0, 1.0, 1.0)" val result = evaluateExpression(expression, functions = FunctionExtensions(functions4 = mapOf("extension" to { x, y, z, w -> x + y + z + w}))) result?.shouldBeNear(4.0, 10E-6) } - describe("extension function5 call") { + @Test + fun `extension function5 call`() { val expression = "extension(1.0, 1.0, 1.0, 1.0, 1.0)" val result = evaluateExpression(expression, functions = FunctionExtensions(functions5 = mapOf("extension" to { x, y, z, w, u -> x + y + z + w + u}))) result?.shouldBeNear(5.0, 10E-6) } - -}) \ No newline at end of file +} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerChannel.kt b/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerChannel.kt index 0977adecc..9f505c18e 100644 --- a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerChannel.kt +++ b/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerChannel.kt @@ -2,34 +2,31 @@ import org.amshove.kluent.`should be` import org.amshove.kluent.shouldBeNear import org.openrndr.extra.keyframer.KeyframerChannel import org.openrndr.extras.easing.Easing -import org.spekframework.spek2.Spek -import org.spekframework.spek2.style.specification.describe -object TestKeyframerChannel : Spek({ +import kotlin.test.Test - describe("a keyframer channel without keys") { +class TestKeyframerChannel { + @Test + fun `a keyframer channel without keys`() { val kfc = KeyframerChannel() - it ("should return null when asking for value before first key time") { - kfc.value(0.0) `should be` null - } + kfc.value(0.0) `should be` null } - describe("a keyframer channel with a single key") { + + @Test + fun `a keyframer channel with a single key`() { val kfc = KeyframerChannel() + kfc.add(0.0, 1.0, Easing.Linear.function) kfc.value(0.0)?.shouldBeNear(1.0, 10E-6) - - it ("should return null when asking for value before first key time") { - kfc.value(-1.0) `should be` null - } + kfc.value(-1.0) `should be` null } - describe("a keyframer channel with two keys") { + + @Test + fun `a keyframer channel with two keys`() { val kfc = KeyframerChannel() kfc.add(0.0, 1.0, Easing.Linear.function) kfc.add(1.0, 2.0, Easing.Linear.function) kfc.value(0.0)?.shouldBeNear(1.0, 10E-6) - - it ("should return null when asking for value before first key time") { - kfc.value(-1.0) `should be` null - } + kfc.value(-1.0) `should be` null } -}) \ No newline at end of file +} \ No newline at end of file diff --git a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerErrors.kt b/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerErrors.kt index 4a5dd55ec..46ac46f20 100644 --- a/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerErrors.kt +++ b/orx-jvm/orx-keyframer/src/test/kotlin/TestKeyframerErrors.kt @@ -1,110 +1,96 @@ -//import org.amshove.kluent.`should throw` -//import org.amshove.kluent.`with message` -//import org.amshove.kluent.invoking -//import org.openrndr.extra.keyframer.ExpressionException -//import org.openrndr.extra.keyframer.Keyframer -//import org.openrndr.extra.keyframer.KeyframerFormat -//import org.spekframework.spek2.Spek -//import org.spekframework.spek2.style.specification.describe -//import java.io.File -//import kotlin.IllegalStateException -// -// -//private fun testFile(path: String) : File { -// val test = File(".") -// return if (test.absolutePath.endsWith("orx-keyframer/.")) { -// File(path) -// } else { -// File("orx-keyframer/$path") -// } -//} -//private fun testName(path: String) : String { -// val test = File(".") -// return (if (test.absolutePath.endsWith("orx-keyframer/.")) { -// path -// } else { -// "orx-keyframer/$path" -// }).replace("/", File.separator) -//} -// -// -//object TestKeyframerErrors : Spek({ -// class Animation : Keyframer() { -// val position by Vector2Channel(arrayOf("x", "y")) -// } -// -// describe("loading a faulty json") { -// val animation = Animation() -// val json = """ -// """ -// it("should throw an exception") { -// invoking { animation.loadFromJsonString(json) } `should throw` (IllegalStateException::class) -// } -// } -// -// describe("loading a non existing json") { -// val animation = Animation() -// it("should throw an exception") { -// invoking { animation.loadFromJson(testFile("this-does-not-exist")) } `should throw` (IllegalArgumentException::class) -// } -// } -// -// describe("loading a json with a faulty time expression (1)") { -// -// File(".").apply { -// println(this.absolutePath) -// } -// -// -// -// val animation = Animation() -// it("should throw an exception") { -// invoking { -// animation.loadFromJson( -// testFile("src/test/resources/error-reporting/time-01.json"), -// format = KeyframerFormat.SIMPLE -// ) -// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-01.json")}': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]" -// } -// } -// // Paths.sep -// // -// //Expected ,1:0] ]>, -// // actual ,1:0] ]>. -// describe("loading a json with a faulty time expression (2) ") { -// val animation = Animation() -// it("should throw an exception") { -// invoking { -// animation.loadFromJson( -// testFile("src/test/resources/error-reporting/time-02.json"), -// format = KeyframerFormat.SIMPLE -// ) -// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-02.json")}': error in keys[0].'time': error in evaluation of 'doesNotExist': unresolved variable: 'doesNotExist'" -// } -// } -// -// describe("loading a json with a non-existing easing") { -// val animation = Animation() -// it("should throw an exception") { -// invoking { -// animation.loadFromJson( -// testFile("src/test/resources/error-reporting/easing.json"), -// format = KeyframerFormat.SIMPLE -// ) -// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/easing.json")}': error in keys[0].'easing': unknown easing name 'garble'" -// } -// } -// -// describe("loading a json with a faulty value (1)") { -// val animation = Animation() -// -// it("should throw an exception") { -// invoking { -// animation.loadFromJson( -// testFile("src/test/resources/error-reporting/value-01.json"), -// format = KeyframerFormat.SIMPLE -// ) -// } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/value-01.json")}': error in keys[0].'x': error in evaluation of 'garble': unresolved variable: 'garble'" -// } -// } -//}) \ No newline at end of file +import org.amshove.kluent.`should throw` +import org.amshove.kluent.invoking +import kotlin.test.Test +import org.openrndr.extra.keyframer.ExpressionException +import org.openrndr.extra.keyframer.Keyframer +import org.openrndr.extra.keyframer.KeyframerFormat +import java.io.File +import kotlin.IllegalStateException + +private fun testFile(path: String): File { + val test = File(".") + return if (test.absolutePath.replace("\\","/").endsWith("orx-keyframer/.")) { + File(path) + } else { + File("orx-keyframer/$path") + } +} + +class TestKeyframerErrors { + class Animation : Keyframer() { + val position by Vector2Channel(arrayOf("x", "y")) + } + + @Test + fun `loading a faulty json`() { + val animation = Animation() + val json = """ + """ + invoking { animation.loadFromJsonString(json) } `should throw` (IllegalStateException::class) + } + + @Test + fun `loading a non existing json`() { + val animation = Animation() + + invoking { animation.loadFromJson(testFile("this-does-not-exist")) } `should throw` (IllegalArgumentException::class) + + } + @Test + fun `loading a json with a faulty time expression (1)`() { + + File(".").apply { + println(this.absolutePath) + } + + + val animation = Animation() + + invoking { + animation.loadFromJson( + testFile("src/test/resources/error-reporting/time-01.json"), + format = KeyframerFormat.SIMPLE + ) + } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-01.json")}': error in keys[0].'time': parser error in expression: ')('; [line: 1, character: 0 , near: [@0,0:0=')',<21>,1:0] ]" + + } + + // Paths.sep + // + //Expected ,1:0] ]>, + // actual ,1:0] ]>. + + @Test + fun `loading a json with a faulty time expression (2) `() { + val animation = Animation() + invoking { + animation.loadFromJson( + testFile("src/test/resources/error-reporting/time-02.json"), + format = KeyframerFormat.SIMPLE + ) + } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/time-02.json")}': error in keys[0].'time': error in evaluation of 'doesNotExist': unresolved variable: 'doesNotExist'" + + } + @Test + fun `loading a json with a non-existing easing`() { + val animation = Animation() + invoking { + animation.loadFromJson( + testFile("src/test/resources/error-reporting/easing.json"), + format = KeyframerFormat.SIMPLE + ) + } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/easing.json")}': error in keys[0].'easing': unknown easing name 'garble'" + } + + @Test + fun `loading a json with a faulty value (1)`() { + val animation = Animation() + + invoking { + animation.loadFromJson( + testFile("src/test/resources/error-reporting/value-01.json"), + format = KeyframerFormat.SIMPLE + ) + } `should throw` ExpressionException::class //`with message` "Error loading from '${testName("src/test/resources/error-reporting/value-01.json")}': error in keys[0].'x': error in evaluation of 'garble': unresolved variable: 'garble'" + } +} diff --git a/orx-jvm/orx-keyframer/src/test/kotlin/TestOperators.kt b/orx-jvm/orx-keyframer/src/test/kotlin/TestOperators.kt index 3c7299b6b..90e7ec80b 100644 --- a/orx-jvm/orx-keyframer/src/test/kotlin/TestOperators.kt +++ b/orx-jvm/orx-keyframer/src/test/kotlin/TestOperators.kt @@ -1,39 +1,53 @@ import org.amshove.kluent.shouldBeNear import org.openrndr.extra.keyframer.evaluateExpression -import org.spekframework.spek2.Spek -import org.spekframework.spek2.style.specification.describe +import kotlin.test.Test -object TestOperators : Spek({ - describe("an addition operation") { +class TestOperators { + @Test + fun `an addition operation`() { val result = evaluateExpression("1 + 2") result?.shouldBeNear(3.0, 10E-6) } - describe("a subtraction operation") { + + @Test + fun `a subtraction operation`() { val result = evaluateExpression("1 - 2") result?.shouldBeNear(-1.0, 10E-6) } - describe("a modulus operation") { + + @Test + fun `a modulus operation`() { val result = evaluateExpression("4 % 2") result?.shouldBeNear(0.0, 10E-6) } - describe("a multiplication operation") { + + @Test + fun `a multiplication operation`() { val result = evaluateExpression("4 * 2") result?.shouldBeNear(8.0, 10E-6) } - describe("a division operation") { + + @Test + fun `a division operation`() { val result = evaluateExpression("4 / 2") result?.shouldBeNear(2.0, 10E-6) } - describe("a multiplication/addition operation") { + + @Test + fun `a multiplication and addition operation`() { val result = evaluateExpression("4 * 2 + 1") result?.shouldBeNear(9.0, 10E-6) } - describe("an addition/multiplication") { + + @Test + fun `an addition and multiplication`() { val result = evaluateExpression("4 + 2 * 3") result?.shouldBeNear(10.0, 10E-6) } - describe("unary minus") { + + @Test + fun `unary minus`() { val result = evaluateExpression("-4.0") result?.shouldBeNear(-4.0, 10E-6) } -}) +}