338 lines
No EOL
12 KiB
Kotlin
338 lines
No EOL
12 KiB
Kotlin
package prolog.builtins
|
|
|
|
import org.junit.jupiter.api.Assertions.assertEquals
|
|
import org.junit.jupiter.api.Assertions.assertFalse
|
|
import org.junit.jupiter.api.Assertions.assertTrue
|
|
import org.junit.jupiter.api.BeforeEach
|
|
import org.junit.jupiter.api.Nested
|
|
import org.junit.jupiter.api.Test
|
|
import org.junit.jupiter.params.ParameterizedTest
|
|
import org.junit.jupiter.params.provider.ValueSource
|
|
import prolog.ast.Database
|
|
import prolog.ast.Database.Program
|
|
import prolog.ast.logic.Clause
|
|
import prolog.ast.logic.Fact
|
|
import prolog.ast.logic.Predicate
|
|
import prolog.ast.logic.Rule
|
|
import prolog.ast.terms.Atom
|
|
import prolog.ast.terms.Functor
|
|
import prolog.ast.terms.Structure
|
|
import prolog.ast.terms.Variable
|
|
|
|
class DatabaseOperatorsTests {
|
|
@BeforeEach
|
|
fun setup() {
|
|
Program.reset()
|
|
}
|
|
|
|
abstract class AssertTestsBase<T : Structure> {
|
|
protected abstract fun createAssert(clause: Clause): Structure
|
|
|
|
@BeforeEach
|
|
fun setup() {
|
|
Program.reset()
|
|
}
|
|
|
|
@ParameterizedTest
|
|
@ValueSource(classes = [AssertA::class, AssertZ::class, Assert::class])
|
|
fun `assert(fact atom)`(assertKind: Class<*>) {
|
|
val fact = Fact(Atom("a"))
|
|
createAssert(fact).satisfy(emptyMap())
|
|
|
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
|
assertEquals(fact, Program.db.predicates["a/_"]!!.clauses[0])
|
|
}
|
|
|
|
@Test
|
|
fun `assert(fact structure)`() {
|
|
val fact = Fact(Structure(Atom("a"), listOf(Atom("b"))))
|
|
createAssert(fact).satisfy(emptyMap())
|
|
|
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
|
assertEquals(fact, Program.db.predicates["a/1"]!!.clauses[0])
|
|
}
|
|
|
|
@Test
|
|
fun `assert(rule)`() {
|
|
val rule = Rule(
|
|
Structure(Atom("a"), listOf(Atom("b"))),
|
|
Atom("c")
|
|
)
|
|
createAssert(rule).satisfy(emptyMap())
|
|
|
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
|
assertEquals(rule, Program.db.predicates["a/1"]!!.clauses[0])
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
class AssertTests : AssertTestsBase<Assert>() {
|
|
override fun createAssert(clause: Clause): Structure {
|
|
return Assert(clause)
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
class AssertATests : AssertTestsBase<AssertA>() {
|
|
override fun createAssert(clause: Clause): Structure {
|
|
return AssertA(clause)
|
|
}
|
|
|
|
@Test
|
|
fun `asserta adds to the beginning`() {
|
|
val rule1 = Rule(
|
|
Structure(Atom("a"), listOf(Atom("b"))),
|
|
Atom("c")
|
|
)
|
|
val rule2 = Rule(
|
|
Structure(Atom("a"), listOf(Atom("d"))),
|
|
Atom("e")
|
|
)
|
|
AssertA(rule1).satisfy(emptyMap())
|
|
AssertA(rule2).satisfy(emptyMap())
|
|
|
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
|
assertEquals(rule2, Program.db.predicates["a/1"]!!.clauses[0])
|
|
assertEquals(rule1, Program.db.predicates["a/1"]!!.clauses[1])
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
class AssertZTests : AssertTestsBase<AssertZ>() {
|
|
override fun createAssert(clause: Clause): Structure {
|
|
return AssertZ(clause)
|
|
}
|
|
|
|
@Test
|
|
fun `assertz adds to the end`() {
|
|
val rule1 = Rule(
|
|
Structure(Atom("a"), listOf(Atom("b"))),
|
|
Atom("c")
|
|
)
|
|
val rule2 = Rule(
|
|
Structure(Atom("a"), listOf(Atom("d"))),
|
|
Atom("e")
|
|
)
|
|
AssertZ(rule1).satisfy(emptyMap())
|
|
AssertZ(rule2).satisfy(emptyMap())
|
|
|
|
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
|
|
assertEquals(rule1, Program.db.predicates["a/1"]!!.clauses[0])
|
|
assertEquals(rule2, Program.db.predicates["a/1"]!!.clauses[1])
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun `retract fails silently for unknown predicates`() {
|
|
val retract = Retract(Atom("unknown"))
|
|
val result = retract.satisfy(emptyMap())
|
|
|
|
assertTrue(result.none(), "Expected no results")
|
|
}
|
|
|
|
@Test
|
|
fun `simple retract`() {
|
|
val predicate = Predicate(listOf(Fact(Atom("a"))), dynamic = true)
|
|
Program.db.load(predicate)
|
|
|
|
assertEquals(1, Program.query(Atom("a")).count())
|
|
|
|
val retract = Retract(Atom("a"))
|
|
|
|
assertEquals(1, retract.satisfy(emptyMap()).toList().size, "Expected 1 result")
|
|
assertEquals(0, predicate.clauses.size, "Expected 0 clauses")
|
|
|
|
assertTrue(retract.satisfy(emptyMap()).none())
|
|
}
|
|
|
|
@Test
|
|
fun `retract atom`() {
|
|
val predicate = Predicate(
|
|
listOf(
|
|
Fact(Atom("a")),
|
|
Fact(Atom("a")),
|
|
Fact(Atom("a"))
|
|
), dynamic = true
|
|
)
|
|
Program.db.load(predicate)
|
|
|
|
val control = Program.query(Atom("a")).toList()
|
|
|
|
assertEquals(3, control.size, "Expected 3 results")
|
|
|
|
val retract = Retract(Atom("a"))
|
|
|
|
val result = retract.satisfy(emptyMap()).iterator()
|
|
|
|
assertEquals(3, predicate.clauses.size, "Expected 3 clauses")
|
|
|
|
assertTrue(result.hasNext(), "Expected more results")
|
|
|
|
val answer = result.next()
|
|
|
|
assertTrue(answer.isSuccess, "Expected success")
|
|
assertTrue(answer.getOrNull()!!.isEmpty(), "Expected no substitutions")
|
|
|
|
assertEquals(2, predicate.clauses.size, "Expected 2 clauses")
|
|
|
|
assertTrue(result.next().isSuccess)
|
|
assertTrue(result.next().isSuccess)
|
|
|
|
assertFalse(result.hasNext(), "Expected more results")
|
|
assertEquals(0, predicate.clauses.size, "Expected no remaining clauses")
|
|
}
|
|
|
|
@Test
|
|
fun `retract compound with variable`() {
|
|
val predicate = Predicate(
|
|
listOf(
|
|
Fact(Structure(Atom("a"), listOf(Atom("b")))),
|
|
Fact(Structure(Atom("a"), listOf(Atom("c")))),
|
|
Fact(Structure(Atom("a"), listOf(Atom("d"))))
|
|
), dynamic = true
|
|
)
|
|
Program.db.load(predicate)
|
|
|
|
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
|
|
|
|
assertEquals(3, control.size, "Expected 3 results")
|
|
|
|
val retract = Retract(Structure(Atom("a"), listOf(Variable("X"))))
|
|
|
|
val result = retract.satisfy(emptyMap()).iterator()
|
|
|
|
assertEquals(3, predicate.clauses.size, "Expected 3 clauses")
|
|
|
|
assertTrue(result.hasNext(), "Expected more results")
|
|
var answer = result.next()
|
|
|
|
assertTrue(answer.isSuccess, "Expected success")
|
|
var subs = answer.getOrNull()!!
|
|
assertTrue(subs.isNotEmpty(), "Expected substitutions")
|
|
assertTrue(Variable("X") in subs, "Expected variable X")
|
|
assertEquals(Atom("b"), subs[Variable("X")], "Expected b")
|
|
assertEquals(2, predicate.clauses.size, "Expected 2 clauses")
|
|
|
|
answer = result.next()
|
|
|
|
assertTrue(answer.isSuccess, "Expected success")
|
|
subs = answer.getOrNull()!!
|
|
assertTrue(subs.isNotEmpty(), "Expected substitutions")
|
|
assertTrue(Variable("X") in subs, "Expected variable X")
|
|
assertEquals(Atom("c"), subs[Variable("X")], "Expected c")
|
|
assertEquals(1, predicate.clauses.size, "Expected 1 clause")
|
|
|
|
answer = result.next()
|
|
|
|
assertTrue(answer.isSuccess, "Expected success")
|
|
subs = answer.getOrNull()!!
|
|
assertTrue(subs.isNotEmpty(), "Expected substitutions")
|
|
assertTrue(Variable("X") in subs, "Expected variable X")
|
|
assertEquals(Atom("d"), subs[Variable("X")], "Expected d")
|
|
assertEquals(0, predicate.clauses.size, "Expected no clauses")
|
|
|
|
assertFalse(result.hasNext(), "Expected no more results")
|
|
}
|
|
|
|
@Test
|
|
fun `custom assert example`() {
|
|
var query = Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza")))
|
|
|
|
var result = Program.query(query).toList()
|
|
assertEquals(0, result.size, "Expected 0 results")
|
|
|
|
var assert: Structure = Assert(Fact(query))
|
|
assert.satisfy(emptyMap())
|
|
|
|
result = Program.query(query).toList()
|
|
assertEquals(1, result.size, "Expected 1 result")
|
|
assertTrue(result[0].getOrNull()!!.isEmpty())
|
|
|
|
assert = AssertZ(Fact(Structure(Atom("likes"), listOf(Atom("bob"), Atom("sushi")))))
|
|
assert.satisfy(emptyMap())
|
|
|
|
query = Structure(Atom("likes"), listOf(Atom("bob"), Variable("X")))
|
|
|
|
result = Program.query(query).toList()
|
|
assertEquals(1, result.size, "Expected 1 result")
|
|
assertTrue(result[0].isSuccess, "Expected success")
|
|
assertEquals(Atom("sushi"), result[0].getOrNull()!![Variable("X")], "Expected sushi")
|
|
|
|
query = Structure(Atom("likes"), listOf(Variable("X"), Variable("Y")))
|
|
|
|
result = Program.query(query).toList()
|
|
assertEquals(2, result.size, "Expected 2 results")
|
|
assertTrue(result[0].isSuccess, "Expected success")
|
|
var result0 = result[0].getOrNull()!!
|
|
assertEquals(Atom("alice"), result0[Variable("X")], "Expected alice")
|
|
assertEquals(Atom("pizza"), result0[Variable("Y")], "Expected pizza")
|
|
assertTrue(result[1].isSuccess, "Expected success")
|
|
var result1 = result[1].getOrNull()!!
|
|
assertEquals(Atom("bob"), result1[Variable("X")], "Expected bob")
|
|
assertEquals(Atom("sushi"), result1[Variable("Y")], "Expected sushi")
|
|
|
|
assert = AssertA(
|
|
Rule(
|
|
Structure(Atom("likes"), listOf(Variable("X"), Atom("italian"))),
|
|
Structure(Atom("likes"), listOf(Variable("X"), Atom("pizza")))
|
|
)
|
|
)
|
|
assert.satisfy(emptyMap())
|
|
|
|
result = Program.query(query).toList()
|
|
assertEquals(3, result.size, "Expected 3 results")
|
|
assertTrue(result[0].isSuccess, "Expected success")
|
|
result0 = result[0].getOrNull()!!
|
|
assertEquals(Atom("alice"), result0[Variable("X")], "Expected alice")
|
|
assertEquals(Atom("italian"), result0[Variable("Y")], "Expected italian")
|
|
assertTrue(result[1].isSuccess, "Expected success")
|
|
result1 = result[1].getOrNull()!!
|
|
assertEquals(Atom("alice"), result1[Variable("X")], "Expected alice")
|
|
assertEquals(Atom("pizza"), result1[Variable("Y")], "Expected pizza")
|
|
assertTrue(result[2].isSuccess, "Expected success")
|
|
val result2 = result[2].getOrNull()!!
|
|
assertEquals(Atom("bob"), result2[Variable("X")], "Expected bob")
|
|
assertEquals(Atom("sushi"), result2[Variable("Y")], "Expected sushi")
|
|
}
|
|
|
|
@Test
|
|
fun `retract all`() {
|
|
val predicate = Predicate(
|
|
listOf(
|
|
Fact(Structure(Atom("a"), listOf(Atom("b")))),
|
|
Fact(Structure(Atom("a"), listOf(Atom("c")))),
|
|
Fact(Structure(Atom("a"), listOf(Atom("d"))))
|
|
), dynamic = true
|
|
)
|
|
|
|
Program.db.load(predicate)
|
|
|
|
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
|
|
assertEquals(3, control.size, "Expected 3 results")
|
|
assertEquals(3, Program.db.predicates["a/1"]!!.clauses.size, "Expected 3 clauses")
|
|
|
|
val retract = RetractAll(Structure(Atom("a"), listOf(Variable("X"))))
|
|
val result = retract.satisfy(emptyMap()).toList()
|
|
|
|
assertEquals(1, result.size, "Expected 1 result")
|
|
assertTrue(result[0].isSuccess, "Expected success")
|
|
assertEquals(0, Program.db.predicates["a/1"]!!.clauses.size, "Expected 0 clauses")
|
|
}
|
|
|
|
@Test
|
|
fun `If Head refers to a predicate that is not defined, it is implicitly created as a dynamic predicate`() {
|
|
val predicateName = "idonotyetexist"
|
|
val predicateFunctor = "$predicateName/1"
|
|
|
|
assertFalse(predicateFunctor in Program.db.predicates, "Expected predicate to not exist before")
|
|
|
|
val retractAll = RetractAll(Structure(Atom(predicateName), listOf(Variable("X"))))
|
|
val result = retractAll.satisfy(emptyMap()).toList()
|
|
|
|
assertEquals(1, result.size, "Expected 1 result")
|
|
assertTrue(result[0].isSuccess, "Expected success")
|
|
|
|
assertTrue(predicateFunctor in Program.db.predicates, "Expected predicate to exist after")
|
|
assertTrue(Program.db.predicates[predicateFunctor]!!.dynamic, "Expected predicate to be dynamic")
|
|
}
|
|
} |