fix: allow setting empty strings with TestElement.set(...) API, and remove properties only in case the value is null
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/testelement/TestElement.kt b/src/core/src/main/kotlin/org/apache/jmeter/testelement/TestElement.kt
index d6cb9ec..0ec7924 100644
--- a/src/core/src/main/kotlin/org/apache/jmeter/testelement/TestElement.kt
+++ b/src/core/src/main/kotlin/org/apache/jmeter/testelement/TestElement.kt
@@ -285,13 +285,13 @@
getPropertyOrNull(property)?.stringValue ?: property.defaultValueAsString ?: ""
/**
- * Set property as string, or remove it if the given value is `null` or empty.
+ * Set property as string, or remove it if the given value is `null`.
* @since 5.6
*/
@JMeterPropertySchemaUnchecked
@API(status = API.Status.EXPERIMENTAL, since = "5.6")
public operator fun set(property: PropertyDescriptor<*, *>, value: String?) {
- removeOrSet(value.isNullOrEmpty(), property.name) {
+ removeOrSet(value == null, property.name) {
StringProperty(it, value)
}
}
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/testelement/schema/PropertiesAccessor.kt b/src/core/src/main/kotlin/org/apache/jmeter/testelement/schema/PropertiesAccessor.kt
index 05c6f0b..2f2f4a7 100644
--- a/src/core/src/main/kotlin/org/apache/jmeter/testelement/schema/PropertiesAccessor.kt
+++ b/src/core/src/main/kotlin/org/apache/jmeter/testelement/schema/PropertiesAccessor.kt
@@ -84,7 +84,7 @@
}
// All properties can be set as strings
- public operator fun set(property: PropertyDescriptor<Schema, *>, value: String) {
+ public operator fun set(property: PropertyDescriptor<Schema, *>, value: String?) {
target[property] = value
}
@@ -116,13 +116,13 @@
public inline operator fun get(propertySelector: Schema.() -> BooleanPropertyDescriptor<Schema>): Boolean =
target[propertySelector(schema)]
- public operator fun set(property: BooleanPropertyDescriptor<Schema>, value: Boolean) {
+ public operator fun set(property: BooleanPropertyDescriptor<Schema>, value: Boolean?) {
target[property] = value
}
public inline operator fun set(
propertySelector: Schema.() -> BooleanPropertyDescriptor<Schema>,
- value: Boolean
+ value: Boolean?
) {
target[propertySelector(schema)] = value
}
diff --git a/src/core/src/test/kotlin/org/apache/jmeter/testelement/property/JMeterElementSchemaTest.kt b/src/core/src/test/kotlin/org/apache/jmeter/testelement/property/JMeterElementSchemaTest.kt
index 58f6869..143a03d 100644
--- a/src/core/src/test/kotlin/org/apache/jmeter/testelement/property/JMeterElementSchemaTest.kt
+++ b/src/core/src/test/kotlin/org/apache/jmeter/testelement/property/JMeterElementSchemaTest.kt
@@ -29,10 +29,14 @@
import org.junit.jupiter.api.Test
class JMeterElementSchemaTest {
+ val warpDrive = WarpDriveElement()
+
abstract class WarpDriveElementSchema : TestElementSchema() {
companion object INSTANCE : WarpDriveElementSchema()
val warpFactor by int("WarpDriveElement.warpFactor", default = 7)
+ val turbo by boolean("WarpDriveElement.turbo")
+ val description by string("WarpDriveElement.description")
}
open class WarpDriveElement : AbstractTestElement() {
@@ -44,7 +48,16 @@
@Test
fun `getPropertyOrNull returns null for unset props`() {
- val warpDrive = WarpDriveElement()
+ assertGetWarpDescription(
+ null,
+ warpDrive,
+ "${WarpDriveElementSchema.warpFactor} should be null for newly created element"
+ )
+ assertGetWarpTurbo(
+ null,
+ warpDrive,
+ "${WarpDriveElementSchema.warpFactor} should be null for newly created element"
+ )
assertNull(warpDrive.getPropertyOrNull(warpDrive.schema.warpFactor)) {
"${WarpDriveElementSchema.warpFactor} should be null for newly created element, getPropertyOrNull(PropertyDescriptor)"
}
@@ -54,22 +67,19 @@
}
@Test
- fun `get returns default value`() {
- val warpDrive = WarpDriveElement()
+ fun `get int returns default value`() {
assertGetWarpFactor(7, warpDrive, "element is empty, so default value expected")
}
@Test
- fun `set modifies value`() {
- val warpDrive = WarpDriveElement()
+ fun `set int modifies value`() {
warpDrive[warpDrive.schema.warpFactor] = 8
assertGetWarpFactor(8, warpDrive, "value was modified with [warpFactor] = 8")
}
@Test
- fun `props set modifies value`() {
- val warpDrive = WarpDriveElement()
+ fun `props set int modifies value`() {
warpDrive.props {
it[warpFactor] = 8
}
@@ -78,6 +88,74 @@
}
@Test
+ fun `set string modifies value`() {
+ var value = "new description"
+ warpDrive[warpDrive.schema.description] = value
+ assertGetWarpDescription(value, warpDrive, "value was modified with [description] = \"$value\"")
+
+ value = ""
+ warpDrive[warpDrive.schema.description] = value
+ assertGetWarpDescription(value, warpDrive, "value was modified with [description] = \"$value\"")
+
+ warpDrive[warpDrive.schema.description] = null
+ assertGetWarpDescription(null, warpDrive, "value should be removed after [description] = null")
+ }
+
+ @Test
+ fun `props set string modifies value`() {
+ var value = "new description"
+ warpDrive.props {
+ it[description] = value
+ }
+ assertGetWarpDescription(value, warpDrive, "value was modified with props { it[description] = \"$value\" }")
+
+ value = ""
+ warpDrive.props {
+ it[description] = value
+ }
+ assertGetWarpDescription(value, warpDrive, "value was modified with props { it[description] = \"$value\" }")
+
+ warpDrive.props {
+ it[description] = null
+ }
+ assertGetWarpDescription(null, warpDrive, "value should be removed after props { it[description] = null }")
+ }
+
+ @Test
+ fun `set boolean modifies value`() {
+ var value = true
+ warpDrive[warpDrive.schema.turbo] = value
+ assertGetWarpTurbo(value, warpDrive, "value was modified with [turbo] = \"$value\"")
+
+ value = false
+ warpDrive[warpDrive.schema.turbo] = value
+ assertGetWarpTurbo(value, warpDrive, "value was modified with [turbo] = \"$value\"")
+
+ warpDrive[warpDrive.schema.turbo] = null as Boolean?
+ assertGetWarpTurbo(null, warpDrive, "value should be removed after [turbo] = null")
+ }
+
+ @Test
+ fun `props set boolean modifies value`() {
+ var value = true
+ warpDrive.props {
+ it[turbo] = value
+ }
+ assertGetWarpTurbo(value, warpDrive, "value was modified with props { it[turbo] = \"$value\" }")
+
+ value = false
+ warpDrive.props {
+ it[turbo] = value
+ }
+ assertGetWarpTurbo(value, warpDrive, "value was modified with props { it[turbo] = \"$value\" }")
+
+ warpDrive.props {
+ it[turbo] = null as Boolean?
+ }
+ assertGetWarpTurbo(null, warpDrive, "value should be removed after props { it[turbo] = null }")
+ }
+
+ @Test
fun `property descriptor equals`() {
assertEquals(TestElementSchema.name, ThreadGroupSchema.name) {
"TestElementSchema.name and ThreadGroupSchema.name should be equal"
@@ -90,7 +168,6 @@
@Test
fun `test string setter`() {
- val warpDrive = WarpDriveElement()
warpDrive.props {
it[warpFactor] = "\${hello}"
}
@@ -103,7 +180,7 @@
assertEquals(expected, warpDrive[warpDrive.schema.warpFactor]) {
"get(warpFactor): ${WarpDriveElementSchema.warpFactor}, $message"
}
- assertEquals(expected, warpDrive.props[ { warpDrive.schema.warpFactor }]) {
+ assertEquals(expected, warpDrive.props[ { warpFactor }]) {
"props.get[{warpFactor}]: ${WarpDriveElementSchema.warpFactor}, $message"
}
assertEquals(expected.toString(), warpDrive.getString(warpDrive.schema.warpFactor)) {
@@ -111,6 +188,52 @@
}
}
+ private fun assertGetWarpDescription(expected: String?, warpDrive: WarpDriveElement, message: String) {
+ assertEquals(expected ?: "", warpDrive[warpDrive.schema.description]) {
+ "get(description): ${WarpDriveElementSchema.description}, $message"
+ }
+ assertEquals(expected ?: "", warpDrive.props[ { description }]) {
+ "props.get[{description}]: ${WarpDriveElementSchema.description}, $message"
+ }
+ assertEquals(expected ?: "", warpDrive.getString(warpDrive.schema.description)) {
+ "getString(description): ${WarpDriveElementSchema.description}, $message"
+ }
+ assertEquals(expected ?: "", warpDrive.getPropertyAsString(warpDrive.schema.description.name)) {
+ "getPropertyAsString(description): ${WarpDriveElementSchema.description}, $message"
+ }
+ if (expected == null) {
+ assertNull(warpDrive.getPropertyOrNull(warpDrive.schema.description)) {
+ "getPropertyOrNull(description) should return null for absent property, ${WarpDriveElementSchema.description}, $message"
+ }
+ assertNull(warpDrive.getPropertyOrNull(warpDrive.schema.description.name)) {
+ "getPropertyOrNull(description.name) should return null for absent property, ${WarpDriveElementSchema.description}, $message"
+ }
+ }
+ }
+
+ private fun assertGetWarpTurbo(expected: Boolean?, warpDrive: WarpDriveElement, message: String) {
+ assertEquals(expected ?: false, warpDrive[warpDrive.schema.turbo]) {
+ "get(turbo): ${WarpDriveElementSchema.turbo}, $message"
+ }
+ assertEquals(expected ?: false, warpDrive.props[ { turbo }]) {
+ "props.get[{turbo}]: ${WarpDriveElementSchema.turbo}, $message"
+ }
+ assertEquals((expected ?: false).toString(), warpDrive.getString(warpDrive.schema.turbo)) {
+ "getString(turbo): ${WarpDriveElementSchema.turbo}, $message"
+ }
+ assertEquals(expected?.toString() ?: "", warpDrive.getPropertyAsString(warpDrive.schema.turbo.name)) {
+ "getPropertyAsString(turbo): ${WarpDriveElementSchema.turbo}, $message"
+ }
+ if (expected == null) {
+ assertNull(warpDrive.getPropertyOrNull(warpDrive.schema.turbo)) {
+ "getPropertyOrNull(turbo) should return null for absent property, ${WarpDriveElementSchema.turbo}, $message"
+ }
+ assertNull(warpDrive.getPropertyOrNull(warpDrive.schema.turbo.name)) {
+ "getPropertyOrNull(turbo.name) should return null for absent property, ${WarpDriveElementSchema.turbo}, $message"
+ }
+ }
+ }
+
@Suppress("UNUSED_VARIABLE", "ReplaceGetOrSet")
fun `compilation succeeds`() {
// Below code does not make much sense, and it tests different styles of using the properties