Tests for incrementally removing and removing+adding rules

This commit is contained in:
Grigorii Kirgizov 2019-06-12 19:09:06 +03:00
parent d7b7d40f8a
commit 9863569323
2 changed files with 366 additions and 9 deletions

View File

@ -88,8 +88,6 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
val toRemove = ruleIds.contains(chunk.match.rule().uniqueTag())
if (toRemove) {
justificationRoots.add(chunk.id)
// Remove the chunk from the journal
it.remove()
// We removed the match, so need to reactivate all still valid occurrences from the head
// by definition of Chunk and principal rule, all occurrences from the head are principal
@ -107,16 +105,19 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
ExecPos(chunkPos, occ)
}
})
continue
}
// Invalidating dependent chunks
val toInvalidate = chunk.justifications.intersects(justificationRoots)
val toInvalidate = toRemove || chunk.justifications.intersects(justificationRoots)
if (toInvalidate) {
// Remove the chunk from the journal
it.remove()
// Need to
chunk.match.matchHeadReplaced().forEach {
dispatchingFront = dispatchingFront.contract(it as Occurrence)
}
// Seems, it's not strictly necessary, because some of its head occurrences are anyway invalidated forever
// and storing this invalid consumed match can make no harm, except some memory overhead.
// fixme: move Chunk's interface to RuleMatchEx instead of RuleMatch

View File

@ -2,12 +2,14 @@ import jetbrains.mps.logic.reactor.core.IncrementalProgramSpec
import jetbrains.mps.logic.reactor.core.Occurrence
import jetbrains.mps.logic.reactor.core.ReactorLifecycle
import jetbrains.mps.logic.reactor.core.SessionToken
import jetbrains.mps.logic.reactor.core.internal.MatchJournal
import jetbrains.mps.logic.reactor.evaluation.EvaluationResult
import jetbrains.mps.logic.reactor.evaluation.EvaluationSession
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.Rule as CRule
import org.junit.*
import org.junit.Assert.assertNotEquals
import program.MockConstraint
/*
@ -77,6 +79,10 @@ class TestIncrementalProgram {
}
private fun EvaluationResult.chunksSymbolView() = this.token().journalView.chunks.map {
it.entriesLog().map { entry -> !entry.isDiscarded to entry.occ.constraint().symbol() }
}
private fun EvaluationResult.lastChunk() = this.token().journalView.chunks.last()
private fun EvaluationResult.countChunks() = this.token().journalView.chunks.size
@ -566,8 +572,6 @@ class TestIncrementalProgram {
))
).relaunch("withBar", progSpec, evalRes.token()) { result ->
// println(result.token().journalView.chunks)
// if "foobar" happens too early, "1st" occ won't be produced
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"), sym0("marker"))
result.lastChunkSymbols() shouldBe listOf(sym0("marker"))
@ -578,7 +582,7 @@ class TestIncrementalProgram {
@Test
fun rmSingleKeptMatch() {
fun rmSinglePropagationMatch() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.bar", "foo.baz", "baz.qux"),
setOf(sym0("foo"), sym0("baz"))
@ -614,7 +618,6 @@ class TestIncrementalProgram {
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
// Insert rule before foo.baz: produce bar before discarding foo in foo.baz
builder.removeRules(listOf("baz.qux"))
.relaunch("removed", progSpec, evalRes.token()) { result ->
@ -624,4 +627,357 @@ class TestIncrementalProgram {
}
}
}
@Test
fun rmSingleSimplificationMatch() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.bar", "foo.baz", "baz.qux"),
setOf(sym0("foo"), sym0("baz"))
)
programWithRules(
rule("main",
headReplaced(
constraint("main")
),
body(
princConstraint("foo")
)),
rule("foo.baz",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("baz")
)),
rule("baz.qux",
headReplaced(
princConstraint("baz")
),
body(
constraint("qux")
)
)
).launch("initial run", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("qux"))
result.lastChunkSymbols() shouldBe listOf(sym0("qux"))
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
builder.removeRules(listOf("baz.qux"))
.relaunch("removed", progSpec, evalRes.token()) { result ->
// 'baz' must be restored
result.storeView().constraintSymbols() shouldBe setOf(sym0("baz"))
result.lastChunkSymbols() shouldBe listOf(sym0("baz"))
result.countChunks() shouldBe (nPrincipalMatches - 1)
}
}
}
@Test
fun rmMatchWithDependencies() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.baz", "foo.bar", "barbaz.qux", "baz.lax"),
setOf(sym0("foo"), sym0("baz"), sym0("bar"))
)
programWithRules(
rule("main",
headReplaced(
constraint("main")
),
body(
princConstraint("foo")
)),
rule("foo.bar",
headKept(
princConstraint("foo")
),
body(
princConstraint("bar")
)),
rule("foo.baz",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("baz")
)),
rule("barbaz.qux",
headKept(
princConstraint("bar"),
princConstraint("baz")
),
body(
constraint("qux")
)),
rule("baz.lax",
headReplaced(
princConstraint("baz")
),
body(
constraint("lax")
)
)
).launch("initial run", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"), sym0("qux"), sym0("lax"))
result.lastChunkSymbols() shouldBe listOf(sym0("lax"))
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
// "barbaz.qux" rule match is removed as a dependency of "foo.bar" match
builder.removeRules(listOf("foo.bar"))
.relaunch("removed", progSpec, evalRes.token()) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("lax"))
result.lastChunkSymbols() shouldBe listOf(sym0("lax"))
result.countChunks() shouldBe (nPrincipalMatches - 2)
}
}
}
@Test
fun rmSimplificationMatchAndMakeAnotherMatch() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.baz", "foo.bar", "baz.lax"),
setOf(sym0("foo"), sym0("baz"))
)
programWithRules(
rule("main",
headReplaced(
constraint("main")
),
body(
princConstraint("foo")
)),
// Note two discarding rules one after another.
// After removing "foo.bar" "foo.baz" must match.
rule("foo.bar",
headReplaced(
princConstraint("foo")
),
body(
constraint("bar")
)),
rule("foo.baz",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("baz")
)),
rule("baz.lax",
headReplaced(
princConstraint("baz")
),
body(
constraint("lax")
)
)
).launch("initial run", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"))
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
// "barbaz.qux" rule match is removed as a dependency of "foo.bar" match
builder.removeRules(listOf("foo.bar"))
.relaunch("removed", progSpec, evalRes.token()) { result ->
println(result.token().journalView.chunks)
result.storeView().constraintSymbols() shouldBe setOf(sym0("qux"), sym0("lax"))
result.lastChunkSymbols() shouldBe listOf(sym0("lax"))
result.countChunks() shouldBe (nPrincipalMatches - 1 + 2)
}
}
}
@Test
fun rmAndAddSamePropagationMatch() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.bar", "foo.baz", "baz.qux"),
setOf(sym0("foo"), sym0("baz"))
)
val readdedRule =
rule("foo.baz",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("baz")
))
programWithRules(
rule("main",
headReplaced(
constraint("main")
),
body(
princConstraint("foo")
)),
readdedRule,
rule("baz.qux",
headKept(
princConstraint("baz")
),
body(
constraint("qux")
)
)
).launch("initial run", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("baz"), sym0("qux"))
result.lastChunkSymbols() shouldBe listOf(sym0("qux"))
}.also { (builder, evalRes) ->
val oldChunksView = evalRes.chunksSymbolView()
val oldLastChunk = evalRes.lastChunk()
builder.removeRules(listOf("foo.baz"))
.relaunch("removed", progSpec, evalRes.token()) { result ->
// ensure something changed
result.lastChunkSymbols() shouldBe listOf(sym0("foo"))
}.also { (builder, evalRes) ->
builder.insertRulesAt(1, readdedRule)
.relaunch("readded", progSpec, evalRes.token()) { result ->
// ensure chunks are essentially the same, but have different identities
result.chunksSymbolView() shouldBe oldChunksView
assertNotEquals(result.lastChunk().id, oldLastChunk.id)
}
}
}
}
@Test
fun rmAndAddDifferentMatch() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.bar", "foo.baz", "baz.qux"),
setOf(sym0("foo"), sym0("baz"))
)
programWithRules(
rule("main",
headReplaced(
constraint("main")
),
body(
princConstraint("foo")
)),
rule("foo.baz",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("baz")
)),
rule("baz.qux",
headKept(
princConstraint("baz")
),
body(
constraint("qux")
)
)
).launch("initial run", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("baz"), sym0("qux"))
result.lastChunkSymbols() shouldBe listOf(sym0("qux"))
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
builder.removeRules(listOf("foo.baz")).insertRulesAt(1,
rule("foo.bar",
headReplaced(
princConstraint("foo")
),
body(
constraint("bar")
))
).relaunch("substituted", progSpec, evalRes.token()) { result ->
println(result.token().journalView.chunks)
result.countChunks() shouldBe (nPrincipalMatches - 1)
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}
}
}
@Test
fun rmAndTryMatchWithRemovedOccurrences() {
val progSpec = MockIncrProgSpec(
setOf("main", "foo.baz", "barbaz.cant", "baz.lax2"),
setOf(sym0("foo"), sym0("baz"), sym0("bar"))
)
programWithRules(
rule("main",
headReplaced(
constraint("main")
),
body(
princConstraint("foo")
)),
// <<< removed
rule("foo.baz",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("baz")
)),
// >>>
// can't match, there's no 'bar'
rule("barbaz.cant",
headKept(
princConstraint("bar"),
princConstraint("baz")
),
body(
constraint("cant")
)
)
).launch("initial run", progSpec) { result ->
println(result.token().journalView.chunks)
result.storeView().constraintSymbols() shouldBe setOf(sym0("baz"))
result.lastChunkSymbols() shouldBe listOf(sym0("baz"))
}.also { (builder, evalRes) ->
val nPrincipalMatches = evalRes.countChunks()
builder.removeRules(listOf("foo.baz")).programWithRules(
rule("foo.bar",
headReplaced(
princConstraint("foo")
),
body(
princConstraint("bar")
)),
rule("baz.lax2",
headKept(
princConstraint("baz")
),
body(
constraint("lax2")
))
).relaunch("substituted", progSpec, evalRes.token()) { result ->
println(result.token().journalView.chunks)
result.countChunks() shouldBe (nPrincipalMatches)
result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"))
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}
}
}
}