You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by pm...@apache.org on 2018/03/31 10:32:34 UTC

svn commit: r1828095 - in /jmeter/trunk: src/components/org/apache/jmeter/sampler/ src/components/org/apache/jmeter/sampler/gui/ src/core/org/apache/jmeter/gui/ src/core/org/apache/jmeter/reporters/ src/core/org/apache/jmeter/resources/ src/core/org/ap...

Author: pmouawad
Date: Sat Mar 31 10:32:33 2018
New Revision: 1828095

URL: http://svn.apache.org/viewvc?rev=1828095&view=rev
Log:
Bug 62238 - Add ability to Switch to next iteration of Current Loop
Contributed by Ubik Load Pack
Bugzilla Id: 62238

Modified:
    jmeter/trunk/src/components/org/apache/jmeter/sampler/TestAction.java
    jmeter/trunk/src/components/org/apache/jmeter/sampler/gui/TestActionGui.java
    jmeter/trunk/src/core/org/apache/jmeter/gui/OnErrorPanel.java
    jmeter/trunk/src/core/org/apache/jmeter/reporters/ResultAction.java
    jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
    jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
    jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java
    jmeter/trunk/src/core/org/apache/jmeter/testelement/OnErrorTestElement.java
    jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
    jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterContext.java
    jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java
    jmeter/trunk/xdocs/changes.xml

Modified: jmeter/trunk/src/components/org/apache/jmeter/sampler/TestAction.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/sampler/TestAction.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/sampler/TestAction.java (original)
+++ jmeter/trunk/src/components/org/apache/jmeter/sampler/TestAction.java Sat Mar 31 10:32:33 2018
@@ -32,6 +32,7 @@ import org.apache.jmeter.testelement.Tes
 import org.apache.jmeter.testelement.property.IntegerProperty;
 import org.apache.jmeter.testelement.property.StringProperty;
 import org.apache.jmeter.threads.JMeterContext;
+import org.apache.jmeter.threads.JMeterContext.TestLogicalAction;
 import org.apache.jmeter.threads.JMeterContextService;
 import org.apache.jmeter.timers.TimerService;
 import org.slf4j.Logger;
@@ -45,10 +46,12 @@ import org.slf4j.LoggerFactory;
 public class TestAction extends AbstractSampler implements Interruptible {
 
     private static final Logger log = LoggerFactory.getLogger(TestAction.class);
+    
+    private static final String MSG_STOP_CURRENT_THREAD = "Stopping current thread from element {}";
 
     private static final TimerService TIMER_SERVICE = TimerService.getInstance(); 
 
-    private static final long serialVersionUID = 241L;
+    private static final long serialVersionUID = 242L;
 
     private static final Set<String> APPLIABLE_CONFIG_CLASSES = new HashSet<>(
             Arrays.asList("org.apache.jmeter.config.gui.SimpleConfigGui"));
@@ -57,7 +60,18 @@ public class TestAction extends Abstract
     public static final int STOP = 0;
     public static final int PAUSE = 1;
     public static final int STOP_NOW = 2;
+    /**
+     * Start next iteration of Thread Loop
+     */
     public static final int RESTART_NEXT_LOOP = 3;
+    /**
+     * Start next iteration of Current Looop
+     */
+    public static final int START_NEXT_ITERATION_CURRENT_LOOP = 4;
+    /**
+     * Break Current Looop
+     */
+    public static final int BREAK_CURRENT_LOOP = 5;
 
     // Action targets
     public static final int THREAD = 0;
@@ -87,24 +101,40 @@ public class TestAction extends Abstract
             pause(getDurationAsString());
         } else if (action == STOP || action == STOP_NOW) {
             if (target == THREAD) {
-                log.info("Stopping current thread from element {}", getName());
+                if(log.isInfoEnabled()) {
+                    log.info(MSG_STOP_CURRENT_THREAD, getName());
+                }
                 context.getThread().stop();
             } else if (target == TEST) {
                 if (action == STOP_NOW) {
-                    log.info("Stopping current thread from element {}", getName());
+                    if(log.isInfoEnabled()) {
+                        log.info(MSG_STOP_CURRENT_THREAD, getName());
+                    }
                     context.getThread().stop();
-                    log.info("Stopping all threads now from element {}", getName());
+                    if(log.isInfoEnabled()) {
+                        log.info("Stopping all threads now from element {}", getName());
+                    } 
                     context.getEngine().stopTest();
                 } else {
-                    log.info("Stopping current thread from element {}", getName());
+                    if(log.isInfoEnabled()) {
+                        log.info(MSG_STOP_CURRENT_THREAD, getName());
+                    }
                     context.getThread().stop();
-                    log.info("Stopping all threads from element {}", getName());
+                    if(log.isInfoEnabled()) {
+                        log.info("Stopping all threads from element {}", getName());
+                    }
                     context.getEngine().askThreadsToStop();
                 }
             }
         } else if (action == RESTART_NEXT_LOOP) {
-            log.info("Restarting next loop from element {}", getName());
-            context.setStartNextThreadLoop(true);
+            log.info("Restarting next thread loop from element {}", getName());
+            context.setTestLogicalAction(TestLogicalAction.START_NEXT_ITERATION_OF_THREAD);
+        } else if (action == START_NEXT_ITERATION_CURRENT_LOOP) {
+            log.info("Switching to next loop iteration from element {}", getName());
+            context.setTestLogicalAction(TestLogicalAction.START_NEXT_ITERATION_OF_CURRENT_LOOP);
+        } else if (action == BREAK_CURRENT_LOOP) {
+            log.info("Breaking current loop from element {}", getName());
+            context.setTestLogicalAction(TestLogicalAction.BREAK_CURRENT_LOOP);
         }
 
         return null; // This means no sample is saved

Modified: jmeter/trunk/src/components/org/apache/jmeter/sampler/gui/TestActionGui.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/sampler/gui/TestActionGui.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/sampler/gui/TestActionGui.java (original)
+++ jmeter/trunk/src/components/org/apache/jmeter/sampler/gui/TestActionGui.java Sat Mar 31 10:32:33 2018
@@ -18,23 +18,26 @@
 
 package org.apache.jmeter.sampler.gui;
 
+import java.awt.GridLayout;
+
+import javax.swing.BorderFactory;
 import javax.swing.ButtonGroup;
 import javax.swing.DefaultComboBoxModel;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
