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: diff --git a/src/core/org/apache/jmeter/resources/messages.properties b/src/core/org/apache/jmeter/resources/messages.properties index 9a67475734e..63e93bf3ce1 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 f7883c25733..ec03294f4b1 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 daefdb76d93..06ecc636b46 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: + * + * @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 03eb44de407..2cec5e72bd9 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 JMeterThread(HashTree test, JMeterThreadMonitor monitor, ListenerNotifier sampleMonitors = sampleMonitorSearcher.getSearchResults(); notifier = note; running = true; + this.isSameUserOnNextIteration = isSameUserOnNextIteration; } public void setInitialContext(JMeterContext context) { @@ -237,13 +244,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 +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, 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 7a005ed94a7..8180e485606 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 Iterator> getIterator(){ public Set> 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 72c2d7a4be4..6e70fe270a6 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 void start(int groupNum, ListenerNotifier notifier, ListedHashTree thread 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 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)); + startNewThread(notifier, threadGroupTree, engine, threadNum, context, + nowInMillis, Math.max(0, delayForNextThreadInMillis), isSameUserOnNextIteration); } } log.info("Started thread group number {}", groupNumber); @@ -255,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) { - 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 @@ private void registerStartedThread(JMeterThread jMeterThread, Thread newThread) * @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 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, isSameUserOnNextIteration()); JMeterContextService.addTotalThreads( 1 ); log.info("Started new thread in group {}", groupNumber); return newJmThread; @@ -597,6 +599,7 @@ public void run() { 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 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, 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 1c05e8fa2f4..1010c3a72fd 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(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION,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(AbstractThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION, 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); @@ -252,4 +268,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/AuthManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/AuthManager.java index 13ed6d3141b..8bc51487b87 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()) + || (!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 a75a6f89b70..529e460b5a3 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.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 1dc80a40cd6..189030abcc4 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.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 1cf6aab6c78..9b34541789d 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(); } 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 0c1c789784a..8f9ada6a02c 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(); @@ -57,15 +61,18 @@ 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 public TestElement createTestElement() { CacheManager element = new CacheManager(); modifyTestElement(element); + controlledByThreadGroup.setSelected(element.getControlledByThread()); + clearEachIteration.setEnabled(!element.getControlledByThread()); return element; } @@ -75,6 +82,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 +99,7 @@ public void clearGui() { clearEachIteration.setSelected(false); useExpires.setSelected(true); maxCacheSize.setText(""); //$NON-NLS-1$ + controlledByThreadGroup.setSelected(false); } /** @@ -102,12 +111,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 +138,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 61f5d6fbea0..4f77a11636a 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 aad06a9417b..f93728fd3d0 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.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 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/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..d434416f7a3 --- /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 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 + 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 00000000000..20c058658ec --- /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 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/xdocs/images/screenshots/webtest/threadgroup3.png b/xdocs/images/screenshots/webtest/threadgroup3.png new file mode 100644 index 00000000000..ed6c8d41ec8 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 00000000000..56f16ffd87e 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 00000000000..72bec099bee 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 00000000000..74ff0a66a7c 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 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
+
+