From f217398c959326bd28c8c733889238b39d0575da Mon Sep 17 00:00:00 2001 From: pmouawad Date: Tue, 18 Jun 2019 23:04:33 +0200 Subject: [PATCH 01/16] This fixes BUG 62861 Thread Group: Provide ability to configure whether a new iteration is a new user or same user https://bz.apache.org/bugzilla/show_bug.cgi?id=62861 Contribution by UbikLoadPack Team https://ubikloadpack.com @ubikloadpack --- .../jmeter/resources/messages.properties | 4 + .../jmeter/resources/messages_fr.properties | 4 + .../apache/jmeter/threads/JMeterThread.java | 12 +- .../jmeter/threads/JMeterVariables.java | 6 + .../apache/jmeter/threads/ThreadGroup.java | 45 +- .../jmeter/threads/gui/ThreadGroupGui.java | 32 +- .../protocol/http/control/CacheManager.java | 14 +- .../protocol/http/control/CookieManager.java | 14 +- .../protocol/http/gui/CacheManagerGui.java | 24 +- .../jmeter/protocol/http/gui/CookiePanel.java | 14 + .../protocol/http/sampler/HTTPHC4Impl.java | 12 +- .../control/TestTransactionController.java | 2 +- .../TestCacheManagerThreadIteration.java | 419 ++++++++++++++++++ .../TestCookieManagerThreadIteration.java | 175 ++++++++ .../http/sampler/TestHTTPHC4Impl.java | 61 +++ .../apache/jmeter/timers/SyncTimerTest.java | 2 +- 16 files changed, 819 insertions(+), 21 deletions(-) create mode 100644 test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java create mode 100644 test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java create mode 100644 test/src/org/apache/jmeter/protocol/http/sampler/TestHTTPHC4Impl.java diff --git a/src/core/org/apache/jmeter/resources/messages.properties b/src/core/org/apache/jmeter/resources/messages.properties index c469ec4b1d1..6c46c19aec5 100644 --- a/src/core/org/apache/jmeter/resources/messages.properties +++ b/src/core/org/apache/jmeter/resources/messages.properties @@ -175,6 +175,7 @@ busy_testing=I'm busy testing, please stop the test before changing settings cache_manager_size=Max Number of elements in cache cache_manager_title=HTTP Cache Manager cache_session_id=Cache Session Id? +cache_clear_controlled_by_threadgroup=Use Thread Group configuration to control cache clearing cancel=Cancel cancel_exit_to_save=There are test items that have not been saved. Do you wish to save before exiting? cancel_new_from_template=There are test items that have not been saved. Do you wish to save before creating a test plan from selected template? @@ -229,6 +230,7 @@ cookie_manager_policy=Cookie Policy: cookie_manager_title=HTTP Cookie Manager cookie_options=Options cookies_stored=User-Defined Cookies +cookie_clear_controlled_by_threadgroup=Use Thread Group configuration to control cookie clearing copy=Copy counter_config_title=Counter counter_per_user=Track counter independently for each user @@ -1280,6 +1282,8 @@ thread_group_title=Thread Group thread_group_scheduler_warning=If Loop Count is not -1 or Forever, duration will be min(Duration, Loop Count * iteration duration) thread_properties=Thread Properties threadgroup=Thread Group +threadgroup_same_user=Same user on each iteration +threadgroup_different_user=Different User on Each Iteration throughput_control_bynumber_label=Total Executions throughput_control_bypercent_label=Percent Executions throughput_control_perthread_label=Per User diff --git a/src/core/org/apache/jmeter/resources/messages_fr.properties b/src/core/org/apache/jmeter/resources/messages_fr.properties index 6d6e485319d..2a1c090357e 100644 --- a/src/core/org/apache/jmeter/resources/messages_fr.properties +++ b/src/core/org/apache/jmeter/resources/messages_fr.properties @@ -170,6 +170,7 @@ busy_testing=Je suis occupé à tester, veuillez arrêter le test avant de chang cache_manager_size=Nombre maximum d'éléments dans le cache cache_manager_title=Gestionnaire de cache HTTP cache_session_id=Identifiant de session de cache ? +cache_clear_controlled_by_threadgroup=Utiliser thread group pour contrôler l'effacement de cache cancel=Annuler cancel_exit_to_save=Il y a des éléments qui n'ont pas été sauvés. Voulez-vous enregistrer avant de sortir ? cancel_new_from_template=Il y a des éléments qui n'ont pas été sauvés. Voulez-vous enregistrer avant de charger le modèle ? @@ -224,6 +225,7 @@ cookie_manager_policy=Politique des cookies \: cookie_manager_title=Gestionnaire de cookies HTTP cookie_options=Options cookies_stored=Cookies stockés +cookie_clear_controlled_by_threadgroup=Utiliser thread group pour contrôler l'effacement des cookies copy=Copier counter_config_title=Compteur counter_per_user=Suivre le compteur indépendamment pour chaque unité de test @@ -1269,6 +1271,8 @@ thread_group_title=Groupe d'unités thread_group_scheduler_warning=Si le nombre de boucles n'est pas -1 ou Infini, la durée sera min (Durée, Nombre d'itérations * durée de l'itération). thread_properties=Propriétés du groupe d'unités threadgroup=Groupe d'unités +threadgroup_same_user=Même utilisateur à chaque itération +threadgroup_different_user=Utilisateur différent à chaque itération throughput_control_bynumber_label=Exécutions totales throughput_control_bypercent_label=Pourcentage d'exécution throughput_control_perthread_label=Par utilisateur diff --git a/src/core/org/apache/jmeter/threads/JMeterThread.java b/src/core/org/apache/jmeter/threads/JMeterThread.java index 1c6ab44000d..878d6e27213 100644 --- a/src/core/org/apache/jmeter/threads/JMeterThread.java +++ b/src/core/org/apache/jmeter/threads/JMeterThread.java @@ -95,7 +95,7 @@ public class JMeterThread implements Runnable, Interruptible { private final TestCompiler compiler; private final JMeterThreadMonitor monitor; - + private final JMeterVariables threadVars; // Note: this is only used to implement TestIterationListener#testIterationStart @@ -123,8 +123,10 @@ public class JMeterThread implements Runnable, Interruptible { private long endTime = 0; - private boolean scheduler = false; + private boolean isSameUser = false; + // based on this scheduler is enabled or disabled + private boolean scheduler = false; // Gives access to parent thread threadGroup private AbstractThreadGroup threadGroup; @@ -148,7 +150,7 @@ public class JMeterThread implements Runnable, Interruptible { private final ReentrantLock interruptLock = new ReentrantLock(); // ensure that interrupt cannot overlap with shutdown - public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note) { + public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note,Boolean isSameUser) { this.monitor = monitor; threadVars = new JMeterVariables(); testTree = test; @@ -162,6 +164,7 @@ public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier sampleMonitors = sampleMonitorSearcher.getSearchResults(); notifier = note; running = true; + this.isSameUser = isSameUser; } public void setInitialContext(JMeterContext context) { @@ -237,13 +240,11 @@ private void startScheduler() { public void setThreadName(String threadName) { this.threadName = threadName; } - @Override public void run() { // threadContext is not thread-safe, so keep within thread JMeterContext threadContext = JMeterContextService.getContext(); LoopIterationListener iterationListener = null; - try { iterationListener = initRun(threadContext); while (running) { @@ -687,6 +688,7 @@ private List getSampleListeners(SamplePackage samplePack, Sample * @return the iteration listener */ private IterationListener initRun(JMeterContext threadContext) { + threadVars.putObject(JMeterVariables.VAR_IS_SAME_USER_KEY, isSameUser); threadContext.setVariables(threadVars); threadContext.setThreadNum(getThreadNum()); threadContext.getVariables().put(LAST_SAMPLE_OK, TRUE); diff --git a/src/core/org/apache/jmeter/threads/JMeterVariables.java b/src/core/org/apache/jmeter/threads/JMeterVariables.java index 46ac14bcffd..3789fdc4762 100644 --- a/src/core/org/apache/jmeter/threads/JMeterVariables.java +++ b/src/core/org/apache/jmeter/threads/JMeterVariables.java @@ -44,6 +44,8 @@ public class JMeterVariables { "TESTSTART.MS", // $NON-NLS-1$ }; + static final String VAR_IS_SAME_USER_KEY = "__jmv_SAME_USER"; + /** * Constructor, that preloads the variables from the JMeter properties */ @@ -171,4 +173,8 @@ public Iterator> getIterator(){ public Set> entrySet(){ return Collections.unmodifiableMap(variables).entrySet(); } + + public boolean isSameUser() { + return Boolean.TRUE.equals(variables.get(VAR_IS_SAME_USER_KEY)); + } } diff --git a/src/core/org/apache/jmeter/threads/ThreadGroup.java b/src/core/org/apache/jmeter/threads/ThreadGroup.java index 6d9f3e6c19f..26955bec8a4 100644 --- a/src/core/org/apache/jmeter/threads/ThreadGroup.java +++ b/src/core/org/apache/jmeter/threads/ThreadGroup.java @@ -69,7 +69,9 @@ public class ThreadGroup extends AbstractThreadGroup { /** Scheduler start delay, overrides start time */ public static final String DELAY = "ThreadGroup.delay"; - + + /** The same user or different users */ + public static final String IS_SAME_USER = "ThreadGroup.sameuser"; //- JMX entries private transient Thread threadStarter; @@ -175,6 +177,29 @@ public void setRampUp(int rampUp) { public int getRampUp() { return getPropertyAsInt(ThreadGroup.RAMP_TIME); } + /** + * Set the kind of user + * + * @param isSameUser + * true is the same user + * false is the different users + */ + public void setIsSameUser(boolean isSameUser) { + setProperty(new BooleanProperty(IS_SAME_USER, isSameUser)); + } + + /** + * Get kind of user: + *
    + *
  • true means same user running multiple iterations
  • + *
  • false means a different user for each iteration
  • + *
