This is an automated email from the ASF dual-hosted git repository. sergeykamov pushed a commit to branch NLPCRAFT-474 in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-474 by this push: new 0df8361 IDL initial tests. 0df8361 is described below commit 0df836154093ee67e3882fa4ad7504c50f189797 Author: Sergey Kamov <skhdlem...@gmail.com> AuthorDate: Tue Jan 25 20:12:12 2022 +0300 IDL initial tests. --- .../intent/compiler/NCIdlCompilerSpec.scala | 296 +++++++++++++++++++++ .../intent/compiler/functions/NCIdlFunctions.scala | 203 ++++++++++++++ .../functions/NCIdlFunctionsCollections.scala | 79 ++++++ .../compiler/functions/NCIdlFunctionsDate.scala | 61 +++++ .../compiler/functions/NCIdlFunctionsEntity.scala | 255 ++++++++++++++++++ .../compiler/functions/NCIdlFunctionsMath.scala | 92 +++++++ .../compiler/functions/NCIdlFunctionsMeta.scala | 86 ++++++ .../compiler/functions/NCIdlFunctionsOther.scala | 73 +++++ .../compiler/functions/NCIdlFunctionsRequest.scala | 53 ++++ .../compiler/functions/NCIdlFunctionsStat.scala | 53 ++++ .../compiler/functions/NCIdlFunctionsStrings.scala | 92 +++++++ .../functions/NCIdlFunctionsTokensUsed.scala | 61 +++++ .../nlpcraft/internal/intent/compiler/test_ok.idl | 84 ++++++ 13 files changed, 1488 insertions(+) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/NCIdlCompilerSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/NCIdlCompilerSpec.scala new file mode 100644 index 0000000..abca683 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/NCIdlCompilerSpec.scala @@ -0,0 +1,296 @@ +/* + * 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 + * + * https://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.internal.intent.compiler + +import org.apache.nlpcraft.* +import org.apache.nlpcraft.nlp.util.opennlp.* +import org.junit.jupiter.api.Test + +/** + * Tests for IDL compiler. + */ +class NCIdlCompilerSpec: + private final val MODEL_ID = "test.mdl.id" + + /** + * + * @param idl + */ + private def checkCompileOk(idl: String): Unit = + try + NCIDLCompiler.compile(idl, CFG, MODEL_ID) + + assert(true) + catch + case e: Exception => assert(assertion = false, e) + + /** + * + * @param txt + */ + private def checkCompileError(txt: String): Unit = + try + NCIDLCompiler.compile(txt, CFG, MODEL_ID) + + assert(false) + catch + case e: NCException => + println(e.getMessage) + assert(true) + + @Test + @throws[NCException] + def testInlineCompileOk(): Unit = + NCIDLCompilerGlobal.clearCache(MODEL_ID) + + checkCompileOk( + """ + |import('org/apache/nlpcraft/model/intent/idl/compiler/test_ok.idl') + |""".stripMargin + ) + checkCompileOk( + """ + |intent=i1 + | flow="a[^0-9]b" + | meta={'a': true, 'b': {'Москва': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + checkCompileOk( + """ + |intent=i1 + | flow="a[^0-9]b" + | term(t1)={has(json("{'a': true, 'b\'2': {'arr': [1, 2, 3]}}"), list("موسكو\"", 'v1\'v1', "k2", "v2"))} + |""".stripMargin + ) + checkCompileOk( + """ + |// Some comments. + |fragment=f1 + | term(ft1)={2==2} /* Term block comment. */ + | term~/class#method/ + |/* + | * +=====================+ + | * | block comments......| + | * +=====================+ + | */ + |intent=i1 + | options={ + | 'ordered': false, + | 'unused_free_words': true, + | 'unused_sys_toks': true, + | 'unused_usr_toks': false, + | 'allow_stm_only': false + | } + | flow="a[^0-9]b" // Flow comment. + | term(t1)={has(json("{'a': true, 'b\'2': {'arr': [1, 2, 3]}}"), list("موسكو\"", 'v1\'v1', "k2", "v2"))} + | fragment(f1, {'a': true, 'b': ["s1", "s2"]}) /* Another fragment. */ + |""".stripMargin + ) + checkCompileOk( + """ + |fragment=f21 + | term(f21_t1)={2==2} + | term~/class#method/ + | + |fragment=f22 + | term(f22_t1)={2==2_000_000.23} + | fragment(f21) + | term~/class#method/ + | + |intent=i1 + | options={} + | flow="a[^0-9]b" + | term(t1)={has(json("{'a': true, 'b\'2': {'arr': [1, 2, 3]}}"), list("موسكو\"", 'v1\'v1', "k2", "v2"))} + | fragment(f21, {'a': true, 'b': ["s1", "s2"]}) + |""".stripMargin + ) + + @Test + @throws[NCException] + def testInlineCompileFail(): Unit = + NCIDLCompilerGlobal.clearCache(MODEL_ID) + + checkCompileError( + """ + |intent=i1 + | options={'ordered': 1} + | flow="a[^0-9]b" + | meta={'a': true, 'b': {'Москва': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + + checkCompileError( + """ + |intent=i1 + | options={'ordered1': false} + | flow="a[^0-9]b" + | meta={'a': true, 'b': {'Москва': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + + checkCompileError( + """ + |intent=i1 + | options={'ordered': false, 'unknown': 1} + | flow="a[^0-9]b" + | meta={'a': true, 'b': {'Москва': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + + checkCompileError( + """ + |intent=i1 + | options={'ordered': false_1} # Broken JSON. + | flow="a[^0-9]b" + | meta={'a': true, 'b': {'Москва': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + + checkCompileError( + """ + |intent=i1 + | options={'ordered': null} + | flow="a[^0-9]b" + | meta={'a': true, 'b': {'Москва': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + + checkCompileError( + """ + |intent=i1 + | options={'ordered': false, 'ordered': true} + | flow="a[^0-9]b" + | meta={'a': true, 'b': {'Москва': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + + checkCompileError( + """ + |intent=i1 + |/* + | * +=====================+ + | * | block comments......| + | * +=====================+ + | */ + | flow="a[^0-9]b" + | meta={{'a': true, 'b': {'arr': [1, 2, 3]}} + | term(t1)={2 == 2 && size(#) != -25} + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | meta={'a': true, 'b': {'arr': [1, 2, 3]}} + | term(t1)={ + | @x == 2 && size(#) != -25 + | } + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | flow="a[^0-9b" + | term(t1)={true} + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | flow="a[^0-9b]" + | term(t1)={ + | @x = 2 + | @x = 2 + | + | true + | } + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | flow="a[^0-9b]" + | term(t1)={ + | true + | + | @x = 2 + | } + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | term(t1)={true}[2,1] + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | flow="a[^0-9b]" + | term(t1)={true} + | term(t1)={true} + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | flow="a[^0-9b]" + | term(t1)={true} + |intent=i1 + | flow="a[^0-9b]" + | term(t1)={true} + |""".stripMargin + ) + checkCompileError( + """ + |intent=i1 + | flow="a[^0-9]b" + | term(t1)={has(json("{'a': true, 'b\'2': {'arr': [1, 2, 3]}}"), list("k1\"", 'v1\'v1', "k2", "v2"))}[1:2] + |""".stripMargin + ) + checkCompileError( + """ + |fragment=f1 + | term(t1)={2==2} + | term~/class#method/ + | + |intent=i1 + | flow="a[^0-9]b" + | term(t1)={has(json("{'a': true, 'b\'2': {'arr': [1, 2, 3]}}"), list("موسكو\"", 'v1\'v1', "k2", "v2"))} + | fragment(f1, {'a': true, 'b': ["s1", "s2"]}) + |""".stripMargin + ) + checkCompileError( + """ + |fragment=f111 + | options={'ordered': 1} + | term(t1)={2==2} + | term~/class#method/ + | + |intent=i1 + | flow="a[^0-9]b" + | term(t1)={has(json("{'a': true, 'b\'2': {'arr': [1, 2, 3]}}"), list("موسكو\"", 'v1\'v1', "k2", "v2"))} + | fragment(f1_, {'a': true, 'b': ["s1", "s2"]}) + |""".stripMargin + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctions.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctions.scala new file mode 100644 index 0000000..3b6dbb1 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctions.scala @@ -0,0 +1,203 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.* +import org.apache.nlpcraft.internal.intent.* +import org.apache.nlpcraft.internal.intent.compiler.* +import org.apache.nlpcraft.internal.intent.compiler.functions.* +import org.apache.nlpcraft.nlp.util.opennlp.* +import org.junit.jupiter.api.BeforeEach + +import scala.jdk.CollectionConverters.* +import scala.language.implicitConversions + +private[functions] object NCIdlFunctions: + private final val MODEL_ID = "test.mdl.id" + + case class TestDesc( + truth: String, + entity: Option[NCEntity] = None, + idlCtx: NCIDLContext, + isCustom: Boolean = false, + expectedRes: Boolean = true, + tokensUsed: Option[Int] = None + ): + // It should be lazy for errors verification methods. + lazy val term: NCIDLTerm = + val (s1, s2) = if isCustom then ('/', '/') else ('{', '}') + + val intents = NCIDLCompiler.compile(s"intent=i term(t)=$s1$truth$s2", CFG, MODEL_ID) + + require(intents.size == 1) + require(intents.head.terms.sizeIs == 1) + + intents.head.terms.head + + override def toString: String = + entity match + case Some(e) => s"Predicate [body='$truth', token=${e2s(e)}]" + case None => s"Predicate '$truth'" + + object TestDesc: + def apply(truth: String): TestDesc = + new TestDesc(truth = truth, idlCtx = mkIdlContext()) + + def apply(truth: String, entity: NCEntity, idlCtx: NCIDLContext): TestDesc = + new TestDesc(truth = truth, entity = Option(entity), idlCtx = idlCtx) + + def apply(truth: String, entity: NCEntity): TestDesc = + new TestDesc(truth = truth, entity = Option(entity), idlCtx = mkIdlContext(entities = Seq(entity))) + + given Conversion[String, TestDesc] with + def apply(s: String): TestDesc = TestDesc(s) + + private def e2s(t: NCEntity): String = + // TODO: + t.toString + + // def nvl(s: String, name: String): String = if s != null then s else s"$name (not set)" + // + // s"text=${nvl(t.getOriginalText, "text")} [${nvl(t.getId, "id")}]" + + def mkIdlContext( + entities: Seq[NCEntity] = Seq.empty, + reqSrvReqId: String = null, + reqNormText: String = null, + reqTstamp: Long = 0, + reqAddr: String = null, + reqAgent: String = null, + reqData: Map[String, AnyRef] = Map.empty, + intentMeta: Map[String, AnyRef] = Map.empty, + convMeta: Map[String, AnyRef] = Map.empty, + fragMeta: Map[String, AnyRef] = Map.empty + ): NCIDLContext = + NCIDLContext( + CFG, + entities, + intentMeta = intentMeta, + convMeta = convMeta, + fragMeta = fragMeta, + req = new NCRequest: + override def getUserId: String = "userID" // TODO: + override def getRequestId: String = reqSrvReqId + override def getText: String = reqNormText + override def getReceiveTimestamp: Long = reqTstamp + override def getRequestData: java.util.Map[String, AnyRef] = reqData.asJava + ) + + // TODO: + def mkEntity( + id: String = null, + srvReqId: String = null, + parentId: String = null, + value: String = null, + txt: String = null, + normTxt: String = null, + start: Int = 0, + end: Int = 0, + groups: Seq[String] = Seq.empty, + ancestors: Seq[String] = Seq.empty, + aliases: Set[String] = Set.empty, + partTokens: Seq[NCToken] = Seq.empty, + `abstract`: Boolean = false, + meta: Map[String, AnyRef] = Map.empty[String, AnyRef] + ): NCEntity = + // TODO: + null +// val map = new util.HashMap[String, AnyRef] +// +// map.putAll(meta.asJava) +// +// map.put("nlpcraft:nlp:origtext", txt) +// map.put("nlpcraft:nlp:normtext", normTxt) +// +// new NCPropertyMapAdapter with NCEntity(): +// override def getTokens: util.List[NCToken] = ??? +// override def getRequestId: String = ??? +// override def getId: String = ??? +// + + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* + +/** + * Tests for IDL functions. + */ +private[functions] trait NCIdlFunctions: + @BeforeEach + def before(): Unit = NCIDLCompilerGlobal.clearCache(MODEL_ID) + + /** + * + * @param funcs + */ + protected def test(funcs: TestDesc*): Unit = + for (f <- funcs) + val item = + try + // Process declarations. + f.idlCtx.vars ++= f.term.decls + + // Execute term's predicate. + // TODO: index + f.term.pred.apply(NCIDLEntity(f.entity.getOrElse(mkEntity()), 0), f.idlCtx) + catch + case e: NCException => throw e + case e: Exception => throw new Exception(s"Execution error processing: $f", e) + + item.value match + case b: java.lang.Boolean => require(if f.expectedRes then b else !b, s"Unexpected '$b' result for: $f") + case _ => + require( + requirement = false, + s"Unexpected result type [resType=${ + if item.value == null then "null" + else item.value.getClass.getName + }, resValue=${item.value}, function=$f]" + ) + + f.tokensUsed match + case Some(exp) => + require( + exp == item.entUse, + s"Unexpected tokens used [expectedTokensUsed=$exp, resultEntityUsed=${item.entUse}, function=$f]" + ) + + case None => // No-op. + + /** + * + * @param funcs + */ + protected def expectError(funcs: TestDesc*): Unit = + for (f <- funcs) + try + test(f) + + require(false) + catch + case e: Exception => + println(s"Expected error: ${e.getLocalizedMessage}") + + var cause = e.getCause + + while (cause != null) + println(s" Cause: ${cause.getLocalizedMessage} (${cause.getClass.getName})") + + cause = cause.getCause \ No newline at end of file diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsCollections.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsCollections.scala new file mode 100644 index 0000000..9f2498b --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsCollections.scala @@ -0,0 +1,79 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.* +import org.junit.jupiter.api.Test + +import scala.language.implicitConversions + +/** + * Tests for 'collections' functions. + */ +class NCIdlFunctionsCollections extends NCIdlFunctions: + private final val js = "{\"k1\": \"v1\"}" + + @Test + def test(): Unit = + test( + "list(1, 2, 3) == list(1, 2, 3)", + "list(1.0, 2, 3) == list(1.0, 2, 3)", + "list(1, 2, 3) != list(1.0, 2, 3)", + "get(list(1, 2, 3), 1) == 2", + "has(list(1, 2, 3), 1) == true", + "has(list(1.1, 2.1, 3.1), 1.1) == true", + "has(list(1.0, 2.0, 3.0), 1.0) == true", + "has(list(1.0, 2.0, 3.0), 1) == false", // Different types. + "has(list('1', '2', '3'), '1') == true", + "has(list(1, 2, 3), 5) == false", + "has(list(1.1, 2.1, 3.1), 5.1) == false", + "has(list('1', '2', '3'), '5') == false", + "has_any(list('1', '2', '3'), list('1', '20', '30')) == true", + "has_any(list('1', '2', '3'), list('10', '20', '30')) == false", + "has_all(list('1', '2', '3'), list('1', '20', '30')) == false", + "has_all(list('1', '2', '3'), list('1', '2', '3')) == true", + "size(list()) == 0", + "list(1, 2, 3) == list(1, 2, 3)", + "first(list(1, 2, 3)) == 1", + "get(list(1, 2, 3), 0) == 1", + "@lst = list(1, 2, 3) first(reverse(@lst)) == last(@lst)", + "last(list(1, 2, 3)) == 3", + "is_empty(list()) == true", + "is_empty(list(1)) == false", + "non_empty(list()) == false", + "non_empty(list(1)) == true", + "reverse(list(1.0, 2, 3)) == list(3, 2, 1.0)", + "sort(list(2, 1, 3)) == list(1, 2, 3)", + "sort(list('c', 'a', 'b')) == list('a', 'b', 'c')", + "size(list(2.0, 1, 3)) == 3", + "length(list(2.0, 1, 3)) == 3", + "count(list(2.0, 1, 3)) == 3", + "size(list()) == 0", + "length(list()) == 0", + "count(list()) == 0", + s"keys(json('$js')) == list('k1')", + s"values(json('$js')) == list('v1')", + "sort(distinct(list(1, 2, 3, 3, 2))) == sort(list(1, 2, 3))", + "distinct(list(1.0, 2.0, 3.0, 3.0, 2.0)) == list(1.0, 2.0, 3.0)", + "distinct(list('1', '2', '3', '3', '2')) == list('1', '2', '3')", + "sort(concat(list(1, 2, 3), list(1, 2, 3))) == sort(list(1, 2, 3, 1, 2, 3))", + "concat(list(1, 2, 3), list()) == list(1, 2, 3)", + "concat(list(), list()) == list()", + "concat(list(1, 2), list(3.0)) == list(1, 2, 3.0)" + ) + diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsDate.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsDate.scala new file mode 100644 index 0000000..35f4c37 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsDate.scala @@ -0,0 +1,61 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.* +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.apache.nlpcraft.internal.util.NCUtils +import org.junit.jupiter.api.Test + +import java.time.* +import java.time.temporal.IsoFields +import java.util.Calendar as C +import scala.language.implicitConversions + +/** + * Tests for 'dates' functions. + */ +class NCIdlFunctionsDate extends NCIdlFunctions: + @Test + def test(): Unit = + def test0(): Unit = + val d = LocalDate.now + val t = LocalTime.now + val c = C.getInstance() + + test( + s"year() - ${d.getYear} == 0", + s"month() - ${d.getMonthValue} == 0", + s"day_of_month() - ${d.getDayOfMonth} == 0", + s"day_of_week() - ${d.getDayOfWeek.getValue} == 0", + s"day_of_year() - ${d.getDayOfYear} == 0", + s"hour() - ${t.getHour} == 0", + s"minute() - ${t.getMinute} == 0", + s"second() - ${t.getSecond} < 5", + s"week_of_month() - ${c.get(C.WEEK_OF_MONTH)} == 0", + s"week_of_year() - ${c.get(C.WEEK_OF_YEAR)} == 0", + s"quarter() - ${d.get(IsoFields.QUARTER_OF_YEAR)} == 0", + s"now() - ${NCUtils.now()} < 5000" + ) + + try + test0() + catch + case _: AssertionError => + // Some field more than `second` can be changed. One more attempt. + test0() diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsEntity.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsEntity.scala new file mode 100644 index 0000000..545f35d --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsEntity.scala @@ -0,0 +1,255 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +import java.lang.{Boolean as JBool, Integer as JInt} +import scala.language.implicitConversions + +/** + * Tests for 'entities' functions. + */ +class NCIdlFunctionsEntity extends NCIdlFunctions: + private final val meta: Map[String, AnyRef] = Map( + "nlpcraft:nlp:stopword" -> JBool.TRUE, + "nlpcraft:nlp:freeword" -> JBool.TRUE, + "nlpcraft:nlp:origtext" -> "orig text", + "nlpcraft:nlp:index" -> JInt.valueOf(11), + "nlpcraft:nlp:normtext" -> "norm text", + "nlpcraft:nlp:direct" -> JBool.TRUE, + "nlpcraft:nlp:english" -> JBool.TRUE, + "nlpcraft:nlp:swear" -> JBool.TRUE, + "nlpcraft:nlp:quoted" -> JBool.TRUE, + "nlpcraft:nlp:bracketed" -> JBool.TRUE, + "nlpcraft:nlp:dict" -> JBool.TRUE, + "nlpcraft:nlp:lemma" -> "lemma", + "nlpcraft:nlp:stem" -> "stem", + "nlpcraft:nlp:sparsity" -> JInt.valueOf(112), + "nlpcraft:nlp:pos" -> "pos", + "nlpcraft:nlp:unid" -> "21421" + ) + + private def mkMeta(truth: String): TestDesc = TestDesc(truth = truth, entity = mkEntity(meta = meta)) + + @Test + def testMainTokenProperties(): Unit = + test( + TestDesc( + truth = "# == 'a'", + entity = mkEntity(id = "a") + ), + mkMeta(truth = s"tok_lemma == '${meta("nlpcraft:nlp:lemma")}'"), + mkMeta(truth = s"tok_stem == '${meta("nlpcraft:nlp:stem")}'"), + mkMeta(truth = s"tok_pos == '${meta("nlpcraft:nlp:pos")}'"), + mkMeta(truth = s"tok_sparsity == ${meta("nlpcraft:nlp:sparsity")}"), + mkMeta(truth = s"tok_unid == '${meta("nlpcraft:nlp:unid")}'"), + TestDesc( + truth = s"tok_is_abstract()", + entity = mkEntity(`abstract` = true) + ), + mkMeta(truth = s"tok_is_abstract == false"), + mkMeta(truth = s"tok_is_bracketed == ${meta("nlpcraft:nlp:bracketed")}"), + mkMeta(truth = s"tok_is_direct == ${meta("nlpcraft:nlp:direct")}"), + mkMeta(truth = s"tok_is_permutated != ${meta("nlpcraft:nlp:direct")}"), + mkMeta(truth = s"tok_is_english == ${meta("nlpcraft:nlp:english")}"), + mkMeta(truth = s"tok_is_freeword == ${meta("nlpcraft:nlp:freeword")}"), + mkMeta(truth = s"tok_is_quoted == ${meta("nlpcraft:nlp:quoted")}"), + mkMeta(truth = s"tok_is_stopword == ${meta("nlpcraft:nlp:stopword")}"), + mkMeta(truth = s"tok_is_swear == ${meta("nlpcraft:nlp:swear")}"), + TestDesc( + truth = s"tok_is_user()", + entity = mkEntity(id = "aa") + ), + TestDesc( + truth = s"!tok_is_user()", + entity = mkEntity(id = "nlpcraft:nlp") + ), + mkMeta(truth = s"tok_is_wordnet() == ${meta("nlpcraft:nlp:dict")}"), + TestDesc( + truth = s"tok_ancestors() == list('1', '2')", + entity = mkEntity(ancestors = Seq("1", "2")) + ), + TestDesc( + truth = s"tok_parent() == 'parentId'", + entity = mkEntity(parentId = "parentId") + ), + TestDesc( + truth = "tok_groups() == list('1', '2')", + entity = mkEntity(groups = Seq("1", "2")) + ), + TestDesc( + truth = "tok_value() == 'value'", + entity = mkEntity(value = "value") + ), + TestDesc( + truth = "tok_value() == null", + entity = mkEntity() + ), + TestDesc( + truth = "tok_start_idx() == 123", + entity = mkEntity(start = 123) + ), + TestDesc( + truth = "tok_end_idx() == 123", + entity = mkEntity(end = 123) + ), + TestDesc(truth = "tok_this() == tok_this()", idlCtx = mkIdlContext()) + ) + + @Test + def testTokenFirstLast(): Unit = + val e = mkEntity(id = "a") + + // TODO: + //tok.getMetadata.put("nlpcraft:nlp:index", 0) + + test( + TestDesc( + truth = "tok_is_first()", + entity = e, + idlCtx = mkIdlContext(entities = Seq(e)) + ), + TestDesc( + truth = "tok_is_last()", + entity = e, + idlCtx = mkIdlContext(entities = Seq(e)) + ) + ) + + @Test + def testTokenBeforeId(): Unit = + val e1 = mkEntity(id = "1") + val e2 = mkEntity(id = "2") + + // TODO: +// e1.getMetadata.put("nlpcraft:nlp:index", 0) +// e2.getMetadata.put("nlpcraft:nlp:index", 1) + + test( + TestDesc( + truth = "tok_is_before_id('2')", + entity = e1, + idlCtx = mkIdlContext(Seq(e1, e2)) + ) + ) + + @Test + def testTokenAfterId(): Unit = + val e1 = mkEntity(id = "1") + val e2 = mkEntity(id = "2") + + // TODO: +// e1.getMetadata.put("nlpcraft:nlp:index", 0) +// e2.getMetadata.put("nlpcraft:nlp:index", 1) + + test( + TestDesc( + truth = "tok_is_after_id('1')", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2)) + ) + ) + + @Test + def testTokenBetweenIds(): Unit = + val e1 = mkEntity(id = "1", groups = Seq("grp1")) + val e2 = mkEntity(id = "2", groups = Seq("grp2")) + val e3 = mkEntity(id = "3", groups = Seq("grp3")) + + // TODO: +// e1.getMetadata.put("nlpcraft:nlp:index", 0) +// e2.getMetadata.put("nlpcraft:nlp:index", 1) +// e3.getMetadata.put("nlpcraft:nlp:index", 2) + + test( + TestDesc( + truth = "tok_is_between_ids('1', '3')", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2, e3)) + ), + TestDesc( + truth = "tok_is_between_groups('grp1', 'grp3')", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2, e3)) + ) + ) + + @Test + def testTokenCount(): Unit = + val e1 = mkEntity(id = "1") + val e2 = mkEntity(id = "2") + + test( + TestDesc( + truth = "tok_count() == 2", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2)) + ) + ) + + @Test + def testTokenText(): Unit = + val e = mkEntity(id = "1", txt="txt", normTxt = "normTxt") + + test( + TestDesc( + truth = "tok_txt() == 'txt'", + entity = e + ), + TestDesc( + truth = "tok_norm_txt() == 'normTxt'", + entity = e + ) + ) + + @Test + def testTokenForAll(): Unit = + val e1 = mkEntity(id = "1", parentId = "x") + val e2 = mkEntity(id = "2", groups = Seq("g", "z", "w")) + val e3 = mkEntity(id = "2") + + test( + TestDesc( + truth = "size(tok_all_for_id('1')) == 1", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2, e3)) + ), + TestDesc( + truth = "size(tok_all_for_parent('x')) == 1", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2, e3)) + ), + TestDesc( + truth = "size(tok_all()) == 3", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2, e3)) + ), + TestDesc( + truth = "tok_count == size(tok_all())", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2, e3)) + ), + TestDesc( + truth = + "size(tok_all_for_group('g')) == 1 && #(first(tok_all_for_group('w'))) == '2' && is_empty(tok_all_for_group('unknown'))", + entity = e2, + idlCtx = mkIdlContext(Seq(e1, e2, e3)) + ) + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsMath.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsMath.scala new file mode 100644 index 0000000..6726536 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsMath.scala @@ -0,0 +1,92 @@ + +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +import scala.language.implicitConversions + +/** + * Tests for 'math' functions. + */ +class NCIdlFunctionsMath extends NCIdlFunctions: + @Test + def testError(): Unit = + expectError( + // Invalid name. + "xxx(1, 1)", + "xxx()", + // Invalid arguments count. + "atan(1, 1) == 1", + "pi(1)" + ) + + @Test + def test(): Unit = + test( + "abs(2) == 2", + "abs(-2.2) == 2.2", + "ceil(1.8) == 2.0", + "floor(1.1) == 1.0", + "rint(1.8) == 2.0", + "round(1.8) == 2", + "to_double(25) == 25.0", + "to_double(25) == 25", + "to_double('25.25') == 25.25", + "to_int(25.02) == 25", + "to_int('101') == 101", + "round(to_double(25)) == 25", + "signum(-1.8) == -1.0", + "sqrt(4) - 2 < 0.001", + "cbrt(8) - 2 < 0.001", + "acos(8.1) != acos(9.1)", + "asin(8.1) != asin(9)", + "atan(8) != atan(9.1)", + "cos(1.5708) < 0.001", + "cos(1.5708) < 0.001", + "sin(1.5708) > 0.999", + "sin(1.5708) > 0.999", + "tan(8) != tan(9)", + "cosh(8) != cosh(9)", + "sinh(8) != sinh(9)", + "tanh(8) != tanh(9)", + "atan2(8, 2) != atan2(9, 2)", + "atan2(8.1, 2.1) != atan2(9.1, 2.1)", + "degrees(1.5708) - 90 < 0.001", + "radians(90) - 1.5708 < 0.001", + "exp(2) != exp(3)", + "expm1(8) != expm1(9)", + "hypot(2, 3) - 3.606 < 0.001", + "log(8) != log(9)", + "log10(8) != log10(9)", + "log1p(8) != log1p(9)", + "pow(2, 2) - 4 < 0.001", + "pow(2.0, 2.0) - 4 < 0.001", + "pow(2, 2.0) - 4 < 0.001", + "pow(2.0, 2) - 4 < 0.001", + "pow(2.0, 2) - 4 < 0.001", + "rand() < 1", + "pi() - 3.142 < 0.01", + "euler() - 2.718 < 0.01", + "to_double(2) - 2.0 < 0.01", + "1.1 == 1.1", + "1.0 == 1", + "1 == 1.0" + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsMeta.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsMeta.scala new file mode 100644 index 0000000..b4e7d3c --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsMeta.scala @@ -0,0 +1,86 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.* +import org.apache.nlpcraft.internal.intent.NCIDLContext +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +import java.util +import java.util.Optional +import scala.jdk.CollectionConverters.MapHasAsJava +import scala.language.implicitConversions +import scala.sys.SystemProperties + +/** + * Tests for 'meta' functions. + */ +class NCIdlFunctionsMeta extends NCIdlFunctions: + @Test + def testMetaSys(): Unit = + val sys = new SystemProperties + + sys.put("k1", "v1") + + try + testValue("meta_sys") + finally + sys.remove("k1") + + @Test + def testMetaToken(): Unit = + testValue( + "meta_tok", + entity = Option(mkEntity(meta = Map("k1" -> "v1"))) + ) + + @Test + def testMetaRequest(): Unit = + testValue( + "meta_req", + mkIdlContext(reqData = Map("k1" -> "v1")) + ) + + @Test + def testMetaConv(): Unit = + testValue( + "meta_conv", + mkIdlContext(convMeta = Map("k1" -> "v1")) + ) + + @Test + def testMetaFrag(): Unit = + testValue( + "meta_frag", + mkIdlContext(fragMeta = Map("k1" -> "v1")) + ) + + // Simplified test. + @Test + def testMetaModel(): Unit = testNoValue("meta_model", mkIdlContext()) + + // Simplified test. + @Test + def testMetaIntent(): Unit = testNoValue("meta_intent", mkIdlContext()) + + private def testValue(f: String, idlCtx: => NCIDLContext = mkIdlContext(), entity: Option[NCEntity] = None): Unit = + test(TestDesc(truth = s"$f('k1') == 'v1'", entity = entity, idlCtx = idlCtx)) + + private def testNoValue(f: String, idlCtx: => NCIDLContext = mkIdlContext(), entity: Option[NCEntity] = None): Unit = + test(TestDesc(truth = s"$f('k1') == null", entity = entity, idlCtx = idlCtx)) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsOther.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsOther.scala new file mode 100644 index 0000000..c7e57ad --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsOther.scala @@ -0,0 +1,73 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +import scala.language.implicitConversions +import scala.sys.SystemProperties + +/** + * Tests for 'other' functions. + */ +class NCIdlFunctionsOther extends NCIdlFunctions: + @Test + def test1(): Unit = + // If. + test( + TestDesc(truth = "if(true, 1, 0) == 1"), + TestDesc(truth = "if(false, 1, 0) == 0"), + TestDesc(truth = "or_else(null, false) == false"), + TestDesc(truth = "or_else('s', list(1, 2, 3)) == 's'"), + TestDesc(truth = "or_else(meta_model('unknown_prop'), list(1, 2, 3)) == list(1, 2, 3)") + ) + + @Test + def test2(): Unit = + val sys = new SystemProperties + + sys.put("k1", "v1") + + try + val js = "{\"k1\": \"v1\"}" + + // JSON. + test( + s"has(keys(json('$js')), 'k1') == true", + s"has(keys(json('$js')), 'k2') == false" + ) + finally + sys.remove("k1") + + @Test + def test3(): Unit = + test( + "to_string(list(1, 2, 3)) == list('1', '2', '3')", + "to_string(3.123) == '3.123'" + ) + + @Test + def test4(): Unit = + test( + "true", + "true == true", + "false == false", + "false != true", + "true != false" + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsRequest.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsRequest.scala new file mode 100644 index 0000000..d606c80 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsRequest.scala @@ -0,0 +1,53 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +import scala.language.implicitConversions + +/** + * Tests for 'requests' functions. + */ +class NCIdlFunctionsRequest extends NCIdlFunctions: + @Test + def test(): Unit = + val reqSrvReqId = "id" + val reqNormText = "some text" + val reqTstamp: java.lang.Long = 123 + val reqAddr = "address" + val reqAgent = "agent" + + val idlCtx = mkIdlContext( + reqSrvReqId = reqSrvReqId, + reqNormText = reqNormText, + reqTstamp = reqTstamp, + reqAddr = reqAddr, + reqAgent = reqAgent + ) + + def mkTestDesc(truth: String): TestDesc = TestDesc(truth = truth, idlCtx = idlCtx) + + test( + mkTestDesc(s"req_id == '$reqSrvReqId'"), + mkTestDesc(s"req_normtext == '$reqNormText'"), + mkTestDesc(s"req_tstamp == $reqTstamp"), + mkTestDesc(s"req_addr == '$reqAddr'"), + mkTestDesc(s"req_agent == '$reqAgent'") + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsStat.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsStat.scala new file mode 100644 index 0000000..b5e5f3d --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsStat.scala @@ -0,0 +1,53 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +import scala.language.implicitConversions + +/** + * Tests for 'stat' functions. + */ +class NCIdlFunctionsStat extends NCIdlFunctions: + @Test + def testError(): Unit = + expectError( + "avg(list()) == 2", + "avg(list('A')) == 2", + "stdev(list()) == 2", + "stdev(list('A')) == 2" + ) + + @Test + def test(): Unit = + test( + "max(list(1, 2, 3)) == 3", + "max(list(1.0, 2.0, 3.0)) == 3.0", + "min(list(1, 2, 3)) == 1", + "min(list(1.0, 2.0, 3.0)) == 1.0", + "avg(list(1.0, 2.0, 3.0)) == 2.0", + "avg(list(1, 2, 3)) == 2.0", + "avg(list(1.2, 2.2, 3.2)) == 2.2", + "avg(list(1, 2.2, 3.1)) == 2.1", + "stdev(list(1, 2.2, 3.1)) > 0", + "stdev(list(1, 2, 3)) > 0", + "stdev(list(0.0, 0.0, 0.0)) == 0.0", + "stdev(list(0, 0, 0)) == 0.0" + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsStrings.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsStrings.scala new file mode 100644 index 0000000..c3e33c3 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsStrings.scala @@ -0,0 +1,92 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +import scala.language.implicitConversions + +/** + * Tests for 'strings' functions. + */ +class NCIdlFunctionsStrings extends NCIdlFunctions: + @Test + def test(): Unit = + test( + "trim(' a b ') == 'a b'", + "strip(' a b ') == 'a b'", + "uppercase('aB') == 'AB'", + "lowercase('aB') == 'ab'", + "is_alpha('aB') == true", + "is_alpha('aB1') == false", + "is_alphanum('aB1') == true", + "is_whitespace('A') == false", + "is_num('a') == false", + "is_num('1') == true", + "is_numspace('A') == false", + "is_alphaspace('A') == true", + "is_alphanumspace('A') == true", + "is_alphanumspace('1 A') == true", + "starts_with('ab', 'a') == true", + "starts_with('ab', 'b') == false", + "ends_with('ab', 'a') == false", + "ends_with('ab', 'b') == true", + "contains('ab', 'a') == true", + "contains('ab', 'bc') == false", + "index_of('ab', 'b') == 1", + "index_of('ab', 'bc') == -1", + "substr('abc', 0, 1) == 'a'", + "regex('textabc', '^text.*$') == true", + "regex('_textabc', '^text.*$') == false", + "substr('abc', 0, 2) == 'ab'", + "replace('abc', 'a', 'X') == 'Xbc'", + "replace('abc', '0', '0') == 'abc'", + "split('1 A', ' ') == list('1', 'A')", + "split_trim('1 A ', ' ') == list('1', 'A')", + "is_empty('a') == false", + "non_empty('a') == true", + "non_empty('a ') == true", + "is_empty('') == true", + "length(' ') == 1", + "length('') == 0", + "to_double('1.1') == 1.1", + "to_double('1') == 1", + "to_double('1') == 1.0", + "to_int('1') == 1", + "to_int('1') == 1.0", + + // Whitespaces. + "replace('abc', 'ab', '') == 'c'", + "substr('abc', 1, 3) == 'bc'", + "is_alphanumspace(' ') == true", + "is_alphanumspace(' ') == true", + "is_alphanumspace(' ') == true", + "is_whitespace(' ') == true", + "trim(' ') == ''" + ) + + @Test + def testError(): Unit = + expectError( + "substr('abc', 10, 30) == 'bc'", + "split('1 A') == true", + "split_trim('1 A') == true", + "to_double('1, 1') == true", + "to_double('A') == true" + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsTokensUsed.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsTokensUsed.scala new file mode 100644 index 0000000..dabbae9 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/functions/NCIdlFunctionsTokensUsed.scala @@ -0,0 +1,61 @@ +/* + * 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 + * + * https://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.internal.intent.compiler.functions + +import org.apache.nlpcraft.internal.intent.compiler.functions.NCIdlFunctions.* +import org.junit.jupiter.api.Test + +/** + * Tests for 'tokens used' result. + */ +class NCIdlFunctionsTokensUsed extends NCIdlFunctions: + @Test + def test(): Unit = + val entityIdA = mkEntity(id = "a") + val entityAb = mkEntity(id = "a", parentId = "b") + test( + TestDesc( + truth = "1 == 1", + idlCtx = mkIdlContext(), + tokensUsed = Option(0) + ), + TestDesc( + truth = "# == 'a'", + entity = Option(entityIdA), + idlCtx = mkIdlContext(Seq(entityIdA)), + tokensUsed = Option(1) + ), + TestDesc( + truth = "# == 'a' && # == 'a'", + entity = Option(entityIdA), + idlCtx = mkIdlContext(Seq(entityIdA)), + tokensUsed = Option(2) + ), + TestDesc( + truth = "# == 'a' && tok_parent == 'b'", + entity = Option(entityAb), + idlCtx = mkIdlContext(Seq(entityAb)), + tokensUsed = Option(2) + ), + TestDesc( + truth = "# == 'a' && # == 'a' && tok_parent == 'b'", + entity = Option(entityAb), + idlCtx = mkIdlContext(Seq(entityAb)), + tokensUsed = Option(3) + ) + ) diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/test_ok.idl b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/test_ok.idl new file mode 100644 index 0000000..fdb2e83 --- /dev/null +++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/internal/intent/compiler/test_ok.idl @@ -0,0 +1,84 @@ + /* + * 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. + */ + +// ============================ +// Test intents and predicates. +// ============================ + +// Re-usable predicate #1. +fragment=p1 + term={tok_id(tok_find_part(tok_this(), "alias")) == 2} + term={meta_frag('a') && has_any(get(meta_frag('b'), 'Москва'), list(1, 2))} + term(userDefined)=/org.apache.MyShit#myMethod/ + +// Intent #1. +intent=i1 + flow=/org.package#method1/ // User-code flow predicate. + fragment(p1, {'a': true, 'b': {'Москва': [1, 2, 3]}}) /* Macro-expansion. */ + term~{length("some text") > 0} // Normal term. + term={has_all(list(1, 2, 3, 4, 5), list(3, 5))} + term={if(2==2, "string", list(1, 2, 3))} + +// Intent #2. +intent=i2 + flow="a[^0-9]b" + meta={'a': 42, 'b': {'Москва': [1, 2, 3]}} + term(t1)={2 == 2 && !# != -25 && meta_model('a') == 42} + term(t2)={ + @a = meta_model('a') + @list = list(1, 2, 3, 4) + + @a == 42 && has_all(@list, list(3, 2)) + } + +intent=i3 + flow="a[^0-9]b" + term(t1)={ + @x = 2 + @xx = ((@x * @x) / 2) * 3 + + @xx == 6 && has( + json(meta_req('user_json_payload')), + list("موسكو\"", 'v1\'v1', "k2", "v2") + ) + } + +intent=i4 flow=/#flowModelMethod/ term(t1)=/#termModelMethod/ + +intent=i5 + flow="a[^0-9]b" + meta={'a': 42, 'b': {'Москва': [1, 2, 3]}} + term(t1)={month >= 6 && !(#) != -25 && meta_model('a') == 42} + term(t2)={ + @a = meta_model('a') + @list = list(1, 2, 3, 4) + + @a == 42 && has_all(@list, list(3, 2)) + } + +intent=i6 + flow=/#flowModelMethod/ + term(t1)=/org.mypackage.MyClass#termMethod/ + term(t2)={ + @x = 2 + @xx = ((@x * @x) / 2) * 3 + + @xx == 6 && has( + json(meta_req('user_json_payload')), + list("موسكو\"", 'v1\'v1', "k2", "v2") + ) + } \ No newline at end of file