+import javax.swing.JPanel;
 import javax.swing.JRadioButton;
-import javax.swing.JTextField;
 
 import org.apache.jmeter.gui.GUIMenuSortOrder;
-import org.apache.jmeter.gui.util.HorizontalPanel;
 import org.apache.jmeter.sampler.TestAction;
 import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.gui.JLabeledTextField;
 import org.apache.jorphan.gui.layout.VerticalLayout;
 
 @GUIMenuSortOrder(1)
-public class TestActionGui extends AbstractSamplerGui {
+public class TestActionGui extends AbstractSamplerGui { // NOSONAR Ignore hierarchy error
     private static final long serialVersionUID = 240L;
 
     // Gui components
@@ -46,9 +49,13 @@ public class TestActionGui extends Abstr
 
     private JRadioButton stopNowButton;
 
-    private JRadioButton restartNextLoopButton;
+    private JRadioButton breakLoopButton;
+    
+    private JRadioButton restartNextThreadLoopButton;
+    
+    private JRadioButton startNextIterationOfCurrentLoopButton;
     
-    private JTextField durationField;
+    private JLabeledTextField durationField;
 
     // State variables
     private int target;
@@ -57,23 +64,29 @@ public class TestActionGui extends Abstr
 
     // String in the panel
     // Do not make these static, otherwise language changes don't work
-    private final String targetLabel = JMeterUtils.getResString("test_action_target"); // $NON-NLS-1$
+    private static final String TARGET_LABEL = JMeterUtils.getResString("test_action_target"); // $NON-NLS-1$
 
-    private final String threadTarget = JMeterUtils.getResString("test_action_target_thread"); // $NON-NLS-1$
+    private static final String THREAD_TARGET_LABEL = JMeterUtils.getResString("test_action_target_thread"); // $NON-NLS-1$
 
-    private final String testTarget = JMeterUtils.getResString("test_action_target_test"); // $NON-NLS-1$
+    private static final String TEST_TARGET_LABEL = JMeterUtils.getResString("test_action_target_test"); // $NON-NLS-1$
 
-    private final String actionLabel = JMeterUtils.getResString("test_action_action"); // $NON-NLS-1$
+    private static final String ACTION_ON_THREAD_LABEL = JMeterUtils.getResString("test_action_action_thread"); // $NON-NLS-1$
+    
+    private static final String ACTION_ON_THREAD_TEST_LABEL = JMeterUtils.getResString("test_action_action_test_thread"); // $NON-NLS-1$
+
+    private static final String PAUSE_ACTION_LABEL = JMeterUtils.getResString("test_action_pause"); // $NON-NLS-1$
 
-    private final String pauseAction = JMeterUtils.getResString("test_action_pause"); // $NON-NLS-1$
+    private static final String STOP_ACTION_LABEL = JMeterUtils.getResString("test_action_stop"); // $NON-NLS-1$
 
-    private final String stopAction = JMeterUtils.getResString("test_action_stop"); // $NON-NLS-1$
+    private static final String STOP_NOW_ACTION_LABEL = JMeterUtils.getResString("test_action_stop_now"); // $NON-NLS-1$
 
-    private final String stopNowAction = JMeterUtils.getResString("test_action_stop_now"); // $NON-NLS-1$
+    private static final String RESTART_NEXT_THREAD_LOOP_LABEL = JMeterUtils.getResString("test_action_restart_next_loop"); // $NON-NLS-1$
+    
+    private static final String START_NEXT_ITERATION_CURRENT_LOOP_ACTION = JMeterUtils.getResString("test_action_continue_current_loop"); // $NON-NLS-1$
 
-    private final String restartNextLoopAction = JMeterUtils.getResString("test_action_restart_next_loop"); // $NON-NLS-1$
+    private static final String BREAK_CURRENT_LOOP_ACTION = JMeterUtils.getResString("test_action_break_current_loop"); // $NON-NLS-1$
 
-    private final String durationLabel = JMeterUtils.getResString("test_action_duration"); // $NON-NLS-1$
+    private static final String DURATION_LABEL = JMeterUtils.getResString("test_action_duration"); // $NON-NLS-1$
 
     public TestActionGui() {
         super();
@@ -94,19 +107,32 @@ public class TestActionGui extends Abstr
 
         target = ta.getTarget();
         if (target == TestAction.THREAD) {
-            targetBox.setSelectedItem(threadTarget);
+            targetBox.setSelectedItem(THREAD_TARGET_LABEL);
         } else {
-            targetBox.setSelectedItem(testTarget);
+            targetBox.setSelectedItem(TEST_TARGET_LABEL);
         }
         action = ta.getAction();
-        if (action == TestAction.PAUSE) {
-            pauseButton.setSelected(true);
-        } else if (action == TestAction.STOP_NOW) {
-            stopNowButton.setSelected(true);
-        } else if(action == TestAction.STOP ){
-            stopButton.setSelected(true);
-        } else {
-            restartNextLoopButton.setSelected(true);
+        switch (action) {
+            case TestAction.PAUSE:
+                pauseButton.setSelected(true);
+                break;
+            case TestAction.STOP_NOW:
+                stopNowButton.setSelected(true);
+                break;
+            case TestAction.STOP:
+                stopButton.setSelected(true);
+                break;
+            case TestAction.RESTART_NEXT_LOOP:
+                restartNextThreadLoopButton.setSelected(true);
+                break;
+            case TestAction.START_NEXT_ITERATION_CURRENT_LOOP:
+                startNextIterationOfCurrentLoopButton.setSelected(true);
+                break;
+            case TestAction.BREAK_CURRENT_LOOP:
+                breakLoopButton.setSelected(true);
+                break;
+            default:
+                break;
         }
 
         durationField.setText(ta.getDurationAsString());
@@ -156,27 +182,8 @@ public class TestActionGui extends Abstr
         setBorder(makeBorder());
         add(makeTitlePanel());
 
-        // Target
-        HorizontalPanel targetPanel = new HorizontalPanel();
-        targetPanel.add(new JLabel(targetLabel));
-        DefaultComboBoxModel<String> targetModel = new DefaultComboBoxModel<>();
-        targetModel.addElement(threadTarget);
-        targetModel.addElement(testTarget);
-        targetBox = new JComboBox<>(targetModel);
-        targetBox.addActionListener(evt -> {
-            if (((String) targetBox.getSelectedItem()).equals(threadTarget)) {
-                target = TestAction.THREAD;
-            } else {
-                target = TestAction.TEST;
-            }
-        });
-        targetPanel.add(targetBox);
-        add(targetPanel);
-
-        // Action
-        HorizontalPanel actionPanel = new HorizontalPanel();
         ButtonGroup actionButtons = new ButtonGroup();
-        pauseButton = new JRadioButton(pauseAction, true);
+        pauseButton = new JRadioButton(PAUSE_ACTION_LABEL, true);
         pauseButton.addChangeListener(evt -> {
             if (pauseButton.isSelected()) {
                 action = TestAction.PAUSE;
@@ -184,7 +191,7 @@ public class TestActionGui extends Abstr
                 targetBox.setEnabled(false);
             }
         });
-        stopButton = new JRadioButton(stopAction, false);
+        stopButton = new JRadioButton(STOP_ACTION_LABEL, false);
         stopButton.addChangeListener(evt -> {
             if (stopButton.isSelected()) {
                 action = TestAction.STOP;
@@ -192,7 +199,7 @@ public class TestActionGui extends Abstr
                 targetBox.setEnabled(true);
             }
         });
-        stopNowButton = new JRadioButton(stopNowAction, false);
+        stopNowButton = new JRadioButton(STOP_NOW_ACTION_LABEL, false);
         stopNowButton.addChangeListener(evt -> {
             if (stopNowButton.isSelected()) {
                 action = TestAction.STOP_NOW;
@@ -201,9 +208,9 @@ public class TestActionGui extends Abstr
             }
         });
         
-        restartNextLoopButton = new JRadioButton(restartNextLoopAction, false);
-        restartNextLoopButton.addChangeListener(evt -> {
-            if (restartNextLoopButton.isSelected()) {
+        restartNextThreadLoopButton = new JRadioButton(RESTART_NEXT_THREAD_LOOP_LABEL, false);
+        restartNextThreadLoopButton.addChangeListener(evt -> {
+            if (restartNextThreadLoopButton.isSelected()) {
                 action = TestAction.RESTART_NEXT_LOOP;
                 durationField.setEnabled(false);
                 targetBox.setSelectedIndex(TestAction.THREAD);
@@ -211,25 +218,69 @@ public class TestActionGui extends Abstr
             }
         });
         
+        startNextIterationOfCurrentLoopButton = new JRadioButton(START_NEXT_ITERATION_CURRENT_LOOP_ACTION, false);
+        startNextIterationOfCurrentLoopButton.addChangeListener(evt -> {
+            if (startNextIterationOfCurrentLoopButton.isSelected()) {
+                action = TestAction.START_NEXT_ITERATION_CURRENT_LOOP;
+                durationField.setEnabled(false);
+                targetBox.setSelectedIndex(TestAction.THREAD);
+                targetBox.setEnabled(false);
+            }
+        });
+        
+        breakLoopButton = new JRadioButton(BREAK_CURRENT_LOOP_ACTION, false);
+        breakLoopButton.addChangeListener(evt -> {
+            if (breakLoopButton.isSelected()) {
+                action = TestAction.BREAK_CURRENT_LOOP;
+                durationField.setEnabled(false);
+                targetBox.setSelectedIndex(TestAction.THREAD);
+                targetBox.setEnabled(false);
+            }
+        });
+
+        // Duration
+        durationField = new JLabeledTextField(DURATION_LABEL, 15);
+        durationField.setText(""); // $NON-NLS-1$
+
+        
         actionButtons.add(pauseButton);
         actionButtons.add(stopButton);
         actionButtons.add(stopNowButton);
-        actionButtons.add(restartNextLoopButton);
+        actionButtons.add(restartNextThreadLoopButton);
+        actionButtons.add(startNextIterationOfCurrentLoopButton);
+        actionButtons.add(breakLoopButton);
 
-        actionPanel.add(new JLabel(actionLabel));
-        actionPanel.add(pauseButton);
-        actionPanel.add(stopButton);
-        actionPanel.add(stopNowButton);
-        actionPanel.add(restartNextLoopButton);
-        add(actionPanel);
+        // Action
+        JPanel actionOnThreadPanel = new JPanel(new GridLayout(3, 2));
+        actionOnThreadPanel.setBorder(BorderFactory.createTitledBorder(ACTION_ON_THREAD_LABEL)); //$NON-NLS-1$
+        actionOnThreadPanel.add(pauseButton);
+        actionOnThreadPanel.add(durationField);
+        actionOnThreadPanel.add(restartNextThreadLoopButton);
+        actionOnThreadPanel.add(startNextIterationOfCurrentLoopButton);
+        actionOnThreadPanel.add(breakLoopButton);
+        
+        
+        // Action
+        JPanel actionOnTestOrThreadPanel = new JPanel(new GridLayout(2, 2));
+        actionOnTestOrThreadPanel.setBorder(BorderFactory.createTitledBorder(ACTION_ON_THREAD_TEST_LABEL)); //$NON-NLS-1$
+        actionOnTestOrThreadPanel.add(stopButton);
+        actionOnTestOrThreadPanel.add(stopNowButton);
+        actionOnTestOrThreadPanel.add(new JLabel(TARGET_LABEL));
+        DefaultComboBoxModel<String> targetModel = new DefaultComboBoxModel<>();
+        targetModel.addElement(THREAD_TARGET_LABEL);
+        targetModel.addElement(TEST_TARGET_LABEL);
+        targetBox = new JComboBox<>(targetModel);
+        targetBox.addActionListener(evt -> {
+            if (((String) targetBox.getSelectedItem()).equals(THREAD_TARGET_LABEL)) {
+                target = TestAction.THREAD;
+            } else {
+                target = TestAction.TEST;
+            }
+        });
+        actionOnTestOrThreadPanel.add(targetBox);
 
-        // Duration
-        HorizontalPanel durationPanel = new HorizontalPanel();
-        durationField = new JTextField(15);
-        durationField.setText(""); // $NON-NLS-1$
-        durationPanel.add(new JLabel(durationLabel));
-        durationPanel.add(durationField);
-        add(durationPanel);
+        add(actionOnThreadPanel);
+        add(actionOnTestOrThreadPanel);
     }
 
 }

Modified: jmeter/trunk/src/core/org/apache/jmeter/gui/OnErrorPanel.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/gui/OnErrorPanel.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/gui/OnErrorPanel.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/gui/OnErrorPanel.java Sat Mar 31 10:32:33 2018
@@ -19,6 +19,7 @@
 package org.apache.jmeter.gui;
 
 import java.awt.BorderLayout;
+import java.awt.GridLayout;
 
 import javax.swing.BorderFactory;
 import javax.swing.ButtonGroup;
@@ -34,44 +35,44 @@ public class OnErrorPanel extends JPanel
     // Sampler error action buttons
     private JRadioButton continueBox;
 
+    private JRadioButton breakLoopBox;
+
     private JRadioButton startNextThreadLoopBox;
 
+    private JRadioButton startNextIterationOfCurrentLoopBox;
+
     private JRadioButton stopThrdBox;
 
     private JRadioButton stopTestBox;
 
     private JRadioButton stopTestNowBox;
 
+
     private JPanel createOnErrorPanel() {
-        JPanel panel = new JPanel();
+        JPanel panel = new JPanel(new GridLayout(4, 2));
         panel.setBorder(BorderFactory.createTitledBorder(JMeterUtils.getResString("sampler_on_error_action"))); //$NON-NLS-1$
 
         ButtonGroup group = new ButtonGroup();
 
-        continueBox = new JRadioButton(JMeterUtils.getResString("sampler_on_error_continue")); //$NON-NLS-1$
-        group.add(continueBox);
+        continueBox = addRadioButton("sampler_on_error_continue", group, panel); //$NON-NLS-1$
+        breakLoopBox = addRadioButton("sampler_on_error_break_loop", group, panel); //$NON-NLS-1$
+        startNextThreadLoopBox = addRadioButton("sampler_on_error_start_next_loop", group, panel); //$NON-NLS-1$
+        startNextIterationOfCurrentLoopBox = addRadioButton("sampler_on_error_start_next_iteration_current_loop", group, panel); //$NON-NLS-1$
+        stopTestBox = addRadioButton("sampler_on_error_stop_test", group, panel); //$NON-NLS-1$
+        stopTestNowBox = addRadioButton("sampler_on_error_stop_test_now", group, panel); //$NON-NLS-1$
+        stopThrdBox = addRadioButton("sampler_on_error_stop_thread", group, panel); //$NON-NLS-1$
+        
         continueBox.setSelected(true);
-        panel.add(continueBox);
-
-        startNextThreadLoopBox = new JRadioButton(JMeterUtils.getResString("sampler_on_error_start_next_loop")); //$NON-NLS-1$
-        group.add(startNextThreadLoopBox);
-        panel.add(startNextThreadLoopBox);
-
-        stopThrdBox = new JRadioButton(JMeterUtils.getResString("sampler_on_error_stop_thread")); //$NON-NLS-1$
-        group.add(stopThrdBox);
-        panel.add(stopThrdBox);
-
-        stopTestBox = new JRadioButton(JMeterUtils.getResString("sampler_on_error_stop_test")); //$NON-NLS-1$
-        group.add(stopTestBox);
-        panel.add(stopTestBox);
-
-        stopTestNowBox = new JRadioButton(JMeterUtils.getResString("sampler_on_error_stop_test_now")); //$NON-NLS-1$
-        group.add(stopTestNowBox);
-        panel.add(stopTestNowBox);
-
         return panel;
     }
 
+    private JRadioButton addRadioButton(String labelKey, ButtonGroup group, JPanel panel) {
+        JRadioButton radioButton = new JRadioButton(JMeterUtils.getResString(labelKey)); 
+        group.add(radioButton);
+        panel.add(radioButton);
+        return radioButton;
+    }
+
     /**
      * Create a new NamePanel with the default name.
      */
@@ -90,9 +91,11 @@ public class OnErrorPanel extends JPanel
     public void configure(int errorAction) {
         stopTestNowBox.setSelected(errorAction == OnErrorTestElement.ON_ERROR_STOPTEST_NOW);
         startNextThreadLoopBox.setSelected(errorAction == OnErrorTestElement.ON_ERROR_START_NEXT_THREAD_LOOP);
+        startNextIterationOfCurrentLoopBox.setSelected(errorAction == OnErrorTestElement.ON_ERROR_START_NEXT_ITERATION_OF_CURRENT_LOOP);
         stopTestBox.setSelected(errorAction == OnErrorTestElement.ON_ERROR_STOPTEST);
         stopThrdBox.setSelected(errorAction == OnErrorTestElement.ON_ERROR_STOPTHREAD);
         continueBox.setSelected(errorAction == OnErrorTestElement.ON_ERROR_CONTINUE);
+        breakLoopBox.setSelected(errorAction == OnErrorTestElement.ON_ERROR_BREAK_CURRENT_LOOP);
     }
 
     public int getOnErrorSetting() {
@@ -108,6 +111,12 @@ public class OnErrorPanel extends JPanel
         if (startNextThreadLoopBox.isSelected()) {
             return OnErrorTestElement.ON_ERROR_START_NEXT_THREAD_LOOP;
         }
+        if (startNextIterationOfCurrentLoopBox.isSelected()) {
+            return OnErrorTestElement.ON_ERROR_START_NEXT_ITERATION_OF_CURRENT_LOOP;
+        }
+        if(breakLoopBox.isSelected()) {
+            return OnErrorTestElement.ON_ERROR_BREAK_CURRENT_LOOP;
+        }
 
         // Defaults to continue
         return OnErrorTestElement.ON_ERROR_CONTINUE;

Modified: jmeter/trunk/src/core/org/apache/jmeter/reporters/ResultAction.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/reporters/ResultAction.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/reporters/ResultAction.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/reporters/ResultAction.java Sat Mar 31 10:32:33 2018
@@ -24,6 +24,7 @@ import org.apache.jmeter.samplers.Sample
 import org.apache.jmeter.samplers.SampleListener;
 import org.apache.jmeter.samplers.SampleResult;
 import org.apache.jmeter.testelement.OnErrorTestElement;
+import org.apache.jmeter.threads.JMeterContext.TestLogicalAction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,7 +34,7 @@ import org.slf4j.LoggerFactory;
  */
 public class ResultAction extends OnErrorTestElement implements Serializable, SampleListener {
 
-    private static final long serialVersionUID = 241L;
+    private static final long serialVersionUID = 242L;
 
     private static final Logger log = LoggerFactory.getLogger(ResultAction.class);
 
@@ -61,15 +62,16 @@ public class ResultAction extends OnErro
         if (!s.isSuccessful()) {
             if (isStopTestNow()) {
                 s.setStopTestNow(true);
-            }
-            if (isStopTest()) {
+            } else if (isStopTest()) {
                 s.setStopTest(true);
-            }
-            if (isStopThread()) {
+            } else if (isStopThread()) {
                 s.setStopThread(true);
-            }
-            if (isStartNextThreadLoop()) {
-               s.setStartNextThreadLoop(true);
+            } else if (isStartNextThreadLoop()) {
+                s.setTestLogicalAction(TestLogicalAction.START_NEXT_ITERATION_OF_THREAD);
+            } else if(isStartNextIterationOfCurrentLoop()) {
+                s.setTestLogicalAction(TestLogicalAction.START_NEXT_ITERATION_OF_CURRENT_LOOP);
+            } else if(isBreakCurrentLoop()) {
+                s.setTestLogicalAction(TestLogicalAction.BREAK_CURRENT_LOOP);
             }
         }
     }

Modified: jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages.properties Sat Mar 31 10:32:33 2018
@@ -975,7 +975,9 @@ sample_scope_parent=Main sample only
 sample_scope_variable=JMeter Variable Name to use
 sampler_label=Label
 sampler_on_error_action=Action to be taken after a Sampler error
+sampler_on_error_break_loop=Break Current Loop
 sampler_on_error_continue=Continue
+sampler_on_error_start_next_iteration_current_loop=Start Next Iteration of Current Loop
 sampler_on_error_start_next_loop=Start Next Thread Loop
 sampler_on_error_stop_test=Stop Test
 sampler_on_error_stop_test_now=Stop Test Now
@@ -1204,13 +1206,16 @@ template_merge_from=Merge
 template_reload=Reload templates
 template_title=Templates
 test=Test
-test_action_action=Action
-test_action_duration=Duration (milliseconds)
+test_action_action_thread=Logical Action on Thread
+test_action_action_test_thread=Logical Action on Thread/Test
+test_action_break_current_loop=Break Current Loop
+test_action_continue_current_loop=Switch to next iteration of Current Loop
+test_action_duration=Duration (milliseconds)\:
 test_action_pause=Pause
 test_action_restart_next_loop=Start Next Thread Loop
 test_action_stop=Stop
 test_action_stop_now=Stop Now
-test_action_target=Target
+test_action_target=Target\:
 test_action_target_test=All Threads
 test_action_target_thread=Current Thread
 test_action_title=Test Action

Modified: jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/resources/messages_fr.properties Sat Mar 31 10:32:33 2018
@@ -967,8 +967,10 @@ sample_timeout_timeout=D\u00E9lai d'atte
 sample_timeout_title=Compteur Interruption
 sampler_label=Libell\u00E9
 sampler_on_error_action=Action \u00E0 suivre apr\u00E8s une erreur d'\u00E9chantillon
+sampler_on_error_break_loop=Quitter la boucle courante
 sampler_on_error_continue=Continuer
-sampler_on_error_start_next_loop=D\u00E9marrer it\u00E9ration suivante
+sampler_on_error_start_next_iteration_current_loop=D\u00E9marrer it\u00E9ration suivante de la boucle courante
+sampler_on_error_start_next_loop=D\u00E9marrer it\u00E9ration suivante du Thread
 sampler_on_error_stop_test=Arr\u00EAter le test
 sampler_on_error_stop_test_now=Arr\u00EAter le test imm\u00E9diatement
 sampler_on_error_stop_thread=Arr\u00EAter l'unit\u00E9
@@ -1194,9 +1196,11 @@ template_reload=Recharger les mod\u00E8l
 template_title=Mod\u00E8les
 test=Test
 test_action_action=Action \:
+test_action_break_current_loop=Quitter la boucle courante
+test_action_continue_current_loop=Passer \u00e0 It\u00E9ration suivante de la Boucle courante
 test_action_duration=Dur\u00E9e (millisecondes) \:
 test_action_pause=Mettre en pause
-test_action_restart_next_loop=D\u00E9marrer it\u00E9ration suivante
+test_action_restart_next_loop=D\u00E9marrer it\u00E9ration suivante du Thread
 test_action_stop=Arr\u00EAter
 test_action_stop_now=Arr\u00EAter imm\u00E9diatement
 test_action_target=Cible \:

Modified: jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/samplers/SampleResult.java Sat Mar 31 10:32:33 2018
@@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.jmeter.assertions.AssertionResult;
 import org.apache.jmeter.gui.Searchable;
+import org.apache.jmeter.threads.JMeterContext.TestLogicalAction;
 import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jorphan.util.JOrphanUtils;
 import org.slf4j.Logger;
@@ -228,9 +229,9 @@ public class SampleResult implements Ser
     /** time to end connecting */
     private long connectTime = 0;
 
-    /** Should thread start next iteration ? */
-    private boolean startNextThreadLoop = false;
-
+    /** Way to signal what to do on Test */
+    private TestLogicalAction testLogicalAction = TestLogicalAction.CONTINUE;
+    
     /** Should thread terminate? */
     private boolean stopThread = false;
 
@@ -331,7 +332,7 @@ public class SampleResult implements Ser
         stopTest = res.stopTest;
         stopTestNow = res.stopTestNow;
         stopThread = res.stopThread;
-        startNextThreadLoop = res.startNextThreadLoop;
+        testLogicalAction = res.testLogicalAction;
         subResults = res.subResults; 
         success = res.success;//OK
         threadName = res.threadName;//OK
@@ -1466,16 +1467,24 @@ public class SampleResult implements Ser
 
     /**
      * @return the startNextThreadLoop
+     * @deprecated use {@link SampleResult#getTestLogicalAction()}
      */
+    @Deprecated
     public boolean isStartNextThreadLoop() {
-        return startNextThreadLoop;
+        return testLogicalAction == TestLogicalAction.START_NEXT_ITERATION_OF_THREAD;
     }
 
     /**
+     * @deprecated use {@link SampleResult#setTestLogicalAction(TestLogicalAction)}
      * @param startNextThreadLoop the startNextLoop to set
      */
+    @Deprecated
     public void setStartNextThreadLoop(boolean startNextThreadLoop) {
-        this.startNextThreadLoop = startNextThreadLoop;
+        if(startNextThreadLoop) {
+            testLogicalAction = TestLogicalAction.START_NEXT_ITERATION_OF_THREAD;
+        } else {
+            testLogicalAction = TestLogicalAction.CONTINUE;
+        }
     }
 
     /**
@@ -1536,4 +1545,18 @@ public class SampleResult implements Ser
         }
         return message;
     }
+
+    /**
+     * @return the testLogicalAction
+     */
+    public TestLogicalAction getTestLogicalAction() {
+        return testLogicalAction;
+    }
+
+    /**
+     * @param testLogicalAction the testLogicalAction to set
+     */
+    public void setTestLogicalAction(TestLogicalAction testLogicalAction) {
+        this.testLogicalAction = testLogicalAction;
+    }
 }

Modified: jmeter/trunk/src/core/org/apache/jmeter/testelement/OnErrorTestElement.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/testelement/OnErrorTestElement.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/testelement/OnErrorTestElement.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/testelement/OnErrorTestElement.java Sat Mar 31 10:32:33 2018
@@ -39,6 +39,10 @@ public abstract class OnErrorTestElement
     public static final int ON_ERROR_STOPTEST_NOW = 3;
 
     public static final int ON_ERROR_START_NEXT_THREAD_LOOP = 4;
+    
+    public static final int ON_ERROR_START_NEXT_ITERATION_OF_CURRENT_LOOP = 5;
+    
+    public static final int ON_ERROR_BREAK_CURRENT_LOOP = 6;
 
     /* Property name */
     public static final String ON_ERROR_ACTION = "OnError.action";
@@ -70,8 +74,16 @@ public abstract class OnErrorTestElement
     public boolean isStopTestNow() {
         return getErrorAction() == ON_ERROR_STOPTEST_NOW;
     }
-    
+
     public boolean isStartNextThreadLoop() {
         return getErrorAction() == ON_ERROR_START_NEXT_THREAD_LOOP;
     }
+
+    public boolean isStartNextIterationOfCurrentLoop() {
+        return getErrorAction() == ON_ERROR_START_NEXT_ITERATION_OF_CURRENT_LOOP;
+    }
+    
+    public boolean isBreakCurrentLoop() {
+        return getErrorAction() == ON_ERROR_BREAK_CURRENT_LOOP;
+    }
 }

Modified: jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java Sat Mar 31 10:32:33 2018
@@ -295,4 +295,8 @@ public abstract class AbstractThreadGrou
      * This gracefully stops threads of Group
      */
     public abstract void stop();
+
+    public void breakThreadLoop() {
+        ((LoopController) getSamplerController()).breakLoop();
+    }
 }

Modified: jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterContext.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterContext.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterContext.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterContext.java Sat Mar 31 10:32:33 2018
@@ -34,6 +34,13 @@ import org.apache.jmeter.util.JMeterUtil
  * The class is not thread-safe - it is only intended for use within a single thread.
  */
 public class JMeterContext {
+    
+    public enum TestLogicalAction {
+        CONTINUE,
+        START_NEXT_ITERATION_OF_THREAD,
+        START_NEXT_ITERATION_OF_CURRENT_LOOP,
+        BREAK_CURRENT_LOOP
+    }
 
     private JMeterVariables variables;
     private SampleResult previousResult;
@@ -44,7 +51,7 @@ public class JMeterContext {
     private JMeterThread thread;
     private AbstractThreadGroup threadGroup;
     private int threadNum;
-    private boolean restartNextLoop = false;
+    private TestLogicalAction testLogicalAction = TestLogicalAction.CONTINUE;
     private ConcurrentHashMap<String, Object> samplerContext = new ConcurrentHashMap<>(5);
     private boolean recording;
 
@@ -199,19 +206,43 @@ public class JMeterContext {
     public void setSamplingStarted(boolean b) {
         samplingStarted = b;
     }
+    
 
     /**
+     * @param startNextIterationOfCurrentLoop start next iteration of current loop in which this component is present
+     */
+    public void setTestLogicalAction(TestLogicalAction actionOnExecution) {
+        this.testLogicalAction = actionOnExecution;
+    }
+
+    /**
+     * @return TestLogicalAction to start next iteration of current loop in which this component is present
+     */
+    public TestLogicalAction getTestLogicalAction() {
+        return testLogicalAction;
+    }
+    
+    /**
+     * @deprecated
      * @param restartNextLoop if set to <code>true</code> a restart of the loop will occur
+     * @deprecated use {@link JMeterContext#setTestLogicalAction(TestLogicalAction)}
      */
+    @Deprecated
     public void setStartNextThreadLoop(boolean restartNextLoop) {
-        this.restartNextLoop = restartNextLoop;
+        if(restartNextLoop) {
+            this.testLogicalAction = TestLogicalAction.START_NEXT_ITERATION_OF_THREAD;
+        } else {
+            this.testLogicalAction = TestLogicalAction.CONTINUE;
+        }
     }
     
     /**
-     * @return {@code true} when current loop iteration will be interrupted and JMeter will go to next iteration
+     * @return {@code true} when current loop iteration of Thread Group will be interrupted and JMeter will go to next iteration of the Thread Group loop
+     * @deprecated use {@link JMeterContext#isTestLogicalAction()}
      */
+    @Deprecated
     public boolean isStartNextThreadLoop() {
-        return restartNextLoop;
+        return this.testLogicalAction == TestLogicalAction.START_NEXT_ITERATION_OF_THREAD;
     }
     
     /**

Modified: jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/threads/JMeterThread.java Sat Mar 31 10:32:33 2018
@@ -23,10 +23,12 @@ import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
 
 import org.apache.jmeter.assertions.Assertion;
 import org.apache.jmeter.assertions.AssertionResult;
 import org.apache.jmeter.control.Controller;
+import org.apache.jmeter.control.IteratingController;
 import org.apache.jmeter.control.TransactionSampler;
 import org.apache.jmeter.engine.StandardJMeterEngine;
 import org.apache.jmeter.engine.event.LoopIterationEvent;
@@ -46,6 +48,7 @@ import org.apache.jmeter.testelement.Abs
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.testelement.TestIterationListener;
 import org.apache.jmeter.testelement.ThreadListener;
+import org.apache.jmeter.threads.JMeterContext.TestLogicalAction;
 import org.apache.jmeter.timers.Timer;
 import org.apache.jmeter.timers.TimerService;
 import org.apache.jmeter.util.JMeterUtils;
@@ -141,7 +144,7 @@ public class JMeterThread implements Run
 
     private volatile boolean onErrorStartNextLoop;
 
-    private volatile Sampler currentSampler;
+    private volatile Sampler currentSamplerForInterruption;
 
     private final ReentrantLock interruptLock = new ReentrantLock(); // ensure that interrupt cannot overlap with shutdown
 
@@ -253,23 +256,36 @@ public class JMeterThread implements Run
                     // restart of the next loop 
                     // - was requested through threadContext
                     // - or the last sample failed AND the onErrorStartNextLoop option is enabled
-                    if (threadContext.isStartNextThreadLoop()
+                    if (threadContext.getTestLogicalAction() != TestLogicalAction.CONTINUE
                             || (onErrorStartNextLoop
                             && !TRUE.equals(threadContext.getVariables().get(LAST_SAMPLE_OK)))) {
-                        if (log.isDebugEnabled() && onErrorStartNextLoop && !threadContext.isStartNextThreadLoop()) {
-                            log.debug("StartNextLoop option is on, Last sample failed, starting next loop");
+                        if (log.isDebugEnabled() && onErrorStartNextLoop
+                                && threadContext.getTestLogicalAction() != TestLogicalAction.CONTINUE) {
+                            log.debug("Start Next Thread Loop option is on, Last sample failed, starting next thread loop");
                         }
-
-                        triggerEndOfLoopOnParentControllers(sam, threadContext);
+                        switch (threadContext.getTestLogicalAction()) {
+                            case BREAK_CURRENT_LOOP:
+                                triggerLoopLogicalActionOnParentControllers(sam, threadContext, JMeterThread::breakOnCurrentLoop);
+                                break;
+                            case START_NEXT_ITERATION_OF_THREAD:
+                                triggerLoopLogicalActionOnParentControllers(sam, threadContext, JMeterThread::continueOnThreadLoop);
+                                break;
+                            case START_NEXT_ITERATION_OF_CURRENT_LOOP:
+                                triggerLoopLogicalActionOnParentControllers(sam, threadContext, JMeterThread::continueOnCurrentLoop);
+                                break;
+                            default:
+                                break;
+                        }
+                        threadContext.setTestLogicalAction(TestLogicalAction.CONTINUE);
                         sam = null;
                         threadContext.getVariables().put(LAST_SAMPLE_OK, TRUE);
-                        threadContext.setStartNextThreadLoop(false);
                     }
                     else {
                         sam = threadGroupLoopController.next();
                     }
                 }
-                
+
+                // It would be possible to add finally for Thread Loop here
                 if (threadGroupLoopController.isDone()) {
                     running = false;
                     log.info("Thread is done: {}", threadName);
@@ -297,7 +313,7 @@ public class JMeterThread implements Run
         } catch (ThreadDeath e) {
             throw e; // Must not ignore this one
         } finally {
-            currentSampler = null; // prevent any further interrupts
+            currentSamplerForInterruption = null; // prevent any further interrupts
             try {
                 interruptLock.lock();  // make sure current interrupt is finished, prevent another starting yet
                 threadContext.clear();
@@ -312,44 +328,94 @@ public class JMeterThread implements Run
     }
 
     /**
-     * Trigger end of loop on parent controllers up to Thread Group
-     * @param sam Sampler Base sampler
+     * Trigger break/continue/switch to next thread Loop  depending on consumer implementation
+     * @param sampler Sampler Base sampler
      * @param threadContext 
+     * @param consumer Consumer that will process the tree of elements up to root node 
      */
-    private void triggerEndOfLoopOnParentControllers(Sampler sam, JMeterContext threadContext) {
+    private void triggerLoopLogicalActionOnParentControllers(Sampler sampler, JMeterContext threadContext, 
+            Consumer<FindTestElementsUpToRootTraverser> consumer) {
         TransactionSampler transactionSampler = null;
-        if (sam instanceof TransactionSampler) {
-            transactionSampler = (TransactionSampler) sam;
+        if (sampler instanceof TransactionSampler) {
+            transactionSampler = (TransactionSampler) sampler;
         }
 
-        Sampler realSampler = findRealSampler(sam);
+        Sampler realSampler = findRealSampler(sampler);
         if (realSampler == null) {
             throw new IllegalStateException(
                     "Got null subSampler calling findRealSampler for:" +
-                    (sam != null ? sam.getName() : "null") + ", sam:" + sam);
+                    (sampler != null ? sampler.getName() : "null") + ", sampler:" + sampler);
         }
         // Find parent controllers of current sampler
         FindTestElementsUpToRootTraverser pathToRootTraverser = new FindTestElementsUpToRootTraverser(realSampler);
         testTree.traverse(pathToRootTraverser);
 
-        // Trigger end of loop condition on all parent controllers of current sampler
+        consumer.accept(pathToRootTraverser);
+
+        // bug 52968
+        // When using Start Next Loop option combined to TransactionController.
+        // if an error occurs in a Sample (child of TransactionController) 
+        // then we still need to report the Transaction in error (and create the sample result)
+        if (transactionSampler != null) {
+            SamplePackage transactionPack = compiler.configureTransactionSampler(transactionSampler);
+            doEndTransactionSampler(transactionSampler, null, transactionPack, threadContext);
+        }
+    }
+
+    /**
+     * Executes a continue of current loop, equivalent of "continue" in algorithm.
+     * As a consequence it ends the first loop it finds on the path to root 
+     * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
+     */
+    private static void continueOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) {
         List<Controller> controllersToReinit = pathToRootTraverser.getControllersToRoot();
         for (Controller parentController : controllersToReinit) {
             if (parentController instanceof AbstractThreadGroup) {
                 AbstractThreadGroup tg = (AbstractThreadGroup) parentController;
                 tg.startNextLoop();
+            } else if (parentController instanceof IteratingController) {
+                ((IteratingController) parentController).startNextLoop();
+                break;
             } else {
                 parentController.triggerEndOfLoop();
             }
         }
+    }
+    
+    /**
+     * Executes a break of current loop, equivalent of "break" in algorithm.
+     * As a consequence it ends the first loop it finds on the path to root 
+     * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
+     */
+    private static void breakOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) {
+        List<Controller> controllersToReinit = pathToRootTraverser.getControllersToRoot();
+        for (Controller parentController : controllersToReinit) {
+            if (parentController instanceof AbstractThreadGroup) {
+                AbstractThreadGroup tg = (AbstractThreadGroup) parentController;
+                tg.breakThreadLoop();
+            } else if (parentController instanceof IteratingController) {
+                ((IteratingController) parentController).breakLoop();
+                break;
+            } else {
+                parentController.triggerEndOfLoop();
+            }
+        }
+    }
 
-        // bug 52968
-        // When using Start Next Loop option combined to TransactionController.
-        // if an error occurs in a Sample (child of TransactionController) 
-        // then we still need to report the Transaction in error (and create the sample result)
-        if (transactionSampler != null) {
-            SamplePackage transactionPack = compiler.configureTransactionSampler(transactionSampler);
-            doEndTransactionSampler(transactionSampler, null, transactionPack, threadContext);
+    /**
+     * Executes a restart of Thread loop, equivalent of "continue" in algorithm but on Thread Loop.
+     * As a consequence it ends all loop on the path to root 
+     * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
+     */
+    private static void continueOnThreadLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) {
+        List<Controller> controllersToReinit = pathToRootTraverser.getControllersToRoot();
+        for (Controller parentController : controllersToReinit) {
+            if (parentController instanceof AbstractThreadGroup) {
+                AbstractThreadGroup tg = (AbstractThreadGroup) parentController;
+                tg.startNextLoop();
+            } else {
+                parentController.triggerEndOfLoop();
+            }
         }
     }
 
@@ -452,7 +518,7 @@ public class JMeterThread implements Run
         return transactionResult;
     }
 
-    /*
+    /**
      * Execute the sampler with its pre/post processors, timers, assertions
      * Broadcast the result to the sample listeners
      */
@@ -473,29 +539,7 @@ public class JMeterThread implements Run
         SampleResult result = null;
         if (running) {
             Sampler sampler = pack.getSampler();
-            sampler.setThreadContext(threadContext);
-            // TODO should this set the thread names for all the subsamples?
-            // might be more efficient than fetching the name elsewhere
-            sampler.setThreadName(threadName);
-            TestBeanHelper.prepare(sampler);
-
-            // Perform the actual sample
-            currentSampler = sampler;
-            if (!sampleMonitors.isEmpty()) {
-                for (SampleMonitor sampleMonitor : sampleMonitors) {
-                    sampleMonitor.sampleStarting(sampler);
-                }
-            }
-            try {
-                result = sampler.sample(null);
-            } finally {
-                if (!sampleMonitors.isEmpty()) {
-                    for (SampleMonitor sampleMonitor : sampleMonitors) {
-                        sampleMonitor.sampleEnded(sampler);
-                    }
-                }
-            }
-            currentSampler = null;
+            result = doSampling(threadContext, sampler);
         }
         // If we got any results, then perform processing on the result
         if (result != null && !result.isIgnore()) {
@@ -536,14 +580,49 @@ public class JMeterThread implements Run
             if (result.isStopTestNow() || (!result.isSuccessful() && onErrorStopTestNow)) {
                 stopTestNow();
             }
-            if(result.isStartNextThreadLoop()) {
-                threadContext.setStartNextThreadLoop(true);
-            }
+            threadContext.setTestLogicalAction(result.getTestLogicalAction());
         } else {
             compiler.done(pack); // Finish up
         }
     }
 
+    /**
+     * Call sample on Sampler handling:
+     * <ul>
+     *  <li>setting up ThreadContext</li>
+     *  <li>initializing sampler if needed</li>
+     *  <li>positionning currentSamplerForInterruption for potential interruption</li>
+     *  <li>Playing SampleMonitor before and after sampling</li>
+     *  <li>resetting currentSamplerForInterruption</li>
+     * </ul>
+     * @param threadContext {@link JMeterContext}
+     * @param sampler {@link Sampler}
+     * @return {@link SampleResult}
+     */
+    private SampleResult doSampling(JMeterContext threadContext, Sampler sampler) {
+        sampler.setThreadContext(threadContext);
+        sampler.setThreadName(threadName);
+        TestBeanHelper.prepare(sampler);
+
+        // Perform the actual sample
+        currentSamplerForInterruption = sampler;
+        if (!sampleMonitors.isEmpty()) {
+            for (SampleMonitor sampleMonitor : sampleMonitors) {
+                sampleMonitor.sampleStarting(sampler);
+            }
+        }
+        try {
+            return sampler.sample(null);
+        } finally {
+            if (!sampleMonitors.isEmpty()) {
+                for (SampleMonitor sampleMonitor : sampleMonitors) {
+                    sampleMonitor.sampleEnded(sampler);
+                }
+            }
+            currentSamplerForInterruption = null;
+        }
+    }
+
     private SampleResult doEndTransactionSampler(
             TransactionSampler transactionSampler, Sampler parent,
             SamplePackage transactionPack, JMeterContext threadContext) {
@@ -716,7 +795,7 @@ public class JMeterThread implements Run
     public boolean interrupt(){
         try {
             interruptLock.lock();
-            Sampler samp = currentSampler; // fetch once; must be done under lock
+            Sampler samp = currentSamplerForInterruption; // fetch once; must be done under lock
             if (samp instanceof Interruptible){ // (also protects against null)
                 if (log.isWarnEnabled()) {
                     log.warn("Interrupting: {} sampler: {}", threadName, samp.getName());
@@ -738,7 +817,7 @@ public class JMeterThread implements Run
                 }
             }
         } finally {
-            interruptLock.unlock();            
+            interruptLock.unlock();
         }
         return false;
     }

Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1828095&r1=1828094&r2=1828095&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Sat Mar 31 10:32:33 2018
@@ -127,6 +127,7 @@ this behaviour, set <code>httpclient.res
   <li><bug>62155</bug>Search Feature: Make Search text field get focus</li>
   <li><bug>62156</bug>Search Feature : Distinguish between node that matches search and node that contains a child that matches search</li>
   <li><bug>62234</bug>Search/Replace Feature : Enhance UX and add Replace/Next/Previous/Replace &amp; Find features. Contributed by Ubik Load Pack (support at ubikloadpack.com)</li>
+  <li><bug>62238</bug>Add ability to Switch to next iteration of Current Loop. Contributed by Ubik Load Pack (support at ubikloadpack.com)</li>
   <li><bug>62065</bug>Use Maven artifact for JAF Module instead of embedded module</li>
   <li><pr>379</pr> Improve chinese translations. Contributed by XmeterNet</li>
 </ul>