Skip to content
Merged
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 @@ -23,12 +23,9 @@ import org.apache.jmeter.junit.JMeterTestCase
import org.apache.jmeter.modifiers.CounterConfig
import org.apache.jmeter.sampler.DebugSampler
import org.apache.jmeter.testelement.TestPlan
import org.apache.jmeter.testelement.property.TestElementProperty
import org.apache.jmeter.threads.AbstractThreadGroup
import org.apache.jorphan.collections.ListedHashTree
import org.apache.jorphan.test.JMeterSerialTest
import org.junit.Assert
import org.junit.Ignore
import org.junit.Test
import java.time.Duration

Expand All @@ -37,20 +34,18 @@ class OpenModelThreadGroupConfigElementTest : JMeterTestCase(), JMeterSerialTest
* Create Test Plan with Open Model Thread Group and Counter Config.
*/
@Test
@Ignore("Sometimes the listener gets no results for unknown reason")
// Un-comment if you want try running the test multiple times locally:
// @RepeatedTest(value = 10)
fun `ensure thread group initializes counter only once for each thread`() {
val listener = TestTransactionController.TestSampleListener()

val tree = ListedHashTree().apply {
add(TestPlan()).apply {
val threadGroup = OpenModelThreadGroup().apply {
name = "Thread Group"
scheduleString = "rate(5 / sec) random_arrivals(1 sec)"
setProperty(
TestElementProperty(
AbstractThreadGroup.MAIN_CONTROLLER, OpenModelThreadGroupController()
)
)
// 5 samples within 100ms
// Then 2 sec pause to let all the threads to finish, especially the ones that start at 99ms
scheduleString = "rate(50 / sec) random_arrivals(100 ms) pause(2 s)"
}
add(threadGroup).apply {
add(listener)
Expand Down Expand Up @@ -78,7 +73,7 @@ class OpenModelThreadGroupConfigElementTest : JMeterTestCase(), JMeterSerialTest
awaitTermination(Duration.ofSeconds(10))
}

// There's no guarantee that thread execute exactly in order, so we sort
// There's no guarantee that threads execute exactly in order, so we sort
// the labels to avoid test failure in case the thread execute out of order.
val actual = listener.events.map { it.result.sampleLabel }.sorted()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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.testelement.property;

import java.util.concurrent.TimeUnit;

import org.apache.jmeter.testelement.TestElementSchema;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.testelement.schema.BooleanPropertyDescriptor;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@Fork(value = 1, jvmArgsPrepend = {"-Xmx128m"})
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class PropertyGetBooleanBenchmarkJava {
TestPlan testPlan;
BooleanPropertyDescriptor<TestElementSchema> enabled;

@Setup
public void setup() {
testPlan = new TestPlan();
testPlan.setName("test plan name");
testPlan.setComment("test plan comment");
testPlan.setSerialized(true);
enabled = TestElementSchema.INSTANCE.getEnabled();
}

@Benchmark
public TestPlan configure_and_getBoolean() {
// Does not allocate heap
testPlan.getProps().invoke(
(plan, klass) -> {
// Note: set.. would allocate BooleanProperty, so we test get here
if (!plan.get(klass.getEnabled())) {
throw new IllegalStateException("enabled must be true");
}
}
);
return testPlan;
}

@Benchmark
public boolean props_klass_prop_getBoolean() {
return testPlan.getProps().getSchema().getEnabled().get(testPlan);
}

@Benchmark
public boolean booleanDescriptor_getBoolean() {
return enabled.get(testPlan);
}

@Benchmark
public boolean props_getBoolean() {
return testPlan.getProps().get(enabled);
}

@Benchmark
public boolean props_selector_getBoolean() {
return testPlan.getProps().get(TestElementSchema::getEnabled);
}

public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(PropertyGetBooleanBenchmarkJava.class.getSimpleName())
.addProfiler(GCProfiler.class)
.detectJvmArgs()
.build();
new Runner(opt).run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.testelement.property

import org.apache.jmeter.testelement.TestElementSchema
import org.apache.jmeter.testelement.TestPlan
import org.apache.jmeter.testelement.schema.BooleanPropertyDescriptor
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.BenchmarkMode
import org.openjdk.jmh.annotations.Fork
import org.openjdk.jmh.annotations.Measurement
import org.openjdk.jmh.annotations.Mode
import org.openjdk.jmh.annotations.OutputTimeUnit
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.Setup
import org.openjdk.jmh.annotations.State
import org.openjdk.jmh.annotations.Warmup
import org.openjdk.jmh.profile.GCProfiler
import org.openjdk.jmh.runner.Runner
import org.openjdk.jmh.runner.options.Options
import org.openjdk.jmh.runner.options.OptionsBuilder
import java.util.concurrent.TimeUnit

@Fork(value = 1, jvmArgsPrepend = ["-Xmx129m"])
@Measurement(iterations = 500, time = 1, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
open class PropertyGetBooleanBenchmarkKotlin {
lateinit var testPlan: TestPlan
lateinit var enabled: BooleanPropertyDescriptor<TestElementSchema>

@Setup
fun setup() {
testPlan = TestPlan().apply {
name = "test plan name"
comment = "test plan comment"
isSerialized = true
}
enabled = TestElementSchema.enabled
}

@Benchmark
fun getBoolean(): Boolean =
testPlan.isSerialized

@Benchmark
fun configure_and_getBoolean(): TestPlan {
// Does not allocate heap
testPlan.props {
// Note: set.. would allocate BooleanProperty, so we test get here
if (!it[enabled]) {
throw IllegalStateException("enabled must be true")
}
}
return testPlan
}

@Benchmark
open fun props_klass_prop_getBoolean(): Boolean =
testPlan.props.schema.enabled[testPlan]

@Benchmark
fun booleanDescriptor_getBoolean(): Boolean =
enabled[testPlan]

@Benchmark
fun props_getBoolean(): Boolean =
testPlan.props[enabled]

@Benchmark
fun props_selector_getBoolean(): Boolean =
testPlan.props[ { enabled }]
}

fun main() {
println(PropertyGetBooleanBenchmarkKotlin::class.java.simpleName)
val opt: Options = OptionsBuilder()
.include(PropertyGetBooleanBenchmarkKotlin::class.java.simpleName)
.addProfiler(GCProfiler::class.java)
.detectJvmArgs()
.build()
Runner(opt).run()
}
24 changes: 24 additions & 0 deletions src/core/src/main/java/org/apache/jmeter/JMeter.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestElementSchema;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.threads.RemoteThreadsListenerTestElement;
import org.apache.jmeter.util.BeanShellInterpreter;
Expand Down Expand Up @@ -426,6 +427,29 @@ public void start(String[] args) {
JMeterUtils.setProperty("START.YMD", getFormatter("yyyyMMdd").format(now));// $NON-NLS-1$ $NON-NLS-2$
JMeterUtils.setProperty("START.HMS", getFormatter("HHmmss").format(now));// $NON-NLS-1$ $NON-NLS-2$

// For unknown reason, TestElementSchema might fail to initialize in remote execution mode
// It reproduces with Java 11.0.13, and the error is StackOverflowError with the following stacktrace
// The workaround is to initialize Kotlin reflection before deserializing the test plan.
// at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174) ~[?:?]
// at java.net.URLClassLoader.defineClass(URLClassLoader.java:555) ~[?:?]
// at java.net.URLClassLoader$1.run(URLClassLoader.java:458) ~[?:?]
// at java.net.URLClassLoader$1.run(URLClassLoader.java:452) ~[?:?]
// at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
// at java.net.URLClassLoader.findClass(URLClassLoader.java:451) ~[?:?]
// at java.lang.ClassLoader.loadClass(ClassLoader.java:589) ~[?:?]
// at org.apache.jmeter.DynamicClassLoader.loadClass(DynamicClassLoader.java:81) ~[ApacheJMeter.jar:5.5.1-SNAPSHOT]
// at java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[?:?]
// at kotlin.jvm.internal.ClassReference.<clinit>(ClassReference.kt:156) ~[kotlin-stdlib-1.8.21.jar:1.8.21-release-380(1.8.21)]
// at kotlin.jvm.internal.ReflectionFactory.getOrCreateKotlinClass(ReflectionFactory.java:30) ~[kotlin-stdlib-1.8.21.jar)]
// at kotlin.jvm.internal.Reflection.getOrCreateKotlinClass(Reflection.java:60) ~[kotlin-stdlib-1.8.21.jar:1.8.21-release-380(1.8.21)]
// at org.apache.jmeter.testelement.TestElementSchema.<init>(TestElementSchema.kt:33) ~[ApacheJMeter_core.jar:5.5.1-SNAPSHOT]
// at org.apache.jmeter.testelement.TestElementSchema$INSTANCE.<init>(TestElementSchema.kt:26) ~[ApacheJMeter_core.jar:5.5.1-SNAPSHOT]
// at org.apache.jmeter.testelement.TestElementSchema$INSTANCE.<init>(TestElementSchema.kt) ~[ApacheJMeter_core.jar:5.5.1-SNAPSHOT]
// at org.apache.jmeter.testelement.TestElementSchema.<clinit>(TestElementSchema.kt) ~[ApacheJMeter_core.jar:5.5.1-SNAPSHOT]
// at org.apache.jmeter.protocol.java.sampler.BeanShellSampler.<clinit>(BeanShellSampler.java:53) ~[ApacheJMeter_java.jar:5.5.1-SNAPSHOT]
// at jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method) ~[?:?]
TestElementSchema.INSTANCE.getGuiClass();

if (parser.getArgumentById(VERSION_OPT) != null) {
displayAsciiArt();
} else if (parser.getArgumentById(HELP_OPT) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.awt.BorderLayout;
import java.util.Collection;

import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
Expand All @@ -29,13 +28,15 @@
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.gui.ArgumentsPanel;
import org.apache.jmeter.gui.AbstractJMeterGuiComponent;
import org.apache.jmeter.gui.JBooleanPropertyEditor;
import org.apache.jmeter.gui.TestElementMetadata;
import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.util.FileListPanel;
import org.apache.jmeter.gui.util.MenuFactory;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.testelement.TestPlanSchema;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.util.JMeterUtils;

Expand All @@ -52,11 +53,20 @@ public class TestPlanGui extends AbstractJMeterGuiComponent {
* A checkbox allowing the user to specify whether or not JMeter should do
* functional testing.
*/
private final JCheckBox functionalMode;
private final JBooleanPropertyEditor functionalMode =
new JBooleanPropertyEditor(
TestPlanSchema.INSTANCE.getFunctionalMode(),
JMeterUtils.getResString("functional_mode"));

private final JCheckBox serializedMode;
private final JBooleanPropertyEditor serializedMode =
new JBooleanPropertyEditor(
TestPlanSchema.INSTANCE.getSerializeThreadgroups(),
JMeterUtils.getResString("testplan.serialized"));

private final JCheckBox tearDownOnShutdown;
private final JBooleanPropertyEditor tearDownOnShutdown =
new JBooleanPropertyEditor(
TestPlanSchema.INSTANCE.getTearDownOnShutdown(),
JMeterUtils.getResString("teardown_on_shutdown"));

/** A panel allowing the user to define variables. */
private final ArgumentsPanel argsPanel;
Expand All @@ -69,9 +79,6 @@ public class TestPlanGui extends AbstractJMeterGuiComponent {
public TestPlanGui() {
browseJar = new FileListPanel(JMeterUtils.getResString("test_plan_classpath_browse"), ".jar"); // $NON-NLS-1$ $NON-NLS-2$
argsPanel = new ArgumentsPanel(JMeterUtils.getResString("user_defined_variables")); // $NON-NLS-1$
serializedMode = new JCheckBox(JMeterUtils.getResString("testplan.serialized")); // $NON-NLS-1$
functionalMode = new JCheckBox(JMeterUtils.getResString("functional_mode")); // $NON-NLS-1$
tearDownOnShutdown = new JCheckBox(JMeterUtils.getResString("teardown_on_shutdown"), true); // $NON-NLS-1$
init();
}

Expand Down Expand Up @@ -124,9 +131,10 @@ public void modifyTestElement(TestElement plan) {
super.configureTestElement(plan);
if (plan instanceof TestPlan) {
TestPlan tp = (TestPlan) plan;
tp.setFunctionalMode(functionalMode.isSelected());
tp.setTearDownOnShutdown(tearDownOnShutdown.isSelected());
tp.setSerialized(serializedMode.isSelected());
functionalMode.updateElement(tp);
tearDownOnShutdown.updateElement(tp);
serializedMode.updateElement(tp);
// TODO: set expression to TestPlan somehow
tp.setUserDefinedVariables((Arguments) argsPanel.createTestElement());
tp.setTestPlanClasspathArray(browseJar.getFiles());
}
Expand Down Expand Up @@ -164,9 +172,9 @@ public void configure(TestElement el) {
super.configure(el);
if (el instanceof TestPlan) {
TestPlan tp = (TestPlan) el;
functionalMode.setSelected(tp.isFunctionalMode());
serializedMode.setSelected(tp.isSerialized());
tearDownOnShutdown.setSelected(tp.isTearDownOnShutdown());
functionalMode.updateUi(tp);
serializedMode.updateUi(tp);
tearDownOnShutdown.updateUi(tp);
final JMeterProperty udv = tp.getUserDefinedVariablesAsProperty();
if (udv != null) {
argsPanel.configure((Arguments) udv.getObjectValue());
Expand Down Expand Up @@ -200,9 +208,9 @@ private void init() { // WARNING: called from ctor so must not be overridden (i.
@Override
public void clearGui() {
super.clearGui();
functionalMode.setSelected(false);
serializedMode.setSelected(false);
tearDownOnShutdown.setSelected(true);
functionalMode.reset();
serializedMode.reset();
tearDownOnShutdown.reset();
argsPanel.clear();
browseJar.clearFiles();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.util.Map;

@SuppressWarnings("deprecation")
abstract class AbstractTransformer implements ValueTransformer {

private CompoundVariable masterFunction;
Expand Down
Loading