This is an automated email from the ASF dual-hosted git repository. sergeykamov pushed a commit to branch NLPCRAFT-278 in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
commit dedcf823693a82936c10866c795b16ae7ee1a913 Author: Sergey Kamov <[email protected]> AuthorDate: Tue Mar 23 11:52:57 2021 +0300 WIP. --- .../idl/compiler/NCIdlCompilerSpecFunctions.scala | 192 +++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/NCIdlCompilerSpecFunctions.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/NCIdlCompilerSpecFunctions.scala new file mode 100644 index 0000000..5f3c687 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/NCIdlCompilerSpecFunctions.scala @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.nlpcraft.model.intent.idl.compiler + +import org.apache.nlpcraft.model.intent.compiler.{NCIdlCompiler, NCIdlCompilerGlobal} +import org.apache.nlpcraft.model.intent.{NCIdlContext, NCIdlFunction, NCIdlStackItem} +import org.apache.nlpcraft.model.{NCModel, NCModelView, NCToken} +import org.junit.jupiter.api.{BeforeEach, Test} + +import java.util +import java.util.{Collections, UUID} +import scala.collection.JavaConverters._ + +/** + * Tests for IDL functions. + */ +class NCIdlCompilerSpecFunctions { + private final val MODEL_ID = "test.mdl.id" + + private final val MODEL: NCModel = new NCModel { + override val getId: String = MODEL_ID + override val getName: String = MODEL_ID + override val getVersion: String = "1.0.0" + + override def getOrigin: String = "test" + } + + private final val DUMMY_CTX: NCIdlContext = NCIdlContext(req = null) + + @BeforeEach + def before(): Unit = NCIdlCompilerGlobal.clearCache(MODEL_ID) + + import BoolFunc._ + + case class BoolFunc(func: NCIdlFunction, token: NCToken, result: Boolean) { + override def toString: String = + s"Boolean function [" + + s"token=${t2s(token)}, " + + s"function=$func, " + + s"expected=$result" + + s"]" + } + + object BoolFunc { + private def t2s(t: NCToken) = s"${t.getOriginalText} (${t.getId})" + + private def mkToken( + id: String = null, + value: String = null, + txt: String = null, + start: Int = 0, + end: Int = 0, + meta: Map[String, AnyRef] = Map.empty[String, AnyRef] + ): NCToken = { + val map = new util.HashMap[String, AnyRef] + + map.putAll(meta.asJava) + + def nvl(v: String): String = if (v != null) v else "(not set)" + + map.put("nlpcraft:nlp:origtext", nvl(txt)) + + new NCToken { + override def getModel: NCModelView = MODEL + override def getServerRequestId: String = UUID.randomUUID().toString + override def getId: String = nvl(id) + override def getParentId: String = null + override def getAncestors: util.List[String] = Collections.emptyList() + override def getPartTokens: util.List[NCToken] = Collections.emptyList() + override def getAliases: util.Set[String] = Collections.emptySet() + override def getValue: String = value + override def getGroups: util.List[String] = Collections.singletonList(id) + override def getStartCharIndex: Int = start + override def getEndCharIndex: Int = end + override def isAbstract: Boolean = false + override def getMetadata: util.Map[String, AnyRef] = map + } + } + + private def mkFunc(term: String): NCIdlFunction = { + val intents = NCIdlCompiler.compileIntents(s"intent=i term(t)={$term}", MODEL, MODEL_ID) + + require(intents.size == 1) + + val intent = intents.head + + require(intent.terms.size == 1) + + new NCIdlFunction() { + override def apply(v1: NCToken, v2: NCIdlContext): NCIdlStackItem = intent.terms.head.pred.apply(v1, v2) + override def toString(): String = s"Function, based on term: $term" + } + } + + def apply(boolCondition: String, token: String, result: Boolean): BoolFunc = + BoolFunc(func = mkFunc(boolCondition), token = mkToken(token), result = result) + + def apply(boolCondition: String, tokenId: String): BoolFunc = + BoolFunc(func = mkFunc(boolCondition), token = mkToken(tokenId), result = true) + + def apply(boolCondition: String, token: NCToken, result: Boolean): BoolFunc = + BoolFunc(func = mkFunc(boolCondition), token, result = result) + + def apply(bool: String): BoolFunc = + BoolFunc(func = mkFunc(bool), mkToken(), result = true) + } + + private def test(funcs: BoolFunc*): Unit = + for ((func, idx) ← funcs.zipWithIndex) { + val res = + try + func.func.apply(func.token, DUMMY_CTX).value + catch { + case e: Exception ⇒ throw new Exception(s"Execution error [index=$idx, testFunc=$func]", e) + } + + res match { + case b: java.lang.Boolean ⇒ + require(b == func.result, + s"Unexpected result [" + + s"index=$idx, " + + s"testFunc=$func, " + + s"expected=${func.result}, " + + s"result=$res" + + s"]" + ) + case _ ⇒ + require(requirement = false, + s"Unexpected result type [" + + s"index=$idx, " + + s"testFunc=$func, " + + s"expected=${func.result}, " + + s"result=$res" + + s"]" + ) + } + + } + + @Test + def test(): Unit = { + val now = System.currentTimeMillis() + + test( + BoolFunc(boolCondition = "id() == 'a'", tokenId = "a"), + + // Math. + // BoolFunc(boolCondition = "sin(90.0) == 0") + // BoolFunc(boolCondition = "rand() < 1") + + // String. + BoolFunc(bool = "trim(' a b ') == 'a b'"), + BoolFunc(bool = "strip(' a b ') == 'a b'"), + BoolFunc(bool = "uppercase('aB') == 'AB'"), + BoolFunc(bool = "lowercase('aB') == 'ab'"), + BoolFunc(bool = "is_num('a') == false"), + BoolFunc(bool = "is_num('1') == true"), + + // Statistical. + // BoolFunc(boolCondition = "max(list(1, 2, 3)) == 3"), + // BoolFunc(boolCondition = "min(list(1, 2, 3)) == 1") + + // Collection. + // BoolFunc(boolCondition = "first(list(1, 2, 3)) == 1"), + // BoolFunc(boolCondition = "last(list(1, 2, 3)) == 3") + BoolFunc(bool = "is_empty(list()) == true"), + BoolFunc(bool = "is_empty(list(1)) == false"), + BoolFunc(bool = "non_empty(list()) == false"), + BoolFunc(bool = "non_empty(list(1)) == true"), + + // Date-time functions. + + + BoolFunc(bool = s"now() - $now < 1000") + ) + } +}
