Tests for incrementally removing and removing+adding rules
This commit is contained in:
parent
d7b7d40f8a
commit
9863569323
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue