From 1b190b84e5ce9a62ad64700269377042d455b366 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 20 Jan 2026 12:52:33 +0100 Subject: [PATCH 01/26] RocksDB log ingested init path on debug level --- .../main/java/org/thingsboard/server/edqs/util/TbRocksDb.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java index 9141321dcb..c731cf4548 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java @@ -16,6 +16,7 @@ package org.thingsboard.server.edqs.util; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.rocksdb.Options; import org.rocksdb.RocksDB; import org.rocksdb.RocksIterator; @@ -26,6 +27,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.function.BiConsumer; +@Slf4j public class TbRocksDb { protected final String path; @@ -45,6 +47,7 @@ public class TbRocksDb { @SneakyThrows public void init() { + log.debug("RocksDB init in {}", path); Files.createDirectories(Path.of(path).getParent()); db = RocksDB.open(dbOptions, path); } From f4e2ebde5463a5a1a420bc96069256fe3270b70e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 20 Jan 2026 12:58:35 +0100 Subject: [PATCH 02/26] Tests Edge - replaced port 7070 with dynamic port to allow parallel tests in surefire forks --- .../server/controller/EdgeControllerTest.java | 10 +++++++++- .../thingsboard/server/edge/AbstractEdgeTest.java | 14 +++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index 8c12f8713d..0d0de0a66c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -36,7 +36,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.util.TestSocketUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.AdminSettings; @@ -121,7 +124,12 @@ import static org.thingsboard.server.edge.AbstractEdgeTest.CONNECT_MESSAGE_COUNT public class EdgeControllerTest extends AbstractControllerTest { public static final String EDGE_HOST = "localhost"; - public static final int EDGE_PORT = 7070; + public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort(); + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.debug("edges.rpc.port = {}", EDGE_PORT); + registry.add("edges.rpc.port", () -> EDGE_PORT); + } private IdComparator idComparator = new IdComparator<>(); diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index cf2f1d1161..b46811daf5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -26,7 +26,10 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.util.TestSocketUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.Customer; @@ -116,6 +119,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. }) @Slf4j abstract public class AbstractEdgeTest extends AbstractControllerTest { + + public static final String EDGE_HOST = "localhost"; + public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort(); + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.debug("edges.rpc.port = {}", EDGE_PORT); + registry.add("edges.rpc.port", () -> EDGE_PORT); + } + public static final Integer CONNECT_MESSAGE_COUNT = 17; public static final Integer INSTALLATION_MESSAGE_COUNT = 8; public static final Integer SYNC_MESSAGE_COUNT = CONNECT_MESSAGE_COUNT + INSTALLATION_MESSAGE_COUNT; @@ -142,7 +154,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { //8 installation messages installation(); - edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); + edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret()); // 17 connect messages + 8 installation messages edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT); edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class); From 8599020aed9243afab004275b20307131fae298b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 20 Jan 2026 13:10:24 +0100 Subject: [PATCH 03/26] Tests: Rock DB location moved to the target for safe surefire parallel fork run and easy clean by maven --- pom.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ccc305b6b3..bfb4bbf1ae 100755 --- a/pom.xml +++ b/pom.xml @@ -662,8 +662,10 @@ ${surefire.version} - -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=20 - --add-opens java.base/java.lang.reflect=ALL-UNNAMED + -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200 + --add-opens=java.base/java.lang.reflect=ALL-UNNAMED + -Dqueue.edqs.local.rocksdb_path="target/rocks/fork_${surefire.forkNumber}/edqs" + -Dqueue.calculated_fields.rocks_db_path="target/rocks/fork_${surefire.forkNumber}/cf" From 2c533c0f86dc3854e5a818d487cbd614d4bf7e37 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 20 Jan 2026 13:27:09 +0100 Subject: [PATCH 04/26] tests: reducing logging noise --- .../org/thingsboard/server/controller/AbstractWebTest.java | 2 +- application/src/test/resources/logback-test.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index d540fcf49e..599dcf3138 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -404,7 +404,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { jdbcTemplate.execute("TRUNCATE TABLE notification"); - log.info("Executed web test teardown"); + log.debug("Executed web test teardown"); } private void verifyNoTenantsLeft() throws Exception { diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 13c93da411..eafb4e1fba 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -42,6 +42,9 @@ + + + From 9a2489f5b69f3a449e977785af435c402f2e78f2 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 21 Jan 2026 22:23:07 +0100 Subject: [PATCH 05/26] tests: DefaultTbResourceDataCache destroy cache on bean destroy that fixes flaky test --- .../server/dao/resource/DefaultTbResourceDataCache.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java b/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java index 343c37299e..925b8c2912 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java @@ -19,6 +19,7 @@ import com.github.benmanes.caffeine.cache.AsyncLoadingCache; import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.util.concurrent.FluentFuture; import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -55,6 +56,12 @@ public class DefaultTbResourceDataCache implements TbResourceDataCache { .buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> resourceService.getResourceDataInfo(key.tenantId(), key.resourceId()), executor)); } + @PreDestroy + private void destroy() { + cache.synchronous().invalidateAll(); + cache = null; + } + @Override public FluentFuture getResourceDataInfoAsync(TenantId tenantId, TbResourceId resourceId) { log.trace("Retrieving resource data info by id [{}], tenant id [{}] from cache", resourceId, tenantId); From 7b9c53d348c66f4d398d4e71893d3ab6272574ef Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 21 Jan 2026 22:29:01 +0100 Subject: [PATCH 06/26] tests: EdgeImitator - fix for ConcurrentModification at AbstractEdgeTest.validateMsgsCnt:387 --- .../server/edge/imitator/EdgeImitator.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java index 36579e8e22..d1596b1c59 100644 --- a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java +++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java @@ -72,6 +72,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -102,8 +103,13 @@ public class EdgeImitator { @Getter private EdgeConfiguration configuration; - @Getter - private final List downlinkMsgs; + private final ConcurrentLinkedDeque downlinkMsgs; + + //Returns collection copy as Unmodifiable list + //This addressing the issue: DeviceEdgeTest>AbstractEdgeTest.setupEdgeTest:212->AbstractEdgeTest.verifyEdgeConnectionAndInitialData:306->AbstractEdgeTest.validateMsgsCnt:387 » ConcurrentModification + public List getDownlinkMsgs() { + return downlinkMsgs.stream().toList(); + } @Getter private UplinkResponseMsg latestResponseMsg; @@ -112,7 +118,7 @@ public class EdgeImitator { edgeRpcClient = new EdgeGrpcClient(); messagesLatch = new CountDownLatch(0); responsesLatch = new CountDownLatch(0); - downlinkMsgs = new ArrayList<>(); + downlinkMsgs = new ConcurrentLinkedDeque<>(); ignoredTypes = new ArrayList<>(); this.routingKey = routingKey; this.routingSecret = routingSecret; @@ -452,7 +458,7 @@ public class EdgeImitator { } public AbstractMessage getLatestMessage() { - return downlinkMsgs.get(downlinkMsgs.size() - 1); + return downlinkMsgs.peekLast(); } public void ignoreType(Class type) { From 5305ccf18081662c454b8e1f92e9973a3e4b2607 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 21 Jan 2026 22:45:56 +0100 Subject: [PATCH 07/26] tests: standardizing timeouts to address flakiness in parallel runs and busy CI env --- .../notification/NotificationRuleApiTest.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 41982ab91a..7175f0981d 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -237,12 +237,12 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .set("bool", BooleanNode.TRUE); doPost("/api/plugins/telemetry/" + device.getId() + "/" + DataConstants.SHARED_SCOPE, attr); - await().atMost(10, TimeUnit.SECONDS) + await().atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); long ts = System.currentTimeMillis(); - await().atMost(15, TimeUnit.SECONDS) + await().atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> clients.values().stream().allMatch(client -> client.getLastDataUpdate() != null)); clients.forEach((expectedDelay, wsClient) -> { Notification notification = wsClient.getLastDataUpdate().getUpdate(); @@ -290,8 +290,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { alarm.setOriginator(device.getId()); alarm = doPost("/api/alarm", alarm, Alarm.class); - await().atMost(15, TimeUnit.SECONDS) - .pollDelay(2, TimeUnit.SECONDS) + await().atMost(TIMEOUT, TimeUnit.SECONDS) + .pollDelay(1, TimeUnit.SECONDS) .untilAsserted(() -> { List notifications = getMyNotifications(false, 10); assertThat(notifications).singleElement().matches(notification -> { @@ -344,7 +344,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .set("bool", BooleanNode.TRUE); doPost("/api/plugins/telemetry/" + device.getId() + "/" + DataConstants.SHARED_SCOPE, attr); - await().atMost(10, TimeUnit.SECONDS) + await().atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); getWsClient().waitForUpdate(true); @@ -354,14 +354,14 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class)) .extracting(AlarmNotificationInfo::getAlarmId).isEqualTo(alarm.getUuidId()); - await().atMost(10, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size()); + await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size()); NotificationRequestInfo scheduledNotificationRequest = findNotificationRequests(EntityType.ALARM).getData().stream() .filter(NotificationRequest::isScheduled) .findFirst().orElse(null); assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo()); alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null); - await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty(); }); } @@ -479,7 +479,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { } loginTenantAdmin(); - List notifications = await().atMost(15, TimeUnit.SECONDS) + List notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> getMyNotifications(true, 10).stream() .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) .collect(Collectors.toList()), list -> list.size() == 3); @@ -499,7 +499,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { }); loginSysAdmin(); - notifications = await().atMost(15, TimeUnit.SECONDS) + notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> getMyNotifications(true, 10).stream() .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) .collect(Collectors.toList()), list -> list.size() == 1); @@ -720,7 +720,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .updateInfo(new UpdateMessage(true, "CHANGED", "test", "test", "test", "test")) .build()); - await().atMost(5, TimeUnit.SECONDS) + await().atMost(TIMEOUT, TimeUnit.SECONDS) .untilAsserted(() -> { assertThat(getMyNotifications(false, 100)).size().isEqualTo(2); }); @@ -803,7 +803,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { method.setAccessible(true); method.invoke(systemInfoService); - await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); + await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); Notification notification = getMyNotifications(false, 100).get(0); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage"); @@ -830,7 +830,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .build()); TimeUnit.MILLISECONDS.sleep(300); } - await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); + await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); Notification notification = getMyNotifications(false, 100).get(0); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage"); @@ -842,7 +842,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .serviceType("serviceType") .serviceId("serviceId") .build()); - await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); + await("").atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); } @Test @@ -873,7 +873,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { method.invoke(systemInfoService); - await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); + await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); Notification notification = getMyNotifications(false, 100).get(0); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage"); From b35f46a8eedd4e3117aa10078ff30dc7172c198a Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 21 Jan 2026 23:22:44 +0100 Subject: [PATCH 08/26] tests: reducing logging noise for dao, cache for CaffeineCacheMetrics --- common/cache/src/test/resources/logback-test.xml | 3 +++ dao/src/test/resources/logback-test.xml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/common/cache/src/test/resources/logback-test.xml b/common/cache/src/test/resources/logback-test.xml index 29dddea8df..9b4c5b62df 100644 --- a/common/cache/src/test/resources/logback-test.xml +++ b/common/cache/src/test/resources/logback-test.xml @@ -9,6 +9,9 @@ + + + diff --git a/dao/src/test/resources/logback-test.xml b/dao/src/test/resources/logback-test.xml index fe0887c678..92cd10f2ca 100644 --- a/dao/src/test/resources/logback-test.xml +++ b/dao/src/test/resources/logback-test.xml @@ -19,6 +19,9 @@ + + + From 243fbec9301a07a643754de4c3e06fdbf173620b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 01:58:54 +0100 Subject: [PATCH 09/26] tests: spring.main.banner-mode=off --- application/src/test/resources/application-test.properties | 1 + dao/src/test/resources/application-test.properties | 1 + 2 files changed, 2 insertions(+) diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index b8bdcf67e6..e79289340c 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -1,3 +1,4 @@ +spring.main.banner-mode=off js.evaluator=mock transport.lwm2m.server.security.credentials.enabled=true transport.lwm2m.server.security.credentials.type=KEYSTORE diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 1c2c0c5519..60605e1710 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -1,3 +1,4 @@ +spring.main.banner-mode=off zk.enabled=false zk.url=localhost:2181 zk.zk_dir=/thingsboard From 141ac802650dfe72061bf094371d4e5d792927b0 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 02:14:35 +0100 Subject: [PATCH 10/26] tests: transport.mqtt.bind_port is @DynamicPropertySource. Dynamic MQTT port are set in a single AbstractMqttIntegrationTest to be compatible with parallel test run --- .../controller/EntityViewControllerTest.java | 14 ++++++++++++-- .../thingsboard/server/edge/DeviceEdgeTest.java | 10 ++++++++++ .../AbstractTransportIntegrationTest.java | 1 - .../mqtt/AbstractMqttIntegrationTest.java | 13 +++++++++++++ .../transport/mqtt/MqttGatewayRateLimitsTest.java | 10 ++++++++++ .../transport/mqtt/mqttv3/MqttTestClient.java | 6 +++++- .../transport/mqtt/mqttv5/MqttV5TestClient.java | 6 +++++- .../server/msa/rule/node/MqttNodeTest.java | 3 ++- 8 files changed, 57 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java index 7812f769b1..421f3785ca 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java @@ -38,6 +38,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.ThingsBoardExecutors; @@ -85,6 +87,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_URL; @TestPropertySource(properties = { "transport.mqtt.enabled=true", @@ -94,6 +98,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @ContextConfiguration(classes = {EntityViewControllerTest.Config.class}) @DaoSqlTest public class EntityViewControllerTest extends AbstractControllerTest { + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.warn("transport.mqtt.bind_port = {}", MQTT_PORT); + registry.add("transport.mqtt.bind_port", () -> MQTT_PORT); + } + static final TypeReference> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() { }; static final TypeReference> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() { @@ -650,7 +660,7 @@ public class EntityViewControllerTest extends AbstractControllerTest { String viewDeviceId = testDevice.getId().getId().toString(); String clientId = MqttAsyncClient.generateClientId(); - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); MqttConnectOptions options = new MqttConnectOptions(); options.setUserName(accessToken); @@ -701,7 +711,7 @@ public class EntityViewControllerTest extends AbstractControllerTest { assertNotNull(accessToken); String clientId = MqttAsyncClient.generateClientId(); - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); MqttConnectOptions options = new MqttConnectOptions(); options.setUserName(accessToken); diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java index f9b89f4d2c..a8f82f8b7d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -22,10 +22,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.gson.JsonObject; import com.google.protobuf.AbstractMessage; import io.netty.handler.codec.mqtt.MqttQoS; +import lombok.extern.slf4j.Slf4j; import org.awaitility.Awaitility; import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.adaptor.JsonConverter; @@ -85,12 +88,19 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.gen.edge.v1.UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT; @TestPropertySource(properties = { "transport.mqtt.enabled=true" }) +@Slf4j @DaoSqlTest public class DeviceEdgeTest extends AbstractEdgeTest { + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.warn("transport.mqtt.bind_port = {}", MQTT_PORT); + registry.add("transport.mqtt.bind_port", () -> MQTT_PORT); + } private static final String DEFAULT_DEVICE_TYPE = "default"; diff --git a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java index 0badcd2656..bc06d81588 100644 --- a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java @@ -30,7 +30,6 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30; - protected static final String MQTT_URL = "tcp://localhost:1883"; protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; protected static final AtomicInteger atomicInteger = new AtomicInteger(2); diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java index 364b8fbe6a..962d85ec7c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java @@ -19,7 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.handler.codec.mqtt.MqttQoS; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttException; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.util.TestSocketUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -58,6 +61,16 @@ import static org.junit.Assert.assertNotNull; @Slf4j public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest { + public static final String MQTT_HOST = "localhost"; + public static final int MQTT_PORT = TestSocketUtils.findAvailableTcpPort(); + public static final String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT; + + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.warn("transport.mqtt.bind_port = {}", MQTT_PORT); + registry.add("transport.mqtt.bind_port", () -> MQTT_PORT); + } + protected Device savedGateway; protected String gatewayAccessToken; diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java index c455ae45d4..541ca40072 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java @@ -16,12 +16,15 @@ package org.thingsboard.server.transport.mqtt; import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; import org.awaitility.Awaitility; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; @@ -43,13 +46,20 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.eq; import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY; import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT; @DaoSqlTest @TestPropertySource(properties = { "service.integrations.supported=ALL", "transport.mqtt.enabled=true", }) +@Slf4j public class MqttGatewayRateLimitsTest extends AbstractControllerTest { + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.warn("transport.mqtt.bind_port = {}", MQTT_PORT); + registry.add("transport.mqtt.bind_port", () -> MQTT_PORT); + } private static final String GATEWAY_TOPIC = "v1/gateway/telemetry"; private static final String DEVICE_TOPIC = "v1/devices/me/telemetry"; diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java index 7e5cc031a5..4c34d9bc81 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java @@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils; import java.util.concurrent.TimeUnit; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT; + public class MqttTestClient { - private static final String MQTT_URL = "tcp://localhost:1883"; private static final int TIMEOUT = 30; // seconds public static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT); + private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT; + private final MqttAsyncClient client; public void setCallback(MqttTestCallback callback) { diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java index 545fc148e2..558fa1a46a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java @@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils; import java.util.concurrent.TimeUnit; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST; +import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT; + public class MqttV5TestClient { // We should copy part of MqttV3TestClient, due to different package names in import - private static final String MQTT_URL = "tcp://localhost:1883"; private static final int TIMEOUT = 30; // seconds private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT); + private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT; + private final MqttAsyncClient client; public void setCallback(MqttCallback callback) { diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java index 67f6258767..6fff0b4b6a 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java @@ -65,6 +65,7 @@ import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevi public class MqttNodeTest extends AbstractContainerTest { private static final String TOPIC = "tb/mqtt/device"; + private static final String CONTAINER_MQTT_URL = "tcp://localhost:1883"; private Device device; @@ -94,7 +95,7 @@ public class MqttNodeTest extends AbstractContainerTest { responseClient.connect(); responseClient.subscribe(TOPIC, messageListener); - MqttClient mqttClient = new MqttClient("tcp://localhost:1883", StringUtils.randomAlphanumeric(10), new MemoryPersistence()); + MqttClient mqttClient = new MqttClient(CONTAINER_MQTT_URL, StringUtils.randomAlphanumeric(10), new MemoryPersistence()); MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); mqttConnectOptions.setUserName(deviceCredentials.getCredentialsId()); mqttClient.connect(mqttConnectOptions); From 9577cb0aeed2127d6911109b5084e8bd1c27135a Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 03:17:00 +0100 Subject: [PATCH 11/26] tests: PortFinder.findAvailableUdpPort added --- .../thingsboard/server/utils/PortFinder.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 application/src/test/java/org/thingsboard/server/utils/PortFinder.java diff --git a/application/src/test/java/org/thingsboard/server/utils/PortFinder.java b/application/src/test/java/org/thingsboard/server/utils/PortFinder.java new file mode 100644 index 0000000000..23990363ce --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/utils/PortFinder.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed 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.thingsboard.server.utils; + +import java.net.DatagramSocket; +import java.net.SocketException; + +public class PortFinder { + public static int findAvailableUdpPort() { + try (DatagramSocket socket = new DatagramSocket(0)) { + return socket.getLocalPort(); + } catch (SocketException e) { + throw new IllegalStateException("No available UDP ports found", e); + } + } +} From bcfe4c26417d09a55d33d11e8b0879dd50de5a5b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 03:18:48 +0100 Subject: [PATCH 12/26] tests: coap transport tests refactored to use dynamic UPD ports to be able to test in parallel --- .../AbstractTransportIntegrationTest.java | 2 -- .../coap/AbstractCoapIntegrationTest.java | 14 ++++++++ .../server/transport/coap/CoapTestClient.java | 3 +- .../AbstractCoapSecurityIntegrationTest.java | 36 ++++++++++++------- .../server/msa/AbstractCoapClientTest.java | 4 +-- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java index bc06d81588..b0a1d21992 100644 --- a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java @@ -30,8 +30,6 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30; - protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; - protected static final AtomicInteger atomicInteger = new AtomicInteger(2); public static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" + diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java index 7240ab0afa..cf23a5be97 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java @@ -16,7 +16,10 @@ package org.thingsboard.server.transport.coap; import lombok.extern.slf4j.Slf4j; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.util.TestSocketUtils; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -40,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.transport.AbstractTransportIntegrationTest; +import org.thingsboard.server.utils.PortFinder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -52,6 +56,16 @@ import static org.junit.Assert.assertNotNull; @Slf4j public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest { + public static final String COAP_HOST = "localhost"; + public static final int COAP_PORT = PortFinder.findAvailableUdpPort(); + public static final String COAP_BASE_URL = "coap://" + COAP_HOST + ":" + COAP_PORT + "/api/v1/"; + + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.info("coap.bind_port = {}", COAP_PORT); + registry.add("coap.bind_port", () -> COAP_PORT); + } + protected final byte[] EMPTY_PAYLOAD = new byte[0]; protected CoapTestClient client; protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java index ce72d68fc6..df7e9babc9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java @@ -28,9 +28,10 @@ import org.thingsboard.server.common.msg.session.FeatureType; import java.io.IOException; +import static org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest.COAP_BASE_URL; + public class CoapTestClient { - private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; private static final long CLIENT_REQUEST_TIMEOUT = 60000L; private final CoapClient client; diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java index cfb449aa33..9c42d87369 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java @@ -21,7 +21,10 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.coap.CoAP; import org.junit.Assert; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; +import org.springframework.test.util.TestSocketUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.Device; @@ -37,9 +40,11 @@ import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; import org.thingsboard.server.transport.coap.x509.CertPrivateKey; import org.thingsboard.server.transport.coap.x509.CoapClientX509Test; import org.thingsboard.server.transport.coap.CoapTestConfigProperties; +import org.thingsboard.server.utils.PortFinder; import java.io.IOException; import java.io.InputStream; +import java.net.DatagramSocket; import java.net.ServerSocket; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -70,7 +75,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. "transport.coap.enabled=true", }) public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest { - private static final String COAPS_BASE_URL = "coaps://localhost:5684/api/v1/"; + + public static final String COAPS_HOST = "localhost"; + public static final int COAPS_PORT = PortFinder.findAvailableUdpPort(); + public static final String COAPS_BASE_URL = "coaps://" + COAPS_HOST + ":" + COAPS_PORT + "/api/v1/"; + + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.info("coap.dtls.bind_port = {}", COAPS_PORT); + registry.add("coap.dtls.bind_port", () -> COAPS_PORT); + } + protected final String CREDENTIALS_PATH = "coap/credentials/"; protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/"; protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem"; @@ -160,17 +175,20 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM); CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem", CREDENTIALS_PATH_CLIENT + "key_01.pem"); - Integer fixedPort = getFreePort(); + int fixedPort = PortFinder.findAvailableUdpPort(); CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey, "CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort); clientX509.disconnect(); await("Need to make port " + fixedPort + " free") .atMost(40, TimeUnit.SECONDS) - .until(() -> isPortAvailable(fixedPort)); + .until(() -> isUDPPortAvailable(fixedPort)); CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01, "CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(), fixedPort, PAYLOAD_VALUES_STR_01); clientX509_01.disconnect(); + await("Await to make port " + fixedPort + " free") + .atMost(40, TimeUnit.SECONDS) + .until(() -> isUDPPortAvailable(fixedPort)); } private CoapClientX509Test clientX509UpdateTest(FeatureType featureType, CertPrivateKey certPrivateKey, @@ -274,15 +292,9 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn } } - private static int getFreePort() throws IOException { - try (ServerSocket socket = new ServerSocket(0)) { - return socket.getLocalPort(); - } - } - - private static boolean isPortAvailable(int port) { - try (ServerSocket serverSocket = new ServerSocket(port)) { - serverSocket.setReuseAddress(true); + private static boolean isUDPPortAvailable(int port) { + try (DatagramSocket socket = new DatagramSocket(port)) { + socket.setReuseAddress(true); return true; } catch (IOException e) { return false; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java index d4d9d08474..e409a82600 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java @@ -28,7 +28,7 @@ import org.thingsboard.server.common.msg.session.FeatureType; public abstract class AbstractCoapClientTest extends AbstractContainerTest{ - private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; + private static final String CONTAINER_COAP_BASE_URL = "coap://localhost:5683/api/v1/"; private static final long CLIENT_REQUEST_TIMEOUT = 60000L; @@ -54,7 +54,7 @@ public abstract class AbstractCoapClientTest extends AbstractContainerTest{ protected byte[] createCoapClientAndPublish(String deviceName) throws Exception { String provisionRequestMsg = createTestProvisionMessage(deviceName); Configuration.addDefaultModule(MODULE_DEFINITIONS_PROVIDER); - String featureTokenUrl = COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase(); + String featureTokenUrl = CONTAINER_COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase(); client = new CoapClient(featureTokenUrl); return client.setTimeout(CLIENT_REQUEST_TIMEOUT) .post(provisionRequestMsg.getBytes(), MediaTypeRegistry.APPLICATION_JSON) From f4334436ff45c0653fafd53d80f818ff3b6b9c7e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 03:46:12 +0100 Subject: [PATCH 13/26] tests: log warn dynamic property coap.bind_port coap.dtls.bind_port --- .../server/transport/coap/AbstractCoapIntegrationTest.java | 2 +- .../coap/security/AbstractCoapSecurityIntegrationTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java index cf23a5be97..69d40f5409 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java @@ -62,7 +62,7 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg @DynamicPropertySource static void props(DynamicPropertyRegistry registry) { - log.info("coap.bind_port = {}", COAP_PORT); + log.warn("coap.bind_port = {}", COAP_PORT); registry.add("coap.bind_port", () -> COAP_PORT); } diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java index 9c42d87369..b0531457a4 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java @@ -82,7 +82,7 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn @DynamicPropertySource static void props(DynamicPropertyRegistry registry) { - log.info("coap.dtls.bind_port = {}", COAPS_PORT); + log.warn("coap.dtls.bind_port = {}", COAPS_PORT); registry.add("coap.dtls.bind_port", () -> COAPS_PORT); } @@ -297,6 +297,7 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn socket.setReuseAddress(true); return true; } catch (IOException e) { + log.warn("Failed to open UDP port on port " + port, e); return false; } } From 189c2ac6ae30245e989b509751a9b529071cde7a Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 03:48:43 +0100 Subject: [PATCH 14/26] tests: fixed Coap X509 Test client by calling dtlsConnector.destroy() on client.disconnect(); --- .../server/transport/coap/x509/CoapClientX509Test.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java index 0f65298d86..aff3902609 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java @@ -97,6 +97,9 @@ public class CoapClientX509Test { if (clientX509 != null) { clientX509.shutdown(); } + if (dtlsConnector != null) { + dtlsConnector.destroy(); + } } public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException { From 94f30191884478eea88ca05fafdcde5c8f44591e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 14:51:20 +0100 Subject: [PATCH 15/26] tests: AbstractCoapSecurityIntegrationTest import cleanup --- .../coap/security/AbstractCoapSecurityIntegrationTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java index b0531457a4..ec68a346f0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java @@ -45,7 +45,6 @@ import org.thingsboard.server.utils.PortFinder; import java.io.IOException; import java.io.InputStream; import java.net.DatagramSocket; -import java.net.ServerSocket; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; From 59ff87d2bcd905b025b20a84f7c56b3b41d9674c Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 14:53:21 +0100 Subject: [PATCH 16/26] DeviceProfileDataValidator - refactored validation to allow use-defined ports for LWM@M (also useful for tests in parallel). Addressing the issue: DeviceProfile object is invalid: [LwM2M Server "Port" value = 53197. This value for security PSK must be 5686!] --- .../validator/DeviceProfileDataValidator.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java index 130251eb35..6402ec0d1f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java @@ -96,6 +96,18 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator Date: Thu, 22 Jan 2026 16:06:26 +0100 Subject: [PATCH 17/26] tests: CoapClientX509Test disconnect() is awaiting until UDP fixedPort released. reuse socket removed for UPD isUDPPortAvailable as not useful --- .../AbstractTransportIntegrationTest.java | 2 +- .../AbstractCoapSecurityIntegrationTest.java | 19 ------------------- .../coap/x509/CoapClientX509Test.java | 11 +++++++++++ .../thingsboard/server/utils/PortFinder.java | 13 +++++++++++++ 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java index b0a1d21992..c91fea282d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java @@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest { - protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30; + public static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30; protected static final AtomicInteger atomicInteger = new AtomicInteger(2); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java index ec68a346f0..04434ff53d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java @@ -24,7 +24,6 @@ import org.junit.Assert; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.util.TestSocketUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.Device; @@ -44,7 +43,6 @@ import org.thingsboard.server.utils.PortFinder; import java.io.IOException; import java.io.InputStream; -import java.net.DatagramSocket; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; @@ -55,9 +53,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; -import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -178,16 +174,10 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey, "CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort); clientX509.disconnect(); - await("Need to make port " + fixedPort + " free") - .atMost(40, TimeUnit.SECONDS) - .until(() -> isUDPPortAvailable(fixedPort)); CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01, "CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(), fixedPort, PAYLOAD_VALUES_STR_01); clientX509_01.disconnect(); - await("Await to make port " + fixedPort + " free") - .atMost(40, TimeUnit.SECONDS) - .until(() -> isUDPPortAvailable(fixedPort)); } private CoapClientX509Test clientX509UpdateTest(FeatureType featureType, CertPrivateKey certPrivateKey, @@ -291,14 +281,5 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn } } - private static boolean isUDPPortAvailable(int port) { - try (DatagramSocket socket = new DatagramSocket(port)) { - socket.setReuseAddress(true); - return true; - } catch (IOException e) { - log.warn("Failed to open UDP port on port " + port, e); - return false; - } - } } diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java index aff3902609..ebbed25243 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java @@ -51,6 +51,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.awaitility.Awaitility.await; import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES; @@ -71,6 +72,8 @@ import static org.eclipse.californium.scandium.config.DtlsConfig.MODULE; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA; +import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS; +import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable; @Slf4j public class CoapClientX509Test { @@ -82,6 +85,7 @@ public class CoapClientX509Test { private final Configuration config; private final CertPrivateKey certPrivateKey; private final String coapsBaseUrl; + private final Integer fixedPort; @Getter private CoAP.Type type = CoAP.Type.CON; @@ -90,6 +94,7 @@ public class CoapClientX509Test { this.certPrivateKey = certPrivateKey; this.coapsBaseUrl = coapsBaseUrl; this.config = createConfiguration(); + this.fixedPort = fixedPort; this.dtlsConnector = createDTLSConnector(fixedPort); this.clientX509 = createClient(getFeatureTokenUrl(featureType)); } @@ -99,6 +104,12 @@ public class CoapClientX509Test { } if (dtlsConnector != null) { dtlsConnector.destroy(); + if (fixedPort != null) { + log.debug("Awaiting releasing UDP fixedPort {}", fixedPort); + await("Await client UDP port " + fixedPort + " to disconnect") + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> isUDPPortAvailable(fixedPort)); + } } } diff --git a/application/src/test/java/org/thingsboard/server/utils/PortFinder.java b/application/src/test/java/org/thingsboard/server/utils/PortFinder.java index 23990363ce..21a1f1eb0d 100644 --- a/application/src/test/java/org/thingsboard/server/utils/PortFinder.java +++ b/application/src/test/java/org/thingsboard/server/utils/PortFinder.java @@ -15,9 +15,13 @@ */ package org.thingsboard.server.utils; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; import java.net.DatagramSocket; import java.net.SocketException; +@Slf4j public class PortFinder { public static int findAvailableUdpPort() { try (DatagramSocket socket = new DatagramSocket(0)) { @@ -26,4 +30,13 @@ public class PortFinder { throw new IllegalStateException("No available UDP ports found", e); } } + + public static boolean isUDPPortAvailable(int port) { + try (DatagramSocket socket = new DatagramSocket(port)) { + return true; + } catch (IOException e) { + log.debug("Failed to open UDP port {}", port, e); + return false; + } + } } From a706859711f995bfa53c358e20101f77377d6fac Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 19:13:46 +0100 Subject: [PATCH 18/26] tests: log MockMvc error responses >= 400 for easier investigation --- .../thingsboard/server/controller/AbstractWebTest.java | 10 +++++++++- application/src/test/resources/logback-test.xml | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 599dcf3138..44c383a5f0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -60,6 +60,7 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.context.WebApplicationContext; @@ -330,7 +331,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { if (this.mockMvc == null) { this.mockMvc = webAppContextSetup(webApplicationContext) - .apply(springSecurity()).build(); + .apply(springSecurity()) + // conditional printing of non 2xx responses + .alwaysDo(result -> { + if (result.getResponse().getStatus() >= 400) { + MockMvcResultHandlers.log().handle(result); + } + }) + .build(); } loginSysAdmin(); diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index eafb4e1fba..6faaf97536 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -45,6 +45,9 @@ + + + From 45da022944b4bb0e66a6491154b32763bd43ab0b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 19:15:35 +0100 Subject: [PATCH 19/26] tests: bump versions 3.5.4 1.21.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bfb4bbf1ae..58e0c32e43 100755 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 1.2.5 1.2.5 1.7.1 - 3.2.5 + 3.5.4 3.4.0 2.8.8TB 2.2.30 @@ -127,7 +127,7 @@ 5.0.0 7.10.1 - 1.20.6 + 1.21.4 1.0.2 1.12 6.1.0 From a4f755b624457d77024f56fa6250e6645590c159 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 19:21:31 +0100 Subject: [PATCH 20/26] tests: refactored blackbox test for LWM2M test client (UDP DatagramSocket instead TCP ServerSocket), ensure UDP socket is available before and after LWM2M client start stop --- .../thingsboard/server/msa/PortFinder.java | 42 +++++++++++++++++++ .../lwm2m/AbstractLwm2mClientTest.java | 8 ++-- .../lwm2m/client/LwM2MTestClient.java | 21 +++++++++- 3 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java new file mode 100644 index 0000000000..8bd036bf15 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed 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.thingsboard.server.msa; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.SocketException; + +@Slf4j +public class PortFinder { + public static int findAvailableUdpPort() { + try (DatagramSocket socket = new DatagramSocket(0)) { + return socket.getLocalPort(); + } catch (SocketException e) { + throw new IllegalStateException("No available UDP ports found", e); + } + } + + public static boolean isUDPPortAvailable(int port) { + try (DatagramSocket socket = new DatagramSocket(port)) { + return true; + } catch (IOException e) { + log.debug("Failed to open UDP port {}", port, e); + return false; + } + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java index f991e1b29c..c5673ae667 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java @@ -57,12 +57,12 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.msa.AbstractContainerTest; +import org.thingsboard.server.msa.PortFinder; import org.thingsboard.server.msa.WsClient; import org.thingsboard.server.msa.connectivity.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState; import org.thingsboard.server.msa.mapper.WsTelemetryResponse; -import java.net.ServerSocket; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; @@ -229,10 +229,8 @@ public class AbstractLwm2mClientTest extends AbstractContainerTest { String endpoint, ScheduledExecutorService executor) throws Exception { this.executor = executor; LwM2MTestClient lwM2MTestClient = new LwM2MTestClient(executor, endpoint); - try (ServerSocket socket = new ServerSocket(0)) { - int clientPort = socket.getLocalPort(); - lwM2MTestClient.init(security, clientPort); - } + int clientPort = PortFinder.findAvailableUdpPort(); + lwM2MTestClient.init(security, clientPort); return lwM2MTestClient; } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java index 64f4d6f08a..61a763562c 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java @@ -65,6 +65,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; @@ -72,6 +73,7 @@ import static org.eclipse.leshan.core.LwM2mId.DEVICE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.SECURITY; import static org.eclipse.leshan.core.LwM2mId.SERVER; +import static org.thingsboard.server.msa.PortFinder.isUDPPortAvailable; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED; @@ -95,7 +97,6 @@ import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelp import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.serverId; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.shortServerId; - @Slf4j @Data public class LwM2MTestClient { @@ -115,8 +116,11 @@ public class LwM2MTestClient { private int countUpdateRegistrationSuccess; private int countReadObserveAfterUpdateRegistrationSuccess; + private int clientPort; + public void init(Security security, int clientPort) throws InvalidDDFFileException, IOException { assertThat(leshanClient).as("client already initialized").isNull(); + this.clientPort = clientPort; List models = new ArrayList<>(); for (String resourceName : resources) { @@ -342,12 +346,27 @@ public class LwM2MTestClient { } }); + } + + public void start() { leshanClient.start(); + if (clientPort > 0) { + log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort); + await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()") + .atMost(30, TimeUnit.SECONDS) + .until(() -> isUDPPortAvailable(clientPort)); + } } public void destroy() { if (leshanClient != null) { leshanClient.destroy(true); + if (clientPort > 0) { + log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort); + await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)") + .atMost(30, TimeUnit.SECONDS) + .until(() -> isUDPPortAvailable(clientPort)); + } } if (lwM2MDevice != null) { lwM2MDevice.destroy(); From 4bd812b6fdcabe022ba52ad0449067fc601d561f Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 19:33:56 +0100 Subject: [PATCH 21/26] tests: LWM2M transport refactored and fixed to run in parallel. Improved UPD port operation experience for stability. additional logs added --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 61 ++++++++++++------- .../transport/lwm2m/client/FwLwM2MDevice.java | 2 +- .../lwm2m/client/LwM2MTestClient.java | 24 +++++++- .../lwm2m/client/LwM2mTemperatureSensor.java | 1 + .../AbstractSecurityLwM2MIntegrationTest.java | 4 +- .../security/sql/PskLwm2mIntegrationTest.java | 4 +- pom.xml | 15 ++++- 7 files changed, 81 insertions(+), 30 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index c323df200a..8d0f0f2af0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -38,6 +38,8 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.HttpStatus; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; @@ -84,8 +86,8 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import org.thingsboard.server.utils.PortFinder; -import java.net.ServerSocket; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -140,13 +142,27 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte private LwM2mClientContext clientContextTest; // Lwm2m Server - public static final int port = 5685; - public static final int securityPort = 5686; - public static final int portBs = 5687; - public static final int securityPortBs = 5688; + public static final String LWM2M_HOST = "localhost"; + public static final int LWM2M_PORT = PortFinder.findAvailableUdpPort(); // 5685 + public static final int LWM2MS_PORT = PortFinder.findAvailableUdpPort(); //5686 + + public static final String LWM2M_BOOTSTRAP_HOST = "localhost"; + public static final int LWM2M_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5687 + public static final int LWM2MS_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5688 + + @DynamicPropertySource + static void props(DynamicPropertyRegistry registry) { + log.warn("transport.lwm2m.server.bind_port = {}", LWM2M_PORT); + registry.add("transport.lwm2m.server.bind_port", () -> LWM2M_PORT); + log.warn("transport.lwm2m.server.security.bind_port = {}", LWM2MS_PORT); + registry.add("transport.lwm2m.server.security.bind_port", () -> LWM2MS_PORT); + + log.warn("transport.lwm2m.bootstrap.bind_port = {}", LWM2M_BOOTSTRAP_PORT); + registry.add("transport.lwm2m.bootstrap.bind_port", () -> LWM2M_BOOTSTRAP_PORT); + log.warn("transport.lwm2m.bootstrap.security.bind_port = {}", LWM2MS_BOOTSTRAP_PORT); + registry.add("transport.lwm2m.bootstrap.security.bind_port", () -> LWM2MS_BOOTSTRAP_PORT); + } - public static final String host = "localhost"; - public static final String hostBs = "localhost"; public static final Integer shortServerId = 123; public static final Integer shortServerIdBs0 = 0; public static final int serverId = 1; @@ -154,10 +170,10 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte public static final String COAP = "coap://"; public static final String COAPS = "coaps://"; - public static final String URI = COAP + host + ":" + port; - public static final String SECURE_URI = COAPS + host + ":" + securityPort; - public static final String URI_BS = COAP + hostBs + ":" + portBs; - public static final String SECURE_URI_BS = COAPS + hostBs + ":" + securityPortBs; + public static final String URI = COAP + LWM2M_HOST + ":" + LWM2M_PORT; + public static final String SECURE_URI = COAPS + LWM2M_HOST + ":" + LWM2MS_PORT; + public static final String URI_BS = COAP + LWM2M_BOOTSTRAP_HOST + ":" + LWM2M_BOOTSTRAP_PORT; + public static final String SECURE_URI_BS = COAPS + LWM2M_BOOTSTRAP_HOST + ":" + LWM2MS_BOOTSTRAP_PORT; public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId); protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS = @@ -572,12 +588,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte this.clientDestroy(false); lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources); - try (ServerSocket socket = new ServerSocket(0)) { - int clientPort = socket.getLocalPort(); - lwM2MTestClient.init(security, securityBs, clientPort, isRpc, - this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest, - clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9); - } + int clientPort = PortFinder.findAvailableUdpPort(); + //automatic client port assignment + lwM2MTestClient.init(security, securityBs, 0 /* clientPort */, isRpc, + this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest, + clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9); lwM2MTestClient.setDeviceIdStr(deviceIdStr); } @@ -715,8 +730,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte bootstrapServerCredential.setServerPublicKey(""); bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId); bootstrapServerCredential.setBootstrapServerIs(isBootstrap); - bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); - bootstrapServerCredential.setPort(isBootstrap ? portBs : port); + bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST); + bootstrapServerCredential.setPort(isBootstrap ? LWM2M_BOOTSTRAP_PORT : LWM2M_PORT); return bootstrapServerCredential; } @@ -731,11 +746,15 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte return credentials; } - protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception { + awaitObserveReadAll(cntObserve, deviceIdStr, ""); + } + + protected void awaitObserveReadAll(int cntObserve, String deviceIdStr, String assertionAlias) throws Exception { try { await("ObserveReadAll: countObserve " + cntObserve) - .atMost(40, TimeUnit.SECONDS) + .alias(assertionAlias) + .atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> cntObserve == getCntObserveAll(deviceIdStr)); } catch (ConditionTimeoutException e) { int current = getCntObserveAll(deviceIdStr); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java index e2d57077a3..979a2b8c96 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java @@ -33,7 +33,6 @@ import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicInteger; import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TARGET_FW_VERSION; import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TITLE; @@ -137,6 +136,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { @Override public void destroy() { scheduler.shutdownNow(); + this.leshanClient = null; } private void startDownloading() { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 37b30dca97..908c2d52d3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -78,6 +78,7 @@ import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import static org.awaitility.Awaitility.await; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; @@ -88,6 +89,7 @@ import static org.eclipse.leshan.core.LwM2mId.SECURITY; import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder.getDefaultPathEncoder; +import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS; import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.serverId; import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.serverIdBs; import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId; @@ -118,6 +120,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INST import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.lwm2mClientResources; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength; +import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable; @Slf4j @@ -140,12 +143,14 @@ public class LwM2MTestClient { private LwM2mClientContext clientContext; private LwM2mTemperatureSensor lwM2mTemperatureSensor12; private String deviceIdStr; + private int clientPort; - public void init(Security security, Security securityBs, int port, boolean isRpc, + public void init(Security security, Security securityBs, int clientPort, boolean isRpc, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode, boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException { Assert.assertNull("client already initialized", leshanClient); + this.clientPort = clientPort; this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler; this.clientContext = clientContext; @@ -266,7 +271,7 @@ public class LwM2MTestClient { // Set Californium Configuration endpointsBuilder.setConfiguration(clientCoapConfig); - endpointsBuilder.setClientAddress(new InetSocketAddress(port).getAddress()); + endpointsBuilder.setClientAddress(new InetSocketAddress(clientPort).getAddress()); // creates EndpointsProvider @@ -461,10 +466,19 @@ public class LwM2MTestClient { if (lwM2MTemperatureSensor != null) { lwM2MTemperatureSensor.destroy(); } + if (lwM2mTemperatureSensor12 != null) { + lwM2mTemperatureSensor12.destroy(); + } } public void start(boolean isStartLw) { if (leshanClient != null) { + if (clientPort > 0) { + log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort); + await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()") + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> isUDPPortAvailable(clientPort)); + } leshanClient.start(); if (isStartLw) { this.awaitClientAfterStartConnectLw(); @@ -477,6 +491,12 @@ public class LwM2MTestClient { public void stop(boolean deregister) { if (leshanClient != null) { leshanClient.stop(deregister); + if (clientPort > 0) { + log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort); + await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)") + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> isUDPPortAvailable(clientPort)); + } } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java index 64978af44d..bca551e989 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java @@ -169,6 +169,7 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr @Override public void destroy() { + this.leshanClient = null; } private void sendCollected() { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index d6e9e2d9b0..4adc398fa8 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -359,8 +359,8 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M } bootstrapServerCredential.setShortServerId(isBootstrap ? shortServerIdBs0 : shortServerId); bootstrapServerCredential.setBootstrapServerIs(isBootstrap); - bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); - bootstrapServerCredential.setPort(isBootstrap ? securityPortBs : securityPort); + bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST); + bootstrapServerCredential.setPort(isBootstrap ? LWM2MS_BOOTSTRAP_PORT : LWM2MS_PORT); return bootstrapServerCredential; } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index 420ebd16b2..29beace952 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -136,7 +136,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes ON_REGISTRATION_SUCCESS, true); - awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); + awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "before client stops for the first time"); lwM2MTestClient.stop(true); DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class); @@ -146,7 +146,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes Assert.assertNotNull(lwm2mDeviceProfileManyParams); lwM2MTestClient.start(true); - awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); + awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "second after client restart"); awaitUpdateReg(3); } diff --git a/pom.xml b/pom.xml index 58e0c32e43..8f5708bcec 100755 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,16 @@ true + + default-surefire-java-opts + + + !env.SUREFIRE_JAVA_OPTS + + + + + @@ -664,8 +674,9 @@ -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200 --add-opens=java.base/java.lang.reflect=ALL-UNNAMED - -Dqueue.edqs.local.rocksdb_path="target/rocks/fork_${surefire.forkNumber}/edqs" - -Dqueue.calculated_fields.rocks_db_path="target/rocks/fork_${surefire.forkNumber}/cf" + -Dqueue.edqs.local.rocksdb_path="target/rocksdb/fork_${surefire.forkNumber}/edqs" + -Dqueue.calculated_fields.rocks_db_path="target/rocksdb/fork_${surefire.forkNumber}/cf" + ${env.SUREFIRE_JAVA_OPTS} From 342060c54a75a49b2d4e5609ba373db9079d4dc3 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 23:36:58 +0100 Subject: [PATCH 22/26] tests: fixed EdgeImitator usage in controller test by providing Modifiable Deque getDownlinkMsgsDeque --- .../server/controller/EdgeControllerTest.java | 87 ++++++++++--------- .../server/edge/imitator/EdgeImitator.java | 5 ++ 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index 0d0de0a66c..fc65d67949 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -102,6 +102,7 @@ import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsS import java.util.ArrayList; import java.util.Collections; +import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -924,11 +925,11 @@ public class EdgeControllerTest extends AbstractControllerTest { verifyFetchersMsgs(edgeImitator, savedDevice); // verify queue msgs - Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); - Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId())); - Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); + Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId())); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); printQueueMsgsIfNotEmpty(edgeImitator); // 17 connect messages @@ -1008,32 +1009,32 @@ public class EdgeControllerTest extends AbstractControllerTest { } private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) { - Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); - Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); - Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator))); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general")); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail")); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity")); - Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "jwt")); - Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgs(), currentUserId)); - Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN)); - Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public")); - Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); - Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId())); - Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); - Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); - Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId)); - Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgs(), tenantProfileId)); - Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgs())); + Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); + Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); + Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator))); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "general")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "mail")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "connectivity")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "jwt")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), currentUserId)); + Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN)); + Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); + Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId())); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); + Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgsDeque(), tenantId)); + Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgsDeque(), tenantProfileId)); + Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgsDeque())); } - private boolean popQueueMsg(List messages, UpdateMsgType msgType, String name) { + private boolean popQueueMsg(Deque messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { if (message instanceof QueueUpdateMsg queueUpdateMsg) { Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); @@ -1047,7 +1048,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popRuleChainMsg(List messages, UpdateMsgType msgType, String name) { + private boolean popRuleChainMsg(Deque messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) { RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); @@ -1063,7 +1064,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popRuleChainMetadataMsg(List messages, UpdateMsgType msgType, RuleChainId ruleChainId) { + private boolean popRuleChainMetadataMsg(Deque messages, UpdateMsgType msgType, RuleChainId ruleChainId) { for (AbstractMessage message : messages) { if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) { RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true); @@ -1078,7 +1079,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popAdminSettingsMsg(List messages, String key) { + private boolean popAdminSettingsMsg(Deque messages, String key) { for (AbstractMessage message : messages) { if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) { AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true); @@ -1092,7 +1093,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popDeviceProfileMsg(List messages, UpdateMsgType msgType, String name) { + private boolean popDeviceProfileMsg(Deque messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) { DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); @@ -1107,7 +1108,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popDeviceMsg(List messages, UpdateMsgType msgType, String name) { + private boolean popDeviceMsg(Deque messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { if (message instanceof DeviceUpdateMsg deviceUpdateMsg) { Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); @@ -1122,7 +1123,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popDeviceCredentialsMsg(List messages, DeviceId deviceId) { + private boolean popDeviceCredentialsMsg(Deque messages, DeviceId deviceId) { for (AbstractMessage message : messages) { if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); @@ -1136,7 +1137,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popAssetProfileMsg(List messages, UpdateMsgType msgType, String name) { + private boolean popAssetProfileMsg(Deque messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) { AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); @@ -1151,7 +1152,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popAssetMsg(List messages, UpdateMsgType msgType, String name) { + private boolean popAssetMsg(Deque messages, UpdateMsgType msgType, String name) { for (AbstractMessage message : messages) { if (message instanceof AssetUpdateMsg assetUpdateMsg) { Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); @@ -1166,7 +1167,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popUserCredentialsMsg(List messages, UserId userId) { + private boolean popUserCredentialsMsg(Deque messages, UserId userId) { for (AbstractMessage message : messages) { if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) { UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); @@ -1180,7 +1181,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popUserMsg(List messages, UpdateMsgType msgType, String email, Authority authority) { + private boolean popUserMsg(Deque messages, UpdateMsgType msgType, String email, Authority authority) { for (AbstractMessage message : messages) { if (message instanceof UserUpdateMsg userUpdateMsg) { User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); @@ -1196,7 +1197,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popCustomerMsg(List messages, UpdateMsgType msgType, String title) { + private boolean popCustomerMsg(Deque messages, UpdateMsgType msgType, String title) { for (AbstractMessage message : messages) { if (message instanceof CustomerUpdateMsg customerUpdateMsg) { Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); @@ -1211,7 +1212,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popTenantMsg(List messages, TenantId tenantId1) { + private boolean popTenantMsg(Deque messages, TenantId tenantId1) { for (AbstractMessage message : messages) { if (message instanceof TenantUpdateMsg tenantUpdateMsg) { Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); @@ -1226,7 +1227,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popTenantProfileMsg(List messages, TenantProfileId tenantProfileId) { + private boolean popTenantProfileMsg(Deque messages, TenantProfileId tenantProfileId) { for (AbstractMessage message : messages) { if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) { TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); @@ -1241,7 +1242,7 @@ public class EdgeControllerTest extends AbstractControllerTest { return false; } - private boolean popSyncCompletedMsg(List messages) { + private boolean popSyncCompletedMsg(Deque messages) { for (AbstractMessage message : messages) { if (message instanceof SyncCompletedMsg) { messages.remove(message); diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java index d1596b1c59..266a4dbb62 100644 --- a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java +++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java @@ -68,6 +68,7 @@ import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -111,6 +112,10 @@ public class EdgeImitator { return downlinkMsgs.stream().toList(); } + public Deque getDownlinkMsgsDeque() { + return downlinkMsgs; + } + @Getter private UplinkResponseMsg latestResponseMsg; From 29812b0c8dae1e5d533d387aa5a861174f17aa08 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 23 Jan 2026 00:16:33 +0100 Subject: [PATCH 23/26] tests: noted how to run tests in parallel fast --- TEST_FAST.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 TEST_FAST.md diff --git a/TEST_FAST.md b/TEST_FAST.md new file mode 100644 index 0000000000..4219d7470b --- /dev/null +++ b/TEST_FAST.md @@ -0,0 +1,27 @@ + +## Running tests in parallel with a reasonable memory usage + +```bash +mvn clean install -T6 -DskipTests +mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4 +mvn test -pl dao -Dparallel=packages -DforkCount=4 + +mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 + +#the rest of application tests +mvn test -pl application -Dtest=' +!**/nosql/*Test.java, +!org.thingsboard.server.controller.**, +!org.thingsboard.server.edge.**, +!org.thingsboard.server.service.**, +!org.thingsboard.server.transport.mqtt.**, +!org.thingsboard.server.transport.coap.**, +!org.thingsboard.server.transport.lwm2m.** +' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 +``` From da608805af3f946e233716ee0e9c4a23c0bcea28 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 23 Jan 2026 07:59:17 +0100 Subject: [PATCH 24/26] test example for memory management --- TEST_FAST.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TEST_FAST.md b/TEST_FAST.md index 4219d7470b..c0f1174584 100644 --- a/TEST_FAST.md +++ b/TEST_FAST.md @@ -2,6 +2,10 @@ ## Running tests in parallel with a reasonable memory usage ```bash +export MAVEN_OPTS="-Xmx1024m" +export NODE_OPTIONS="--max_old_space_size=4096" +export SUREFIRE_JAVA_OPTS="-Xmx1200m -Xss256k -XX:+ExitOnOutOfMemoryError" + mvn clean install -T6 -DskipTests mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4 mvn test -pl dao -Dparallel=packages -DforkCount=4 From efead4abd387fcfdccfc163ebe18d5a45bf0712e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 23 Jan 2026 13:24:49 +0100 Subject: [PATCH 25/26] tests: fixed LWM2M clientDestroy with PortFinder.isUDPPortAvailable --- .../transport/lwm2m/AbstractLwM2MIntegrationTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index a0f3f07883..f3a3212e67 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import jnr.ffi.annotations.In; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.awaitility.core.ConditionTimeoutException; @@ -672,10 +673,9 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte try { if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) { boolean serverAlive = false; - for (int port = AbstractLwM2MIntegrationTest.port; port <= securityPortBs; port++) { - try (ServerSocket socket = new ServerSocket(port)) { - log.info("Port {} is free.", port); - } catch (IOException e) { + List ports = List.of(LWM2M_PORT, LWM2MS_PORT, LWM2MS_BOOTSTRAP_PORT, LWM2MS_BOOTSTRAP_PORT); + for (Integer port : ports) { + if (!PortFinder.isUDPPortAvailable(port)) { log.debug("Port {} is busy — CoAP server still active.", port); serverAlive = true; break; From 1fcc419dbca09b5af21a587a52618cf169036470 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 23 Jan 2026 16:19:02 +0200 Subject: [PATCH 26/26] Fix LwM2MTestClient --- .../lwm2m/client/LwM2MTestClient.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index fd531e8022..c9c39bc701 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -33,7 +33,6 @@ import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.resource.DummyInstanceEnabler; -import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler; import org.eclipse.leshan.client.resource.LwM2mObjectEnabler; import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter; @@ -142,7 +141,7 @@ public class LwM2MTestClient { private Map clientDtlsCid; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mClientContext clientContext; - private LwM2mTemperatureSensor lwM2mTemperatureSensor12; + private LwM2mTemperatureSensor lwM2MTemperatureSensor12; private String deviceIdStr; private int clientPort; @@ -161,7 +160,7 @@ public class LwM2MTestClient { if (securityLwm2m != null && securityLwm2m.getId() != null) { forceNullSecurityId(securityLwm2m); } - if (securityBs!= null && securityBs.getId() != null) { + if (securityBs != null && securityBs.getId() != null) { forceNullSecurityId(securityBs); } if (securityBs != null && securityLwm2m != null) { @@ -170,7 +169,7 @@ public class LwM2MTestClient { } else if (securityBs != null) { log.warn("Security BS only: securityBs: [{}] ", securityBs.getId()); initializer.setInstancesForObject(SECURITY, securityBs); - } else if (securityLwm2m != null){ + } else if (securityLwm2m != null) { // SECURITY log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId()); initializer.setInstancesForObject(SECURITY, securityLwm2m); @@ -191,8 +190,8 @@ public class LwM2MTestClient { locationParams.getPos(); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0); - lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); - initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12); + lwM2MTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); + initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2MTemperatureSensor12); List enablers = initializer.createAll(); @@ -213,7 +212,9 @@ public class LwM2MTestClient { builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid)); return builder; - }; + } + + ; }; } }; @@ -479,7 +480,7 @@ public class LwM2MTestClient { if (isStartLw) { this.awaitClientAfterStartConnectLw(); } - lwM2mTemperatureSensor12.setLeshanClient(leshanClient); + lwM2MTemperatureSensor12.setLeshanClient(leshanClient); fwLwM2MDevice.setLeshanClient(leshanClient); } } @@ -554,5 +555,6 @@ public class LwM2MTestClient { log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e); } } + }