I recently encountered some problems with the design of a controller,
and discussed them with M. Stover in the "TriggeredController design"
thread on jmeter-user.  Here's what Mike said about
GenericController.next(): 

> The semantics of the "next()" method are supposed to be like so: 
> 
> returns Sampler - sampler is intended to be executed. 
> 
> returns null, isDone() = false: This should indicate the controller 
> has finished a full round of iteration, and the test should move on to
> the next controller.  If the test runs another iteration, the 
> controller should be ready for everything to begin again.  This is why
> the "nextIsNull()" method calls reinitialize(). 
> 
> returns null, isDone() = true; The controller has finished entirely, 
> and should not be run again.  Indeed, it is generally dropped from the
> test entirely.  

The current version of JMeter fails to behave in the manner described in
the second case.  If a subclass of GenericController is an immediate
child of a LoopController (e.g. the one in the ThreadGroup) and its
next() method returns null without setting "done", an infinite chain of
recursive calls results.  The attached test case demonstrates the
problem.

It's interesting that the "loops" property of some LoopController in the
tree needs to be set to -1 for this problem to surface.  (That's what
ThreadGroupGui does when creating the loop controller for a thread
group.)  What's even more interesting is that if the test tree contains
an explicit loop controller with "loops" set to a positive value, and
that loop controller contains the controller that returns null, then it
still fails.  These observations might be of interest to someone who
knows JMeter internals. In the absence of any Javadoc I can't really
determine a firm definition of the "loops" property, so I'm leaving this
to the experts.

jp

package net.jpulley.jmeter19.controller;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.jmeter.control.GenericController;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.log.Hierarchy;
import org.apache.log.Priority;

public class LoopControllerTest2 extends TestCase {
    public LoopControllerTest2(String name) {
        super(name);
    }
    
    public static Test suite() {
        return new TestSuite(LoopControllerTest2.class);
    }
    
    public static void main(String[] args) {
        junit.textui.TestRunner.run(suite());
    }
    
    protected void setUp() {
        Hierarchy.getDefaultHierarchy()
                .setDefaultPriority(Priority.NONE);
    }
    
    public void testInThreadGroup() throws Exception {
        //set up a ThreadGroup like ThreadGroupGui does
        ThreadGroup threadGroup = new ThreadGroup();
        LoopController threadGroupLoopCtlr = new LoopController();
        threadGroupLoopCtlr.setLoops(-1);
        threadGroup.setSamplerController(threadGroupLoopCtlr);

        NullController nullCtlr = new NullController(false, 0);
        threadGroup.addTestElement(nullCtlr);
        
        //test
        try {
            Sampler smplr = threadGroup.next();
        } catch (java.lang.StackOverflowError err) {
            fail("Stack overflow");
        }
    }
    
    public void testAsSubcontroller() throws Exception {
        //set up a ThreadGroup like ThreadGroupGui does
        ThreadGroup threadGroup = new ThreadGroup();
        LoopController threadGroupLoopCtlr = new LoopController();
        threadGroupLoopCtlr.setLoops(-1);
        threadGroup.setSamplerController(threadGroupLoopCtlr);
        
        //add a loop controller to the thread group
        LoopController loopCtlr = new LoopController();
        loopCtlr.setLoops(10);
        threadGroup.addTestElement(loopCtlr);
        
        //add a test controller to the loop controller
        NullController nullCtlr = new NullController(false, 0);
        loopCtlr.addTestElement(nullCtlr);
        
        //test
        try {
            Sampler smplr = threadGroup.next();
        } catch (java.lang.StackOverflowError err) {
            fail("Stack overflow");
        }
    }
    
    public void testStandaloneFinite() throws Exception {
        LoopController loopCtlr = new LoopController();
        loopCtlr.setLoops(10);

        NullController nullCtlr = new NullController(false, 0);
        loopCtlr.addTestElement(nullCtlr);
        
        //test
        try {
            Sampler smplr = loopCtlr.next();
        } catch (java.lang.StackOverflowError err) {
            fail("Stack overflow");
        }
    }
    
    public void testStandaloneInfinite() throws Exception {
        LoopController loopCtlr = new LoopController();
        loopCtlr.setLoops(-1);

        NullController nullCtlr = new NullController(false, 0);
        loopCtlr.addTestElement(nullCtlr);
        
        //test
        try {
            Sampler smplr = loopCtlr.next();
        } catch (java.lang.StackOverflowError err) {
            fail("Stack overflow");
        }
    }
    
    //A simple controller that's never done and always
    //returns null.  Create it with (true, n) to get a
    //stack dump at the nth call.
    public static class NullController extends GenericController {
        private int callCount;
        private boolean dumpStack;
        private int whichIter;
        
        public NullController(boolean dumpStack, int whichIter) {
            super();
            this.dumpStack = dumpStack;
            this.whichIter = whichIter;
        }
        
        public NullController() {
            this(true, 5);
        }
        
        public Sampler next() {
            if (dumpStack && ++callCount == whichIter) {
                Thread.dumpStack();
            }
            return null;
        }
    }
}



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to