Skip to content

Commit d42764a

Browse files
committed
feat: add ConstantThroughputTimerSchema and use it in ConstantThroughputTimer
1 parent 7a86a43 commit d42764a

File tree

8 files changed

+210
-98
lines changed

8 files changed

+210
-98
lines changed

src/components/src/main/java/org/apache/jmeter/config/CSVDataSet.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@
2929
import org.apache.jmeter.testbeans.TestBean;
3030
import org.apache.jmeter.testbeans.gui.GenericTestBeanCustomizer;
3131
import org.apache.jmeter.testelement.property.JMeterProperty;
32-
import org.apache.jmeter.testelement.property.StringProperty;
3332
import org.apache.jmeter.threads.JMeterContext;
3433
import org.apache.jmeter.threads.JMeterVariables;
3534
import org.apache.jmeter.util.JMeterUtils;
3635
import org.apache.jorphan.util.JMeterStopThreadException;
3736
import org.apache.jorphan.util.JOrphanUtils;
3837
import org.apache.jorphan.util.StringUtilities;
38+
import org.jspecify.annotations.Nullable;
3939
import org.slf4j.Logger;
4040
import org.slf4j.LoggerFactory;
4141

@@ -68,6 +68,24 @@
6868
@TestElementMetadata(labelResource = "displayName")
6969
public class CSVDataSet extends ConfigTestElement
7070
implements TestBean, LoopIterationListener, NoConfigMerge {
71+
72+
public enum ShareMode {
73+
ALL("shareMode.all"),
74+
GROUP("shareMode.group"),
75+
THREAD("shareMode.thread");
76+
77+
private final String value;
78+
79+
ShareMode(String value) {
80+
this.value = value;
81+
}
82+
83+
@Override
84+
public String toString() {
85+
return value;
86+
}
87+
}
88+
7189
private static final Logger log = LoggerFactory.getLogger(CSVDataSet.class);
7290

7391
private static final long serialVersionUID = 233L;
@@ -119,9 +137,10 @@ private Object readResolve(){
119137
public void setProperty(JMeterProperty property) {
120138
String propName = property.getName();
121139
if ("shareMode".equals(propName)) {
122-
String enumLabel = GenericTestBeanCustomizer.normalizeEnumStringValue(getClass(), CSVDataSetBeanInfo.ShareMode.class, property);
123-
if (enumLabel != null && (!(property instanceof StringProperty) || !enumLabel.equals(property.getStringValue()))) {
124-
super.setProperty(propName, enumLabel);
140+
ShareMode shareMode =
141+
GenericTestBeanCustomizer.normalizeEnumProperty(getClass(), ShareMode.class, property);
142+
if (shareMode != null) {
143+
super.setProperty(propName, shareMode.toString());
125144
return;
126145
}
127146
}
@@ -193,13 +212,15 @@ private void initVars(FileServer server, final JMeterContext context, String del
193212
}
194213

195214
private void setAlias(final JMeterContext context, String alias) {
196-
String mode = getShareMode();
197-
int modeInt = CSVDataSetBeanInfo.getShareModeAsInt(mode);
198-
this.alias = switch(modeInt){
199-
case CSVDataSetBeanInfo.SHARE_ALL -> alias;
200-
case CSVDataSetBeanInfo.SHARE_GROUP -> alias + "@" + System.identityHashCode(context.getThreadGroup());
201-
case CSVDataSetBeanInfo.SHARE_THREAD -> alias + "@" + System.identityHashCode(context.getThread());
202-
default -> alias + "@" + mode; // user-specified key
215+
ShareMode modeEnum = getShareModeAsEnum();
216+
if (modeEnum == null) {
217+
this.alias = alias + "@" + getShareMode(); // user-specified key
218+
return;
219+
}
220+
this.alias = switch (modeEnum) {
221+
case ALL -> alias;
222+
case GROUP -> alias + "@" + System.identityHashCode(context.getThreadGroup());
223+
case THREAD -> alias + "@" + System.identityHashCode(context.getThread());
203224
};
204225
}
205226

@@ -294,6 +315,10 @@ public String getShareMode() {
294315
return shareMode;
295316
}
296317

318+
@Nullable ShareMode getShareModeAsEnum() {
319+
return GenericTestBeanCustomizer.normalizeEnumStringValue(getShareMode(), getClass(), ShareMode.class);
320+
}
321+
297322
public void setShareMode(String value) {
298323
this.shareMode = value;
299324
}

src/components/src/main/java/org/apache/jmeter/config/CSVDataSetBeanInfo.java

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,12 @@ public class CSVDataSetBeanInfo extends BeanInfoSupport {
3939
private static final String QUOTED_DATA = "quotedData"; //$NON-NLS-1$
4040
private static final String SHAREMODE = "shareMode"; //$NON-NLS-1$
4141

42-
// Access needed from CSVDataSet
43-
enum ShareMode {
44-
ALL("shareMode.all"),
45-
GROUP("shareMode.group"),
46-
THREAD("shareMode.thread");
47-
48-
private final String value;
49-
50-
ShareMode(String value) {
51-
this.value = value;
52-
}
53-
54-
@Override
55-
public String toString() {
56-
return value;
57-
}
58-
}
5942
private static final String[] SHARE_TAGS = new String[3];
6043
static final int SHARE_ALL = 0;
61-
static final int SHARE_GROUP = 1;
62-
static final int SHARE_THREAD = 2;
6344

6445
// Store the resource keys
6546
static {
66-
for (ShareMode value : ShareMode.values()) {
47+
for (CSVDataSet.ShareMode value : CSVDataSet.ShareMode.values()) {
6748
@SuppressWarnings("EnumOrdinal")
6849
int index = value.ordinal();
6950
SHARE_TAGS[index] = value.toString();

src/components/src/main/java/org/apache/jmeter/timers/ConstantThroughputTimer.java

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,26 @@
2626
import org.apache.jmeter.testbeans.TestBean;
2727
import org.apache.jmeter.testbeans.gui.GenericTestBeanCustomizer;
2828
import org.apache.jmeter.testelement.AbstractTestElement;
29-
import org.apache.jmeter.testelement.property.DoubleProperty;
30-
import org.apache.jmeter.testelement.property.IntegerProperty;
3129
import org.apache.jmeter.testelement.property.JMeterProperty;
32-
import org.apache.jmeter.testelement.property.StringProperty;
30+
import org.apache.jmeter.testelement.schema.PropertiesAccessor;
3331
import org.apache.jmeter.threads.AbstractThreadGroup;
3432
import org.apache.jmeter.threads.JMeterContextService;
3533
import org.apache.jmeter.util.JMeterUtils;
3634
import org.apache.jorphan.collections.IdentityKey;
35+
import org.apache.jorphan.util.EnumUtils;
36+
import org.apiguardian.api.API;
3737

3838
/**
3939
* This class implements a constant throughput timer. A Constant Throughput
4040
* Timer paces the samplers under its influence so that the total number of
4141
* samples per unit of time approaches a given constant as much as possible.
4242
*
43+
* <p>
4344
* There are two different ways of pacing the requests:
44-
* - delay each thread according to when it last ran
45-
* - delay each thread according to when any thread last ran
45+
* <ul>
46+
* <li>delay each thread according to when it last ran</li>
47+
* <li>delay each thread according to when any thread last ran</li>
48+
* </ul>
4649
*/
4750
@GUIMenuSortOrder(4)
4851
@TestElementMetadata(labelResource = "displayName")
@@ -75,8 +78,6 @@ public enum Mode {
7578
;
7679

7780
private final String propertyName; // The property name to be used to look up the display string
78-
// Enum#values() clones the array, and we don't want to pay that cost as we know we don't modify the array
79-
private static final Mode[] CACHED_VALUES = values();
8081

8182
Mode(String name) {
8283
this.propertyName = name;
@@ -108,14 +109,24 @@ public String toString() {
108109
public ConstantThroughputTimer() {
109110
}
110111

112+
@Override
113+
public ConstantThroughputTimerSchema getSchema() {
114+
return ConstantThroughputTimerSchema.INSTANCE;
115+
}
116+
117+
@Override
118+
public PropertiesAccessor<? extends ConstantThroughputTimer, ? extends ConstantThroughputTimerSchema> getProps() {
119+
return new PropertiesAccessor<>(this, getSchema());
120+
}
121+
111122
/**
112123
* Sets the desired throughput.
113124
*
114125
* @param throughput
115126
* Desired sampling rate, in samples per minute.
116127
*/
117128
public void setThroughput(double throughput) {
118-
setProperty(new DoubleProperty(THROUGHPUT, throughput));
129+
getSchema().getThroughput().set(this, throughput);
119130
}
120131

121132
/**
@@ -124,18 +135,40 @@ public void setThroughput(double throughput) {
124135
* @return the rate at which samples should occur, in samples per minute.
125136
*/
126137
public double getThroughput() {
127-
return getPropertyAsDouble(THROUGHPUT);
138+
return getSchema().getThroughput().get(this);
128139
}
129140

141+
@Deprecated
130142
@SuppressWarnings("EnumOrdinal")
131143
public int getCalcMode() {
132-
return getPropertyAsInt(CALC_MODE, DEFAULT_CALC_MODE.ordinal());
144+
Mode mode = getMode();
145+
if (mode == null) {
146+
mode = DEFAULT_CALC_MODE;
147+
}
148+
return mode.ordinal();
149+
}
150+
151+
@API(status = API.Status.MAINTAINED, since = "6.0.0")
152+
public Mode getMode() {
153+
String value = getSchema().getCalcMode().get(this);
154+
for (Mode mode : EnumUtils.values(Mode.class)) {
155+
if (mode.toString().equals(value)) {
156+
return mode;
157+
}
158+
};
159+
return DEFAULT_CALC_MODE;
133160
}
134161

162+
@Deprecated
135163
@SuppressWarnings("EnumOrdinal")
136164
public void setCalcMode(int mode) {
137-
Mode resolved = Mode.CACHED_VALUES[mode];
138-
setProperty(new IntegerProperty(CALC_MODE, resolved.ordinal()));
165+
setMode(EnumUtils.values(Mode.class).get(mode));
166+
}
167+
168+
@SuppressWarnings("EnumOrdinal")
169+
@API(status = API.Status.MAINTAINED, since = "6.0.0")
170+
public void setMode(Mode newMode) {
171+
getSchema().getCalcMode().set(this, newMode.toString());
139172
}
140173

141174
/**
@@ -266,24 +299,12 @@ public String toString() {
266299
public void setProperty(JMeterProperty property) {
267300
String propertyName = property.getName();
268301
if (propertyName.equals("calcMode")) {
269-
String enumLabel = GenericTestBeanCustomizer.normalizeEnumStringValue(getClass(), Mode.class, property);
270-
if (enumLabel != null && (!(property instanceof StringProperty) || !enumLabel.equals(property.getStringValue()))) {
271-
super.setProperty(propertyName, enumLabel);
302+
Mode mode = GenericTestBeanCustomizer.normalizeEnumProperty(getClass(), Mode.class, property);
303+
if (mode != null) {
304+
super.setProperty(propertyName, mode.toString());
272305
return;
273306
}
274307
}
275308
super.setProperty(property);
276309
}
277-
278-
// For access from test code
279-
Mode getMode() {
280-
int mode = getCalcMode();
281-
return Mode.CACHED_VALUES[mode];
282-
}
283-
284-
// For access from test code
285-
@SuppressWarnings("EnumOrdinal")
286-
void setMode(Mode newMode) {
287-
setCalcMode(newMode.ordinal());
288-
}
289310
}

src/components/src/main/java/org/apache/jmeter/timers/poissonarrivals/PreciseThroughputTimer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public long delay() {
107107
delay = Math.max(0, delay);
108108
long endTime = getThreadContext().getThread().getEndTime();
109109
if (endTime > 0 && now + delay > endTime) {
110-
throw new JMeterStopThreadException("The thread is scheduled to stop in " +
110+
throw new JMeterStopThreadException("The thread is sceduled to stop in " +
111111
(endTime - now) + " ms" +
112112
" and the throughput timer generates a delay of " + delay + "." +
113113
" Terminating the thread manually."
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.jmeter.timers
19+
20+
import org.apache.jmeter.testelement.TestElementSchema
21+
import org.apache.jmeter.testelement.schema.DoublePropertyDescriptor
22+
import org.apache.jmeter.testelement.schema.StringPropertyDescriptor
23+
import org.apiguardian.api.API
24+
25+
/**
26+
* Lists properties of a [ConstantThroughputTimer].
27+
* @see ConstantThroughputTimer
28+
* @since 6.0
29+
*/
30+
@API(status = API.Status.EXPERIMENTAL, since = "6.0.0")
31+
public abstract class ConstantThroughputTimerSchema : TestElementSchema() {
32+
public companion object INSTANCE : ConstantThroughputTimerSchema()
33+
34+
public val throughput: DoublePropertyDescriptor<ConstantThroughputTimerSchema>
35+
by double("throughput")
36+
37+
public val calcMode: StringPropertyDescriptor<ConstantThroughputTimerSchema>
38+
by string("calcMode")
39+
}

src/components/src/test/java/org/apache/jmeter/timers/ConstantThroughputTimerTest.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ class ConstantThroughputTimerTest {
3434
@Test
3535
void testTimer1() throws Exception {
3636
ConstantThroughputTimer timer = new ConstantThroughputTimer();
37-
assertEquals(0, timer.getCalcMode());// Assume this thread only
37+
@SuppressWarnings("deprecation")
38+
int calcMode = timer.getCalcMode();
39+
assertEquals(0, calcMode);// Assume this thread only
40+
assertEquals(ConstantThroughputTimer.Mode.ThisThreadOnly, timer.getMode());// Assume this thread only
3841
timer.setThroughput(60.0);// 1 per second
3942
long start = System.currentTimeMillis();
4043
long delay = timer.delay(); // Initialise
@@ -64,7 +67,9 @@ private static void assertAlmostEquals(long expected, long actual, long delta) {
6467
@Test
6568
void testTimer2() throws Exception {
6669
ConstantThroughputTimer timer = new ConstantThroughputTimer();
67-
assertEquals(0, timer.getCalcMode());// Assume this thread only
70+
@SuppressWarnings("deprecation")
71+
int calcMode = timer.getCalcMode();
72+
assertEquals(0, calcMode);// Assume this thread only
6873
timer.setThroughput(60.0);// 1 per second
6974
assertEquals(1000, timer.calculateCurrentTarget(0)); // Should delay for 1 second
7075
timer.setThroughput(60000.0);// 1 per milli-second
@@ -75,7 +80,10 @@ void testTimer2() throws Exception {
7580
void testTimer3() throws Exception {
7681
ConstantThroughputTimer timer = new ConstantThroughputTimer();
7782
timer.setMode(ConstantThroughputTimer.Mode.AllActiveThreads); //$NON-NLS-1$ - all threads
78-
assertEquals(1, timer.getCalcMode());// All threads
83+
@SuppressWarnings("deprecation")
84+
int calcMode = timer.getCalcMode();
85+
assertEquals(1, calcMode);// All threads
86+
assertEquals(ConstantThroughputTimer.Mode.AllActiveThreads, timer.getMode());// All threads
7987
for(int i=1; i<=10; i++){
8088
TestJMeterContextService.incrNumberOfThreads();
8189
}

0 commit comments

Comments
 (0)