This repository has been archived on 2025-09-23. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
2025LogProg-project-GhentPr.../tests/prolog/builtins/DatabaseOperatorsTests.kt
2025-05-04 21:50:58 +02:00

295 lines
No EOL
10 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.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"))))
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"))
))
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")
assertTrue(result.hasNext(), "Expected more results")
assertEquals(2, predicate.clauses.size, "Expected 2 clauses")
assertTrue(result.next().isSuccess)
assertTrue(result.hasNext(), "Expected more results")
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"))))
))
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")
assertTrue(result.hasNext(), "Expected more results")
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")
assertTrue(result.hasNext(), "Expected more results")
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")
assertFalse(result.hasNext(), "Expected no more results")
assertEquals(0, predicate.clauses.size, "Expected no clauses")
}
@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")
}
}