Fix small issue related to mutable justifications in Occurrence. Upd tests with latest changes.

This commit is contained in:
Grigorii Kirgizov 2019-07-04 16:47:04 +03:00 committed by Fedor Isakov
parent ef2627cab4
commit 769e7fd2ba
7 changed files with 73 additions and 34 deletions

View File

@ -31,6 +31,7 @@ typealias Justs = TIntSet
fun emptyJusts() = TIntHashSet(1)
fun justsOf(vararg elements: Int) = TIntHashSet(elements)
fun justsFromCollection(collection: Collection<Int>) = TIntHashSet(collection)
fun justsCopy(other: Justs) = TIntHashSet(other)
data class OccurrenceObserver(val occurrence: Occurrence, val controller: Controller) : LogicalObserver {

View File

@ -207,7 +207,7 @@ internal class ControllerImpl (
val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context)
return context.eval { status ->
state.processActivated(this, constraint.occurrence(this, args, justs, context.logicalContext), status)
state.processActivated(this, constraint.occurrence(this, args, justsCopy(justs), context.logicalContext), status)
}
}

View File

@ -161,7 +161,6 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
}
class OccChunk(override val id: Int, val occ: Occurrence) : Chunk {
init { occ.justifications.add(id) }
override val justifications: Justs

View File

@ -17,6 +17,7 @@
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.ProcessingState
import jetbrains.mps.logic.reactor.program.IncrementalProgramSpec
import java.lang.IllegalArgumentException
@ -32,7 +33,10 @@ interface StoreAwareJournal : MatchJournal, ProcessingState {
// for tests
companion object {
fun fromView(view: MatchJournal.View? = null): StoreAwareJournal = StoreAwareJournalImpl(MatchJournalImpl(view))
fun fromView(
ispec: IncrementalProgramSpec = IncrementalProgramSpec.DefaultSpec,
view: MatchJournal.View? = null
): StoreAwareJournal = StoreAwareJournalImpl(MatchJournalImpl(ispec, view))
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed 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.
*/
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.IncrementalProgramSpec
import jetbrains.mps.logic.reactor.program.Rule
class MockIncrProgSpec(val principalRuleTags: Set<Any>, val principalCtrSyms: Set<ConstraintSymbol>) : IncrementalProgramSpec {
override fun isPrincipal(ctr: Constraint): Boolean = principalCtrSyms.contains(ctr.symbol())
override fun isPrincipal(rule: Rule): Boolean = principalRuleTags.contains(rule.uniqueTag())
}

View File

@ -7,7 +7,6 @@ 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
@ -48,11 +47,6 @@ class TestIncrementalProgram {
}
}
private class MockIncrProgSpec(val principalRuleTags: Set<Any>, val principalCtrSyms: Set<ConstraintSymbol>) : IncrementalProgramSpec {
override fun isPrincipal(ctr: Constraint): Boolean = principalCtrSyms.contains(ctr.symbol())
override fun isPrincipal(rule: CRule): Boolean = principalRuleTags.contains(rule.uniqueTag())
}
private fun Builder.launch(name: String, incrSpec: IncrementalProgramSpec, resultHandler: (EvaluationResult) -> Unit)
: Pair<Builder, EvaluationResult>
{
@ -230,7 +224,7 @@ class TestIncrementalProgram {
).launch("addRule", progSpec) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"))
result.countChunks() shouldBe 2
result.countChunks() shouldBe 3
result.lastChunkSymbols() shouldBe listOf(sym0("foo"))
}.also { (builder, evalRes) ->
@ -246,7 +240,7 @@ class TestIncrementalProgram {
).relaunch("atStart", progSpec, evalRes.token()) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"), sym0("bar"))
result.countChunks() shouldBe 3
result.countChunks() shouldBe 3+1
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}
}
@ -635,7 +629,7 @@ class TestIncrementalProgram {
// 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"))
result.countChunks() shouldBe (2 + nPrincipalMatches) // +[.bar, foobar]
result.countChunks() shouldBe (2 + 1 + nPrincipalMatches) // +[.bar, foobar] +activation of bar
}
}
}
@ -859,7 +853,7 @@ class TestIncrementalProgram {
result.storeView().constraintSymbols() shouldBe setOf(sym0("lax"))
result.lastChunkSymbols() shouldBe listOf(sym0("lax"))
result.countChunks() shouldBe (nPrincipalMatches - 2)
result.countChunks() shouldBe (nPrincipalMatches - 3)
}
}
}
@ -910,13 +904,12 @@ class TestIncrementalProgram {
}.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 - 1 + 2)
result.countChunks() shouldBe (nPrincipalMatches - 1 + 3)
}
}
}
@ -1026,7 +1019,7 @@ class TestIncrementalProgram {
))
).relaunch("substituted", progSpec, evalRes.token()) { result ->
result.countChunks() shouldBe (nPrincipalMatches - 1)
result.countChunks() shouldBe (nPrincipalMatches - 3 + 1)
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}
}
@ -1097,7 +1090,7 @@ class TestIncrementalProgram {
// println(result.token().journalView.chunks)
result.countChunks() shouldBe (nPrincipalMatches)
result.countChunks() shouldBe (nPrincipalMatches - 1)
result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"))
result.lastChunkSymbols() shouldBe listOf(sym0("bar"))
}

View File

