diff --git a/TEST_FAST.md b/TEST_FAST.md new file mode 100644 index 0000000000..c0f1174584 --- /dev/null +++ b/TEST_FAST.md @@ -0,0 +1,31 @@ + +## 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 + +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 +``` 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 457f723644..6fbe9d9864 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; @@ -349,7 +350,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(); @@ -423,7 +431,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/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index 43f08f42f9..9d50e3a62c 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; @@ -99,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; @@ -121,7 +125,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<>(); @@ -916,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 @@ -1000,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); @@ -1039,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); @@ -1055,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); @@ -1070,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); @@ -1084,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); @@ -1099,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); @@ -1114,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); @@ -1128,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); @@ -1143,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); @@ -1158,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); @@ -1172,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); @@ -1188,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); @@ -1203,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); @@ -1218,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); @@ -1233,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/controller/EntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java index 6d095dc95f..a4d6b62a0c 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; @@ -88,6 +90,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", @@ -97,6 +101,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<>() { @@ -683,7 +693,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); @@ -734,7 +744,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/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 3629d7ff49..260476fe4d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -27,7 +27,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; @@ -118,6 +121,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; @@ -144,7 +156,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); 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 9292d5bc06..9349f79c02 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; @@ -84,12 +87,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/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java index 8b2cef2e8b..8cf2b309f6 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 @@ -69,10 +69,12 @@ 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; import java.util.Optional; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -103,8 +105,17 @@ 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(); + } + + public Deque getDownlinkMsgsDeque() { + return downlinkMsgs; + } @Getter private UplinkResponseMsg latestResponseMsg; @@ -113,7 +124,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; @@ -458,7 +469,7 @@ public class EdgeImitator { } public AbstractMessage getLatestMessage() { - return downlinkMsgs.get(downlinkMsgs.size() - 1); + return downlinkMsgs.peekLast(); } public void ignoreType(Class type) { 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 89f743aee4..e3342dbf43 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 @@ -238,12 +238,12 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .set("createAlarm", BooleanNode.TRUE); postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); - 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(); @@ -291,8 +291,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 -> { @@ -345,7 +345,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .set("createAlarm", BooleanNode.TRUE); postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); - 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); @@ -355,14 +355,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(); }); } @@ -480,7 +480,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); @@ -500,7 +500,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); @@ -721,7 +721,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); }); @@ -809,7 +809,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"); @@ -836,7 +836,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"); @@ -848,7 +848,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 @@ -887,7 +887,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"); 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..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,10 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest { - 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/"; + 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/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java index 7240ab0afa..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 @@ -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.warn("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..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 @@ -21,6 +21,8 @@ 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.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.CoapDeviceType; @@ -37,10 +39,10 @@ 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.ServerSocket; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; @@ -51,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; @@ -70,7 +70,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.warn("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,13 +170,10 @@ 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)); CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01, "CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(), fixedPort, PAYLOAD_VALUES_STR_01); @@ -274,19 +281,5 @@ 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); - return true; - } catch (IOException 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 0f65298d86..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)); } @@ -97,6 +102,15 @@ public class CoapClientX509Test { if (clientX509 != null) { clientX509.shutdown(); } + 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)); + } + } } public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException { 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 096903f8df..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; @@ -38,6 +39,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,9 +87,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.io.IOException; -import java.net.ServerSocket; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -141,21 +143,35 @@ 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 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 = @@ -570,12 +586,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte this.clientDestroy(); 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); } @@ -658,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; @@ -730,8 +744,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte bootstrapServerCredential.setServerPublicKey(""); bootstrapServerCredential.setShortServerId(isBootstrap ? null : 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; } @@ -746,11 +760,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 bda2ef17e1..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; @@ -82,6 +81,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +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; @@ -92,6 +92,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.shortServerId; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState; @@ -119,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 @@ -139,14 +141,16 @@ public class LwM2MTestClient { private Map clientDtlsCid; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mClientContext clientContext; - private LwM2mTemperatureSensor lwM2mTemperatureSensor12; + private LwM2mTemperatureSensor lwM2MTemperatureSensor12; private String deviceIdStr; + private int clientPort; - public void init(Security securityLwm2m, Security securityBs, int port, boolean isRpc, + public void init(Security securityLwm2m, 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; @@ -156,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) { @@ -165,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); @@ -186,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(); @@ -208,7 +212,9 @@ public class LwM2MTestClient { builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid)); return builder; - }; + } + + ; }; } }; @@ -245,7 +251,7 @@ public class LwM2MTestClient { // Set Californium Configuration endpointsBuilder.setConfiguration(clientCoapConfig); - endpointsBuilder.setClientAddress(new InetSocketAddress(port).getAddress()); + endpointsBuilder.setClientAddress(new InetSocketAddress(clientPort).getAddress()); // creates EndpointsProvider @@ -438,12 +444,14 @@ public class LwM2MTestClient { destroySafe(swLwM2MDevice); destroySafe(lwM2MBinaryAppDataContainer); destroySafe(lwM2MTemperatureSensor); + destroySafe(lwM2MTemperatureSensor12); lwM2MDevice = null; fwLwM2MDevice = null; swLwM2MDevice = null; lwM2MBinaryAppDataContainer = null; lwM2MTemperatureSensor = null; + lwM2MTemperatureSensor12 = null; } @@ -462,11 +470,17 @@ public class LwM2MTestClient { 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(); } - lwM2mTemperatureSensor12.setLeshanClient(leshanClient); + lwM2MTemperatureSensor12.setLeshanClient(leshanClient); fwLwM2MDevice.setLeshanClient(leshanClient); } } @@ -474,6 +488,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)); + } } } @@ -535,5 +555,6 @@ public class LwM2MTestClient { log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e); } } + } 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 20af793052..52bef912f2 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 @@ -366,8 +366,8 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M } bootstrapServerCredential.setShortServerId(isBootstrap ? null : 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 899fdc06a0..6083966837 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 @@ -133,7 +133,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); @@ -143,7 +143,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/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/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..21a1f1eb0d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/utils/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.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)) { + 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/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/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 13c93da411..6faaf97536 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -42,6 +42,12 @@ + + + + + + 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/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); } 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); 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 8006793b87..74fa56dc87 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 @@ -100,6 +100,18 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator --> + + + 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) 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(); 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); diff --git a/pom.xml b/pom.xml index 353c295038..880a6ebc89 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 @@ -172,6 +172,16 @@ true + + default-surefire-java-opts + + + !env.SUREFIRE_JAVA_OPTS + + + + + @@ -662,8 +672,11 @@ ${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/rocksdb/fork_${surefire.forkNumber}/edqs" + -Dqueue.calculated_fields.rocks_db_path="target/rocksdb/fork_${surefire.forkNumber}/cf" + ${env.SUREFIRE_JAVA_OPTS}