diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Typed.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Typed.kt index d999f63..9d8b414 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Typed.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/ast/Typed.kt @@ -40,8 +40,10 @@ fun interface Typed { } fun toStringTypeCoercion(value: Any?): String? { - return if (value == null) null - else (value as? VariableValue)?.valueOrDefault()?.toString() ?: value.toString() + if (value == null) return null + if (value is VariableValue) return toStringTypeCoercion(value.valueOrDefault()); + if (value is Number) return if (isNonFractionValue(value)) value.toInt().toString() else value.toString(); + return value.toString() } fun toMixedTypeTypeCoercion(value: Any?): Any? { diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt index 1dc09bb..2627fb2 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/spi/ExpressionFunctions.kt @@ -218,7 +218,7 @@ fun interface ExpressionFunctions { fun d2_minutesBetween(start: LocalDate?, end: LocalDate?): Int { require(start != null) { "start parameter of d2:minutesBetween must not be null" } require(end != null) { "end parameter of d2:minutesBetween must not be null" } - return d2_daysBetween(start, end).times(25 * 60) + return d2_daysBetween(start, end).times(24 * 60) } fun d2_minValue(value: VariableValue?): Double { @@ -285,7 +285,7 @@ fun interface ExpressionFunctions { fun d2_yearsBetween(start: LocalDate?, end: LocalDate?): Int { require(start != null) { "start parameter of d2:yearsBetween must not be null" } require(end != null) { "end parameter of d2:yearsBetween must not be null" } - return if (start.toEpochDays() < end.toEpochDays()) start.yearsUntil(end) / 7 else end.yearsUntil(start) / 7 + return if (start.toEpochDays() < end.toEpochDays()) start.yearsUntil(end) else end.yearsUntil(start) } /** diff --git a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/Literals.kt b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/Literals.kt index d997e74..cfb7c14 100644 --- a/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/Literals.kt +++ b/src/commonMain/kotlin/org/hisp/dhis/lib/expression/syntax/Literals.kt @@ -28,7 +28,7 @@ object Literals { } } - fun parseUnaryOp(expr: Expr): String { + private fun parseUnaryOp(expr: Expr): String { val c = expr.peek() if (isUnaryOperator(c)) { expr.error("unary operator") @@ -82,7 +82,7 @@ object Literals { return expr.rawMatch("name", Chars::isName) } - fun parseInteger(expr: Expr): String { + private fun parseInteger(expr: Expr): String { val s = expr.position() expr.gobbleIf(Chars::isSignOperator) expr.skipWhile(Chars::isDigit) @@ -109,11 +109,11 @@ object Literals { return expr.raw(s) } - fun parseBoolean(expr: Expr): String { + private fun parseBoolean(expr: Expr): String { return expr.rawMatch("boolean", if (expr.peek() == 't') "true" else "false") } - fun parseDate(expr: Expr): String { + private fun parseDate(expr: Expr): String { // [1-9] [0-9] [0-9] [0-9] '-' [0-1]? [0-9] '-' [0-3]? [0-9] val s = expr.position() expr.expect("digit", Chars::isDigit) diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/AggregateFunctionsExpressionTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/AggregateFunctionsExpressionTest.kt deleted file mode 100644 index 8ff51e4..0000000 --- a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/AggregateFunctionsExpressionTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.hisp.dhis.lib.expression - -import org.hisp.dhis.lib.expression.spi.* -import kotlin.test.Test -import kotlin.test.assertEquals - -/** - * @author Jan Bernitt - */ -internal class AggregateFunctionsExpressionTest { - @Test - fun testAvg() { - val dataValues = mapOf( - Pair(newDeDataItem("u1234567890"), doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0))) - assertEquals(5.0, evaluate("avg(#{u1234567890})", dataValues)) - } - - @Test - fun testAvg2() { - val dataValues = mapOf( - newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0), - newDeDataItem("v1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) - assertEquals(10.0, evaluate("avg(#{u1234567890} + #{v1234567890})", dataValues)) - } - - @Test - fun testSum() { - val dataValues = mapOf( - newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) - assertEquals(25.0, evaluate("sum(#{u1234567890})", dataValues)) - } - - @Test - fun testMin() { - val dataValues = mapOf( - newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) - assertEquals(0.0, evaluate("min(#{u1234567890})", dataValues)) - } - - @Test - fun testMax() { - val dataValues = mapOf( - newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) - assertEquals(10.0, evaluate("max(#{u1234567890})", dataValues)) - } - - companion object { - private fun evaluate(expression: String, dataValues: Map): Double { - return Expression(expression).evaluate( - { _: String -> null }, - ExpressionData().copy(dataItemValues = dataValues)) as Double - } - - private fun newDeDataItem(u1234567890: String): DataItem { - return DataItem( - DataItemType.DATA_ELEMENT, ID(ID.Type.DataElementUID, u1234567890), - QueryModifiers().copy(periodAggregation = true)) - } - } -} diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/FunctionsExpressionTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/FunctionsExpressionTest.kt deleted file mode 100644 index d01835a..0000000 --- a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/FunctionsExpressionTest.kt +++ /dev/null @@ -1,169 +0,0 @@ -package org.hisp.dhis.lib.expression - -import org.hisp.dhis.lib.expression.spi.IllegalExpressionException -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertNull - -/** - * Tests the [NamedFunction]s. - * - * @author Jan Bernitt - */ -internal class FunctionsExpressionTest { - @Test - fun testFirstNonNull_Numbers() { - assertEquals(42.0, evaluate("firstNonNull(null, 42, 23)")) - } - - @Test - fun testFirstNonNull_Strings() { - assertEquals("42", evaluate("firstNonNull(null,null, \"42\", \"23\")")) - } - - @Test - fun testFirstNonNull_Booleans() { - assertEquals(true, evaluate("firstNonNull(true, null, false)")) - } - - @Test - fun testGreatest() { - assertEquals(5.0, evaluate("greatest(3,2.2,5.0,0.4)")) - } - - @Test - fun testGreatest_Null() { - assertEquals(5.0, evaluate("greatest(null,4,5.0)")) - } - - @Test - fun testGreatest_NaN() { - assertEquals(Double.NaN, evaluate("greatest(1%0,4,5.0)")) - } - - @Test - fun testGreatest_PositiveInfinity() { - assertEquals(Double.POSITIVE_INFINITY, evaluate("greatest(1/0,4,5.0)")) - } - - @Test - fun testGreatest_Empty() { - assertNull(evaluate("greatest()")) - } - - @Test - fun testIf_True() { - assertEquals(1.0, evaluate("if(true, 1, 0)")) - assertEquals(true, evaluate("if(1 < 4, true, false)")) - assertNull(evaluate("if(1, null, 42)")) - } - - @Test - fun testIf_False() { - assertNull(evaluate("if(false, 1, null)")) - assertEquals(false, evaluate("if(1 > 4, true, false)")) - assertEquals(42.0, evaluate("if(0, null, 42)")) - } - - @Test - fun testIf_Null() { - assertEquals(false, evaluate("if(null, true, false)")) - } - - @Test - fun testIf_NaN() { - val ex = assertFailsWith(IllegalExpressionException::class) { evaluate("if(1%0, true, false)") } - assertEquals( - "Failed to coerce value 'NaN' (Double) to Boolean in expression: 1 % 0", ex.message) - } - - @Test - fun testIsNotNull() { - assertEquals(true, evaluate("isNotNull(2)")) - assertEquals(true, evaluate("isNotNull(42.0)")) - assertEquals(true, evaluate("isNotNull('hello')")) - assertEquals(true, evaluate("isNotNull(false)")) - assertEquals(true, evaluate("isNotNull(1%0)")) - assertEquals(true, evaluate("isNotNull(1/0)")) - assertEquals(false, evaluate("isNotNull(null)")) - } - - @Test - fun testIsNull() { - assertEquals(false, evaluate("isNull(2)")) - assertEquals(false, evaluate("isNull(42.0)")) - assertEquals(false, evaluate("isNull('hello')")) - assertEquals(false, evaluate("isNull(false)")) - assertEquals(false, evaluate("isNull(1%0)")) - assertEquals(false, evaluate("isNull(1/0)")) - assertEquals(true, evaluate("isNull(null)")) - } - - @Test - fun testLeast() { - assertEquals(0.4, evaluate("least(3,2.2,5.0,0.4)")) - } - - @Test - fun testLeast_Null() { - assertEquals(4.0, evaluate("least(null,4,5.0)")) - } - - @Test - fun testLeast_NaN() { - assertEquals(4.0, evaluate("least(1%0,4,5.0)")) - } - - @Test - fun testLeast_PositiveInfinity() { - assertEquals(Double.NEGATIVE_INFINITY, evaluate("least(-1/0,4,5.0,1/0)")) - } - - @Test - fun testLeast_Empty() { - assertNull(evaluate("least()")) - } - - @Test - fun testRemoveZeros() { - assertNull(evaluate("removeZeros(0.0)")) - assertNull(evaluate("removeZeros(0)")) - assertEquals(42.0, evaluate("removeZeros(42.0)")) - } - - @Test - fun testBlockWhitespaceSingleArgs() { - assertEquals(false, evaluate("isNull(2)")) - assertEquals(false, evaluate("isNull( 2)")) - assertEquals(false, evaluate("isNull(2 )")) - assertEquals(false, evaluate("isNull( 2 )")) - } - - @Test - fun testBlockWhitespaceMultiArgs() { - assertEquals(Double.POSITIVE_INFINITY, evaluate("log(2,1)")) - assertEquals(Double.POSITIVE_INFINITY, evaluate("log( 2, 1)")) - assertEquals(Double.POSITIVE_INFINITY, evaluate("log(2 , 1 )")) - assertEquals(Double.POSITIVE_INFINITY, evaluate("log( 2 , 1 )")) - } - - @Test - fun testBlockWhitespaceVaragrs() { - assertNull(evaluate("firstNonNull()")) - assertEquals(2.0, evaluate("firstNonNull(2)")) - assertEquals(2.0, evaluate("firstNonNull( 2)")) - assertEquals(2.0, evaluate("firstNonNull(2 )")) - assertEquals(2.0, evaluate("firstNonNull( 2 )")) - assertEquals(2.0, evaluate("firstNonNull(2,3)")) - assertEquals(2.0, evaluate("firstNonNull( 2, 3)")) - assertEquals(2.0, evaluate("firstNonNull(2 , 3 )")) - assertEquals(2.0, evaluate("firstNonNull( 2 , 3 )")) - } - - companion object { - private fun evaluate(expression: String): Any? { - return Expression(expression).evaluate() - } - } -} diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionAddDaysTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AddDaysTest.kt similarity index 94% rename from src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionAddDaysTest.kt rename to src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AddDaysTest.kt index 90cd32d..5345741 100644 --- a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionAddDaysTest.kt +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AddDaysTest.kt @@ -1,6 +1,7 @@ -package org.hisp.dhis.lib.expression +package org.hisp.dhis.lib.expression.function import kotlinx.datetime.LocalDate +import org.hisp.dhis.lib.expression.Expression import org.hisp.dhis.lib.expression.spi.IllegalExpressionException import org.hisp.dhis.lib.expression.Expression.Mode @@ -11,12 +12,11 @@ import kotlin.test.assertFailsWith /** * Test of the `d2:ceil` function. * - * * Translated from existing test of same name in rule-engine. * * @author Jan Bernitt */ -internal class RuleFunctionAddDaysTest { +internal class AddDaysTest { @Test fun return_new_date_with_days_added() { assertEquals(LocalDate.parse("2011-01-07"), evaluate("d2:addDays('2011-01-01', 6.0)")) diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AggregateFunctionTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AggregateFunctionTest.kt new file mode 100644 index 0000000..ddcd1fb --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AggregateFunctionTest.kt @@ -0,0 +1,19 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import org.hisp.dhis.lib.expression.spi.* + +abstract class AggregateFunctionTest { + + fun evaluate(expression: String, dataValues: Map): Double { + return Expression(expression).evaluate( + { _: String -> null }, + ExpressionData().copy(dataItemValues = dataValues)) as Double + } + + fun newDeDataItem(u1234567890: String): DataItem { + return DataItem( + DataItemType.DATA_ELEMENT, ID(ID.Type.DataElementUID, u1234567890), + QueryModifiers().copy(periodAggregation = true)) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AvgTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AvgTest.kt new file mode 100644 index 0000000..b88a97f --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/AvgTest.kt @@ -0,0 +1,27 @@ +package org.hisp.dhis.lib.expression.function + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `avg` function + * + * @author Jan Bernitt + */ +internal class AvgTest : AggregateFunctionTest() { + + @Test + fun testAvg_Single() { + val dataValues = mapOf( + newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) + assertEquals(5.0, evaluate("avg(#{u1234567890})", dataValues)) + } + + @Test + fun testAvg_Complex() { + val dataValues = mapOf( + newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0), + newDeDataItem("v1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) + assertEquals(10.0, evaluate("avg(#{u1234567890} + #{v1234567890})", dataValues)) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionCeilTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/CeilTest.kt similarity index 91% rename from src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionCeilTest.kt rename to src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/CeilTest.kt index cd8a9c8..1fa3fe0 100644 --- a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionCeilTest.kt +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/CeilTest.kt @@ -1,5 +1,6 @@ -package org.hisp.dhis.lib.expression +package org.hisp.dhis.lib.expression.function +import org.hisp.dhis.lib.expression.Expression import org.hisp.dhis.lib.expression.spi.IllegalExpressionException import org.hisp.dhis.lib.expression.Expression.Mode @@ -15,7 +16,7 @@ import kotlin.test.assertFailsWith * * @author Jan Bernitt */ -internal class RuleFunctionCeilTest { +internal class CeilTest { @Test fun evaluateMustReturnCeiledValue() { assertEquals(5.0, evaluate("d2:ceil(4.1)")) diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ConcatenateTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ConcatenateTest.kt new file mode 100644 index 0000000..070207c --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ConcatenateTest.kt @@ -0,0 +1,22 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Test of the `d2:concatenate` function. + * + * @author Jan Bernitt + */ +internal class ConcatenateTest { + + @Test + fun testConcatenate() { + assertEquals("hello world", evaluate("d2:concatenate(\"hello\", \" world\")")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.RULE_ENGINE_ACTION).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/CountTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/CountTest.kt new file mode 100644 index 0000000..c78c21c --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/CountTest.kt @@ -0,0 +1,19 @@ +package org.hisp.dhis.lib.expression.function + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `count` function + * + * @author Jan Bernitt + */ +internal class CountTest : AggregateFunctionTest() { + + @Test + fun testCount() { + val dataValues = mapOf( + newDeDataItem("u1234567890") to doubleArrayOf(0.0, 20.0, 5.0, 3.0, 7.0)) + assertEquals(5.0, evaluate("count(#{u1234567890})", dataValues)) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/DaysBetweenTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/DaysBetweenTest.kt new file mode 100644 index 0000000..4a2b8f9 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/DaysBetweenTest.kt @@ -0,0 +1,35 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +/** + * Tests the `d2:daysBetween` function + * + * @author Jan Bernitt + */ +internal class DaysBetweenTest { + + @Test + fun testDaysBetween() { + assertEquals(6, evaluate("d2:daysBetween(\"2020-01-01\", \"2020-01-07\")")) + assertEquals(6, evaluate("d2:daysBetween(\"2020-01-07\", \"2020-01-01\")")) + assertEquals(31, evaluate("d2:daysBetween(\"2020-01-01\", \"2020-02-01\")")) + assertEquals(29, evaluate("d2:daysBetween(\"2020-02-01\", \"2020-03-01\")")) + assertEquals(366, evaluate("d2:daysBetween(\"2020-01-01\", \"2021-01-01\")")) + } + + @Test + fun testDaysBetween_Null() { + val ex = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:daysBetween(null, \"2021-01-01\")") } + assertEquals("start parameter of d2:daysBetween must not be null", ex.message) + val ex2 = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:daysBetween(\"2021-01-01\", null)") } + assertEquals("end parameter of d2:daysBetween must not be null", ex2.message) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.RULE_ENGINE_ACTION).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionExtractDataMatrixValueTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ExtractDataMatrixValueTest.kt similarity index 93% rename from src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionExtractDataMatrixValueTest.kt rename to src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ExtractDataMatrixValueTest.kt index a2b401c..93df5f5 100644 --- a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionExtractDataMatrixValueTest.kt +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/ExtractDataMatrixValueTest.kt @@ -1,5 +1,6 @@ -package org.hisp.dhis.lib.expression +package org.hisp.dhis.lib.expression.function +import org.hisp.dhis.lib.expression.Expression import org.hisp.dhis.lib.expression.Expression.Mode import kotlin.test.Test @@ -14,7 +15,7 @@ import kotlin.test.assertFailsWith * * @author Jan Bernitt */ -internal class RuleFunctionExtractDataMatrixValueTest { +internal class ExtractDataMatrixValueTest { @Test fun throw_argument_exception_if_value_is_not_gs1() { assertFailsWith(IllegalArgumentException::class) { extractDataMatrixValue("serial number", "testingvalue") } diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/FirstNonNullTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/FirstNonNullTest.kt new file mode 100644 index 0000000..f2e84c5 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/FirstNonNullTest.kt @@ -0,0 +1,46 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +/** + * Tests the `firstNonNull` function + * + * @author Jan Bernitt + */ +internal class FirstNonNullTest { + + @Test + fun testFirstNonNull_Numbers() { + assertEquals(42.0, evaluate("firstNonNull(null, 42, 23)")) + } + + @Test + fun testFirstNonNull_Strings() { + assertEquals("42", evaluate("firstNonNull(null,null, \"42\", \"23\")")) + } + + @Test + fun testFirstNonNull_Booleans() { + assertEquals(true, evaluate("firstNonNull(true, null, false)")) + } + + @Test + fun testFirstNonNull_Whitespace() { + assertNull(evaluate("firstNonNull()")) + assertEquals(2.0, evaluate("firstNonNull(2)")) + assertEquals(2.0, evaluate("firstNonNull( 2)")) + assertEquals(2.0, evaluate("firstNonNull(2 )")) + assertEquals(2.0, evaluate("firstNonNull( 2 )")) + assertEquals(2.0, evaluate("firstNonNull(2,3)")) + assertEquals(2.0, evaluate("firstNonNull( 2, 3)")) + assertEquals(2.0, evaluate("firstNonNull(2 , 3 )")) + assertEquals(2.0, evaluate("firstNonNull( 2 , 3 )")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/FloorTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/FloorTest.kt new file mode 100644 index 0000000..73a9823 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/FloorTest.kt @@ -0,0 +1,37 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `d2:floor` function + * + * @author Jan Bernitt + */ +internal class FloorTest { + + @Test + fun testFloor() { + assertEquals(1.0, evaluate("d2:floor(1.2)")) + assertEquals(1.0, evaluate("d2:floor(1.5)")) + assertEquals(1.0, evaluate("d2:floor(1.6)")) + assertEquals(1.0, evaluate("d2:floor(1.9)")) + assertEquals(2.0, evaluate("d2:floor(2.0)")) + + } + + @Test + fun testFloor_NegativeNumber() { + assertEquals(-2.0, evaluate("d2:floor(-1.2)")) + } + + @Test + fun testFloor_Null() { + assertEquals(0.0, evaluate("d2:floor(null)")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.RULE_ENGINE_ACTION).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/GreatestTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/GreatestTest.kt new file mode 100644 index 0000000..d410776 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/GreatestTest.kt @@ -0,0 +1,43 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +/** + * Tests the `greatest` function + * + * @author Jan Bernitt + */ +internal class GreatestTest { + + @Test + fun testGreatest() { + assertEquals(5.0, evaluate("greatest(3,2.2,5.0,0.4)")) + } + + @Test + fun testGreatest_Null() { + assertEquals(5.0, evaluate("greatest(null,4,5.0)")) + } + + @Test + fun testGreatest_NaN() { + assertEquals(Double.NaN, evaluate("greatest(1%0,4,5.0)")) + } + + @Test + fun testGreatest_PositiveInfinity() { + assertEquals(Double.POSITIVE_INFINITY, evaluate("greatest(1/0,4,5.0)")) + } + + @Test + fun testGreatest_Empty() { + assertNull(evaluate("greatest()")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionHasValueTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/HasValueTest.kt similarity index 92% rename from src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionHasValueTest.kt rename to src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/HasValueTest.kt index a972b43..e640360 100644 --- a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/RuleFunctionHasValueTest.kt +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/HasValueTest.kt @@ -1,5 +1,6 @@ -package org.hisp.dhis.lib.expression +package org.hisp.dhis.lib.expression.function +import org.hisp.dhis.lib.expression.Expression import org.hisp.dhis.lib.expression.spi.ExpressionData import org.hisp.dhis.lib.expression.spi.ValueType import org.hisp.dhis.lib.expression.spi.VariableValue @@ -17,7 +18,7 @@ import kotlin.test.assertEquals * * @author Jan Bernitt */ -internal class RuleFunctionHasValueTest { +internal class HasValueTest { @Test fun return_false_for_non_existing_variable() { assertHasValue("d2:hasValue(#{nonexisting})", mapOf(), false) diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IfTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IfTest.kt new file mode 100644 index 0000000..1b8dffa --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IfTest.kt @@ -0,0 +1,46 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import org.hisp.dhis.lib.expression.spi.IllegalExpressionException +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull + +/** + * Tests the `if` function + * + * @author Jan Bernitt + */ +class IfTest { + + @Test + fun testIf_True() { + assertEquals(1.0, evaluate("if(true, 1, 0)")) + assertEquals(true, evaluate("if(1 < 4, true, false)")) + assertNull(evaluate("if(1, null, 42)")) + } + + @Test + fun testIf_False() { + assertNull(evaluate("if(false, 1, null)")) + assertEquals(false, evaluate("if(1 > 4, true, false)")) + assertEquals(42.0, evaluate("if(0, null, 42)")) + } + + @Test + fun testIf_Null() { + assertEquals(false, evaluate("if(null, true, false)")) + } + + @Test + fun testIf_NaN() { + val ex = assertFailsWith(IllegalExpressionException::class) { evaluate("if(1%0, true, false)") } + assertEquals( + "Failed to coerce value 'NaN' (Double) to Boolean in expression: 1 % 0", ex.message) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IsNotNullTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IsNotNullTest.kt new file mode 100644 index 0000000..9a1343e --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IsNotNullTest.kt @@ -0,0 +1,39 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `isNotNull` function + * + * @author Jan Bernitt + */ +internal class IsNotNullTest { + + @Test + fun testIsNotNull() { + assertEquals(true, evaluate("isNotNull(2)")) + assertEquals(true, evaluate("isNotNull(42.0)")) + assertEquals(true, evaluate("isNotNull('hello')")) + assertEquals(true, evaluate("isNotNull(false)")) + assertEquals(true, evaluate("isNotNull(1%0)")) + assertEquals(true, evaluate("isNotNull(1/0)")) + assertEquals(false, evaluate("isNotNull(null)")) + } + + @Test + fun testIsNull() { + assertEquals(false, evaluate("isNull(2)")) + assertEquals(false, evaluate("isNull(42.0)")) + assertEquals(false, evaluate("isNull('hello')")) + assertEquals(false, evaluate("isNull(false)")) + assertEquals(false, evaluate("isNull(1%0)")) + assertEquals(false, evaluate("isNull(1/0)")) + assertEquals(true, evaluate("isNull(null)")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IsNullTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IsNullTest.kt new file mode 100644 index 0000000..dfc8035 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/IsNullTest.kt @@ -0,0 +1,35 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `isNull` function + * + * @author Jan Bernitt + */ +internal class IsNullTest { + + @Test + fun testIsNull() { + assertEquals(true, evaluate("isNull(null)")) + } + + @Test + fun testIsNull_EmptyString() { + assertEquals(false, evaluate("isNull(\"\")")) + } + + @Test + fun testIsNull_Whitespace() { + assertEquals(false, evaluate("isNull(2)")) + assertEquals(false, evaluate("isNull( 2)")) + assertEquals(false, evaluate("isNull(2 )")) + assertEquals(false, evaluate("isNull( 2 )")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LeastTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LeastTest.kt new file mode 100644 index 0000000..33c316f --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LeastTest.kt @@ -0,0 +1,43 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +/** + * Tests the `least` function + * + * @author Jan Bernitt + */ +internal class LeastTest { + + @Test + fun testLeast() { + assertEquals(0.4, evaluate("least(3,2.2,5.0,0.4)")) + } + + @Test + fun testLeast_Null() { + assertEquals(4.0, evaluate("least(null,4,5.0)")) + } + + @Test + fun testLeast_NaN() { + assertEquals(4.0, evaluate("least(1%0,4,5.0)")) + } + + @Test + fun testLeast_PositiveInfinity() { + assertEquals(Double.NEGATIVE_INFINITY, evaluate("least(-1/0,4,5.0,1/0)")) + } + + @Test + fun testLeast_Empty() { + assertNull(evaluate("least()")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LengthTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LengthTest.kt new file mode 100644 index 0000000..8d60530 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LengthTest.kt @@ -0,0 +1,38 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Test of the `d2:length` function. + * + * @author Jan Bernitt + */ +internal class LengthTest { + + @Test + fun testLength() { + assertEquals(0, evaluate("d2:length(\"\")")) + assertEquals(5, evaluate("d2:length(\"hello\")")) + } + + @Test + fun testLength_Number() { + assertEquals(1, evaluate("d2:length(0)")) + } + + @Test + fun testLength_Boolean() { + assertEquals(4, evaluate("d2:length(true)")) + } + + @Test + fun testLength_Null() { + assertEquals(0, evaluate("d2:length(null)")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.RULE_ENGINE_ACTION).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/Log10Test.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/Log10Test.kt new file mode 100644 index 0000000..463915f --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/Log10Test.kt @@ -0,0 +1,24 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `log10` function + * + * @author Jan Bernitt + */ +internal class Log10Test { + + @Test + fun testLog() { + assertEquals(Double.NEGATIVE_INFINITY, evaluate("log10(0)")) + assertEquals(0.0, evaluate("log10(1)")) + assertEquals(1.0, evaluate("log10(10)")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LogTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LogTest.kt new file mode 100644 index 0000000..b4070d5 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/LogTest.kt @@ -0,0 +1,31 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `log` function + * + * @author Jan Bernitt + */ +internal class LogTest { + + @Test + fun testLog() { + assertEquals(Double.NEGATIVE_INFINITY, evaluate("log(0)")) + assertEquals(0.0, evaluate("log(1)")) + } + + @Test + fun testLog_Whitespace() { + assertEquals(Double.POSITIVE_INFINITY, evaluate("log(2,1)")) + assertEquals(Double.POSITIVE_INFINITY, evaluate("log( 2, 1)")) + assertEquals(Double.POSITIVE_INFINITY, evaluate("log(2 , 1 )")) + assertEquals(Double.POSITIVE_INFINITY, evaluate("log( 2 , 1 )")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MaxTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MaxTest.kt new file mode 100644 index 0000000..813cdbe --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MaxTest.kt @@ -0,0 +1,19 @@ +package org.hisp.dhis.lib.expression.function + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `max` function + * + * @author Jan Bernitt + */ +internal class MaxTest : AggregateFunctionTest() { + + @Test + fun testMax() { + val dataValues = mapOf( + newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) + assertEquals(10.0, evaluate("max(#{u1234567890})", dataValues)) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MinTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MinTest.kt new file mode 100644 index 0000000..ad07eaa --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MinTest.kt @@ -0,0 +1,19 @@ +package org.hisp.dhis.lib.expression.function + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `min` function + * + * @author Jan Bernitt + */ +internal class MinTest : AggregateFunctionTest() { + + @Test + fun testMin() { + val dataValues = mapOf( + newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) + assertEquals(0.0, evaluate("min(#{u1234567890})", dataValues)) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MinutesBetweenTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MinutesBetweenTest.kt new file mode 100644 index 0000000..2a4078d --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MinutesBetweenTest.kt @@ -0,0 +1,36 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +/** + * Tests the `d2:minutesBetween` function + * + * @author Jan Bernitt + */ +internal class MinutesBetweenTest { + + @Test + fun testMinutesBetween() { + val minPerDay = 60 * 24 + assertEquals(6 * minPerDay, evaluate("d2:minutesBetween(\"2020-01-01\", \"2020-01-07\")")) + assertEquals(6 * minPerDay, evaluate("d2:minutesBetween(\"2020-01-07\", \"2020-01-01\")")) + assertEquals(31 * minPerDay, evaluate("d2:minutesBetween(\"2020-01-01\", \"2020-02-01\")")) + assertEquals(29 * minPerDay, evaluate("d2:minutesBetween(\"2020-02-01\", \"2020-03-01\")")) + assertEquals(366 * minPerDay, evaluate("d2:minutesBetween(\"2020-01-01\", \"2021-01-01\")")) + } + + @Test + fun testMinutesBetween_Null() { + val ex = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:minutesBetween(null, \"2021-01-01\")") } + assertEquals("start parameter of d2:minutesBetween must not be null", ex.message) + val ex2 = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:minutesBetween(\"2021-01-01\", null)") } + assertEquals("end parameter of d2:minutesBetween must not be null", ex2.message) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.PROGRAM_INDICATOR_EXPRESSION).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MonthsBetweenTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MonthsBetweenTest.kt new file mode 100644 index 0000000..f8ff52f --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/MonthsBetweenTest.kt @@ -0,0 +1,36 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +/** + * Tests the `d2:monthsBetween` function + * + * @author Jan Bernitt + */ +internal class MonthsBetweenTest { + + @Test + fun testMonthsBetween() { + assertEquals(0, evaluate("d2:monthsBetween(\"2020-01-01\", \"2020-01-07\")")) + assertEquals(0, evaluate("d2:monthsBetween(\"2020-01-07\", \"2020-01-01\")")) + assertEquals(1, evaluate("d2:monthsBetween(\"2020-01-01\", \"2020-02-01\")")) + assertEquals(1, evaluate("d2:monthsBetween(\"2020-02-01\", \"2020-03-01\")")) + assertEquals(12, evaluate("d2:monthsBetween(\"2020-01-01\", \"2021-01-01\")")) + assertEquals(12, evaluate("d2:monthsBetween(\"2021-01-01\", \"2020-01-01\")")) + } + + @Test + fun testMonthsBetween_Null() { + val ex = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:monthsBetween(null, \"2021-01-01\")") } + assertEquals("start parameter of d2:monthsBetween must not be null", ex.message) + val ex2 = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:monthsBetween(\"2021-01-01\", null)") } + assertEquals("end parameter of d2:monthsBetween must not be null", ex2.message) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.RULE_ENGINE_ACTION).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/RemoveZerosTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/RemoveZerosTest.kt new file mode 100644 index 0000000..a238190 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/RemoveZerosTest.kt @@ -0,0 +1,25 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +/** + * Tests the `removeZeros` function + * + * @author Jan Bernitt + */ +internal class RemoveZerosTest { + + @Test + fun testRemoveZeros() { + assertNull(evaluate("removeZeros(0.0)")) + assertNull(evaluate("removeZeros(0)")) + assertEquals(42.0, evaluate("removeZeros(42.0)")) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/SumTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/SumTest.kt new file mode 100644 index 0000000..ef2a939 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/SumTest.kt @@ -0,0 +1,19 @@ +package org.hisp.dhis.lib.expression.function + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests the `sum` function + * + * @author Jan Bernitt + */ +internal class SumTest : AggregateFunctionTest() { + + @Test + fun testSum() { + val dataValues = mapOf( + newDeDataItem("u1234567890") to doubleArrayOf(0.0, 10.0, 5.0, 3.0, 7.0)) + assertEquals(25.0, evaluate("sum(#{u1234567890})", dataValues)) + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/WeeksBetweenTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/WeeksBetweenTest.kt new file mode 100644 index 0000000..c7390d3 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/WeeksBetweenTest.kt @@ -0,0 +1,36 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +/** + * Tests the `d2:weeksBetween` function + * + * @author Jan Bernitt + */ +internal class WeeksBetweenTest { + + @Test + fun testWeeksBetween() { + assertEquals(0, evaluate("d2:weeksBetween(\"2020-01-01\", \"2020-01-07\")")) + assertEquals(0, evaluate("d2:weeksBetween(\"2020-01-07\", \"2020-01-01\")")) + assertEquals(4, evaluate("d2:weeksBetween(\"2020-01-01\", \"2020-02-01\")")) + assertEquals(4, evaluate("d2:weeksBetween(\"2020-02-01\", \"2020-03-01\")")) + assertEquals(52, evaluate("d2:weeksBetween(\"2020-01-01\", \"2021-01-01\")")) + assertEquals(52, evaluate("d2:weeksBetween(\"2021-01-01\", \"2020-01-01\")")) + } + + @Test + fun testWeeksBetween_Null() { + val ex = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:weeksBetween(null, \"2021-01-01\")") } + assertEquals("start parameter of d2:weeksBetween must not be null", ex.message) + val ex2 = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:weeksBetween(\"2021-01-01\", null)") } + assertEquals("end parameter of d2:weeksBetween must not be null", ex2.message) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.RULE_ENGINE_ACTION).evaluate() + } +} \ No newline at end of file diff --git a/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/YearsBetweenTest.kt b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/YearsBetweenTest.kt new file mode 100644 index 0000000..3850516 --- /dev/null +++ b/src/commonTest/kotlin/org/hisp/dhis/lib/expression/function/YearsBetweenTest.kt @@ -0,0 +1,36 @@ +package org.hisp.dhis.lib.expression.function + +import org.hisp.dhis.lib.expression.Expression +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +/** + * Tests the `d2:yearsBetween` function + * + * @author Jan Bernitt + */ +internal class YearsBetweenTest { + + @Test + fun testYearsBetween() { + assertEquals(0, evaluate("d2:yearsBetween(\"2020-01-01\", \"2020-01-07\")")) + assertEquals(0, evaluate("d2:yearsBetween(\"2020-01-07\", \"2020-01-01\")")) + assertEquals(0, evaluate("d2:yearsBetween(\"2020-01-01\", \"2020-02-01\")")) + assertEquals(0, evaluate("d2:yearsBetween(\"2020-02-01\", \"2020-03-01\")")) + assertEquals(1, evaluate("d2:yearsBetween(\"2020-01-01\", \"2021-01-01\")")) + assertEquals(1, evaluate("d2:yearsBetween(\"2021-01-01\", \"2022-01-01\")")) + } + + @Test + fun testYearsBetween_Null() { + val ex = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:yearsBetween(null, \"2021-01-01\")") } + assertEquals("start parameter of d2:yearsBetween must not be null", ex.message) + val ex2 = assertFailsWith(IllegalArgumentException::class) { evaluate("d2:yearsBetween(\"2021-01-01\", null)") } + assertEquals("end parameter of d2:yearsBetween must not be null", ex2.message) + } + + private fun evaluate(expression: String): Any? { + return Expression(expression, Expression.Mode.RULE_ENGINE_ACTION).evaluate() + } +} \ No newline at end of file