You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by pm...@apache.org on 2019/07/02 20:10:07 UTC
[jmeter] branch master updated: This implements "Provide ability to
configure whether a new iteration is a new user or same user" (BUG 62861)
(#470)
This is an automated email from the ASF dual-hosted git repository.
pmouawad pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git
The following commit(s) were added to refs/heads/master by this push:
new 629c643 This implements "Provide ability to configure whether a new iteration is a new user or same user" (BUG 62861) (#470)
629c643 is described below
commit 629c6430e17df7f7f75891d29e2129ab55a57d68
Author: Philippe M <pm...@users.noreply.github.com>
AuthorDate: Tue Jul 2 22:10:02 2019 +0200
This implements "Provide ability to configure whether a new iteration is a new user or same user" (BUG 62861) (#470)
* 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
* Run clean
* Fix build error
* Take into account @vsi remark
* Rename getters/variables for better understanding
Move method from ThreadGroup to AbstractThreadGroup
* Remove added newline
* Remove trailing whitespace
* Make isSameUserOnNextIteration final as per @vlsi review
* Remove unintended space as per @vlsi review
* Fix :
controlledByThreadGroup is not configured
* Make Authentication Manager use Thread Group config
* modify clear of cookiemanager and cachemanager
* modify clear of cookiemanager and cachemanager
* Use same logic
* Add docs
Add JUnit
* Rename some vars and add spaces
---
.travis.yml | 2 +-
.../apache/jmeter/resources/messages.properties | 5 +
.../apache/jmeter/resources/messages_fr.properties | 5 +
.../apache/jmeter/threads/AbstractThreadGroup.java | 28 ++
.../org/apache/jmeter/threads/JMeterThread.java | 12 +-
.../org/apache/jmeter/threads/JMeterVariables.java | 9 +
.../org/apache/jmeter/threads/ThreadGroup.java | 25 +-
.../apache/jmeter/threads/gui/ThreadGroupGui.java | 31 +-
.../jmeter/protocol/http/control/AuthManager.java | 17 +-
.../jmeter/protocol/http/control/CacheManager.java | 14 +-
.../protocol/http/control/CookieManager.java | 14 +-
.../apache/jmeter/protocol/http/gui/AuthPanel.java | 23 +-
.../jmeter/protocol/http/gui/CacheManagerGui.java | 27 +-
.../jmeter/protocol/http/gui/CookiePanel.java | 14 +
.../jmeter/protocol/http/sampler/HTTPHC4Impl.java | 12 +-
.../control/TestAuthManagerThreadIteration.java | 117 ++++++
.../control/TestCacheManagerThreadIteration.java | 427 +++++++++++++++++++++
.../control/TestCookieManagerThreadIteration.java | 182 +++++++++
.../protocol/http/sampler/TestHTTPHC4Impl.java | 61 +++
xdocs/images/screenshots/webtest/threadgroup3.png | Bin 0 -> 64355 bytes
xdocs/images/screenshots/webtest/threadgroup4.png | Bin 0 -> 25471 bytes
xdocs/images/screenshots/webtest/threadgroup5.png | Bin 0 -> 29946 bytes
xdocs/images/screenshots/webtest/threadgroup6.png | Bin 0 -> 21381 bytes
xdocs/usermanual/build-web-test-plan.xml | 21 +
24 files changed, 1022 insertions(+), 24 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index d53e617..37ad1a7 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:
diff --git a/src/core/org/apache/jmeter/resources/messages.properties b/src/core/org/apache/jmeter/resources/messages.properties
index 9a67475..63e93bf 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
@@ -175,6 +176,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 +231,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 +1283,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 f7883c2..ec03294 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
@@ -170,6 +171,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 +226,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 +1272,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/AbstractThreadGroup.java b/src/core/org/apache/jmeter/threads/AbstractThreadGroup.java
index daefdb7..06ecc63 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.engine.event.LoopIterationListener;
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 abstract class AbstractThreadGroup extends AbstractTestElement
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:
+ * <ul>
+ * <li>true means same user running multiple iterations</li>
+ * <li>false means a different user for each iteration</li>
+ * </ul>
+ * @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 03eb44d..2cec5e7 100644
--- a/src/core/org/apache/jmeter/threads/JMeterThread.java
+++ b/src/core/org/apache/jmeter/threads/JMeterThread.java
@@ -123,8 +123,10 @@ public class JMeterThread implements Runnable, Interruptible {
private long endTime = 0;
- private boolean scheduler = false;
+ private final boolean isSameUserOnNextIteration;
+
// based on this scheduler is enabled or disabled
+ private boolean scheduler = false;
// Gives access to parent thread threadGroup
private AbstractThreadGroup threadGroup;
@@ -149,6 +151,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 isSameUserOnNextIteration) {
this.monitor = monitor;
threadVars = new JMeterVariables();
testTree = test;
@@ -162,6 +168,7 @@ public class JMeterThread implements Runnable, Interruptible {
sampleMonitors = sampleMonitorSearcher.getSearchResults();
notifier = note;
running = true;
+ this.isSameUserOnNextIteration = isSameUserOnNextIteration;
}
public void setInitialContext(JMeterContext context) {
@@ -237,13 +244,11 @@ public class JMeterThread implements Runnable, Interruptible {
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 +692,7 @@ public class JMeterThread implements Runnable, Interruptible {
* @return the iteration listener
*/
private IterationListener initRun(JMeterContext threadContext) {
+ 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 7a005ed..8180e48 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,11 @@ public class JMeterVariables {
public Set<Entry<String, Object>> entrySet(){
return Collections.unmodifiableMap(variables).entrySet();
}
+
+ /**
+ * @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 72c2d7a..6e70fe2 100644
--- a/src/core/org/apache/jmeter/threads/ThreadGroup.java
+++ b/src/core/org/apache/jmeter/threads/ThreadGroup.java
@@ -69,7 +69,6 @@ public class ThreadGroup extends AbstractThreadGroup {
/** Scheduler start delay, overrides start time */
public static final String DELAY = "ThreadGroup.delay";
-
//- JMX entries
private transient Thread threadStarter;
@@ -216,6 +215,7 @@ public class ThreadGroup extends AbstractThreadGroup {
this.threadGroupTree = threadGroupTree;
int numThreads = getNumThreads();
int rampUpPeriodInSeconds = getRampUp();
+ 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 class ThreadGroup extends AbstractThreadGroup {
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));
+ startNewThread(notifier, threadGroupTree, engine, threadNum, context,
+ nowInMillis, Math.max(0, delayForNextThreadInMillis), isSameUserOnNextIteration);
}
}
log.info("Started thread group number {}", groupNumber);
@@ -255,11 +255,12 @@ public class ThreadGroup extends AbstractThreadGroup {
* @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) {
- JMeterThread jmThread = makeThread(notifier, threadGroupTree, engine, threadNum, context);
+ 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());
@@ -292,18 +293,19 @@ public class ThreadGroup extends AbstractThreadGroup {
* @param engine {@link StandardJMeterEngine}
* @param threadNumber int thread number
* @param context {@link JMeterContext}
+ * @param isSameUserOnNextIteration Boolean
* @return {@link JMeterThread}
*/
private JMeterThread makeThread(
ListenerNotifier notifier, ListedHashTree threadGroupTree,
- StandardJMeterEngine engine, int threadNumber,
- JMeterContext context) { // N.B. Context needs to be fetched in the correct thread
+ StandardJMeterEngine engine, int threadNumber,
+ 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);
+ final JMeterThread jmeterThread = new JMeterThread(cloneTree(threadGroupTree), this, notifier, isSameUserOnNextIteration);
jmeterThread.setThreadNum(threadNumber);
jmeterThread.setThreadGroup(this);
jmeterThread.setInitialContext(context);
@@ -329,7 +331,7 @@ public class ThreadGroup extends AbstractThreadGroup {
numThreads = getNumThreads();
setNumThreads(numThreads + 1);
}
- newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay);
+ newJmThread = startNewThread(notifier, threadGroupTree, engine, numThreads, context, now, delay, isSameUserOnNextIteration());
JMeterContextService.addTotalThreads( 1 );
log.info("Started new thread in group {}", groupNumber);
return newJmThread;
@@ -597,6 +599,7 @@ public class ThreadGroup extends AbstractThreadGroup {
final int numThreads = getNumThreads();
final float rampUpOriginInMillis = (float) getRampUp() * 1000;
final long startTimeInMillis = System.currentTimeMillis();
+ final boolean isSameUserOnNextIteration = isSameUserOnNextIteration();
for (int threadNumber = 0; running && threadNumber < numThreads; threadNumber++) {
if (threadNumber > 0) {
long elapsedInMillis = System.currentTimeMillis() - startTimeInMillis;
@@ -607,7 +610,7 @@ public class ThreadGroup extends AbstractThreadGroup {
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, isSameUserOnNextIteration);
jmThread.setInitialDelay(0); // Already waited
if (usingScheduler) {
jmThread.setScheduled(true);
diff --git a/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java b/src/core/org/apache/jmeter/threads/gui/ThreadGroupGui.java
index 1c05e8f..1010c3a 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.ItemEvent;
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 class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
tg.setProperty(new BooleanProperty(ThreadGroup.SCHEDULER, scheduler.isSelected()));
tg.setProperty(ThreadGroup.DURATION, duration.getText());
tg.setProperty(ThreadGroup.DELAY, delay.getText());
+ tg.setProperty(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION,sameUserBox.isSelected());
}
@Override
@@ -117,6 +125,12 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
duration.setText(tg.getPropertyAsString(ThreadGroup.DURATION));
delay.setText(tg.getPropertyAsString(ThreadGroup.DELAY));
+ final boolean isSameUser = tg.getPropertyAsBoolean(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION, false);
+ if (isSameUser){
+ sameUserBox.setSelected(true);
+ } else {
+ differentUserBox.setSelected(true);
+ }
}
@Override
@@ -193,6 +207,8 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
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 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
// LOOP COUNT
threadPropsPanel.add(createControllerPanel());
-
+ threadPropsPanel.add(createUserOptionsPanel());
if (showDelayedStart) {
delayedStart = new JCheckBox(JMeterUtils.getResString("delayed_start")); // $NON-NLS-1$
threadPropsPanel.add(delayedStart);
@@ -252,4 +268,17 @@ public class ThreadGroupGui extends AbstractThreadGroupGui implements ItemListen
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/AuthManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java
index 13ed6d3..8bc5148 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.engine.event.LoopIterationEvent;
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 class AuthManager extends ConfigTestElement implements TestStateListener,
/** {@inheritDoc} */
@Override
public void testIterationStart(LoopIterationEvent event) {
- if (getClearEachIteration()) {
+ JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
+ if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration())
+ || (!getControlledByThread() && 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/control/CacheManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java
index a75a6f8..529e460 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.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.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 class CacheManager extends ConfigTestElement implements TestStateListener
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 class CacheManager extends ConfigTestElement implements TestStateListener
@Override
public void testIterationStart(LoopIterationEvent event) {
- if (getClearEachIteration()) {
+ JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
+ if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration())
+ || (!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 1dc80a4..189030a 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.CollectionProperty;
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 class CookieManager extends ConfigTestElement implements TestStateListene
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 class CookieManager extends ConfigTestElement implements TestStateListene
/** {@inheritDoc} */
@Override
public void testIterationStart(LoopIterationEvent event) {
- if (getClearEachIteration()) {
+ JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
+ if ((getControlledByThread() && !jMeterVariables.isSameUserOnNextIteration())
+ || (!getControlledByThread() && 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/AuthPanel.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/AuthPanel.java
index 1cf6aab..9b34541 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 class AuthPanel extends AbstractConfigGui implements ActionListener {
authManager.clear();
authManager.addTestElement((TestElement) tableModel.manager.clone());
authManager.setClearEachIteration(clearEachIteration.isSelected());
+ authManager.setControlledByThread(controlledByThreadGroup.isSelected());
configureTestElement(el);
}
@@ -132,14 +137,17 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
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 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
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 @@ public class AuthPanel extends AbstractConfigGui implements ActionListener {
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();
}
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 0c1c789..8f9ada6 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 @@ import org.apache.jorphan.gui.layout.VerticalLayout;
* 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();
@@ -57,15 +61,18 @@ public class CacheManagerGui extends AbstractConfigGui {
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
public TestElement createTestElement() {
CacheManager element = new CacheManager();
modifyTestElement(element);
+ controlledByThreadGroup.setSelected(element.getControlledByThread());
+ clearEachIteration.setEnabled(!element.getControlledByThread());
return element;
}
@@ -75,6 +82,7 @@ public class CacheManagerGui extends AbstractConfigGui {
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 +99,7 @@ public class CacheManagerGui extends AbstractConfigGui {
clearEachIteration.setSelected(false);
useExpires.setSelected(true);
maxCacheSize.setText(""); //$NON-NLS-1$
+ controlledByThreadGroup.setSelected(false);
}
/**
@@ -102,12 +111,19 @@ public class CacheManagerGui extends AbstractConfigGui {
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 +138,11 @@ public class CacheManagerGui extends AbstractConfigGui {
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 61f5d6f..4f77a11 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 class CookiePanel extends AbstractConfigGui implements ActionListener {
@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 class CookiePanel extends AbstractConfigGui implements ActionListener {
cookieManager.add(cookie);
}
cookieManager.setClearEachIteration(clearEachIteration.isSelected());
+ cookieManager.setControlledByThread(controlledByThreadGroup.isSelected());
cookieManager.setCookiePolicy(policy.getText());
}
}
@@ -217,6 +223,7 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
tableModel.clearData();
clearEachIteration.setSelected(false);
+ controlledByThreadGroup.setSelected(false);
policy.setText(DEFAULT_POLICY);
configureButtonsState();
}
@@ -265,6 +272,8 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
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 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
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 @@ public class CookiePanel extends AbstractConfigGui implements ActionListener {
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 aad06a9..f93728f 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 @@ public class HTTPHC4Impl extends HTTPHCAbstractImpl {
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.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);
+ } 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/protocol/http/control/TestAuthManagerThreadIteration.java b/test/src/org/apache/jmeter/protocol/http/control/TestAuthManagerThreadIteration.java
new file mode 100644
index 0000000..4282a2d
--- /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<String, Future<Subject>> subjects = new ConcurrentHashMap<>();
+
+ public KerberosManager initKerberosManager() throws IllegalAccessException, NoSuchFieldException {
+ KerberosManager kerberosManager = new KerberosManager();
+ Future<Subject> future = Executors.newSingleThreadExecutor().submit(new Callable<Subject>() {
+ @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/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 0000000..d434416f
--- /dev/null
+++ b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerThreadIteration.java
@@ -0,0 +1,427 @@
+/*
+ * 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<org.apache.http.Header> 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<String, CacheManager.CacheEntry> getThreadCache() throws Exception {
+ Field threadLocalfield = CacheManager.class.getDeclaredField("threadCache");
+ threadLocalfield.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ ThreadLocal<Map<String, CacheManager.CacheEntry>> threadLocal = (ThreadLocal<Map<String, CacheManager.CacheEntry>>) 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
+ 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));
+ 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
+ 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));
+ 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 0000000..20c0586
--- /dev/null
+++ b/test/src/org/apache/jmeter/protocol/http/control/TestCookieManagerThreadIteration.java
@@ -0,0 +1,182 @@
+/*
+ * 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
+ 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();
+ 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
+ jmvars.putObject(SAME_USER, false);
+ jmctx.setVariables(jmvars);
+ cookieManagerDynamic.setControlledByThread(false);
+ 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 0000000..c63ee04
--- /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/xdocs/images/screenshots/webtest/threadgroup3.png b/xdocs/images/screenshots/webtest/threadgroup3.png
new file mode 100644
index 0000000..ed6c8d4
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup3.png differ
diff --git a/xdocs/images/screenshots/webtest/threadgroup4.png b/xdocs/images/screenshots/webtest/threadgroup4.png
new file mode 100644
index 0000000..56f16ff
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup4.png differ
diff --git a/xdocs/images/screenshots/webtest/threadgroup5.png b/xdocs/images/screenshots/webtest/threadgroup5.png
new file mode 100644
index 0000000..72bec09
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup5.png differ
diff --git a/xdocs/images/screenshots/webtest/threadgroup6.png b/xdocs/images/screenshots/webtest/threadgroup6.png
new file mode 100644
index 0000000..74ff0a6
Binary files /dev/null and b/xdocs/images/screenshots/webtest/threadgroup6.png differ
diff --git a/xdocs/usermanual/build-web-test-plan.xml b/xdocs/usermanual/build-web-test-plan.xml
index 84f8e1c..6afe4ab 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</figure>
</section>
+<section name="§-num;.7 choose the same user or different users" anchor="choose_users">
+<p>
+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.
+</p>
+<figure image="webtest/threadgroup3.png">
+Figure §-num;.9. Choose the same user or different users</figure>
+<p>
+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.
+</p>
+<figure image="webtest/threadgroup4.png">
+Figure §-num;.10. Use Thread Group to control CookieManager</figure>
+<figure image="webtest/threadgroup5.png">
+Figure §-num;.11. Use Thread Group to control CacheManager</figure>
+<figure image="webtest/threadgroup6.png">
+Figure §-num;.12. Use Thread Group to control Authorization Manager</figure>
+</section>
+
</body>
</document>