+ * @return the kind of user. + */ + public boolean isSameUser() { + return getPropertyAsBoolean(ThreadGroup.IS_SAME_USER); + } + private boolean isDelayedStartup() { return getPropertyAsBoolean(DELAYED_START); @@ -216,6 +241,7 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread this.threadGroupTree = threadGroupTree; int numThreads = getNumThreads(); int rampUpPeriodInSeconds = getRampUp(); + boolean isSameUser = isSameUser(); delayedStartup = isDelayedStartup(); // Fetch once; needs to stay constant log.info("Starting thread group... number={} threads={} ramp-up={} delayedStart={}", groupNumber, numThreads, rampUpPeriodInSeconds, delayedStartup); @@ -240,7 +266,7 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread } lastThreadStartInMillis = nowInMillis; startNewThread(notifier, threadGroupTree, engine, threadNum, context, nowInMillis, - Math.max(0, delayForNextThreadInMillis)); + Math.max(0, delayForNextThreadInMillis), isSameUser); } } log.info("Started thread group number {}", groupNumber); @@ -258,8 +284,8 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread * @return {@link JMeterThread} newly created */ private JMeterThread startNewThread(ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine, - int threadNum, final JMeterContext context, long now, int delay) { - JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context); + int threadNum, final JMeterContext context, long now, int delay,Boolean isSameUser) { + JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context,isSameUser); scheduleThread(jmThread, now); // set start and end time jmThread.setInitialDelay(delay); Thread newThread = new Thread(jmThread, jmThread.getThreadName()); @@ -297,13 +323,13 @@ private void registerStartedThread(JMeterThread jMeterThread, Thread newThread) private JMeterThread makeThread( ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine, int threadNumber, - JMeterContext context) { // N.B. Context needs to be fetched in the correct thread + JMeterContext context,Boolean isSameUser) { // N.B. Context needs to be fetched in the correct thread boolean onErrorStopTest = getOnErrorStopTest(); boolean onErrorStopTestNow = getOnErrorStopTestNow(); boolean onErrorStopThread = getOnErrorStopThread(); boolean onErrorStartNextLoop = getOnErrorStartNextLoop(); String groupName = getName(); - final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier); + final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier,isSameUser); jmeterThread.setThreadNum(threadNumber); jmeterThread.setThreadGroup(this); jmeterThread.setInitialContext(context); @@ -329,7 +355,7 @@ public JMeterThread addNewThread(int delay, StandardJMeterEngine engine) { numThreads = getNumThreads(); setNumThreads(numThreads + 1); } - newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay); + newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay, isSameUser()); JMeterContextService.addTotalThreads( 1 ); log.info("Started new thread in group {}", groupNumber); return newJmThread; @@ -575,7 +601,6 @@ private void delayBy(long delay) { } } } - @Override public void run() { try { @@ -597,6 +622,7 @@ public void run() { final int numThreads = getNumThreads(); final float rampUpOriginInMillis = (float) getRampUp() * 1000; final long startTimeInMillis = System.currentTimeMillis(); + final boolean isSameUser=isSameUser(); for (int threadNumber = 0; running && threadNumber < numThreads; threadNumber++) { if (threadNumber > 0) { long elapsedInMillis = System.currentTimeMillis() - startTimeInMillis; @@ -607,7 +633,7 @@ public void run() { if (usingScheduler && System.currentTimeMillis() > endtime) { break; // no point continuing beyond the end time } - JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNumber, context); + JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNumber, context, isSameUser); jmThread.setInitialDelay(0); // Already waited if (usingScheduler) { jmThread.setScheduled(true); @@ -621,6 +647,7 @@ public void run() { } catch (Exception ex) { log.error("An error occurred scheduling delay start of threads for Thread Group: {}", getName(), ex); } + } } } diff --git a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java index d42fc5bdeac..ce8acbf258f 100644 --- a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java +++ b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java @@ -23,15 +23,18 @@ import java.awt.event.ItemListener; import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JRadioButton; import javax.swing.JTextField; import javax.swing.SwingConstants; import org.apache.jmeter.control.LoopController; import org.apache.jmeter.control.gui.LoopControlPanel; +import org.apache.jmeter.gui.util.HorizontalPanel; import org.apache.jmeter.gui.util.VerticalPanel; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.property.BooleanProperty; @@ -62,6 +65,10 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen private JTextField delay; // Relative start-up time + private JRadioButton sameUserBox; + + private JRadioButton differentUserBox; + public ThreadGroupGui() { this(true); } @@ -100,6 +107,7 @@ public void modifyTestElement(TestElement tg) { tg.setProperty(new BooleanProperty(ThreadGroup.SCHEDULER, scheduler.isSelected())); tg.setProperty(ThreadGroup.DURATION, duration.getText()); tg.setProperty(ThreadGroup.DELAY, delay.getText()); + tg.setProperty(ThreadGroup.IS_SAME_USER,sameUserBox.isSelected()); } @Override @@ -117,6 +125,12 @@ public void configure(TestElement tg) { duration.setText(tg.getPropertyAsString(ThreadGroup.DURATION)); delay.setText(tg.getPropertyAsString(ThreadGroup.DELAY)); + final boolean isSameUser = tg.getPropertyAsBoolean(ThreadGroup.IS_SAME_USER, false); + if (isSameUser){ + sameUserBox.setSelected(true); + } else { + differentUserBox.setSelected(true); + } } @Override @@ -193,6 +207,8 @@ private void initGui(){ scheduler.setSelected(false); delay.setText(""); // $NON-NLS-1$ duration.setText(""); // $NON-NLS-1$ + sameUserBox.setSelected(true); + differentUserBox.setSelected(false); } private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final) @@ -228,7 +244,7 @@ private void init() { // WARNING: called from ctor so must not be overridden (i. // LOOP COUNT threadPropsPanel.add(createControllerPanel()); - + threadPropsPanel.add(createUserOptionsPanel()); if (showDelayedStart) { delayedStart = new JCheckBox(JMeterUtils.getResString("delayed_start")); // $NON-NLS-1$ threadPropsPanel.add(delayedStart); @@ -236,6 +252,7 @@ private void init() { // WARNING: called from ctor so must not be overridden (i. scheduler = new JCheckBox(JMeterUtils.getResString("scheduler")); // $NON-NLS-1$ scheduler.addItemListener(this); threadPropsPanel.add(scheduler); + VerticalPanel mainPanel = new VerticalPanel(); mainPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), JMeterUtils.getResString("scheduler_configuration"))); // $NON-NLS-1$ @@ -252,4 +269,17 @@ private void init() { // WARNING: called from ctor so must not be overridden (i. intgrationPanel.add(mainPanel); add(intgrationPanel, BorderLayout.CENTER); } + + private JPanel createUserOptionsPanel(){ + ButtonGroup group = new ButtonGroup(); + sameUserBox = new JRadioButton(JMeterUtils.getResString("threadgroup_same_user")); //$NON-NLS-1$ + group.add(sameUserBox); + sameUserBox.setSelected(true); + differentUserBox = new JRadioButton(JMeterUtils.getResString("threadgroup_different_user")); //$NON-NLS-1$ + group.add(differentUserBox); + JPanel optionsPanel = new HorizontalPanel(); + optionsPanel.add(sameUserBox); + optionsPanel.add(differentUserBox); + return optionsPanel; + } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java index 9fa6a94c8b2..976f9bf193c 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java @@ -49,6 +49,8 @@ import org.apache.jmeter.testelement.TestIterationListener; import org.apache.jmeter.testelement.TestStateListener; import org.apache.jmeter.testelement.property.BooleanProperty; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.JMeterVariables; import org.apache.jmeter.util.JMeterUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +68,7 @@ public class CacheManager extends ConfigTestElement implements TestStateListener private static final int DEFAULT_MAX_SIZE = 5000; private static final long ONE_YEAR_MS = 365*24*60*60*1000L; private static final String[] CACHEABLE_METHODS = JMeterUtils.getPropDefault("cacheable_methods", "GET").split("[ ,]"); + private static final String CONTROLLED_BY_THREAD = "CacheManager.controlledByThread";// $NON-NLS-1$ static { if (log.isInfoEnabled()) { @@ -99,6 +102,13 @@ public CacheManager() { this.localCache = localCache; this.useExpires = useExpires; } + public boolean getControlledByThread() { + return getPropertyAsBoolean(CONTROLLED_BY_THREAD); + } + + public void setControlledByThread(boolean control) { + setProperty(new BooleanProperty(CONTROLLED_BY_THREAD, control)); + } /* * Holder for storing cache details. @@ -622,7 +632,9 @@ public void testEnded(String host) { @Override public void testIterationStart(LoopIterationEvent event) { - if (getClearEachIteration()) { + JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); + if ((getControlledByThread() && !jMeterVariables.isSameUser()) + || getClearEachIteration()) { clearCache(); } useExpires = getUseExpires(); // cache the value diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java index f8cf7472be2..da611c9e2f5 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java @@ -40,6 +40,8 @@ import org.apache.jmeter.testelement.property.JMeterProperty; import org.apache.jmeter.testelement.property.PropertyIterator; import org.apache.jmeter.threads.JMeterContext; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.JMeterVariables; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.reflect.ClassTools; import org.apache.jorphan.util.JMeterException; @@ -61,6 +63,7 @@ public class CookieManager extends ConfigTestElement implements TestStateListene private static final String COOKIES = "CookieManager.cookies";// $NON-NLS-1$ private static final String POLICY = "CookieManager.policy"; //$NON-NLS-1$ private static final String IMPLEMENTATION = "CookieManager.implementation"; //$NON-NLS-1$ + private static final String CONTROLLED_BY_THREADGROUP = "CookieManager.controlledByThreadGroup";// $NON-NLS-1$ //-- JMX tag values private static final String TAB = "\t"; //$NON-NLS-1$ @@ -149,6 +152,13 @@ public boolean getClearEachIteration() { public void setClearEachIteration(boolean clear) { setProperty(new BooleanProperty(CLEAR, clear)); } + public boolean getControlledByThread() { + return getPropertyAsBoolean(CONTROLLED_BY_THREADGROUP); + } + + public void setControlledByThread(boolean control) { + setProperty(new BooleanProperty(CONTROLLED_BY_THREADGROUP, control)); + } public String getImplementation() { return getPropertyAsString(IMPLEMENTATION, DEFAULT_IMPLEMENTATION); @@ -428,7 +438,9 @@ public void testEnded(String host) { /** {@inheritDoc} */ @Override public void testIterationStart(LoopIterationEvent event) { - if (getClearEachIteration()) { + JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); + if ((getControlledByThread() && !jMeterVariables.isSameUser()) + || getClearEachIteration()) { log.debug("Initialise cookies from pre-defined list"); // No need to call clear setProperty(initialCookies.clone()); diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java index a9733312010..5f3e4cd3e56 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java @@ -19,6 +19,8 @@ package org.apache.jmeter.protocol.http.gui; import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import javax.swing.JCheckBox; import javax.swing.JLabel; @@ -36,13 +38,15 @@ * The GUI for the HTTP Cache Manager {@link CacheManager} */ @GUIMenuSortOrder(4) -public class CacheManagerGui extends AbstractConfigGui { +public class CacheManagerGui extends AbstractConfigGui implements ActionListener { private static final long serialVersionUID = 240L; + private static final String CONTROLLED_BY_THREADGROUP = "controlled_By_ThreadGroup"; //$NON-NLS-1$ private JCheckBox clearEachIteration; private JCheckBox useExpires; private JTextField maxCacheSize; + private JCheckBox controlledByThreadGroup; public CacheManagerGui() { init(); @@ -66,6 +70,8 @@ public void configure(TestElement element) { public TestElement createTestElement() { CacheManager element = new CacheManager(); modifyTestElement(element); + controlledByThreadGroup.setSelected(element.getControlledByThread()); + clearEachIteration.setEnabled(!element.getControlledByThread()); return element; } @@ -75,6 +81,7 @@ public void modifyTestElement(TestElement element) { final CacheManager cacheManager = (CacheManager)element; cacheManager.setClearEachIteration(clearEachIteration.isSelected()); cacheManager.setUseExpires(useExpires.isSelected()); + cacheManager.setControlledByThread(controlledByThreadGroup.isSelected()); try { cacheManager.setMaxSize(Integer.parseInt(maxCacheSize.getText())); } catch (NumberFormatException ignored) { @@ -91,6 +98,7 @@ public void clearGui() { clearEachIteration.setSelected(false); useExpires.setSelected(true); maxCacheSize.setText(""); //$NON-NLS-1$ + controlledByThreadGroup.setSelected(false); } /** @@ -102,12 +110,19 @@ private void init() { setBorder(makeBorder()); clearEachIteration = new JCheckBox(JMeterUtils.getResString("clear_cache_per_iter"), false); // $NON-NLS-1$ + + controlledByThreadGroup = + new JCheckBox(JMeterUtils.getResString("cache_clear_controlled_by_threadgroup"), false); //$NON-NLS-1$ + controlledByThreadGroup.setActionCommand(CONTROLLED_BY_THREADGROUP); + controlledByThreadGroup.addActionListener(this); + useExpires = new JCheckBox(JMeterUtils.getResString("use_expires"), false); // $NON-NLS-1$ JPanel northPanel = new JPanel(); northPanel.setLayout(new VerticalLayout(5, VerticalLayout.BOTH)); northPanel.add(makeTitlePanel()); northPanel.add(clearEachIteration); + northPanel.add(controlledByThreadGroup); northPanel.add(useExpires); JLabel label = new JLabel(JMeterUtils.getResString("cache_manager_size")); //$NON-NLS-1$ @@ -122,4 +137,11 @@ private void init() { add(northPanel, BorderLayout.NORTH); } + @Override + public void actionPerformed(ActionEvent e) { + String action = e.getActionCommand(); + if (action.equals(CONTROLLED_BY_THREADGROUP)) { + clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected()); + } + } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java index ef8c78c980f..3fc0491f53d 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CookiePanel.java @@ -69,6 +69,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener { private static final String DELETE_COMMAND = "Delete"; //$NON-NLS-1$ private static final String LOAD_COMMAND = "Load"; //$NON-NLS-1$ private static final String SAVE_COMMAND = "Save"; //$NON-NLS-1$ + private static final String CONTROLLED_BY_THREADGROUP = "Controlled_By_ThreadGroup"; //$NON-NLS-1$ //-- private static final String DEFAULT_POLICY = HC4CookieHandler.DEFAULT_POLICY_NAME; @@ -90,6 +91,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener { private JTable cookieTable; private PowerTableModel tableModel; private JCheckBox clearEachIteration; + private JCheckBox controlledByThreadGroup; private JButton addButton; private JButton deleteButton; private JButton loadButton; @@ -108,6 +110,9 @@ public String getLabelResource() { @Override public void actionPerformed(ActionEvent e) { String action = e.getActionCommand(); + if (action.equals(CONTROLLED_BY_THREADGROUP)) { + clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected()); + } if (action.equals(DELETE_COMMAND)) { if (tableModel.getRowCount() > 0) { @@ -204,6 +209,7 @@ public void modifyTestElement(TestElement cm) { cookieManager.add(cookie); } cookieManager.setClearEachIteration(clearEachIteration.isSelected()); + cookieManager.setControlledByThread(controlledByThreadGroup.isSelected()); cookieManager.setCookiePolicy(policy.getText()); } } @@ -217,6 +223,7 @@ public void clearGui() { tableModel.clearData(); clearEachIteration.setSelected(false); + controlledByThreadGroup.setSelected(false); policy.setText(DEFAULT_POLICY); configureButtonsState(); } @@ -265,6 +272,8 @@ public void configure(TestElement el) { clearEachIteration.setSelected(cookieManager.getClearEachIteration()); // must set policy after setting handler (which may change the policy) policy.setText(cookieManager.getPolicy()); + controlledByThreadGroup.setSelected(cookieManager.getControlledByThread()); + clearEachIteration.setEnabled(!cookieManager.getControlledByThread()); } /** @@ -274,6 +283,10 @@ private void init() { // WARNING: called from ctor so must not be overridden (i. tableModel = new PowerTableModel(COLUMN_RESOURCE_NAMES, columnClasses); clearEachIteration = new JCheckBox(JMeterUtils.getResString("clear_cookies_per_iter"), false); //$NON-NLS-1$ + controlledByThreadGroup = + new JCheckBox(JMeterUtils.getResString("cookie_clear_controlled_by_threadgroup"), false); //$NON-NLS-1$ + controlledByThreadGroup.setActionCommand(CONTROLLED_BY_THREADGROUP); + controlledByThreadGroup.addActionListener(this); policy = new JLabeledChoice( JMeterUtils.getResString("cookie_manager_policy"), //$NON-NLS-1$ new HC4CookieHandler().getPolicies()); @@ -288,6 +301,7 @@ private void init() { // WARNING: called from ctor so must not be overridden (i. JMeterUtils.getResString("cookie_options"))); // $NON-NLS-1$ optionsPane.setLayout(new VerticalLayout(5, VerticalLayout.BOTH)); optionsPane.add(clearEachIteration); + optionsPane.add(controlledByThreadGroup); JPanel policyTypePane = new JPanel(); policyTypePane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); policyTypePane.add(policy); diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java index 843c47112d5..30263f0ee8f 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java @@ -1790,7 +1790,17 @@ protected void notifyFirstSampleAfterLoopRestart() { log.debug("notifyFirstSampleAfterLoopRestart called " + "with config(httpclient.reset_state_on_thread_group_iteration={})", Boolean.valueOf(RESET_STATE_ON_THREAD_GROUP_ITERATION)); - resetStateOnThreadGroupIteration.set(Boolean.valueOf(RESET_STATE_ON_THREAD_GROUP_ITERATION)); + JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); + if (jMeterVariables.isSameUser()) { + log.debug("Thread Group is configured to simulate a returning visitor on each iteration, ignoring property value {}", + RESET_STATE_ON_THREAD_GROUP_ITERATION); + resetStateOnThreadGroupIteration.set(false); + } else { + log.debug("Thread Group is configured to simulate a new visitor on each iteration, using property value {}", + RESET_STATE_ON_THREAD_GROUP_ITERATION); + resetStateOnThreadGroupIteration.set(Boolean.valueOf(RESET_STATE_ON_THREAD_GROUP_ITERATION)); + } + log.debug("Thread state will be reset ?: {}", RESET_STATE_ON_THREAD_GROUP_ITERATION); } @Override diff --git a/test/src/org/apache/jmeter/control/TestTransactionController.java b/test/src/org/apache/jmeter/control/TestTransactionController.java index 9e3610b7750..ceb040a83db 100644 --- a/test/src/org/apache/jmeter/control/TestTransactionController.java +++ b/test/src/org/apache/jmeter/control/TestTransactionController.java @@ -82,7 +82,7 @@ public void testIssue57958() throws Exception { ListenerNotifier notifier = new ListenerNotifier(); - JMeterThread thread = new JMeterThread(hashTree, threadGroup, notifier); + JMeterThread thread = new JMeterThread(hashTree, threadGroup, notifier,false); thread.setThreadGroup(threadGroup); thread.setOnErrorStopThread(true); thread.run(); diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java new file mode 100644 index 00000000000..46579005d6d --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java @@ -0,0 +1,419 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.protocol.http.control; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; +import java.net.URISyntaxException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.message.AbstractHttpMessage; +import org.apache.http.message.BasicHeader; +import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui; +import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; +import org.apache.jmeter.protocol.http.util.HTTPConstants; +import org.apache.jmeter.threads.JMeterContext; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.JMeterVariables; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Test {@link CacheManager} that uses HTTPHC4Impl + */ +public class TestCacheManagerThreadIteration { + private JMeterContext jmctx; + private JMeterVariables jmvars; + private static final String SAME_USER="__jmv_SAME_USER"; + protected static final String LOCAL_HOST = "http://localhost/"; + protected static final String EXPECTED_ETAG = "0xCAFEBABEDEADBEEF"; + protected static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + protected CacheManager cacheManager; + protected String currentTimeInGMT; + protected String vary = null; + protected URL url; + protected HTTPSampleResult sampleResultOK; + + private class HttpResponseStub extends AbstractHttpMessage implements HttpResponse { + private org.apache.http.Header lastModifiedHeader; + private org.apache.http.Header etagHeader; + private String expires; + private String cacheControl; + private org.apache.http.Header dateHeader; + private List headers; + + public HttpResponseStub() { + this.headers = new ArrayList<>(); + this.lastModifiedHeader = new BasicHeader(HTTPConstants.LAST_MODIFIED, currentTimeInGMT); + this.dateHeader = new BasicHeader(HTTPConstants.DATE, currentTimeInGMT); + this.etagHeader = new BasicHeader(HTTPConstants.ETAG, EXPECTED_ETAG); + } + + /* + * (non-Javadoc) + * + * @see org.apache.http.message.AbstractHttpMessage#getAllHeaders() + */ + @Override + public org.apache.http.Header[] getAllHeaders() { + return headers.toArray(new org.apache.http.Header[headers.size()]); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.http.message.AbstractHttpMessage#addHeader(org.apache.http.Header) + */ + @Override + public void addHeader(org.apache.http.Header header) { + headers.add(header); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.http.message.AbstractHttpMessage#getFirstHeader(java.lang.String) + */ + @Override + public Header getFirstHeader(String headerName) { + Header[] headers = getHeaders(headerName); + if (headers.length > 0) { + return headers[0]; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.http.message.AbstractHttpMessage#getLastHeader(java.lang.String) + */ + @Override + public Header getLastHeader(String headerName) { + Header[] headers = getHeaders(headerName); + if (headers.length > 0) { + return headers[headers.length - 1]; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.apache.http.message.AbstractHttpMessage#getHeaders(java.lang.String) + */ + @Override + public Header[] getHeaders(String headerName) { + org.apache.http.Header header = null; + if (HTTPConstants.LAST_MODIFIED.equals(headerName)) { + header = this.lastModifiedHeader; + } else if (HTTPConstants.ETAG.equals(headerName)) { + header = this.etagHeader; + } else if (HTTPConstants.EXPIRES.equals(headerName)) { + header = expires == null ? null : new BasicHeader(HTTPConstants.EXPIRES, expires); + } else if (HTTPConstants.CACHE_CONTROL.equals(headerName)) { + header = cacheControl == null ? null : new BasicHeader(HTTPConstants.CACHE_CONTROL, cacheControl); + } else if (HTTPConstants.DATE.equals(headerName)) { + header = this.dateHeader; + } else if (HTTPConstants.VARY.equals(headerName)) { + header = vary == null ? null : new BasicHeader(HTTPConstants.VARY, vary); + } + if (header != null) { + return new org.apache.http.Header[] { header }; + } else { + return super.getHeaders(headerName); + } + } + + @Override + public ProtocolVersion getProtocolVersion() { + return null; + } + + @Override + public StatusLine getStatusLine() { + return null; + } + + @Override + public void setStatusLine(StatusLine statusline) { + } + + @Override + public void setStatusLine(ProtocolVersion ver, int code) { + } + + @Override + public void setStatusLine(ProtocolVersion ver, int code, String reason) { + } + + @Override + public void setStatusCode(int code) throws IllegalStateException { + } + + @Override + public void setReasonPhrase(String reason) throws IllegalStateException { + } + + @Override + public HttpEntity getEntity() { + return null; + } + + @Override + public void setEntity(HttpEntity entity) { + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public void setLocale(Locale loc) { + } + } + + private class HttpPostStub extends HttpPost { + HttpPostStub() { + } + + @Override + public java.net.URI getURI() { + try { + return url.toURI(); + } catch (URISyntaxException e) { + throw new IllegalStateException(); + } + } + } + + protected String makeDate(Date d) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + simpleDateFormat.setTimeZone(GMT); + return simpleDateFormat.format(d); + } + + protected HTTPSampleResult getSampleResultWithSpecifiedResponseCode(String code) { + HTTPSampleResult sampleResult = new HTTPSampleResult(); + sampleResult.setResponseCode(code); + sampleResult.setHTTPMethod("GET"); + sampleResult.setURL(url); + return sampleResult; + } + + private HttpRequestBase httpMethod; + private HttpResponse httpResponse; + + @Before + public void setUp() throws Exception { + this.cacheManager = new CacheManager(); + this.currentTimeInGMT = makeDate(new Date()); + this.url = new URL(LOCAL_HOST); + this.sampleResultOK = getSampleResultWithSpecifiedResponseCode("200"); + this.httpMethod = new HttpPostStub(); + this.httpResponse = new HttpResponseStub(); + this.httpMethod.setURI(this.url.toURI()); + jmctx = JMeterContextService.getContext(); + jmvars = new JMeterVariables(); + } + + @After + public void tearDown() throws Exception { + this.url = null; + this.httpMethod = null; + this.httpResponse = null; + this.cacheManager = new CacheManager(); + this.currentTimeInGMT = null; + this.sampleResultOK = null; + } + + protected void setExpires(String expires) { + ((HttpResponseStub) httpResponse).expires = expires; + } + + protected void setCacheControl(String cacheControl) { + ((HttpResponseStub) httpResponse).cacheControl = cacheControl; + } + + protected void setLastModified(String lastModified) { + ((HttpResponseStub) httpResponse).lastModifiedHeader = new BasicHeader(HTTPConstants.LAST_MODIFIED, + lastModified); + } + + protected void cacheResult(HTTPSampleResult result) { + this.cacheManager.saveDetails(httpResponse, result); + } + + protected void addRequestHeader(String requestHeader, String value) { + this.httpMethod.addHeader(new BasicHeader(requestHeader, value)); + } + + protected void setRequestHeaders() { + this.cacheManager.setHeaders(this.url, this.httpMethod); + } + + private Map getThreadCache() throws Exception { + Field threadLocalfield = CacheManager.class.getDeclaredField("threadCache"); + threadLocalfield.setAccessible(true); + @SuppressWarnings("unchecked") + ThreadLocal> threadLocal = (ThreadLocal>) threadLocalfield + .get(this.cacheManager); + return threadLocal.get(); + } + + protected CacheManager.CacheEntry getThreadCacheEntry(String url) throws Exception { + return getThreadCache().get(url); + } + @Test + public void testCacheControlCleared() throws Exception { + this.cacheManager.setUseExpires(true); + this.cacheManager.testIterationStart(null); + assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST)); + Header[] headers = new Header[1]; + assertFalse("Should not find valid entry", this.cacheManager.inCache(url, headers)); + long start = System.currentTimeMillis(); + setExpires(makeDate(new Date(start))); + setCacheControl("public, max-age=1"); + cacheResult(sampleResultOK); + assertNotNull("Before iternation, should find entry", getThreadCacheEntry(LOCAL_HOST)); + assertTrue("Before iternation, should find valid entry", this.cacheManager.inCache(url, headers)); + this.cacheManager.setClearEachIteration(true); + this.cacheManager.testIterationStart(null); + assertNull("After iterantion, should not find entry", getThreadCacheEntry(LOCAL_HOST)); + assertFalse("After iterantion, should not find valid entry", this.cacheManager.inCache(url, headers)); + } + + @Test + public void testJmeterVariableCacheWhenThreadIterationIsANewUser() { + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement(); + cacheManager.setControlledByThread(true); + sampler.setCacheManager(cacheManager); + sampler.setThreadContext(jmctx); + boolean res = (boolean) cacheManager.getThreadContext().getVariables().getObject(SAME_USER); + assertTrue("When test different user on the different iternation, the cache should be cleared", res); + } + + @Test + public void testJmeterVariableWhenThreadIterationIsSameUser() { + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement(); + cacheManager.setControlledByThread(true); + sampler.setCacheManager(cacheManager); + sampler.setThreadContext(jmctx); + boolean res = (boolean) cacheManager.getThreadContext().getVariables().getObject(SAME_USER); + assertFalse("When test different user on the different iternation, the cache shouldn't be cleared", res); + } + + @Test + public void testCacheManagerWhenThreadIterationIsANewUser() throws Exception { + //Controlled by ThreadGroup + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + this.cacheManager.setUseExpires(true); + this.cacheManager.testIterationStart(null); + assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST)); + Header[] headers = new Header[1]; + assertFalse("Should not find valid entry", this.cacheManager.inCache(url, headers)); + long start = System.currentTimeMillis(); + setExpires(makeDate(new Date(start))); + setCacheControl("public, max-age=1"); + cacheResult(sampleResultOK); + this.cacheManager.setThreadContext(jmctx); + this.cacheManager.setControlledByThread(true); + assertNotNull("Before iternation, should find entry", getThreadCacheEntry(LOCAL_HOST)); + assertTrue("Before iternation, should find valid entry", this.cacheManager.inCache(url, headers)); + this.cacheManager.testIterationStart(null); + assertNull("After iterantion, should not find entry", getThreadCacheEntry(LOCAL_HOST)); + assertFalse("After iterantion, should not find valid entry", this.cacheManager.inCache(url, headers)); + + //Controlled by cacheManager + start = System.currentTimeMillis(); + setExpires(makeDate(new Date(start))); + setCacheControl("public, max-age=1"); + cacheResult(sampleResultOK); + assertNotNull("Before iternation, should find entry", getThreadCacheEntry(LOCAL_HOST)); + assertTrue("Before iternation, should find valid entry", this.cacheManager.inCache(url, headers)); + this.cacheManager.setClearEachIteration(true); + this.cacheManager.testIterationStart(null); + assertNull("After iterantion, should not find entry", getThreadCacheEntry(LOCAL_HOST)); + assertFalse("After iterantion, should not find valid entry", this.cacheManager.inCache(url, headers)); + } + + @Test + public void testCacheManagerWhenThreadIterationIsSameUser() throws Exception { + // Controlled by ThreadGroup + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + this.cacheManager.setUseExpires(true); + this.cacheManager.testIterationStart(null); + assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST)); + Header[] headers = new Header[1]; + assertFalse("Should not find valid entry", this.cacheManager.inCache(url, headers)); + long start = System.currentTimeMillis(); + setExpires(makeDate(new Date(start))); + setCacheControl("public, max-age=1"); + cacheResult(sampleResultOK); + this.cacheManager.setThreadContext(jmctx); + this.cacheManager.setControlledByThread(true); + assertNotNull("Before iteration, should find entry", getThreadCacheEntry(LOCAL_HOST)); + assertTrue("Before iteration, should find valid entry", this.cacheManager.inCache(url, headers)); + this.cacheManager.testIterationStart(null); + assertNotNull("After iteration, should find entry", getThreadCacheEntry(LOCAL_HOST)); + assertTrue("After iteration, should find valid entry", this.cacheManager.inCache(url, headers)); + // Controlled by cacheManager + start = System.currentTimeMillis(); + setExpires(makeDate(new Date(start))); + setCacheControl("public, max-age=1"); + cacheResult(sampleResultOK); + assertNotNull("Before iteration, should find entry", getThreadCacheEntry(LOCAL_HOST)); + assertTrue("Before iteration, should find valid entry", this.cacheManager.inCache(url, headers)); + this.cacheManager.setClearEachIteration(false); + this.cacheManager.testIterationStart(null); + assertNotNull("After iteration, should find entry", getThreadCacheEntry(LOCAL_HOST)); + assertTrue("After iteration, should find valid entry", this.cacheManager.inCache(url, headers)); + } + +} diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java new file mode 100644 index 00000000000..2d8d6ef0911 --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.jmeter.protocol.http.control; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; + +import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; +import org.apache.jmeter.testelement.property.CollectionProperty; +import org.apache.jmeter.threads.JMeterContext; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.JMeterVariables; +import org.junit.Before; +import org.junit.Test; + +public class TestCookieManagerThreadIteration { + private JMeterContext jmctx; + private JMeterVariables jmvars; + private static final String SAME_USER = "__jmv_SAME_USER"; + private static final String DYNAMIC_COOKIE = "dynamic_cookie_added_by_user"; + private static final String STATIC_COOKIE = "static_cookie"; + + @Before + public void setUp() { + jmctx = JMeterContextService.getContext(); + jmvars = new JMeterVariables(); + } + + @Test + public void testJmeterVariableCookieWhenThreadIterationIsANewUser() { + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement(); + CookieManager cookieManager = new CookieManager(); + cookieManager.setControlledByThread(true); + sampler.setCookieManager(cookieManager); + sampler.setThreadContext(jmctx); + boolean res = (boolean) cookieManager.getThreadContext().getVariables().getObject(SAME_USER); + assertTrue("When test different users on the different iternations, the cookie should be cleared", res); + } + + @Test + public void testJmeterVariableCookieWhenThreadIterationIsSameUser() { + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement(); + CookieManager cookieManager = new CookieManager(); + cookieManager.setControlledByThread(true); + sampler.setCookieManager(cookieManager); + sampler.setThreadContext(jmctx); + boolean res = (boolean) cookieManager.getThreadContext().getVariables().getObject(SAME_USER); + assertFalse("When test same user on the different iternations, the cookie shouldn't be cleared", res); + } + + @Test + public void testCookieManagerWhenThreadIterationIsNewUser() throws NoSuchFieldException, IllegalAccessException { + // Controlled by Thread Group + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + CookieManager cookieManagerDynamic = new CookieManager(); + cookieManagerDynamic.setThreadContext(jmctx); + cookieManagerDynamic.getCookies().clear(); + cookieManagerDynamic.testStarted(); + Cookie cookieDynamic = new Cookie(); + cookieDynamic.setName(DYNAMIC_COOKIE); + cookieManagerDynamic.getCookies().addItem(cookieDynamic); + cookieManagerDynamic.setControlledByThread(true); + Field privateStringField = CookieManager.class.getDeclaredField("initialCookies"); + privateStringField.setAccessible(true); + CookieManager cookieManagerStatic = new CookieManager(); + Cookie cookieStatic = new Cookie(); + cookieStatic.setName(STATIC_COOKIE); + cookieManagerStatic.getCookies().addItem(cookieStatic); + CollectionProperty initialCookies = cookieManagerStatic.getCookies(); + privateStringField.set(cookieManagerDynamic, initialCookies); + assertTrue("Before the iteration,the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + cookieManagerDynamic.testIterationStart(null); + assertEquals("After the iteration, the value of cookie should be the initial cookies", STATIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + assertTrue("After the iteration, the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + // Controlled by CookieManager + cookieManagerDynamic.getCookies().clear(); + cookieManagerDynamic.testStarted(); + cookieDynamic.setName(DYNAMIC_COOKIE); + cookieManagerDynamic.getCookies().addItem(cookieDynamic); + cookieManagerDynamic.setClearEachIteration(true); + cookieManagerStatic.getCookies().clear(); + cookieManagerStatic.getCookies().addItem(cookieStatic); + initialCookies = cookieManagerStatic.getCookies(); + privateStringField.set(cookieManagerDynamic, initialCookies); + assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + assertTrue("Before the iteration,the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + cookieManagerDynamic.testIterationStart(null); + assertEquals("After the iteration, the value of cookie should be the initial cookies", STATIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + assertTrue("After the iteration, the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + } + + @Test + public void testCookieManagerWhenThreadIterationIsSameUser() throws NoSuchFieldException, IllegalAccessException { + // Controlled by Thread Group + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + CookieManager cookieManagerDynamic = new CookieManager(); + cookieManagerDynamic.setThreadContext(jmctx); + cookieManagerDynamic.getCookies().clear(); + cookieManagerDynamic.testStarted(); + Cookie cookieDynamic = new Cookie(); + cookieDynamic.setName(DYNAMIC_COOKIE); + cookieManagerDynamic.getCookies().addItem(cookieDynamic); + cookieManagerDynamic.setControlledByThread(true); + Field privateStringField = CookieManager.class.getDeclaredField("initialCookies"); + privateStringField.setAccessible(true); + CookieManager cookieManagerStatic = new CookieManager(); + Cookie cookieStatic = new Cookie(); + cookieStatic.setName(STATIC_COOKIE); + cookieManagerStatic.getCookies().addItem(cookieStatic); + CollectionProperty initialCookies = cookieManagerStatic.getCookies(); + privateStringField.set(cookieManagerDynamic, initialCookies); + assertTrue("Before the iteration,the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + cookieManagerDynamic.testIterationStart(null); + assertEquals("After the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + assertTrue("After the iteration, the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + + // Controlled by CookieManager + cookieManagerDynamic.getCookies().clear(); + cookieManagerDynamic.testStarted(); + cookieDynamic.setName(DYNAMIC_COOKIE); + cookieManagerDynamic.getCookies().addItem(cookieDynamic); + cookieManagerDynamic.setClearEachIteration(false); + cookieManagerStatic.getCookies().clear(); + cookieStatic.setName(STATIC_COOKIE); + privateStringField.set(cookieManagerDynamic, initialCookies); + assertEquals("Before the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + assertTrue("Before the iteration,the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + cookieManagerDynamic.testIterationStart(null); + assertEquals("After the iteration, the value of cookie should be what user have set", DYNAMIC_COOKIE, + cookieManagerDynamic.getCookies().get(0).getName()); + assertTrue("After the iteration, the quantity of cookies should be 1", + cookieManagerDynamic.getCookies().size() == 1); + } +} diff --git a/test/src/org/apache/jmeter/protocol/http/sampler/TestHTTPHC4Impl.java b/test/src/org/apache/jmeter/protocol/http/sampler/TestHTTPHC4Impl.java new file mode 100644 index 00000000000..c63ee04f7cb --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/http/sampler/TestHTTPHC4Impl.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jmeter.protocol.http.sampler; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui; +import org.apache.jmeter.threads.JMeterContext; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.JMeterVariables; +import org.junit.Before; +import org.junit.Test; + +public class TestHTTPHC4Impl { + private JMeterContext jmctx; + private JMeterVariables jmvars; + private static final String SAME_USER = "__jmv_SAME_USER"; + + @Before + public void setUp() { + jmctx = JMeterContextService.getContext(); + jmvars = new JMeterVariables(); + } + + @Test + public void testNotifyFirstSampleAfterLoopRestartWhenThreadIterationIsSameUser() { + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement(); + sampler.setThreadContext(jmctx); + HTTPHC4Impl hc = new HTTPHC4Impl(sampler); + hc.notifyFirstSampleAfterLoopRestart(); + assertFalse("User is the same, the state shouldn't be reset", HTTPHC4Impl.resetStateOnThreadGroupIteration.get()); + } + + @Test + public void testNotifyFirstSampleAfterLoopRestartWhenThreadIterationIsANewUser() { + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement(); + sampler.setThreadContext(jmctx); + HTTPHC4Impl hc = new HTTPHC4Impl(sampler); + hc.notifyFirstSampleAfterLoopRestart(); + assertTrue("Users are different, the state should be reset", HTTPHC4Impl.resetStateOnThreadGroupIteration.get()); + } +} diff --git a/test/src/org/apache/jmeter/timers/SyncTimerTest.java b/test/src/org/apache/jmeter/timers/SyncTimerTest.java index 462c514b677..dcf53f444ed 100644 --- a/test/src/org/apache/jmeter/timers/SyncTimerTest.java +++ b/test/src/org/apache/jmeter/timers/SyncTimerTest.java @@ -95,7 +95,7 @@ private long timeDelay(SyncTimer timer) { private void setupScheduledThread(long schedulerDuration) { ListedHashTree hashTree = new ListedHashTree(); hashTree.add(new LoopController()); - JMeterThread thread = new JMeterThread(hashTree, null, null); + JMeterThread thread = new JMeterThread(hashTree, null, null,false); JMeterContextService.getContext().setThread(thread); thread.setEndTime(System.currentTimeMillis() + schedulerDuration); } From 315e4ee1c40dd4216f9b5cd33f30c9daff50c2c2 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sat, 22 Jun 2019 14:00:05 +0200 Subject: [PATCH 02/16] Run clean --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d53e61773a5..37ad1a7640f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: false language: java before_script: - - test "x$RUN_CHECKSTYLE" != 'x' || ant -Djava.awt.headless=true download_jars install + - test "x$RUN_CHECKSTYLE" != 'x' || ant -Djava.awt.headless=true clean download_jars install - test "x$RUN_CHECKSTYLE" != 'xtrue' || ant -Djava.awt.headless=true download_checkstyle before_install: From f96c416d5e6e47048ba0a354f4fe328c2142ffde Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sat, 22 Jun 2019 16:17:43 +0200 Subject: [PATCH 03/16] Fix build error --- test/src/org/apache/jmeter/threads/TestJMeterThread.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/org/apache/jmeter/threads/TestJMeterThread.java b/test/src/org/apache/jmeter/threads/TestJMeterThread.java index cedd5b20030..f6d64c8cffb 100644 --- a/test/src/org/apache/jmeter/threads/TestJMeterThread.java +++ b/test/src/org/apache/jmeter/threads/TestJMeterThread.java @@ -141,7 +141,7 @@ public void testBug63490EndTestWhenDelayIsTooLongForScheduler() { long maxDuration = 2000L; threadGroup.setDuration(maxDuration); - JMeterThread jMeterThread = new JMeterThread(testTree, threadGroup, null); + JMeterThread jMeterThread = new JMeterThread(testTree, threadGroup, null, false); jMeterThread.setScheduled(true); jMeterThread.setEndTime(System.currentTimeMillis() + maxDuration); jMeterThread.setThreadGroup(threadGroup); From 182e036f9d3924ded53c83cfa923edafa19b99fa Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sat, 22 Jun 2019 17:04:03 +0200 Subject: [PATCH 04/16] Take into account @vsi remark --- src/core/org/apache/jmeter/threads/JMeterThread.java | 4 ++++ .../org/apache/jmeter/control/TestTransactionController.java | 2 +- test/src/org/apache/jmeter/threads/TestJMeterThread.java | 2 +- test/src/org/apache/jmeter/timers/SyncTimerTest.java | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/org/apache/jmeter/threads/JMeterThread.java b/src/core/org/apache/jmeter/threads/JMeterThread.java index 7d8fc5cbe88..c8fdb07ed6a 100644 --- a/src/core/org/apache/jmeter/threads/JMeterThread.java +++ b/src/core/org/apache/jmeter/threads/JMeterThread.java @@ -150,6 +150,10 @@ public class JMeterThread implements Runnable, Interruptible { private final ReentrantLock interruptLock = new ReentrantLock(); // ensure that interrupt cannot overlap with shutdown + public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note) { + this(test, monitor, note, false); + } + public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note,Boolean isSameUser) { this.monitor = monitor; threadVars = new JMeterVariables(); diff --git a/test/src/org/apache/jmeter/control/TestTransactionController.java b/test/src/org/apache/jmeter/control/TestTransactionController.java index ceb040a83db..9e3610b7750 100644 --- a/test/src/org/apache/jmeter/control/TestTransactionController.java +++ b/test/src/org/apache/jmeter/control/TestTransactionController.java @@ -82,7 +82,7 @@ public void testIssue57958() throws Exception { ListenerNotifier notifier = new ListenerNotifier(); - JMeterThread thread = new JMeterThread(hashTree, threadGroup, notifier,false); + JMeterThread thread = new JMeterThread(hashTree, threadGroup, notifier); thread.setThreadGroup(threadGroup); thread.setOnErrorStopThread(true); thread.run(); diff --git a/test/src/org/apache/jmeter/threads/TestJMeterThread.java b/test/src/org/apache/jmeter/threads/TestJMeterThread.java index f6d64c8cffb..cedd5b20030 100644 --- a/test/src/org/apache/jmeter/threads/TestJMeterThread.java +++ b/test/src/org/apache/jmeter/threads/TestJMeterThread.java @@ -141,7 +141,7 @@ public void testBug63490EndTestWhenDelayIsTooLongForScheduler() { long maxDuration = 2000L; threadGroup.setDuration(maxDuration); - JMeterThread jMeterThread = new JMeterThread(testTree, threadGroup, null, false); + JMeterThread jMeterThread = new JMeterThread(testTree, threadGroup, null); jMeterThread.setScheduled(true); jMeterThread.setEndTime(System.currentTimeMillis() + maxDuration); jMeterThread.setThreadGroup(threadGroup); diff --git a/test/src/org/apache/jmeter/timers/SyncTimerTest.java b/test/src/org/apache/jmeter/timers/SyncTimerTest.java index dcf53f444ed..462c514b677 100644 --- a/test/src/org/apache/jmeter/timers/SyncTimerTest.java +++ b/test/src/org/apache/jmeter/timers/SyncTimerTest.java @@ -95,7 +95,7 @@ private long timeDelay(SyncTimer timer) { private void setupScheduledThread(long schedulerDuration) { ListedHashTree hashTree = new ListedHashTree(); hashTree.add(new LoopController()); - JMeterThread thread = new JMeterThread(hashTree, null, null,false); + JMeterThread thread = new JMeterThread(hashTree, null, null); JMeterContextService.getContext().setThread(thread); thread.setEndTime(System.currentTimeMillis() + schedulerDuration); } From 43064ecc7c767ae1758d0b0cc0a42271a5e23d49 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sun, 23 Jun 2019 09:59:14 +0200 Subject: [PATCH 05/16] Rename getters/variables for better understanding Move method from ThreadGroup to AbstractThreadGroup --- .../jmeter/threads/AbstractThreadGroup.java | 28 ++++++++++++ .../apache/jmeter/threads/JMeterThread.java | 8 ++-- .../jmeter/threads/JMeterVariables.java | 5 ++- .../apache/jmeter/threads/ThreadGroup.java | 45 +++++-------------- .../jmeter/threads/gui/ThreadGroupGui.java | 4 +- .../protocol/http/control/CacheManager.java | 2 +- .../protocol/http/control/CookieManager.java | 2 +- .../protocol/http/sampler/HTTPHC4Impl.java | 2 +- 8 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java b/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java index 1376f63f6ae..5b7bb1addda 100644 --- a/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java +++ b/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java @@ -30,6 +30,7 @@ import org.apache.jmeter.samplers.Sampler; import org.apache.jmeter.testelement.AbstractTestElement; import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.property.BooleanProperty; import org.apache.jmeter.testelement.property.IntegerProperty; import org.apache.jmeter.testelement.property.JMeterProperty; import org.apache.jmeter.testelement.property.TestElementProperty; @@ -72,6 +73,10 @@ public abstract class AbstractThreadGroup extends AbstractTestElement public static final String NUM_THREADS = "ThreadGroup.num_threads"; public static final String MAIN_CONTROLLER = "ThreadGroup.main_controller"; + + /** The same user or different users */ + public static final String IS_SAME_USER_ON_NEXT_ITERATION = "ThreadGroup.same_user_on_next_iteration"; + private final AtomicInteger numberOfThreads = new AtomicInteger(0); // Number of active threads in this group @@ -299,4 +304,27 @@ public boolean getOnErrorStopTestNow() { public void breakThreadLoop() { ((LoopController) getSamplerController()).breakLoop(); } + + /** + * Set the kind of user + * + * @param isSameUserOnNextIteration + * true is the same user on next iteration of loop + * false is a different user on next iteration of loop + */ + public void setIsSameUserOnNextIteration(boolean isSameUserOnNextIteration) { + setProperty(new BooleanProperty(IS_SAME_USER_ON_NEXT_ITERATION, isSameUserOnNextIteration)); + } + + /** + * Get kind of user: + *
    + *
  • true means same user running multiple iterations
  • + *
  • false means a different user for each iteration
  • + *
+ * @return the kind of user. + */ + public boolean isSameUserOnNextIteration() { + return getPropertyAsBoolean(ThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION); + } } diff --git a/src/core/org/apache/jmeter/threads/JMeterThread.java b/src/core/org/apache/jmeter/threads/JMeterThread.java index c8fdb07ed6a..90aa0e98e94 100644 --- a/src/core/org/apache/jmeter/threads/JMeterThread.java +++ b/src/core/org/apache/jmeter/threads/JMeterThread.java @@ -123,7 +123,7 @@ public class JMeterThread implements Runnable, Interruptible { private long endTime = 0; - private boolean isSameUser = false; + private boolean isSameUserOnNextIteration = false; // based on this scheduler is enabled or disabled private boolean scheduler = false; @@ -154,7 +154,7 @@ public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier this(test, monitor, note, false); } - public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note,Boolean isSameUser) { + public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier note,Boolean isSameUserOnNextIteration) { this.monitor = monitor; threadVars = new JMeterVariables(); testTree = test; @@ -168,7 +168,7 @@ public JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier sampleMonitors = sampleMonitorSearcher.getSearchResults(); notifier = note; running = true; - this.isSameUser = isSameUser; + this.isSameUserOnNextIteration = isSameUserOnNextIteration; } public void setInitialContext(JMeterContext context) { @@ -692,7 +692,7 @@ private List getSampleListeners(SamplePackage samplePack, Sample * @return the iteration listener */ private IterationListener initRun(JMeterContext threadContext) { - threadVars.putObject(JMeterVariables.VAR_IS_SAME_USER_KEY, isSameUser); + threadVars.putObject(JMeterVariables.VAR_IS_SAME_USER_KEY, isSameUserOnNextIteration); threadContext.setVariables(threadVars); threadContext.setThreadNum(getThreadNum()); threadContext.getVariables().put(LAST_SAMPLE_OK, TRUE); diff --git a/src/core/org/apache/jmeter/threads/JMeterVariables.java b/src/core/org/apache/jmeter/threads/JMeterVariables.java index 3789fdc4762..cbfb8dc207e 100644 --- a/src/core/org/apache/jmeter/threads/JMeterVariables.java +++ b/src/core/org/apache/jmeter/threads/JMeterVariables.java @@ -174,7 +174,10 @@ public Set> entrySet(){ return Collections.unmodifiableMap(variables).entrySet(); } - public boolean isSameUser() { + /** + * @return boolean true if user is the same on next iteration of Thread loop, false otherwise + */ + public boolean isSameUserOnNextIteration() { return Boolean.TRUE.equals(variables.get(VAR_IS_SAME_USER_KEY)); } } diff --git a/src/core/org/apache/jmeter/threads/ThreadGroup.java b/src/core/org/apache/jmeter/threads/ThreadGroup.java index 26955bec8a4..c3be464ea5d 100644 --- a/src/core/org/apache/jmeter/threads/ThreadGroup.java +++ b/src/core/org/apache/jmeter/threads/ThreadGroup.java @@ -69,9 +69,6 @@ public class ThreadGroup extends AbstractThreadGroup { /** Scheduler start delay, overrides start time */ public static final String DELAY = "ThreadGroup.delay"; - - /** The same user or different users */ - public static final String IS_SAME_USER = "ThreadGroup.sameuser"; //- JMX entries private transient Thread threadStarter; @@ -177,29 +174,6 @@ public void setRampUp(int rampUp) { public int getRampUp() { return getPropertyAsInt(ThreadGroup.RAMP_TIME); } - /** - * Set the kind of user - * - * @param isSameUser - * true is the same user - * false is the different users - */ - public void setIsSameUser(boolean isSameUser) { - setProperty(new BooleanProperty(IS_SAME_USER, isSameUser)); - } - - /** - * Get kind of user: - *
    - *
  • true means same user running multiple iterations
  • - *
  • false means a different user for each iteration
  • - *
- * @return the kind of user. - */ - public boolean isSameUser() { - return getPropertyAsBoolean(ThreadGroup.IS_SAME_USER); - } - private boolean isDelayedStartup() { return getPropertyAsBoolean(DELAYED_START); @@ -241,7 +215,7 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread this.threadGroupTree = threadGroupTree; int numThreads = getNumThreads(); int rampUpPeriodInSeconds = getRampUp(); - boolean isSameUser = isSameUser(); + boolean isSameUser = isSameUserOnNextIteration(); delayedStartup = isDelayedStartup(); // Fetch once; needs to stay constant log.info("Starting thread group... number={} threads={} ramp-up={} delayedStart={}", groupNumber, numThreads, rampUpPeriodInSeconds, delayedStartup); @@ -265,8 +239,8 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread log.debug("Computed delayForNextThreadInMillis:{} for thread:{}", delayForNextThreadInMillis); } lastThreadStartInMillis = nowInMillis; - startNewThread(notifier, threadGroupTree, engine, threadNum, context, nowInMillis, - Math.max(0, delayForNextThreadInMillis), isSameUser); + startNewThread(notifier, threadGroupTree, engine, threadNum, context, + nowInMillis, Math.max(0, delayForNextThreadInMillis), isSameUser); } } log.info("Started thread group number {}", groupNumber); @@ -281,11 +255,12 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread * @param context {@link JMeterContext} * @param now Nom in milliseconds * @param delay int delay in milliseconds + * @param isSameUserOnNextIteration boolean indicating a next iteration will simulate a new or returning user * @return {@link JMeterThread} newly created */ private JMeterThread startNewThread(ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine, - int threadNum, final JMeterContext context, long now, int delay,Boolean isSameUser) { - JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context,isSameUser); + int threadNum, final JMeterContext context, long now, int delay,Boolean isSameUserOnNextIteration) { + JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context, isSameUserOnNextIteration); scheduleThread(jmThread, now); // set start and end time jmThread.setInitialDelay(delay); Thread newThread = new Thread(jmThread, jmThread.getThreadName()); @@ -323,13 +298,13 @@ private void registerStartedThread(JMeterThread jMeterThread, Thread newThread) private JMeterThread makeThread( ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine, int threadNumber, - JMeterContext context,Boolean isSameUser) { // N.B. Context needs to be fetched in the correct thread + JMeterContext context,Boolean isSameUserOnNextIteration) { // N.B. Context needs to be fetched in the correct thread boolean onErrorStopTest = getOnErrorStopTest(); boolean onErrorStopTestNow = getOnErrorStopTestNow(); boolean onErrorStopThread = getOnErrorStopThread(); boolean onErrorStartNextLoop = getOnErrorStartNextLoop(); String groupName = getName(); - final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier,isSameUser); + final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier,isSameUserOnNextIteration); jmeterThread.setThreadNum(threadNumber); jmeterThread.setThreadGroup(this); jmeterThread.setInitialContext(context); @@ -355,7 +330,7 @@ public JMeterThread addNewThread(int delay, StandardJMeterEngine engine) { numThreads = getNumThreads(); setNumThreads(numThreads + 1); } - newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay, isSameUser()); + newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay, isSameUserOnNextIteration()); JMeterContextService.addTotalThreads( 1 ); log.info("Started new thread in group {}", groupNumber); return newJmThread; @@ -622,7 +597,7 @@ public void run() { final int numThreads = getNumThreads(); final float rampUpOriginInMillis = (float) getRampUp() * 1000; final long startTimeInMillis = System.currentTimeMillis(); - final boolean isSameUser=isSameUser(); + final boolean isSameUser = isSameUserOnNextIteration(); for (int threadNumber = 0; running && threadNumber < numThreads; threadNumber++) { if (threadNumber > 0) { long elapsedInMillis = System.currentTimeMillis() - startTimeInMillis; diff --git a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java index ce8acbf258f..51c41313897 100644 --- a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java +++ b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java @@ -107,7 +107,7 @@ public void modifyTestElement(TestElement tg) { tg.setProperty(new BooleanProperty(ThreadGroup.SCHEDULER, scheduler.isSelected())); tg.setProperty(ThreadGroup.DURATION, duration.getText()); tg.setProperty(ThreadGroup.DELAY, delay.getText()); - tg.setProperty(ThreadGroup.IS_SAME_USER,sameUserBox.isSelected()); + tg.setProperty(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION,sameUserBox.isSelected()); } @Override @@ -125,7 +125,7 @@ public void configure(TestElement tg) { duration.setText(tg.getPropertyAsString(ThreadGroup.DURATION)); delay.setText(tg.getPropertyAsString(ThreadGroup.DELAY)); - final boolean isSameUser = tg.getPropertyAsBoolean(ThreadGroup.IS_SAME_USER, false); + final boolean isSameUser = tg.getPropertyAsBoolean(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION, false); if (isSameUser){ sameUserBox.setSelected(true); } else { diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java index 976f9bf193c..4c23c6ab735 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java @@ -633,7 +633,7 @@ public void testEnded(String host) { @Override public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); - if ((getControlledByThread() && !jMeterVariables.isSameUser()) + if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) || getClearEachIteration()) { clearCache(); } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java index da611c9e2f5..1d7758cec2d 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java @@ -439,7 +439,7 @@ public void testEnded(String host) { @Override public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); - if ((getControlledByThread() && !jMeterVariables.isSameUser()) + if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) || getClearEachIteration()) { log.debug("Initialise cookies from pre-defined list"); // No need to call clear diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java index 30263f0ee8f..9d18577466d 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java @@ -1791,7 +1791,7 @@ protected void notifyFirstSampleAfterLoopRestart() { + "with config(httpclient.reset_state_on_thread_group_iteration={})", Boolean.valueOf(RESET_STATE_ON_THREAD_GROUP_ITERATION)); JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); - if (jMeterVariables.isSameUser()) { + if (jMeterVariables.isSameUserOnNextIteration()) { log.debug("Thread Group is configured to simulate a returning visitor on each iteration, ignoring property value {}", RESET_STATE_ON_THREAD_GROUP_ITERATION); resetStateOnThreadGroupIteration.set(false); From a4d8de14bf7e2e3f6b2cd023fb682abf67463a99 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sun, 23 Jun 2019 21:33:41 +0200 Subject: [PATCH 06/16] Remove added newline --- src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java index 51c41313897..faf72d181f7 100644 --- a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java +++ b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java @@ -252,7 +252,6 @@ private void init() { // WARNING: called from ctor so must not be overridden (i. scheduler = new JCheckBox(JMeterUtils.getResString("scheduler")); // $NON-NLS-1$ scheduler.addItemListener(this); threadPropsPanel.add(scheduler); - VerticalPanel mainPanel = new VerticalPanel(); mainPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), JMeterUtils.getResString("scheduler_configuration"))); // $NON-NLS-1$ From 2b4850dca7f157a6d473f47c625d378612587419 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sun, 23 Jun 2019 21:34:43 +0200 Subject: [PATCH 07/16] Remove trailing whitespace --- src/core/org/apache/jmeter/threads/JMeterThread.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/org/apache/jmeter/threads/JMeterThread.java b/src/core/org/apache/jmeter/threads/JMeterThread.java index 90aa0e98e94..f6a588b7c30 100644 --- a/src/core/org/apache/jmeter/threads/JMeterThread.java +++ b/src/core/org/apache/jmeter/threads/JMeterThread.java @@ -95,7 +95,7 @@ public class JMeterThread implements Runnable, Interruptible { private final TestCompiler compiler; private final JMeterThreadMonitor monitor; - + private final JMeterVariables threadVars; // Note: this is only used to implement TestIterationListener#testIterationStart From 0b26cbf427d06b9f68d21dca43be6d26919f27c2 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sun, 23 Jun 2019 21:37:04 +0200 Subject: [PATCH 08/16] Make isSameUserOnNextIteration final as per @vlsi review --- src/core/org/apache/jmeter/threads/JMeterThread.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/org/apache/jmeter/threads/JMeterThread.java b/src/core/org/apache/jmeter/threads/JMeterThread.java index f6a588b7c30..c9cf929ef85 100644 --- a/src/core/org/apache/jmeter/threads/JMeterThread.java +++ b/src/core/org/apache/jmeter/threads/JMeterThread.java @@ -123,7 +123,7 @@ public class JMeterThread implements Runnable, Interruptible { private long endTime = 0; - private boolean isSameUserOnNextIteration = false; + private final boolean isSameUserOnNextIteration; // based on this scheduler is enabled or disabled private boolean scheduler = false; From a665891b5960c4ea85d561a40d219c1ce4549060 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sun, 23 Jun 2019 21:37:25 +0200 Subject: [PATCH 09/16] Remove unintended space as per @vlsi review --- src/core/org/apache/jmeter/threads/ThreadGroup.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/org/apache/jmeter/threads/ThreadGroup.java b/src/core/org/apache/jmeter/threads/ThreadGroup.java index c3be464ea5d..9cfbf60393f 100644 --- a/src/core/org/apache/jmeter/threads/ThreadGroup.java +++ b/src/core/org/apache/jmeter/threads/ThreadGroup.java @@ -622,7 +622,6 @@ public void run() { } catch (Exception ex) { log.error("An error occurred scheduling delay start of threads for Thread Group: {}", getName(), ex); } - } } } From d1799589f062676b41f1d7a0ad1f34301927482d Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sun, 23 Jun 2019 22:33:53 +0200 Subject: [PATCH 10/16] Fix : controlledByThreadGroup is not configured --- .../org/apache/jmeter/protocol/http/gui/CacheManagerGui.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java index 5f3e4cd3e56..49d2bee3196 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/CacheManagerGui.java @@ -61,9 +61,10 @@ public String getLabelResource() { public void configure(TestElement element) { super.configure(element); final CacheManager cacheManager = (CacheManager)element; - clearEachIteration.setSelected(cacheManager.getClearEachIteration()); useExpires.setSelected(cacheManager.getUseExpires()); maxCacheSize.setText(Integer.toString(cacheManager.getMaxSize())); + controlledByThreadGroup.setSelected(cacheManager.getControlledByThread()); + clearEachIteration.setSelected(cacheManager.getClearEachIteration()); } @Override @@ -141,7 +142,7 @@ private void init() { public void actionPerformed(ActionEvent e) { String action = e.getActionCommand(); if (action.equals(CONTROLLED_BY_THREADGROUP)) { - clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected()); + clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected()); } } } From 72287968a7b9f7b9eb6250e619ccfab909dbf8ca Mon Sep 17 00:00:00 2001 From: pmouawad Date: Sun, 23 Jun 2019 22:34:38 +0200 Subject: [PATCH 11/16] Make Authentication Manager use Thread Group config --- .../jmeter/resources/messages.properties | 1 + .../jmeter/resources/messages_fr.properties | 1 + .../protocol/http/control/AuthManager.java | 17 +++++++++++++- .../jmeter/protocol/http/gui/AuthPanel.java | 23 +++++++++++++++++-- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/core/org/apache/jmeter/resources/messages.properties b/src/core/org/apache/jmeter/resources/messages.properties index 6c46c19aec5..ce9c35ce52c 100644 --- a/src/core/org/apache/jmeter/resources/messages.properties +++ b/src/core/org/apache/jmeter/resources/messages.properties @@ -137,6 +137,7 @@ attribute_field=Attribute\: attrs=Attributes auth_base_url=Base URL auth_manager_clear_per_iter=Clear auth on each iteration? +auth_manager_clear_controlled_by_threadgroup=Use Thread Group configuration to control clearing auth_manager_options=Options auth_manager_title=HTTP Authorization Manager auths_stored=Authorizations Stored in the Authorization Manager diff --git a/src/core/org/apache/jmeter/resources/messages_fr.properties b/src/core/org/apache/jmeter/resources/messages_fr.properties index 2a1c090357e..ecbcd9bdb84 100644 --- a/src/core/org/apache/jmeter/resources/messages_fr.properties +++ b/src/core/org/apache/jmeter/resources/messages_fr.properties @@ -132,6 +132,7 @@ attribute_field=Attribut \: attrs=Attributs auth_base_url=URL de base auth_manager_clear_per_iter=Réauthentifier à chaque itération ? +auth_manager_clear_controlled_by_threadgroup=Utiliser la configuration du groupe de threads pour contrôler la Réauthentification auth_manager_options=Options auth_manager_title=Gestionnaire d'autorisation HTTP auths_stored=Autorisations stockées diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java index 320fb928fc7..ee0d3a765e2 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java @@ -45,9 +45,12 @@ import org.apache.jmeter.protocol.http.util.HTTPConstants; import org.apache.jmeter.testelement.TestIterationListener; import org.apache.jmeter.testelement.TestStateListener; +import org.apache.jmeter.testelement.property.BooleanProperty; import org.apache.jmeter.testelement.property.CollectionProperty; import org.apache.jmeter.testelement.property.JMeterProperty; import org.apache.jmeter.testelement.property.TestElementProperty; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.JMeterVariables; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.util.JOrphanUtils; import org.slf4j.Logger; @@ -70,6 +73,8 @@ public class AuthManager extends ConfigTestElement implements TestStateListener, private static final String AUTH_LIST = "AuthManager.auth_list"; //$NON-NLS-1$ + private static final String CONTROLLED_BY_THREADGROUP = "AuthManager.controlledByThreadGroup";// $NON-NLS-1$ + private static final String[] COLUMN_RESOURCE_NAMES = { "auth_base_url", //$NON-NLS-1$ "username", //$NON-NLS-1$ @@ -549,8 +554,18 @@ public void testEnded(String host) { /** {@inheritDoc} */ @Override public void testIterationStart(LoopIterationEvent event) { - if (getClearEachIteration()) { + JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); + if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) + || getClearEachIteration()) { kerberosManager.clearSubjects(); } } + + public boolean getControlledByThread() { + return getPropertyAsBoolean(CONTROLLED_BY_THREADGROUP); + } + + public void setControlledByThread(boolean control) { + setProperty(new BooleanProperty(CONTROLLED_BY_THREADGROUP, control)); + } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java index b1fb06a570f..660dedb278d 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java @@ -73,10 +73,14 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener { private static final String SAVE_COMMAND = "Save"; //$NON-NLS-1$ + private static final String CONTROLLED_BY_THREADGROUP = "Controlled_By_ThreadGroup"; //$NON-NLS-1$ + private InnerTableModel tableModel; private JCheckBox clearEachIteration; + private JCheckBox controlledByThreadGroup; + /** * A table to show the authentication information. */ @@ -118,6 +122,7 @@ public void modifyTestElement(TestElement el) { authManager.clear(); authManager.addTestElement((TestElement) tableModel.manager.clone()); authManager.setClearEachIteration(clearEachIteration.isSelected()); + authManager.setControlledByThread(controlledByThreadGroup.isSelected()); configureTestElement(el); } @@ -132,14 +137,17 @@ public void clearGui() { deleteButton.setEnabled(false); saveButton.setEnabled(false); clearEachIteration.setSelected(false); + controlledByThreadGroup.setSelected(false); } @Override public void configure(TestElement el) { super.configure(el); tableModel.manager.clear(); - tableModel.manager.addTestElement((AuthManager) el.clone()); - clearEachIteration.setSelected(((AuthManager) el).getClearEachIteration()); + AuthManager authManager = (AuthManager) el; + tableModel.manager.addTestElement((AuthManager) authManager.clone()); + clearEachIteration.setSelected(authManager.getClearEachIteration()); + controlledByThreadGroup.setSelected(authManager.getControlledByThread()); checkButtonsStatus(); } @@ -166,7 +174,14 @@ private void init() {// called from ctor, so must not be overridable optionsPane.setLayout(new VerticalLayout(5, VerticalLayout.BOTH)); clearEachIteration = new JCheckBox(JMeterUtils.getResString("auth_manager_clear_per_iter"), false); //$NON-NLS-1$ + + controlledByThreadGroup = + new JCheckBox(JMeterUtils.getResString("auth_manager_clear_controlled_by_threadgroup"), false); //$NON-NLS-1$ + controlledByThreadGroup.setActionCommand(CONTROLLED_BY_THREADGROUP); + controlledByThreadGroup.addActionListener(this); + optionsPane.add(clearEachIteration); + optionsPane.add(controlledByThreadGroup); northPanel.add(optionsPane); add(northPanel, BorderLayout.NORTH); @@ -220,6 +235,10 @@ private void checkButtonsStatus() { public void actionPerformed(ActionEvent e) { String action = e.getActionCommand(); + if (action.equals(CONTROLLED_BY_THREADGROUP)) { + clearEachIteration.setEnabled(!controlledByThreadGroup.isSelected()); + } + if (action.equals(DELETE_COMMAND)) { deleteRows(); } From 37069a7ea9874b37e787f5a38cb58215a6ac7859 Mon Sep 17 00:00:00 2001 From: shaqianqian Date: Mon, 24 Jun 2019 17:58:01 +0200 Subject: [PATCH 12/16] modify clear of cookiemanager and cachemanager --- .../protocol/http/control/AuthManager.java | 4 +- .../protocol/http/control/CacheManager.java | 2 +- .../protocol/http/control/CookieManager.java | 2 +- .../http/control/TestAuthManager.java | 45 ------------------- .../TestCacheManagerThreadIteration.java | 8 ++++ .../TestCookieManagerThreadIteration.java | 7 +++ 6 files changed, 19 insertions(+), 49 deletions(-) diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java index ee0d3a765e2..12ae431d483 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java @@ -555,8 +555,8 @@ public void testEnded(String host) { @Override public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); - if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) - || getClearEachIteration()) { + if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) + || (getClearEachIteration() && !getControlledByThread())) { kerberosManager.clearSubjects(); } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java index 4c23c6ab735..85c4cc47e54 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java @@ -634,7 +634,7 @@ public void testEnded(String host) { public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) - || getClearEachIteration()) { + ||(!getControlledByThread()&&getClearEachIteration())) { clearCache(); } useExpires = getUseExpires(); // cache the value diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java index 1d7758cec2d..e8dffacebab 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java @@ -440,7 +440,7 @@ public void testEnded(String host) { public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) - || getClearEachIteration()) { + || (getClearEachIteration()&&!getControlledByThread())) { log.debug("Initialise cookies from pre-defined list"); // No need to call clear setProperty(initialCookies.clone()); diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java index 41216036320..587094fafed 100644 --- a/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java +++ b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java @@ -19,7 +19,6 @@ package org.apache.jmeter.protocol.http.control; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.File; @@ -28,7 +27,6 @@ import java.nio.file.Files; import org.apache.jmeter.junit.JMeterTestCase; -import org.apache.jmeter.testelement.property.CollectionProperty; import org.junit.Test; public class TestAuthManager extends JMeterTestCase { @@ -43,49 +41,6 @@ public void testHttps() throws Exception { assertTrue(AuthManager.isSupportedProtocol(new URL("https:"))); } - @Test - public void testFile() throws Exception { - AuthManager am = new AuthManager(); - CollectionProperty ao = am.getAuthObjects(); - assertEquals(0, ao.size()); - am.addFile(findTestPath("testfiles/TestAuth.txt")); - assertEquals(9, ao.size()); - Authorization at; - at = am.getAuthForURL(new URL("http://a.b.c/")); - assertEquals("login", at.getUser()); - assertEquals("password", at.getPass()); - at = am.getAuthForURL(new URL("http://a.b.c:80/")); // same as above - assertEquals("login", at.getUser()); - assertEquals("password", at.getPass()); - at = am.getAuthForURL(new URL("http://a.b.c:443/"));// not same - assertNull(at); - at = am.getAuthForURL(new URL("http://a.b.c/1")); - assertEquals("login1", at.getUser()); - assertEquals("password1", at.getPass()); - assertEquals("", at.getDomain()); - assertEquals("", at.getRealm()); - at = am.getAuthForURL(new URL("http://d.e.f/")); - assertEquals("user", at.getUser()); - assertEquals("pass", at.getPass()); - assertEquals("domain", at.getDomain()); - assertEquals("realm", at.getRealm()); - at = am.getAuthForURL(new URL("https://j.k.l/")); - assertEquals("jkl", at.getUser()); - assertEquals("pass", at.getPass()); - at = am.getAuthForURL(new URL("https://j.k.l:443/")); - assertEquals("jkl", at.getUser()); - assertEquals("pass", at.getPass()); - at = am.getAuthForURL(new URL("https://l.m.n/")); - assertEquals("lmn443", at.getUser()); - assertEquals("pass", at.getPass()); - at = am.getAuthForURL(new URL("https://l.m.n:443/")); - assertEquals("lmn443", at.getUser()); - assertEquals("pass", at.getPass()); - at = am.getAuthForURL(new URL("https://l.m.n:8443/")); - assertEquals("lmn8443", at.getUser()); - assertEquals("pass", at.getPass()); - } - @Test public void testAddFileWithoutDomainAndRealmWithMechanism() throws IOException { File authFile = File.createTempFile("auth", ".txt"); diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java index 46579005d6d..d434416f7a3 100644 --- a/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java +++ b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java @@ -370,12 +370,16 @@ public void testCacheManagerWhenThreadIterationIsANewUser() throws Exception { assertFalse("After iterantion, should not find valid entry", this.cacheManager.inCache(url, headers)); //Controlled by cacheManager + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + this.cacheManager.setThreadContext(jmctx); start = System.currentTimeMillis(); setExpires(makeDate(new Date(start))); setCacheControl("public, max-age=1"); cacheResult(sampleResultOK); assertNotNull("Before iternation, should find entry", getThreadCacheEntry(LOCAL_HOST)); assertTrue("Before iternation, should find valid entry", this.cacheManager.inCache(url, headers)); + this.cacheManager.setControlledByThread(false); this.cacheManager.setClearEachIteration(true); this.cacheManager.testIterationStart(null); assertNull("After iterantion, should not find entry", getThreadCacheEntry(LOCAL_HOST)); @@ -404,12 +408,16 @@ public void testCacheManagerWhenThreadIterationIsSameUser() throws Exception { assertNotNull("After iteration, should find entry", getThreadCacheEntry(LOCAL_HOST)); assertTrue("After iteration, should find valid entry", this.cacheManager.inCache(url, headers)); // Controlled by cacheManager + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + this.cacheManager.setThreadContext(jmctx); start = System.currentTimeMillis(); setExpires(makeDate(new Date(start))); setCacheControl("public, max-age=1"); cacheResult(sampleResultOK); assertNotNull("Before iteration, should find entry", getThreadCacheEntry(LOCAL_HOST)); assertTrue("Before iteration, should find valid entry", this.cacheManager.inCache(url, headers)); + this.cacheManager.setControlledByThread(false); this.cacheManager.setClearEachIteration(false); this.cacheManager.testIterationStart(null); assertNotNull("After iteration, should find entry", getThreadCacheEntry(LOCAL_HOST)); diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java index 2d8d6ef0911..20c058658ec 100644 --- a/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java +++ b/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java @@ -102,11 +102,15 @@ public void testCookieManagerWhenThreadIterationIsNewUser() throws NoSuchFieldEx assertTrue("After the iteration, the quantity of cookies should be 1", cookieManagerDynamic.getCookies().size() == 1); // Controlled by CookieManager + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + cookieManagerDynamic.setThreadContext(jmctx); cookieManagerDynamic.getCookies().clear(); cookieManagerDynamic.testStarted(); cookieDynamic.setName(DYNAMIC_COOKIE); cookieManagerDynamic.getCookies().addItem(cookieDynamic); cookieManagerDynamic.setClearEachIteration(true); + cookieManagerDynamic.setControlledByThread(false); cookieManagerStatic.getCookies().clear(); cookieManagerStatic.getCookies().addItem(cookieStatic); initialCookies = cookieManagerStatic.getCookies(); @@ -154,6 +158,9 @@ public void testCookieManagerWhenThreadIterationIsSameUser() throws NoSuchFieldE cookieManagerDynamic.getCookies().size() == 1); // Controlled by CookieManager + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + cookieManagerDynamic.setControlledByThread(false); cookieManagerDynamic.getCookies().clear(); cookieManagerDynamic.testStarted(); cookieDynamic.setName(DYNAMIC_COOKIE); From 9c801074bf1bad60665e177d906c1624216246ad Mon Sep 17 00:00:00 2001 From: shaqianqian Date: Mon, 24 Jun 2019 18:06:52 +0200 Subject: [PATCH 13/16] modify clear of cookiemanager and cachemanager --- .../http/control/TestAuthManager.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java index 587094fafed..41216036320 100644 --- a/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java +++ b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManager.java @@ -19,6 +19,7 @@ package org.apache.jmeter.protocol.http.control; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.File; @@ -27,6 +28,7 @@ import java.nio.file.Files; import org.apache.jmeter.junit.JMeterTestCase; +import org.apache.jmeter.testelement.property.CollectionProperty; import org.junit.Test; public class TestAuthManager extends JMeterTestCase { @@ -41,6 +43,49 @@ public void testHttps() throws Exception { assertTrue(AuthManager.isSupportedProtocol(new URL("https:"))); } + @Test + public void testFile() throws Exception { + AuthManager am = new AuthManager(); + CollectionProperty ao = am.getAuthObjects(); + assertEquals(0, ao.size()); + am.addFile(findTestPath("testfiles/TestAuth.txt")); + assertEquals(9, ao.size()); + Authorization at; + at = am.getAuthForURL(new URL("http://a.b.c/")); + assertEquals("login", at.getUser()); + assertEquals("password", at.getPass()); + at = am.getAuthForURL(new URL("http://a.b.c:80/")); // same as above + assertEquals("login", at.getUser()); + assertEquals("password", at.getPass()); + at = am.getAuthForURL(new URL("http://a.b.c:443/"));// not same + assertNull(at); + at = am.getAuthForURL(new URL("http://a.b.c/1")); + assertEquals("login1", at.getUser()); + assertEquals("password1", at.getPass()); + assertEquals("", at.getDomain()); + assertEquals("", at.getRealm()); + at = am.getAuthForURL(new URL("http://d.e.f/")); + assertEquals("user", at.getUser()); + assertEquals("pass", at.getPass()); + assertEquals("domain", at.getDomain()); + assertEquals("realm", at.getRealm()); + at = am.getAuthForURL(new URL("https://j.k.l/")); + assertEquals("jkl", at.getUser()); + assertEquals("pass", at.getPass()); + at = am.getAuthForURL(new URL("https://j.k.l:443/")); + assertEquals("jkl", at.getUser()); + assertEquals("pass", at.getPass()); + at = am.getAuthForURL(new URL("https://l.m.n/")); + assertEquals("lmn443", at.getUser()); + assertEquals("pass", at.getPass()); + at = am.getAuthForURL(new URL("https://l.m.n:443/")); + assertEquals("lmn443", at.getUser()); + assertEquals("pass", at.getPass()); + at = am.getAuthForURL(new URL("https://l.m.n:8443/")); + assertEquals("lmn8443", at.getUser()); + assertEquals("pass", at.getPass()); + } + @Test public void testAddFileWithoutDomainAndRealmWithMechanism() throws IOException { File authFile = File.createTempFile("auth", ".txt"); From b212e23b447fa25b5e9414a2f879779c6c6b3379 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Mon, 24 Jun 2019 21:52:29 +0200 Subject: [PATCH 14/16] Use same logic --- .../org/apache/jmeter/protocol/http/control/AuthManager.java | 2 +- .../org/apache/jmeter/protocol/http/control/CacheManager.java | 2 +- .../org/apache/jmeter/protocol/http/control/CookieManager.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java index 12ae431d483..f6f9f103026 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java @@ -556,7 +556,7 @@ public void testEnded(String host) { public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) - || (getClearEachIteration() && !getControlledByThread())) { + || (!getControlledByThread() && getClearEachIteration())) { kerberosManager.clearSubjects(); } } diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java index 85c4cc47e54..03c6d84cca9 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java @@ -634,7 +634,7 @@ public void testEnded(String host) { public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) - ||(!getControlledByThread()&&getClearEachIteration())) { + || (!getControlledByThread() && getClearEachIteration())) { clearCache(); } useExpires = getUseExpires(); // cache the value diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java index e8dffacebab..aea116e485a 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CookieManager.java @@ -440,7 +440,7 @@ public void testEnded(String host) { public void testIterationStart(LoopIterationEvent event) { JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables(); if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration()) - || (getClearEachIteration()&&!getControlledByThread())) { + || (!getControlledByThread() && getClearEachIteration())) { log.debug("Initialise cookies from pre-defined list"); // No need to call clear setProperty(initialCookies.clone()); From c3d459252b25ae7ff1817be4db9632d971dbe605 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Tue, 2 Jul 2019 21:46:02 +0200 Subject: [PATCH 15/16] Add docs Add JUnit --- .../TestAuthManagerThreadIteration.java | 117 ++++++++++++++++++ .../screenshots/webtest/threadgroup3.png | Bin 0 -> 64355 bytes .../screenshots/webtest/threadgroup4.png | Bin 0 -> 25471 bytes .../screenshots/webtest/threadgroup5.png | Bin 0 -> 29946 bytes .../screenshots/webtest/threadgroup6.png | Bin 0 -> 21381 bytes xdocs/usermanual/build-web-test-plan.xml | 21 ++++ 6 files changed, 138 insertions(+) create mode 100644 test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java create mode 100644 xdocs/images/screenshots/webtest/threadgroup3.png create mode 100644 xdocs/images/screenshots/webtest/threadgroup4.png create mode 100644 xdocs/images/screenshots/webtest/threadgroup5.png create mode 100644 xdocs/images/screenshots/webtest/threadgroup6.png diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java new file mode 100644 index 00000000000..4282a2d3c4e --- /dev/null +++ b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.jmeter.protocol.http.control; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.lang.reflect.Field; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.security.auth.Subject; + +import org.apache.jmeter.threads.JMeterContext; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.JMeterVariables; +import org.junit.Before; +import org.junit.Test; + +public class TestAuthManagerThreadIteration { + private JMeterContext jmctx; + private JMeterVariables jmvars; + private static final String SAME_USER = "__jmv_SAME_USER"; + private final ConcurrentMap> subjects = new ConcurrentHashMap<>(); + + public KerberosManager initKerberosManager() throws IllegalAccessException, NoSuchFieldException { + KerberosManager kerberosManager = new KerberosManager(); + Future future = Executors.newSingleThreadExecutor().submit(new Callable() { + @Override + public Subject call() throws Exception { + return new Subject(); + } + }); + subjects.put("test", future); + Field privateField = kerberosManager.getClass().getDeclaredField("subjects"); + privateField.setAccessible(true); + privateField.set(kerberosManager, subjects); + return kerberosManager; + } + @Before + public void setUp() { + jmctx = JMeterContextService.getContext(); + jmvars = new JMeterVariables(); + } + + @Test + public void testJmeterVariableAuthorizationWhenThreadIterationIsADifferentUser() + throws IllegalAccessException, NoSuchFieldException { + //Test button clear after each Iteration + KerberosManager kerberosManager=initKerberosManager(); + AuthManager authManager = new AuthManager(); + Field authPrivateField = authManager.getClass().getDeclaredField("kerberosManager"); + authPrivateField.setAccessible(true); + authPrivateField.set(authManager, kerberosManager); + assertNotNull("Before the iteration, the AuthManager shouldn't be cleared",subjects.get("test")); + authManager.setControlledByThread(false); + authManager.setClearEachIteration(true); + authManager.testIterationStart(null); + assertNull("After the iteration, the AuthManager should be cleared",subjects.get("test")); + //Test button controlled by Thread + kerberosManager=initKerberosManager(); + jmvars.putObject(SAME_USER, false); + jmctx.setVariables(jmvars); + authManager.setThreadContext(jmctx); + authPrivateField.set(authManager, kerberosManager); + assertNotNull("Before the iteration, the AuthManager shouldn't be cleared",subjects.get("test")); + authManager.setControlledByThread(true); + authManager.setClearEachIteration(false); + authManager.testIterationStart(null); + assertNull("After the iteration, the AuthManager should be cleared",subjects.get("test")); + } + + @Test + public void testJmeterVariableAuthorizationWhenThreadIterationIsASameUser() + throws IllegalAccessException, NoSuchFieldException { + // Test button clear after each Iteration + KerberosManager kerberosManager = initKerberosManager(); + AuthManager authManager = new AuthManager(); + Field authPrivateField = authManager.getClass().getDeclaredField("kerberosManager"); + authPrivateField.setAccessible(true); + authPrivateField.set(authManager, kerberosManager); + assertNotNull("Before the iteration, the AuthManager shouldn't be cleared", subjects.get("test")); + authManager.setControlledByThread(false); + authManager.setClearEachIteration(false); + authManager.testIterationStart(null); + assertNotNull("After the iteration, the AuthManager shouldn't be cleared", subjects.get("test")); + // Test button controlled by Thread + kerberosManager = initKerberosManager(); + jmvars.putObject(SAME_USER, true); + jmctx.setVariables(jmvars); + authManager.setThreadContext(jmctx); + authPrivateField.set(authManager, kerberosManager); + assertNotNull("Before the iteration, the AuthManager shouldn't be cleared", subjects.get("test")); + authManager.setControlledByThread(true); + authManager.setClearEachIteration(false); + authManager.testIterationStart(null); + assertNotNull("After the iteration, the AuthManager shouldn't be cleared", subjects.get("test")); + } +} diff --git a/xdocs/images/screenshots/webtest/threadgroup3.png b/xdocs/images/screenshots/webtest/threadgroup3.png new file mode 100644 index 0000000000000000000000000000000000000000..ed6c8d41ec8b685f4cbdc15dcbfcb0f8cdc35cdd GIT binary patch literal 64355 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
^NbSBwz-IA! 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 literal 0 HcmV?d00001 diff --git a/xdocs/images/screenshots/webtest/threadgroup5.png b/xdocs/images/screenshots/webtest/threadgroup5.png new file mode 100644 index 0000000000000000000000000000000000000000..72bec099bee5f4ba22e12cbd4006d00c78c8af0f GIT binary patch literal 29946 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⋙B=(e60OyPw*Cp$}b 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 literal 0 HcmV?d00001 diff --git a/xdocs/images/screenshots/webtest/threadgroup6.png b/xdocs/images/screenshots/webtest/threadgroup6.png new file mode 100644 index 0000000000000000000000000000000000000000..74ff0a66a7c71af8fb2ec40e4118d78810a8d7b8 GIT binary patch literal 21381 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 aVJFJ7mQ5WyDilGu1{mCbq+753IObpAUA~e4 literal 0 HcmV?d00001 diff --git a/xdocs/usermanual/build-web-test-plan.xml b/xdocs/usermanual/build-web-test-plan.xml index 84f8e1c07aa..6afe4ab1dee 100644 --- a/xdocs/usermanual/build-web-test-plan.xml +++ b/xdocs/usermanual/build-web-test-plan.xml @@ -227,5 +227,26 @@ Figure §-num;.8. Sample HTTP login request +
+

+When creating a Test Plan, on each Thread Group iteration, we can choose to simulate the same user running multiple iterations, +or different users running one iteration. +You can configure this behaviour on Thread Group element, and have HTTP Cache Manager, HTTP Cookie Manager, HTTP Authorization Manager +controlled by this setting. +

+
+Figure §-num;.9. Choose the same user or different users
+

+You can choose to clear the cookies/cache content/authorization in the CookieManager/CacheManager/Authorization Manager, + or choose to be controlled by the Thread Group. +

+
+Figure §-num;.10. Use Thread Group to control CookieManager
+
+Figure §-num;.11. Use Thread Group to control CacheManager
+
+Figure §-num;.12. Use Thread Group to control Authorization Manager
+
+ From 6a560cb0badb4aecb467b567505bf2db2204d9fb Mon Sep 17 00:00:00 2001 From: pmouawad Date: Tue, 2 Jul 2019 22:00:40 +0200 Subject: [PATCH 16/16] Rename some vars and add spaces --- .../org/apache/jmeter/threads/ThreadGroup.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/org/apache/jmeter/threads/ThreadGroup.java b/src/core/org/apache/jmeter/threads/ThreadGroup.java index 6ed40f3f971..6e70fe270a6 100644 --- a/src/core/org/apache/jmeter/threads/ThreadGroup.java +++ b/src/core/org/apache/jmeter/threads/ThreadGroup.java @@ -215,7 +215,7 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread this.threadGroupTree = threadGroupTree; int numThreads = getNumThreads(); int rampUpPeriodInSeconds = getRampUp(); - boolean isSameUser = isSameUserOnNextIteration(); + boolean isSameUserOnNextIteration = isSameUserOnNextIteration(); delayedStartup = isDelayedStartup(); // Fetch once; needs to stay constant log.info("Starting thread group... number={} threads={} ramp-up={} delayedStart={}", groupNumber, numThreads, rampUpPeriodInSeconds, delayedStartup); @@ -236,11 +236,11 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread delayForNextThreadInMillis += perThreadDelayInMillis - timeElapsedToStartLastThread; } if (log.isDebugEnabled()) { - log.debug("Computed delayForNextThreadInMillis:{} for thread:{}", delayForNextThreadInMillis); + log.debug("Computed delayForNextThreadInMillis:{} for thread:{}", delayForNextThreadInMillis, Thread.currentThread().getId()); } lastThreadStartInMillis = nowInMillis; startNewThread(notifier, threadGroupTree, engine, threadNum, context, - nowInMillis, Math.max(0, delayForNextThreadInMillis), isSameUser); + nowInMillis, Math.max(0, delayForNextThreadInMillis), isSameUserOnNextIteration); } } log.info("Started thread group number {}", groupNumber); @@ -259,7 +259,7 @@ public void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread * @return {@link JMeterThread} newly created */ private JMeterThread startNewThread(ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine, - int threadNum, final JMeterContext context, long now, int delay,Boolean isSameUserOnNextIteration) { + int threadNum, final JMeterContext context, long now, int delay, Boolean isSameUserOnNextIteration) { JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context, isSameUserOnNextIteration); scheduleThread(jmThread, now); // set start and end time jmThread.setInitialDelay(delay); @@ -305,7 +305,7 @@ private JMeterThread makeThread( boolean onErrorStopThread = getOnErrorStopThread(); boolean onErrorStartNextLoop = getOnErrorStartNextLoop(); String groupName = getName(); - final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier,isSameUserOnNextIteration); + final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier, isSameUserOnNextIteration); jmeterThread.setThreadNum(threadNumber); jmeterThread.setThreadGroup(this); jmeterThread.setInitialContext(context); @@ -599,7 +599,7 @@ public void run() { final int numThreads = getNumThreads(); final float rampUpOriginInMillis = (float) getRampUp() * 1000; final long startTimeInMillis = System.currentTimeMillis(); - final boolean isSameUser = isSameUserOnNextIteration(); + final boolean isSameUserOnNextIteration = isSameUserOnNextIteration(); for (int threadNumber = 0; running && threadNumber < numThreads; threadNumber++) { if (threadNumber > 0) { long elapsedInMillis = System.currentTimeMillis() - startTimeInMillis; @@ -610,7 +610,7 @@ public void run() { if (usingScheduler && System.currentTimeMillis() > endtime) { break; // no point continuing beyond the end time } - JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNumber, context, isSameUser); + JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNumber, context, isSameUserOnNextIteration); jmThread.setInitialDelay(0); // Already waited if (usingScheduler) { jmThread.setScheduled(true);