Commit 22e3d143 authored by Martti Käärik's avatar Martti Käärik
Browse files

Permitted and participating components follow the prose of the standard

getPermittedComponents(): the prose says that all component instances are
returned when the starting behaviour is not specified. The bodies returned the
empty set instead, and took the targets of any first interaction, tester-input
event or not. With that, the two constraints about how a block starts stayed
silent on a block that starts wrongly, while the participation constraints
reported testers against a starting event that does not exist. The bodies of
MultipleCombinedBehaviour and ExceptionalBehaviour now return all components
unless the block starts with a tester-input event, and only such an event
counts.

OptionalBehaviour gets the operation the prose already names: the source and
the targets of its starting interaction.

OptionalBehaviour also gets getParticipatingComponents(): seen from its
container, an optional participates through the source of its starting
interaction only. The participation restrictions exist so that no behaviour is
defined for a tester that cannot know whether to execute it; the target of an
optional is triggered explicitly, so it takes no part in the enclosing scope.
A nested optional's target is therefore permitted inside that optional and
nowhere else, and its source has to be permitted in the containing one.

OptionalBehaviourParticipation is reduced to the two operations. The earlier
text carried the permitted components inline, cast the first behaviour to an
interaction without checking, and used select where collect was meant, so it
could not be evaluated at all once an optional was nested.

Six fixtures cover what the suite did not reach: a block without a
tester-input start in an alternative and in a guarded default, a default that
starts with a tester-to-SUT send, and nested optionals with a permitted
target, a source that is not permitted, and a target used outside.

The constraint text is taken from the specification document; a fresh export
differs from the file only in the two intended error(...) wrappers.
parent 551cff36
Loading
Loading
Loading
Loading
+1 −12
Changes for plugins/org.etsi.mts.tdl.model/model/tdl-constraints.ocl: 1 added line, 12 removed lines.
Original line number Diff line number Diff line
@@ -798,18 +798,7 @@ context OptionalBehaviour

  -- No other testers except the participants of starting 'Interaction' within 'OptionalBehaviour' in locally ordered 'TestDescription'
  inv OptionalBehaviourParticipation ('If an \'OptionalBehaviour\' is included in a locally ordered \'TestDescription\' then no other tester \'ComponentInstance\' shall participate in the block of the \'OptionalBehaviour\' than the source and target of the starting \'Interaction\' except when being a target of the starting \'Interaction\' in a nested \'OptionalBehaviour\'. ' + self.toString()):
        let initial = self.block.behaviour->first().oclAsType(Interaction),
          initialComponents = initial->collect(i | i.sourceGate.component->union(i.target.targetGate.component)),
          optionals = self.block->closure(
              b | b.behaviour
              ->select(oclIsKindOf(SingleCombinedBehaviour)).oclAsType(SingleCombinedBehaviour).block
              ->union(b.behaviour
              ->select(oclIsKindOf(MultipleCombinedBehaviour)).oclAsType(MultipleCombinedBehaviour).block)
          ).behaviour->select(oclIsKindOf(OptionalBehaviour)).oclAsType(OptionalBehaviour),
          optionalTargets = optionals.block->select(b | b.behaviour->select(i | i.oclIsKindOf(Interaction))->first().oclAsType(Interaction).target.targetGate.component)
          in
          self.block.getParticipatingComponents()
              ->forAll(c | initialComponents->includes(c) or optionalTargets->includes(c))
        self.getPermittedComponents()->includesAll(self.block.getParticipatingComponents())
          or not self.getParentTestDescription().isLocallyOrdered
  

+14 −3
Changes for plugins/org.etsi.mts.tdl.model/model/tdl.ecore: 14 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -590,7 +590,7 @@
    </eOperations>
    <eOperations name="getPermittedComponents" upperBound="-1" eType="#//ComponentInstance">
      <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL/Pivot">
        <details key="body" value="&#xA;&#x9;&#x9;&#x9;&#x9;OrderedSet(ComponentInstance){}&#xA;          &#x9;&#x9;&#x9;->includingAll(self.block.behaviour->first()->select(oclIsKindOf(Interaction)).oclAsType(Interaction).target.targetGate.component)&#xA;          &#x9;&#x9;&#x9;->includingAll(self.block.behaviour->first()->select(oclIsKindOf(Quiescence)).oclAsType(Quiescence).effectiveComponent())&#xA;          &#x9;&#x9;&#x9;->includingAll(self.block.behaviour->first()->select(oclIsKindOf(TimeOut)).oclAsType(TimeOut).effectiveComponent())&#xA;&#x9;&#x9;&#x9;"/>
        <details key="body" value="&#xA;&#x9;&#x9;&#x9;&#x9;if self.block.behaviour->isEmpty() or not self.block.behaviour->first().isTesterInputEvent() then&#xA;&#x9;&#x9;&#x9;&#x9;    self.getParentTestDescription().testConfiguration.componentInstance->asSet()->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;else (if self.block.behaviour->first().oclIsKindOf(Interaction) then&#xA;&#x9;&#x9;&#x9;&#x9;        self.block.behaviour->first().oclAsType(Interaction).target.targetGate.component->asSet()&#xA;&#x9;&#x9;&#x9;&#x9;    else&#xA;&#x9;&#x9;&#x9;&#x9;        self.block.behaviour->first().effectiveComponent()->asSet()->excluding(null)&#xA;&#x9;&#x9;&#x9;&#x9;    endif)->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;endif&#xA;&#x9;&#x9;&#x9;&#x9;"/>
      </eAnnotations>
      <eAnnotations source="http://www.eclipse.org/OCL/Collection">
        <details key="nullFree" value="false"/>