@ -1,6 +1,9 @@
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.core.internal.*
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.IncrementalProgramSpec
import jetbrains.mps.logic.reactor.program.Rule
import org.junit.Test
import org.junit.Assert.*
import org.junit.Ignore
@ -24,8 +27,16 @@ import org.junit.Ignore
class TestStoreAwareJournal {
private class JournalDispatcherHelper(dispatcher: Dispatcher, val hist: StoreAwareJournal = StoreAwareJournal.fromView())
{
private object LegacyMockIncrProgSpec : IncrementalProgramSpec {
override fun isPrincipal(ctr: Constraint): Boolean = ctr.isPrincipal
override fun isPrincipal(rule: Rule): Boolean = rule.all().any { it is Constraint && it.isPrincipal }
}
private class JournalDispatcherHelper(
dispatcher: Dispatcher,
ispec: IncrementalProgramSpec = LegacyMockIncrProgSpec,
val hist: StoreAwareJournal = StoreAwareJournal.fromView(ispec)
) {
var d: Dispatcher.DispatchingFront = dispatcher.front()
fun logExpand(occ: Occurrence) {
@ -37,7 +48,7 @@ class TestStoreAwareJournal {
// log and expand occurrence while tracking its justifications
fun logExpandJustified(id: String, vararg args: Any) =
logExpand(justifiedOccurrence(id, hist.justs(), * args))
logExpand(justifiedOccurrence(id, justsCopy(hist.justs()), * args))
}
@Test
@ -76,7 +87,7 @@ class TestStoreAwareJournal {
// log first 'foo' match
hist.logMatch(fooMatches.first())
hist.justs() shouldBe justsOf(1)
hist.justs() shouldBe justsOf(1,2)
logExpandJustified("bar")
d.matches().count() shouldBe 0
@ -84,13 +95,13 @@ class TestStoreAwareJournal {
// log second 'foo' match
hist.logMatch(fooMatches.elementAt(1))
hist.justs() shouldBe justsOf(1,2)
hist.justs() shouldBe justsOf(1,4)
logExpandJustified("qux")
d.matches().count() shouldBe 1
logFirstMatch()
hist.justs() shouldBe justsOf(1,2,3)
hist.justs() shouldBe justsOf(1,2,3,4,5,6) // 3 & 5 are ids of OccChunks
}
}
}
@ -130,6 +141,8 @@ class TestStoreAwareJournal {
logExpand(justifiedOccurrence("foo", setOf(1)))
val fooPos = hist.currentPos()
logFirstMatch()
logExpandJustified("bar")
@ -146,7 +159,7 @@ class TestStoreAwareJournal {
storeView().constraintSymbols() shouldBe setOf(sym0("qux"), sym0("lax"))
val nchunks = view().chunks.size
nchunks shouldBe 4
nchunks shouldBe 4 * 2 // 4 rules, each activates 1 principal occurrence
// try replay the last chunk
replay(mockController, lastPos)
@ -164,8 +177,12 @@ class TestStoreAwareJournal {
replay(mockController, initPos)
// storeView replays only the initial chunk, journal remains the same
storeView().constraintSymbols() shouldBe setOf<ConstraintSymbol>()
replay(mockController, fooPos)
storeView().constraintSymbols() shouldBe setOf(sym0("foo"))
// although storeView() results change, journal remains the same
view().chunks.size shouldBe nchunks
}
}
@ -337,7 +354,6 @@ class TestStoreAwareJournal {
hist.testPush()
// last production from rule2
logExpand(quxOcc)
// logExpandJustified("qux")
// rule4
@ -352,12 +368,9 @@ class TestStoreAwareJournal {
val oldStore = hist.storeView().allOccurrences()
val savedPos = hist.currentPos()
// println("chunks on save: ${oldState.chunks}")
hist.testPush()
logExpand(occurrence("last"))
// println("chunks on save: ${oldState.chunks}")
// println("chunks on exec: ${hist.view().chunks}")
oldState.chunks.size shouldBe 3
assertNotEquals(oldState.chunks, hist.view().chunks)
assertNotEquals(oldStore, hist.storeView().allOccurrences())
@ -373,6 +386,7 @@ class TestStoreAwareJournal {
}
@Ignore("manipulating journal without controller is too fragile. not a good test.")
@Test
fun testRmAddInMiddle() {
val mockController = MockController()
@ -452,9 +466,12 @@ class TestStoreAwareJournal {
// continue from the second chunk (match of rule1)
val rmIt = iterator()
rmIt.next() // skip initial chunk
val continueFrom = rmIt.next().toPos()
rmIt.next()
rmIt.remove()
rmIt.next() // 'foo' activation
val continueFrom = rmIt.next().toPos() // 'rule1' activation
rmIt.next() // 'bar1' activation
rmIt.next() // 'bazz' activation
rmIt.next() // 'rule2a' match
rmIt.remove() // rm rule2a match
// store is not longer valid after removing chunks from history, so reset it
resetStore()
@ -462,14 +479,14 @@ class TestStoreAwareJournal {
replay(mockController, continueFrom)
// according to the history 'qux' wasn't activated at this point & 'bar1' wasn't discarded
storeView().constraintSymbols() shouldBe setOf(sym0("bazz"), sym0("bar1"))
storeView().constraintSymbols() shouldBe setOf<ConstraintSymbol>()
}
// add another instance of bar (i.e. bar2) and trigger another rule, rule2b
// (bar2 plays a role of the reactivation of original bar)
logExpandJustified("bar2")
// 'bazz' is already expanded from the first execution
hist.justs() shouldBe justsOf(1) // dependency is only the first chunk
hist.justs() shouldBe justsOf(1,2,7) // dependency is only the first chunk, the first activation + new id
// we have only a single _new_ match; rule3 has been matched already and remains in the history, in future
d.matches().count() shouldBe 1