Skip to content

Commit 241a35a

Browse files
authored
Refactor OperationLimits for lenient type handling (#1665)
- Introduced `toUInteger` helper method for consistent conversion to `UInteger`, handling various numeric types. - Updated `OperationLimits` to use `toUInteger` for value extraction, reducing duplication. - Created a new test in `OperationLimitsTest` to validate behavior with `UShort` values. fixes #1664
1 parent 7dd27c2 commit 241a35a

File tree

3 files changed

+119
-14
lines changed

3 files changed

+119
-14
lines changed

opc-ua-sdk/integration-tests/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@
7777
<version>${junit.version}</version>
7878
<scope>test</scope>
7979
</dependency>
80+
<dependency>
81+
<groupId>org.mockito</groupId>
82+
<artifactId>mockito-core</artifactId>
83+
<version>${mockito.version}</version>
84+
<scope>test</scope>
85+
</dependency>
8086
</dependencies>
8187

8288
<build>

opc-ua-sdk/integration-tests/src/test/java/org/eclipse/milo/opcua/sdk/client/OperationLimitsTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@
1010

1111
package org.eclipse.milo.opcua.sdk.client;
1212

13+
import static org.junit.jupiter.api.Assertions.assertEquals;
1314
import static org.junit.jupiter.api.Assertions.assertThrows;
1415
import static org.junit.jupiter.api.Assertions.assertTrue;
16+
import static org.mockito.ArgumentMatchers.any;
17+
import static org.mockito.ArgumentMatchers.anyDouble;
18+
import static org.mockito.ArgumentMatchers.anyList;
19+
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.when;
1521

22+
import java.util.List;
1623
import org.eclipse.milo.opcua.sdk.test.AbstractClientServerTest;
1724
import org.eclipse.milo.opcua.stack.core.UaException;
25+
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
26+
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
27+
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
1828
import org.junit.jupiter.api.Test;
1929

2030
public class OperationLimitsTest extends AbstractClientServerTest {
@@ -43,4 +53,69 @@ void readThrowsWhenDisconnected() throws UaException {
4353

4454
assertThrows(UaException.class, () -> client.readOperationLimits());
4555
}
56+
57+
@Test
58+
void readHandlesUShortValues() throws UaException {
59+
// Create a mock client that returns UShort values instead of UInteger
60+
var mockClient = mock(OpcUaClient.class);
61+
62+
// Create DataValues with UShort values (simulating a server that returns the wrong type)
63+
var values =
64+
List.of(
65+
new DataValue(new Variant(UShort.valueOf(100))), // maxNodesPerRead
66+
new DataValue(new Variant(UShort.valueOf(200))), // maxNodesPerWrite
67+
new DataValue(new Variant(UShort.valueOf(300))), // maxNodesPerMethodCall
68+
new DataValue(new Variant(UShort.valueOf(400))), // maxNodesPerBrowse
69+
new DataValue(new Variant(UShort.valueOf(500))), // maxNodesPerRegisterNodes
70+
new DataValue(
71+
new Variant(UShort.valueOf(600))), // maxNodesPerTranslateBrowsePathsToNodeIds
72+
new DataValue(new Variant(UShort.valueOf(700))), // maxNodesPerNodeManagement
73+
new DataValue(new Variant(UShort.valueOf(800))), // maxMonitoredItemsPerCall
74+
new DataValue(new Variant(UShort.valueOf(900))), // maxNodesPerHistoryReadData
75+
new DataValue(new Variant(UShort.valueOf(1000))), // maxNodesPerHistoryReadEvents
76+
new DataValue(new Variant(UShort.valueOf(1100))), // maxNodesPerHistoryUpdateData
77+
new DataValue(new Variant(UShort.valueOf(1200)))); // maxNodesPerHistoryUpdateEvents
78+
79+
when(mockClient.readValues(anyDouble(), any(), anyList())).thenReturn(values);
80+
81+
// Call the static read method with our mock
82+
var operationLimits = OperationLimits.read(mockClient);
83+
84+
// Verify all values are present and correctly converted from UShort to UInteger
85+
assertTrue(operationLimits.maxNodesPerRead().isPresent());
86+
assertEquals(100, operationLimits.maxNodesPerRead().get().intValue());
87+
88+
assertTrue(operationLimits.maxNodesPerWrite().isPresent());
89+
assertEquals(200, operationLimits.maxNodesPerWrite().get().intValue());
90+
91+
assertTrue(operationLimits.maxNodesPerMethodCall().isPresent());
92+
assertEquals(300, operationLimits.maxNodesPerMethodCall().get().intValue());
93+
94+
assertTrue(operationLimits.maxNodesPerBrowse().isPresent());
95+
assertEquals(400, operationLimits.maxNodesPerBrowse().get().intValue());
96+
97+
assertTrue(operationLimits.maxNodesPerRegisterNodes().isPresent());
98+
assertEquals(500, operationLimits.maxNodesPerRegisterNodes().get().intValue());
99+
100+
assertTrue(operationLimits.maxNodesPerTranslateBrowsePathsToNodeIds().isPresent());
101+
assertEquals(600, operationLimits.maxNodesPerTranslateBrowsePathsToNodeIds().get().intValue());
102+
103+
assertTrue(operationLimits.maxNodesPerNodeManagement().isPresent());
104+
assertEquals(700, operationLimits.maxNodesPerNodeManagement().get().intValue());
105+
106+
assertTrue(operationLimits.maxMonitoredItemsPerCall().isPresent());
107+
assertEquals(800, operationLimits.maxMonitoredItemsPerCall().get().intValue());
108+
109+
assertTrue(operationLimits.maxNodesPerHistoryReadData().isPresent());
110+
assertEquals(900, operationLimits.maxNodesPerHistoryReadData().get().intValue());
111+
112+
assertTrue(operationLimits.maxNodesPerHistoryReadEvents().isPresent());
113+
assertEquals(1000, operationLimits.maxNodesPerHistoryReadEvents().get().intValue());
114+
115+
assertTrue(operationLimits.maxNodesPerHistoryUpdateData().isPresent());
116+
assertEquals(1100, operationLimits.maxNodesPerHistoryUpdateData().get().intValue());
117+
118+
assertTrue(operationLimits.maxNodesPerHistoryUpdateEvents().isPresent());
119+
assertEquals(1200, operationLimits.maxNodesPerHistoryUpdateEvents().get().intValue());
120+
}
46121
}

opc-ua-sdk/sdk-client/src/main/java/org/eclipse/milo/opcua/sdk/client/OperationLimits.java

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,18 @@ private static OperationLimits readAllNodes(OpcUaClient client) throws UaExcepti
200200
List<DataValue> values =
201201
client.readValues(0.0, TimestampsToReturn.Neither, OPERATION_LIMITS_NODES);
202202

203-
UInteger maxNodesPerRead = (UInteger) values.get(0).value().value();
204-
UInteger maxNodesPerWrite = (UInteger) values.get(1).value().value();
205-
UInteger maxNodesPerMethodCall = (UInteger) values.get(2).value().value();
206-
UInteger maxNodesPerBrowse = (UInteger) values.get(3).value().value();
207-
UInteger maxNodesPerRegisterNodes = (UInteger) values.get(4).value().value();
208-
UInteger maxNodesPerTranslateBrowsePathsToNodeIds = (UInteger) values.get(5).value().value();
209-
UInteger maxNodesPerNodeManagement = (UInteger) values.get(6).value().value();
210-
UInteger maxMonitoredItemsPerCall = (UInteger) values.get(7).value().value();
211-
UInteger maxNodesPerHistoryReadData = (UInteger) values.get(8).value().value();
212-
UInteger maxNodesPerHistoryReadEvents = (UInteger) values.get(9).value().value();
213-
UInteger maxNodesPerHistoryUpdateData = (UInteger) values.get(10).value().value();
214-
UInteger maxNodesPerHistoryUpdateEvents = (UInteger) values.get(11).value().value();
203+
UInteger maxNodesPerRead = toUInteger(values.get(0).value().value());
204+
UInteger maxNodesPerWrite = toUInteger(values.get(1).value().value());
205+
UInteger maxNodesPerMethodCall = toUInteger(values.get(2).value().value());
206+
UInteger maxNodesPerBrowse = toUInteger(values.get(3).value().value());
207+
UInteger maxNodesPerRegisterNodes = toUInteger(values.get(4).value().value());
208+
UInteger maxNodesPerTranslateBrowsePathsToNodeIds = toUInteger(values.get(5).value().value());
209+
UInteger maxNodesPerNodeManagement = toUInteger(values.get(6).value().value());
210+
UInteger maxMonitoredItemsPerCall = toUInteger(values.get(7).value().value());
211+
UInteger maxNodesPerHistoryReadData = toUInteger(values.get(8).value().value());
212+
UInteger maxNodesPerHistoryReadEvents = toUInteger(values.get(9).value().value());
213+
UInteger maxNodesPerHistoryUpdateData = toUInteger(values.get(10).value().value());
214+
UInteger maxNodesPerHistoryUpdateEvents = toUInteger(values.get(11).value().value());
215215

216216
return new OperationLimits(
217217
maxNodesPerRead,
@@ -232,8 +232,8 @@ private static OperationLimits readIndividualNodes(OpcUaClient client) throws Ua
232232
Function<NodeId, UInteger> read =
233233
nodeId -> {
234234
try {
235-
return (UInteger)
236-
client.readValue(0.0, TimestampsToReturn.Neither, nodeId).value().value();
235+
return toUInteger(
236+
client.readValue(0.0, TimestampsToReturn.Neither, nodeId).value().value());
237237
} catch (UaException e) {
238238
return null;
239239
}
@@ -283,4 +283,28 @@ private static OperationLimits readIndividualNodes(OpcUaClient client) throws Ua
283283
maxNodesPerHistoryUpdateData,
284284
maxNodesPerHistoryUpdateEvents);
285285
}
286+
287+
/**
288+
* Converts a value to a {@link UInteger}, handling various numeric types that a server might
289+
* return.
290+
*
291+
* @param value the value to convert.
292+
* @return the value as a {@link UInteger}, or {@code null} if the value is {@code null} or cannot
293+
* be converted.
294+
*/
295+
private static @Nullable UInteger toUInteger(@Nullable Object value) {
296+
if (value == null) {
297+
return null;
298+
}
299+
300+
if (value instanceof UInteger uInteger) {
301+
return uInteger;
302+
}
303+
304+
if (value instanceof Number number) {
305+
return UInteger.valueOf(number.longValue());
306+
}
307+
308+
return null;
309+
}
286310
}

0 commit comments

Comments
 (0)