@@ -613,7 +613,7 @@
    </eOperations>
    <eOperations name="getPermittedComponents" upperBound="-1" eType="#//ComponentInstance">
      <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL/Pivot">
        <details key="body" value="&#xA;&#x9;&#x9;&#x9;&#x9;OrderedSet(ComponentInstance){}&#xA;          &#x9;&#x9;&#x9;->includingAll(self.block->collect(b | b.behaviour->first())->select(oclIsKindOf(Interaction)).oclAsType(Interaction).target.targetGate.component)&#xA;          &#x9;&#x9;&#x9;->includingAll(self.block->collect(b | b.behaviour->first())->select(oclIsKindOf(Quiescence)).oclAsType(Quiescence).effectiveComponent())&#xA;          &#x9;&#x9;&#x9;->includingAll(self.block->collect(b | b.behaviour->first())->select(oclIsKindOf(TimeOut)).oclAsType(TimeOut).effectiveComponent())&#xA;&#x9;&#x9;&#x9;"/>
        <details key="body" value="&#xA;&#x9;&#x9;&#x9;&#x9;self.block->collect(b |&#xA;&#x9;&#x9;&#x9;&#x9;    if b.behaviour->isEmpty() or not b.behaviour->first().isTesterInputEvent() then&#xA;&#x9;&#x9;&#x9;&#x9;        self.getParentTestDescription().testConfiguration.componentInstance->asSet()&#xA;&#x9;&#x9;&#x9;&#x9;    else if b.behaviour->first().oclIsKindOf(Interaction) then&#xA;&#x9;&#x9;&#x9;&#x9;        b.behaviour->first().oclAsType(Interaction).target.targetGate.component->asSet()&#xA;&#x9;&#x9;&#x9;&#x9;    else&#xA;&#x9;&#x9;&#x9;&#x9;        b.behaviour->first().effectiveComponent()->asSet()->excluding(null)&#xA;&#x9;&#x9;&#x9;&#x9;    endif&#xA;&#x9;&#x9;&#x9;&#x9;    endif&#xA;&#x9;&#x9;&#x9;&#x9;)->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;"/>
      </eAnnotations>
      <eAnnotations source="http://www.eclipse.org/OCL/Collection">
        <details key="nullFree" value="false"/>
@@ -640,7 +640,18 @@
    <eStructuralFeatures xsi:type="ecore:EReference" name="block" lowerBound="1" eType="#//Block"
        containment="true"/>
  </eClassifiers>
  <eClassifiers xsi:type="ecore:EClass" name="OptionalBehaviour" eSuperTypes="#//SingleCombinedBehaviour"/>
  <eClassifiers xsi:type="ecore:EClass" name="OptionalBehaviour" eSuperTypes="#//SingleCombinedBehaviour">
    <eOperations name="getParticipatingComponents" upperBound="-1" eType="#//ComponentInstance">
      <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL/Pivot">
        <details key="body" value="&#xA;&#x9;&#x9;&#x9;&#x9;if self.getParentTestDescription()->exists(td | td.isLocallyOrdered) then&#xA;&#x9;&#x9;&#x9;&#x9;    if self.block.behaviour->notEmpty() and self.block.behaviour->first().oclIsKindOf(Interaction) then&#xA;&#x9;&#x9;&#x9;&#x9;        self.block.behaviour->first().oclAsType(Interaction).sourceGate.component->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;    else&#xA;&#x9;&#x9;&#x9;&#x9;        self.block.getParticipatingComponents()->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;    endif&#xA;&#x9;&#x9;&#x9;&#x9;else&#xA;&#x9;&#x9;&#x9;&#x9;    self.getParentTestDescription().testConfiguration.componentInstance->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;endif&#xA;&#x9;&#x9;&#x9;&#x9;"/>
      </eAnnotations>
    </eOperations>
    <eOperations name="getPermittedComponents" upperBound="-1" eType="#//ComponentInstance">
      <eAnnotations source="http://www.eclipse.org/emf/2002/Ecore/OCL/Pivot">
        <details key="body" value="&#xA;&#x9;&#x9;&#x9;&#x9;if self.block.behaviour->isEmpty() or not self.block.behaviour->first().oclIsKindOf(Interaction) then&#xA;&#x9;&#x9;&#x9;&#x9;    self.getParentTestDescription().testConfiguration.componentInstance->asSet()->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;else&#xA;&#x9;&#x9;&#x9;&#x9;    self.block.behaviour->first().oclAsType(Interaction).sourceGate.component->asSet()&#xA;&#x9;&#x9;&#x9;&#x9;        ->union(self.block.behaviour->first().oclAsType(Interaction).target.targetGate.component->asSet())->asOrderedSet()&#xA;&#x9;&#x9;&#x9;&#x9;endif&#xA;&#x9;&#x9;&#x9;&#x9;"/>
      </eAnnotations>
    </eOperations>
  </eClassifiers>
  <eClassifiers xsi:type="ecore:EClass" name="UnboundedLoopBehaviour" eSuperTypes="#//SingleCombinedBehaviour"/>
  <eClassifiers xsi:type="ecore:EClass" name="ConditionalBehaviour" eSuperTypes="#//MultipleCombinedBehaviour"/>
  <eClassifiers xsi:type="ecore:EClass" name="CompoundBehaviour" eSuperTypes="#//SingleCombinedBehaviour"/>
