TestCase *RandomOrderExecutor::seekToCase(const string &path)
{
    const vector<string> components = de::splitString(path, '.');
    size_t compNdx;

    DE_ASSERT(!m_nodeStack.empty() && m_nodeStack.front().node->getNodeType() == NODETYPE_ROOT);

    for (compNdx = 0; compNdx < components.size(); compNdx++)
    {
        const size_t stackPos = compNdx + 1;

        if (stackPos >= m_nodeStack.size())
            break;  // Stack end
        else if (components[compNdx] != m_nodeStack[stackPos].node->getName())
        {
            // Current stack doesn't match any more, prune.
            pruneStack(stackPos);
            break;
        }
    }

    DE_ASSERT(m_nodeStack.size() == compNdx + 1);

    for (; compNdx < components.size(); compNdx++)
    {
        const size_t parentStackPos = compNdx;
        TestNode *const curNode =
            findNodeByName(m_nodeStack[parentStackPos].children, components[compNdx]);

        if (!curNode)
            throw Exception(string("Test hierarchy node not found: ") + path);

        m_nodeStack.push_back(NodeStackEntry(curNode));

        if (curNode->getNodeType() == NODETYPE_PACKAGE)
        {
            TestPackage *const testPackage = static_cast<TestPackage *>(curNode);

            m_inflater.enterTestPackage(testPackage, m_nodeStack.back().children);
            DE_ASSERT(!m_caseExecutor);
            m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor());
        }
        else if (curNode->getNodeType() == NODETYPE_GROUP)
            m_inflater.enterGroupNode(static_cast<TestCaseGroup *>(curNode),
                                      m_nodeStack.back().children);
        else if (compNdx + 1 != components.size())
            throw Exception(string("Invalid test hierarchy path: ") + path);
    }

    DE_ASSERT(m_nodeStack.size() == components.size() + 1);

    if (isTestNodeTypeExecutable(m_nodeStack.back().node->getNodeType()))
        return dynamic_cast<TestCase *>(m_nodeStack.back().node);
    else
        throw Exception(string("Not a test case: ") + path);
}
DepthFirstStateAction::ResultE
DepthFirstStateAction::apply(NodePtr pRoot)
{
    ResultE result = NewActionTypes::Continue;

    startEvent();

    result = startActors();

    if(result & NewActionTypes::Quit)
        return result;

    _itInitialState = getState  ();
    _itActiveState  = cloneState();

    // gained refs: active, root
    incRefCount(_itActiveState, 2);

    _nodeStack.push_back(NodeStackEntry(pRoot, _itActiveState, 1));

    if((_extendLeaveActors.empty() == true) &&
       (_basicLeaveActors .empty() == true)    )
    {
        result = traverseEnter();
    }
    else
    {
        result = traverseEnterLeave();
    }

    setState(_itInitialState);

    // lost refs: active, current node
    decRefCount(_itActiveState, 2);

    _itActiveState   = _itInitialState;
    _stateClonedFlag = true;

    _nodeStack         .clear();
#ifndef OSG_NEWACTION_STATESLOTINTERFACE
    _stateStore        .clear();
#endif
    _stateRefCountStore.clear();

    if(result & NewActionTypes::Quit)
        return result;

    result = stopActors();

    stopEvent();

    return result;
}
RandomOrderExecutor::RandomOrderExecutor(TestPackageRoot &root, TestContext &testCtx)
    : m_testCtx(testCtx), m_inflater(testCtx)
{
    m_nodeStack.push_back(NodeStackEntry(&root));
    root.getChildren(m_nodeStack[0].children);
}
void
DepthFirstStateAction::pushChildren(const NodePtr &pNode, ResultE result)
{
    if(result & (NewActionTypes::Skip  |
                 NewActionTypes::Break |
                 NewActionTypes::Quit   ))
    {
        setChildrenListEnabled(false);
        setNumPasses          (1    );

        getExtraChildrenList().clear();

        return;
    }

    ChildrenList      &cl  = getChildrenList     ();
    ExtraChildrenList &ecl = getExtraChildrenList();

    if(getChildrenListEnabled() == true)
    {
        for(UInt32 i = 0, size = cl.getSize(); i < size; ++i)
        {
            if(( cl.getActive(i)                                 == true  ) &&
               ( cl.getChild (i)                                 != NullFC) &&
               ((cl.getChild (i)->getTravMask() & getTravMask()) != 0     )   )
            {
                // gained refs: child
                incRefCount(_itActiveState);

                _nodeStack.push_back(
                    NodeStackEntry(cl.getChild(i), _itActiveState, 1));
            }
        }
    }
    else
    {
        MFNodePtr::const_iterator itChildren  = pNode->getMFChildren()->begin();
        MFNodePtr::const_iterator endChildren = pNode->getMFChildren()->end  ();

        for(; itChildren != endChildren; ++itChildren)
        {
            if((  *itChildren                                  != NullFC) &&
               (((*itChildren)->getTravMask() & getTravMask()) != 0     )   )
            {
                // gained refs: child
                incRefCount(_itActiveState);

                _nodeStack.push_back(
                    NodeStackEntry(*itChildren, _itActiveState, 1));
            }
        }
    }

    for(UInt32 i = 0, size = ecl.getSize(); i < size; ++i)
    {
        if(( ecl.getActive(i)                                 == true  ) &&
           ( ecl.getChild (i)                                 != NullFC) &&
           ((ecl.getChild (i)->getTravMask() & getTravMask()) != 0     )   )
        {
            // gained refs: extra child
            incRefCount(_itActiveState);

            _nodeStack.push_back(
                NodeStackEntry(ecl.getChild(i), _itActiveState, 1));
        }
    }

    setChildrenListEnabled(false);
    ecl.clear             (     );
    setNumPasses          (1    );
}
DepthFirstStateAction::ResultE
DepthFirstStateAction::traverseEnterLeave(void)
{
    ResultE              result          = NewActionTypes::Continue;
    Int32                nodePass;        // pass over current node
    UInt32               multiPasses;     // requested passes over current node
    NodePtr              pNode;
    StateRefCountStoreIt itStateRefCount;

    while((_nodeStack.empty() == false) && !(result & NewActionTypes::Quit))
    {
        pNode           = _nodeStack.back().getNode         ();
        nodePass        = _nodeStack.back().getPassCount    ();
        itStateRefCount = _nodeStack.back().getStateRefCount();

        if(itStateRefCount != _itActiveState)
        {
#ifdef OSG_NEWACTION_STATISTICS
            getStatistics()->getElem(statStateRestores)->inc();
#endif /* OSG_NEWACTION_STATISTICS */

            setState(itStateRefCount);

            // gained refs: active
            incRefCount(itStateRefCount);

            // lost refs: active
            decRefCount(_itActiveState);

            _itActiveState = itStateRefCount;
        }

        getChildrenList().setParentNode(pNode);

        if(nodePass > 0)
        {
            // positive pass -> enter node
            
#ifdef OSG_NEWACTION_STATISTICS
            getStatistics()->getElem(statNodesEnter)->inc();
#endif /* OSG_NEWACTION_STATISTICS */
            
            _stateClonedFlag = false;

            result      = enterNode   (pNode, static_cast<UInt32>(nodePass - 1));
            multiPasses = getNumPasses(                                        );

            // only initial pass (nodePass == 1) can request multiPass.
            if((nodePass == 1) && (multiPasses > 1))
            {
                // remove current node from stack
                _nodeStack.pop_back();
                
                for(; multiPasses > 1; -- multiPasses)
                {
                    // gained refs: addtional passs
                    incRefCount(_itActiveState);
                    
                    _nodeStack.push_back(
                        NodeStackEntry(pNode, _itActiveState, multiPasses));
                }
                
                // readd current node - with negative pass -> leave
                _nodeStack.push_back(
                    NodeStackEntry(pNode, _itActiveState, -nodePass));
            }
            else
            {
                // change current node passCount to negative -> leave
                _nodeStack.back().setPassCount(-nodePass);
            }
            
            pushChildren(pNode, result);
        }
        else
        {
            // negative pass -> leave node

#ifdef OSG_NEWACTION_STATISTICS
            getStatistics()->getElem(statNodesLeave)->inc();
#endif /* OSG_NEWACTION_STATISTICS */
            
            _stateClonedFlag = true;

            result = leaveNode(pNode, static_cast<UInt32>(-nodePass - 1));

            _nodeStack.pop_back();

            // lost refs: current node
            decRefCount(_itActiveState);
        }
    }

    return result;
}
DepthFirstStateAction::ResultE
DepthFirstStateAction::traverseEnter(void)
{
    ResultE              result          = NewActionTypes::Continue;
    NodePtr              pNode;
    Int32                nodePass;        // pass over current node
    UInt32               multiPasses;     // requested passes over current node
    StateRefCountStoreIt itStateRefCount; // state for current node

    while((_nodeStack.empty() == false) && !(result & NewActionTypes::Quit))
    {
        pNode           = _nodeStack.back().getNode         ();
        nodePass        = _nodeStack.back().getPassCount    ();
        itStateRefCount = _nodeStack.back().getStateRefCount();

#ifdef OSG_NEWACTION_STATISTICS
        getStatistics()->getElem(statNodesEnter)->inc();
#endif /* OSG_NEWACTION_STATISTICS */

        if(itStateRefCount != _itActiveState)
        {
#ifdef OSG_NEWACTION_STATISTICS
            getStatistics()->getElem(statStateRestores)->inc();
#endif /* OSG_NEWACTION_STATISTICS */

            setState(itStateRefCount);

            // gained refs: active
            incRefCount(itStateRefCount);

            // lost refs: active
            decRefCount(_itActiveState);

            _itActiveState = itStateRefCount;
        }

        _stateClonedFlag = false;

        getChildrenList().setParentNode(pNode);

        result      = enterNode   (pNode, static_cast<UInt32>(nodePass - 1));
        multiPasses = getNumPasses(                                        );

        _nodeStack.pop_back();

        // only initial pass (nodePass == 1) can request multiPasses
        if((nodePass == 1) && (multiPasses > 1))
        {
            for(; multiPasses > 1; --multiPasses)
            {
                // gained refs: additional pass
                incRefCount(_itActiveState);
                
                _nodeStack.push_back(
                    NodeStackEntry(pNode, _itActiveState, multiPasses));
            }
        }
        
        pushChildren(pNode, result);

        // lost refs: current node
        decRefCount(_itActiveState);
    }

    return result;
}