locked
Error: ambiguous action reference RRS feed

  • Question

  • I am trying to model a system in which there are multiple components which process messages.  I am using an instance-based Modeling solution, where each component type has an action called processMessage, which checks its queue (a SequenceContainer) and processes any pending messages with its component-specific business logic. 

    I'd like to create a scenario where Component A processes a message, and then Component B processes a message.  However, using syntax like _.processMessage() does not work, as I get error:

    ambiguous action reference 'processMessage'

    This makes perfect sense.  But, I'm wondering if there a way to accomplish what I'm trying to do cord scripting? 
    Thursday, December 10, 2009 4:59 PM

Answers

  • Hi Anne,

    Have you tried declaring names in 'let' clauses?

    let ComponentA a, ComponentB b in

    new a.ComponentA; new b.ComponentB; a.ProcessMessage; b.ProcessMessage

    (You can omit the constructors, but you will need some action to return the actual instance of the component before your generated tests can invoke successive actions on them.)
    Thursday, December 10, 2009 9:34 PM
    Moderator

All replies

  • Hi Anne,

    Have you tried declaring names in 'let' clauses?

    let ComponentA a, ComponentB b in

    new a.ComponentA; new b.ComponentB; a.ProcessMessage; b.ProcessMessage

    (You can omit the constructors, but you will need some action to return the actual instance of the component before your generated tests can invoke successive actions on them.)
    Thursday, December 10, 2009 9:34 PM
    Moderator
  • Worked like a charm.  I had thought of using 'let' clauses, but the examples in the documentation seemed to only use primitive types, so I was not sure that it could be used on Reference types.  Thanks for providing the syntax and example.
    Friday, December 11, 2009 3:48 PM
  • Happy to help!
    Friday, December 11, 2009 8:25 PM
    Moderator
  • I found this "let" statement very useful to test "instance-based" systems. very helpful.
    Thursday, July 5, 2012 2:59 PM
  • Hi,

    I got into a problem of non-accepting state when I interleaved two machines using"|||" and "let" statements. The scenario that got into trouble is TwoInstancesCreateDestroyProgram. BTW: OneInstanceCreateDestroy* works well.

    I explored offline.

    I attach cord, model, and my adapter for your reference.

    Cord
    // This is a Spec Explorer coordination script (Cord version 1.0).
    // Here you define configurations and machines describing the
    // exploration task you want to perform.
    
    using IssueProject.Sample; 
    
    /// Contains actions of the model, bounds, and switches.
    config Main 
    {
        /// Thread constructor action
        action ComplexThread();
    
        action void ComplexThread.Create(bool expectedResult);
    
        action void ComplexThread.Destroy(bool expectedResult);
    
        switch StepBound = 128;
        switch PathDepthBound = 128;
        switch TestClassBase = "vs";
        switch GeneratedTestPath = "..\\IssueProject.TestSuite";
        switch GeneratedTestNamespace = "IssueProject.ThreadTestSuite";
        switch TestEnabled = false;
    }
    
    /// Constructs a machine from the model program. 
    /// Since the model is not finite, this machine explodes
    /// and exploration is stopped by a bound.
    machine ThreadModelProgram() : Main
    {
        construct model program from Main 
        where scope = "IssueProject"
    }
    
    // Test Scenario: Create and Destroy on one instance
    
    machine OneInstanceCreateDestroyScenario() : Main where ForExploration = false
    {
        let ComplexThread conn
    
        in
    
        new conn.ComplexThread;(conn.Create+;conn.Destroy+)+
    }
    
    machine OneInstanceCreateDestroyProgram() : Main where ForExploration = true
    {
       (OneInstanceCreateDestroyScenario) || ThreadModelProgram
    }
    
    machine OneInstanceCreateDestroyTestSuite() : Main where TestEnabled = true
    {
        construct test cases where strategy = "shorttests" for OneInstanceCreateDestroyProgram()
    }
    
    // Test Scenario: Create and Destroy on Two instances
    
    machine TwoInstancesCreateDestroyScenario() : Main where ForExploration = false
    {
        OneInstanceCreateDestroyScenario ||| OneInstanceCreateDestroyScenario
    }
    
    machine TwoInstancesCreateDestroyProgram() : Main where ForExploration = true
    {
        TwoInstancesCreateDestroyScenario || ThreadModelProgram
    }
    
    
    machine TwoInstancesCreateDestroyTestSuite() : Main where TestEnabled = true
    {
        construct test cases where strategy = "shorttests" for TwoInstancesCreateDestroyProgram()
    }
    
    Model
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    using Microsoft.Modeling;
    
    namespace IssueProject
    {
        /// <summary>
        /// A model class, bound to an sample class.
        /// </summary>
        [TypeBinding("IssueProject.Sample.ComplexThread")]
        class SimpleThread
        {
            private bool connectionCreated = false;
    
            /// <summary>
            /// The parameter-less constructor is considered a model action.
            /// </summary>
            [Rule(Action = "new this.ComplexThread()")]
            SimpleThread()
            { 
                
            }
    
            [Rule]
            void Create(bool result)
            {
                connectionCreated = true;
    
                // It should always be possible to create a connection
                Condition.IsTrue(result);
            }
    
            [Rule]
            void Destroy(bool result)
            {
                // We can destroy a connection only if it is created
                Condition.IsTrue(result == connectionCreated);
                connectionCreated = false;
            }
        }
    }

    using System;

    using System.Collections.Generic; using System.Text; namespace IssueProject.Sample { /// <summary> /// The implementation allows a thread to perform more than one step. /// Tests should still pass, because the model's behavior is a subset of the implementation's. /// </summary> public class ComplexThread { public ComplexThread() { } public void Create(bool expectedResult) { } public void Destroy(bool expectedResult) { } } }




    • Edited by Dharma11 Thursday, August 2, 2012 6:27 PM
    Thursday, August 2, 2012 6:26 PM
  • Hi Dharma,

    Here is an idea:
    The given program model does not discriminate two different instances right from the start, but the scenario "TwoInstancesCreateDestroyScenario" does.
    So different steps in the scenario are the same steps in the program model.
    This is a very unusual use-case. The Spec Explorer sample-scenarios do not discriminate same states, but select from existing ones.

    The definition of the composition operator is:
    "In each state, Spec Explorer will explore the steps of the left operand and use those to select steps from the right operand."
    http://social.msdn.microsoft.com/Forums/en/specexplorer/thread/d4efa20c-2992-4e60-b599-12934eb2734e

    So Spec Explorer will not discriminate the states in your scenario when selecting steps from the program model.
    The unusual scenario produces now some subtle situations:

    * If in the scenario two different actions leave a state, but they are the same in the model program you have one transition ending in two different states.

    Hope this helps

    P.S.:

    Another point: a mail from Nico to the "let" construct:
    "How about using “let” statements to create parameter combinations across action invocations? You can still use operators such as “&” to represent different temporal orderings."
    In the given scenario I assume that the |||-operator combinations is also known to be affected due to the let let-construct.

    So finally including both points a working scenario, could look like this.
    It could further be improved to come closer to the |||-operator:

     

    machine TwoScenario() : Main 
    {
        let ComplexThread conn1, 
            ComplexThread conn2
        in
        new conn1.ComplexThread; 
        new conn2.ComplexThread; 
        (OneScenario(conn1) & OneScenario(conn2))
    }
    machine OneScenario(ComplexThread conn) : Main
    {
        (conn.Create+;conn.Destroy+)+
    }


    • Edited by bububa Monday, August 13, 2012 7:55 AM
    Wednesday, August 8, 2012 8:39 AM