Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.apache.jmeter.testelement.TestElementSchema;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.Printable;
import org.apache.jorphan.gui.JEditableCheckBox;
import org.apache.jorphan.gui.JFactory;
import org.apiguardian.api.API;
import org.slf4j.Logger;
Expand Down Expand Up @@ -70,9 +71,6 @@ public abstract class AbstractJMeterGuiComponent extends JPanel implements JMete
/** Logging */
private static final Logger log = LoggerFactory.getLogger(AbstractJMeterGuiComponent.class);

/** Flag indicating whether this component is enabled. */
private boolean enabled = true;

/**
* A GUI panel containing the name of this component.
* @deprecated use {@link #getName()} or {@link AbstractJMeterGuiComponent#createTitleLabel()} for better alignment of the fields
Expand All @@ -84,6 +82,10 @@ public abstract class AbstractJMeterGuiComponent extends JPanel implements JMete

private final JTextArea commentField = JFactory.tabMovesFocus(new JTextArea());

private final JBooleanPropertyEditor enabled = new JBooleanPropertyEditor(
TestElementSchema.INSTANCE.getEnabled(),
JMeterUtils.getResString("enable"));

/**
* Stores a collection of property editors, so GuiCompoenent can have default implementations that
* update the UI fields based on {@link TestElement} properties and vice versa.
Expand All @@ -99,6 +101,7 @@ public abstract class AbstractJMeterGuiComponent extends JPanel implements JMete
protected AbstractJMeterGuiComponent() {
namePanel = new NamePanel();
init();
bindingGroup.add(enabled);
}

/**
Expand Down Expand Up @@ -127,7 +130,7 @@ public void setComment(String comment) {
*/
@Override
public boolean isEnabled() {
return enabled;
return enabled.getValue().equals(JEditableCheckBox.Value.of(true));
}