+28 −0
Changes for plugins/org.etsi.mts.tdl.tx.tests/src/constraints/core/AlternativeBehaviour_AlternativeBlocksComponent.oneBlockWithoutTesterInput.tdltx: 28 added lines, 0 removed lines.
Original line number Diff line number Diff line
//! expect AlternativeBehaviour::AlternativeBlocksComponent
//! expect AlternativeBehaviour::FirstBlockBehaviour
// One block starts with a tester-input event, the other with an action: not all blocks start
// with a tester-input event of the same component.
Package AltOneBlockWithoutTesterInput {
    Import all from TDL
    Type Msg
    Msg m1
    Action doIt
    Message Gate mGate accepts Msg
    Component Node {
        gate mGate g
    }
    Configuration cfg {
        Node tester1 as Tester,
        Node tester2 as Tester,
        Node sut as SUT,
        connect tester1::g to sut::g,
        connect tester2::g to sut::g
    }
    Test td uses cfg {
        alternatively {
            sut::g sends m1 to tester1::g
        } or {
            perform doIt on tester1
        }
    }
}
+32 −0
Changes for plugins/org.etsi.mts.tdl.tx.tests/src/constraints/core/ExceptionalBehaviour_ExceptionalGuardedandTargetComponent.noTesterInput.tdltx: 32 added lines, 0 removed lines.
Original line number Diff line number Diff line
//! expect ExceptionalBehaviour::ExceptionalGuardedandTargetComponent
//! expect ExceptionalBehaviour::FirstExceptionalBehaviour
// The guarded default starts with an action, so there is no tester-input event of the guarded
// component: getPermittedComponents() returns all components and the constraint reports. The
// participation constraint stays silent, because without a starting event nothing is restricted.
Package GuardedNoTesterInput {
    Import all from TDL
    Type Msg
    Msg m1
    Action doIt
    Message Gate mGate accepts Msg
    Component Node {
        gate mGate g
    }
    Configuration cfg {
        Node tester1 as Tester,
        Node tester2 as Tester,
        Node sut as SUT,
        connect tester1::g to sut::g,
        connect tester2::g to sut::g
    }
    Test td uses cfg {
        while {
            sut::g sends m1 to tester1::g
        }
        with {
            default on tester2 {
                perform doIt on tester2
            }
        }
    }
}
+31 −0
Changes for plugins/org.etsi.mts.tdl.tx.tests/src/constraints/core/ExceptionalBehaviour_ExceptionalGuardedandTargetComponent.testerOutput.tdltx: 31 added lines, 0 removed lines.
Original line number Diff line number Diff line
//! expect ExceptionalBehaviour::ExceptionalGuardedandTargetComponent
//! expect ExceptionalBehaviour::FirstExceptionalBehaviour
// The guarded default starts with a tester-to-SUT send. That is an interaction but not a
// tester-input event, so its target must not count as the permitted component.
Package GuardedTesterOutput {
    Import all from TDL
    Type Msg
    Msg m1
    Action doIt
    Message Gate mGate accepts Msg
    Component Node {
        gate mGate g
    }
    Configuration cfg {
        Node tester1 as Tester,
        Node tester2 as Tester,
        Node sut as SUT,
        connect tester1::g to sut::g,
        connect tester2::g to sut::g
    }
    Test td uses cfg {
        while {
            sut::g sends m1 to tester1::g
        }
        with {
            default on tester2 {
                tester2::g sends m1 to sut::g
            }
        }
    }
}
Loading