/**
Expand All @@ -137,7 +140,7 @@ public boolean isEnabled() {
@Override
public void setEnabled(boolean enabled) {
log.debug("Setting enabled: {}", enabled);
this.enabled = enabled;
this.enabled.setValue(JEditableCheckBox.Value.of(enabled));
}

/**
Expand Down Expand Up @@ -211,7 +214,6 @@ protected Component createTitleLabel() {
@Override
public void configure(TestElement element) {
setName(element.getName());
enabled = element.isEnabled();
commentField.setText(element.getComment());
bindingGroup.updateUi(element);
}
Expand All @@ -225,7 +227,6 @@ public void configure(TestElement element) {
@Override
public void clearGui() {
initGui();
enabled = true;
}

private void initGui() {
Expand All @@ -241,7 +242,7 @@ private void init() {
@API(status = EXPERIMENTAL, since = "5.6.3")
public void modifyTestElement(TestElement element) {
JMeterGUIComponent.super.modifyTestElement(element);
modifyTestElementEnabledAndComment(element);
modifyTestElementComment(element);
bindingGroup.updateElement(element);
}

Expand All @@ -264,22 +265,18 @@ protected void configureTestElement(TestElement mc) {
TestElementSchema schema = TestElementSchema.INSTANCE;
mc.set(schema.getGuiClass(), getClass());
mc.set(schema.getTestClass(), mc.getClass());
modifyTestElementEnabledAndComment(mc);
modifyTestElementComment(mc);
// This stores the state of the TestElement
log.debug("setting element to enabled: {}", enabled.getValue());
enabled.updateElement(mc);
}

/**
* Assigns basic fields from UI to the test element: name, comments, gui class, and the registered editors.
*
* @param mc test element
*/
private void modifyTestElementEnabledAndComment(TestElement mc) {
// This stores the state of the TestElement
log.debug("setting element to enabled: {}", enabled);
// We can skip storing "enabled" state if it's true, as it's default value.
// JMeter removes disabled elements early from the tree, so configuration elements
// with enabled=false (~HTTP Request Defaults) can't unexpectedly override the regular ones
// like HTTP Request.
mc.set(TestElementSchema.INSTANCE.getEnabled(), enabled ? null : Boolean.FALSE);
private void modifyTestElementComment(TestElement mc) {
// Note: we can't use editors for "comments" as getComments() is not a final method, so plugins might
// override it and provide a different implementation.
mc.setComment(StringUtils.defaultIfEmpty(getComment(), null));
Expand All @@ -305,6 +302,7 @@ protected Container makeTitlePanel() {
commentField.setWrapStyleWord(true);
commentField.setLineWrap(true);
titlePanel.add(commentField);
titlePanel.add(enabled, "span 2");

// Note: VerticalPanel has a workaround for Box layout which aligns elements, so we can't
// use trivial JPanel.
Expand Down
22 changes: 17 additions & 5 deletions src/core/src/main/java/org/apache/jmeter/threads/JMeterThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,10 @@ public void run() {
iterationListener = initRun(threadContext);
while (running) {
Sampler sam = threadGroupLoopController.next();
while (running && sam != null) {
for (; running && sam != null; sam = threadGroupLoopController.next()) {
if (!sam.isEnabled()) {
continue;
}
processSampler(sam, null, threadContext);
threadContext.cleanAfterSample();

Expand Down Expand Up @@ -296,11 +299,8 @@ public void run() {
}
}
threadContext.setTestLogicalAction(TestLogicalAction.CONTINUE);
sam = null;
setLastSampleOk(threadContext.getVariables(), true);
}
else {
sam = threadGroupLoopController.next();
break;
}
}

Expand Down Expand Up @@ -898,6 +898,9 @@ private void stopThread() {

private static void checkAssertions(List<? extends Assertion> assertions, SampleResult parent, JMeterContext threadContext) {
for (Assertion assertion : assertions) {
if (!((TestElement) assertion).isEnabled()) {
continue;
}
TestBeanHelper.prepare((TestElement) assertion);
if (assertion instanceof AbstractScopedAssertion scopedAssertion) {
String scope = scopedAssertion.fetchScope();
Expand Down Expand Up @@ -965,13 +968,19 @@ private static void processAssertion(SampleResult result, Assertion assertion) {

private static void runPostProcessors(List<? extends PostProcessor> extractors) {
for (PostProcessor ex : extractors) {
if (!((TestElement) ex).isEnabled()) {
continue;
}
TestBeanHelper.prepare((TestElement) ex);
ex.process();
}
}

private static void runPreProcessors(List<? extends PreProcessor> preProcessors) {
for (PreProcessor ex : preProcessors) {
if (!((TestElement) ex).isEnabled()) {
continue;
}
if (log.isDebugEnabled()) {
log.debug("Running preprocessor: {}", ((AbstractTestElement) ex).getName());
}
Expand All @@ -992,6 +1001,9 @@ private static void runPreProcessors(List<? extends PreProcessor> preProcessors)
private void delay(List<? extends Timer> timers) {
long totalDelay = 0;
for (Timer timer : timers) {
if (!((TestElement) timer).isEnabled()) {
continue;
}
TestBeanHelper.prepare((TestElement) timer);
long delay = timer.delay();
if (APPLY_TIMER_FACTOR && timer.isModifiable()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class ListenerNotifier implements Serializable {
public void notifyListeners(SampleEvent res, List<SampleListener> listeners) {
for (SampleListener sampleListener : listeners) {
try {
if (!((TestElement) sampleListener).isEnabled()) {
continue;
}
TestBeanHelper.prepare((TestElement) sampleListener);
sampleListener.sampleOccurred(res);
} catch (RuntimeException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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.control

import org.apache.jmeter.junit.JMeterTestCase
import org.apache.jmeter.sampler.DebugSampler
import org.apache.jmeter.test.assertions.executePlanAndCollectEvents
import org.apache.jmeter.threads.ThreadGroup
import org.apache.jmeter.treebuilder.TreeBuilder
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import kotlin.time.Duration.Companion.seconds

class EnabledWithVariablesTest : JMeterTestCase() {
fun TreeBuilder.oneThread(loops: Int, body: ThreadGroup.() -> Unit) {
ThreadGroup::class {
numThreads = 1
rampUp = 0
setSamplerController(
LoopController().apply {
this.loops = loops
}
)
body()
}
}

@Test
fun `sampler with conditional enable function`() {
val events = executePlanAndCollectEvents(5.seconds) {
oneThread(loops = 4) {
DebugSampler::class {
name = "Conditionally enabled: \${__jm____idx}"
props {
it[enabled] = "\${__javaScript(vars.get('__jm____idx')%2==1)}"
}
}
}
}
Assertions.assertEquals(
"[Conditionally enabled: 1, Conditionally enabled: 3]",
events.map { it.result.sampleLabel }.toString(),
"Test should complete within reasonable time, and the test has 2 debug samplers, so we expect 2 events"
)
}
}
Loading