Skip to content

Commit 0dc108c

Browse files
authored
Add unit tests for core networking and preferences APIs (#3995)
* Add comprehensive tests for IO networking utilities * Handle IOException when stubbing storage streams * Stabilize new network unit tests
1 parent f271922 commit 0dc108c

File tree

5 files changed

+694
-0
lines changed

5 files changed

+694
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.codename1.io;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.ByteArrayOutputStream;
5+
import java.io.IOException;
6+
import java.nio.charset.StandardCharsets;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
10+
import static org.junit.jupiter.api.Assertions.*;
11+
12+
class MultipartRequestTest {
13+
@BeforeEach
14+
void resetImplementation() {
15+
TestImplementationProvider.installImplementation(true);
16+
}
17+
18+
@Test
19+
void buildRequestBodyIncludesArgumentsAndBinaryData() throws IOException {
20+
MultipartRequest request = new MultipartRequest();
21+
request.setBoundary("test-boundary");
22+
request.addArgument("text", "value with spaces");
23+
request.addArgumentNoEncoding("raw", "unchanged + data");
24+
request.addArgument("multi", new String[]{"first", "second"});
25+
request.addData("file", "hello".getBytes(StandardCharsets.UTF_8), "application/octet-stream");
26+
request.setFilename("file", "file.txt");
27+
28+
ByteArrayOutputStream out = new ByteArrayOutputStream();
29+
request.buildRequestBody(out);
30+
byte[] payload = out.toByteArray();
31+
String payloadString = new String(payload, StandardCharsets.ISO_8859_1);
32+
33+
assertTrue(payloadString.contains("--test-boundary"));
34+
assertTrue(payloadString.contains("Content-Disposition: form-data; name=\"text\""));
35+
assertTrue(payloadString.contains("value+with+spaces"));
36+
assertTrue(payloadString.contains("Content-Disposition: form-data; name=\"raw\""));
37+
assertTrue(payloadString.contains("unchanged + data"));
38+
assertTrue(payloadString.contains("Content-Disposition: form-data; name=\"multi\""));
39+
assertTrue(payloadString.contains("first"));
40+
assertTrue(payloadString.contains("second"));
41+
assertTrue(payloadString.contains("Content-Disposition: form-data; name=\"file\"; filename=\"file.txt\""));
42+
assertTrue(payloadString.contains("Content-Type: application/octet-stream"));
43+
assertTrue(payloadString.contains("hello"));
44+
assertTrue(payloadString.endsWith("--test-boundary--\r\n"));
45+
46+
assertEquals(payload.length, request.calculateContentLength());
47+
}
48+
49+
@Test
50+
void base64ToggleControlsEncoding() throws IOException {
51+
MultipartRequest request = new MultipartRequest();
52+
request.setBoundary("boundary");
53+
request.addArgument("text", "encode me");
54+
request.setBase64Binaries(false);
55+
56+
ByteArrayOutputStream out = new ByteArrayOutputStream();
57+
request.buildRequestBody(out);
58+
String payload = out.toString(StandardCharsets.ISO_8859_1.name());
59+
60+
assertTrue(payload.contains("encode me"));
61+
assertFalse(payload.contains("encode+me"));
62+
}
63+
64+
@Test
65+
void manualRedirectFlagIsHonored() {
66+
MultipartRequest request = new MultipartRequest();
67+
assertTrue(request.isManualRedirect());
68+
assertTrue(request.onRedirect("http://example.com"));
69+
70+
request.setManualRedirect(false);
71+
assertFalse(request.isManualRedirect());
72+
assertFalse(request.onRedirect("http://example.com"));
73+
}
74+
75+
@Test
76+
void calculateContentLengthMatchesStreamForInputStreamData() throws IOException {
77+
MultipartRequest request = new MultipartRequest();
78+
request.setBoundary("stream-boundary");
79+
byte[] data = new byte[256];
80+
for (int i = 0; i < data.length; i++) {
81+
data[i] = (byte) i;
82+
}
83+
request.addData("stream", new ByteArrayInputStream(data), data.length, "application/octet-stream");
84+
request.setFilename("stream", "blob.bin");
85+
86+
ByteArrayOutputStream out = new ByteArrayOutputStream();
87+
request.buildRequestBody(out);
88+
89+
assertEquals(out.size(), request.calculateContentLength());
90+
byte[] payload = out.toByteArray();
91+
assertTrue(payload.length > data.length);
92+
assertTrue(containsSubArray(payload, data));
93+
}
94+
95+
private static boolean containsSubArray(byte[] outer, byte[] inner) {
96+
if (inner.length == 0 || outer.length < inner.length) {
97+
return false;
98+
}
99+
for (int i = 0; i <= outer.length - inner.length; i++) {
100+
boolean match = true;
101+
for (int j = 0; j < inner.length; j++) {
102+
if (outer[i + j] != inner[j]) {
103+
match = false;
104+
break;
105+
}
106+
}
107+
if (match) {
108+
return true;
109+
}
110+
}
111+
return false;
112+
}
113+
}
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package com.codename1.io;
2+
3+
import com.codename1.impl.CodenameOneImplementation;
4+
import com.codename1.ui.Display;
5+
import com.codename1.ui.events.ActionListener;
6+
import java.lang.reflect.Array;
7+
import java.lang.reflect.Field;
8+
import java.util.Enumeration;
9+
import java.util.Vector;
10+
import java.util.concurrent.atomic.AtomicInteger;
11+
import org.junit.jupiter.api.BeforeEach;
12+
import org.junit.jupiter.api.Test;
13+
14+
import static org.junit.jupiter.api.Assertions.*;
15+
import static org.mockito.Mockito.reset;
16+
import static org.mockito.Mockito.verify;
17+
import static org.mockito.Mockito.when;
18+
19+
class NetworkManagerTest {
20+
private NetworkManager manager;
21+
private CodenameOneImplementation implementation;
22+
23+
@BeforeEach
24+
void setUp() throws Exception {
25+
implementation = TestImplementationProvider.installImplementation(true);
26+
manager = NetworkManager.getInstance();
27+
resetManagerState();
28+
bootstrapDisplayThread();
29+
}
30+
31+
@Test
32+
void autoDetectUrlMutators() {
33+
NetworkManager.setAutoDetectURL("https://example.com/ping");
34+
assertEquals("https://example.com/ping", NetworkManager.getAutoDetectURL());
35+
}
36+
37+
@Test
38+
void addDefaultHeaderStoresValue() throws Exception {
39+
manager.addDefaultHeader("X-Test", "value");
40+
Field headersField = NetworkManager.class.getDeclaredField("userHeaders");
41+
headersField.setAccessible(true);
42+
Object headers = headersField.get(manager);
43+
assertNotNull(headers);
44+
assertEquals("value", ((java.util.Hashtable) headers).get("X-Test"));
45+
}
46+
47+
@Test
48+
void errorListenersReceiveEvents() {
49+
AtomicInteger invocations = new AtomicInteger();
50+
ActionListener<NetworkEvent> listener = evt -> {
51+
invocations.incrementAndGet();
52+
evt.consume();
53+
};
54+
manager.addErrorListener(listener);
55+
boolean consumed = manager.handleErrorCode(new ConnectionRequest(), 404, "Not Found");
56+
assertTrue(consumed);
57+
assertEquals(1, invocations.get());
58+
}
59+
60+
@Test
61+
void progressListenersTrackUpdates() {
62+
AtomicInteger lengths = new AtomicInteger();
63+
ActionListener<NetworkEvent> listener = evt -> lengths.addAndGet(evt.getLength());
64+
manager.addProgressListener(listener);
65+
manager.fireProgressEvent(new ConnectionRequest(), NetworkEvent.PROGRESS_TYPE_OUTPUT, 50, 10);
66+
assertEquals(50, lengths.get());
67+
assertTrue(manager.hasProgressListeners());
68+
manager.removeProgressListener(listener);
69+
assertFalse(manager.hasProgressListeners());
70+
}
71+
72+
@Test
73+
void removeProgressListenerClearsDispatcher() {
74+
ActionListener<NetworkEvent> listener = evt -> {};
75+
manager.addProgressListener(listener);
76+
assertTrue(manager.hasProgressListeners());
77+
manager.removeProgressListener(listener);
78+
assertFalse(manager.hasProgressListeners());
79+
}
80+
81+
@Test
82+
void setTimeoutDelegatesWhenSupported() {
83+
manager.setTimeout(1234);
84+
verify(implementation).setTimeout(1234);
85+
}
86+
87+
@Test
88+
void setTimeoutStoresValueWhenUnsupported() throws Exception {
89+
reset(implementation);
90+
implementation = TestImplementationProvider.installImplementation(false);
91+
manager = NetworkManager.getInstance();
92+
resetManagerState();
93+
94+
manager.setTimeout(4321);
95+
Field timeoutField = NetworkManager.class.getDeclaredField("timeout");
96+
timeoutField.setAccessible(true);
97+
assertEquals(4321, timeoutField.getInt(manager));
98+
}
99+
100+
@Test
101+
void assignToThreadRecordsMapping() throws Exception {
102+
manager.assignToThread(MockConnectionRequest.class, 2);
103+
Field assignField = NetworkManager.class.getDeclaredField("threadAssignements");
104+
assignField.setAccessible(true);
105+
java.util.Hashtable table = (java.util.Hashtable) assignField.get(manager);
106+
assertEquals(2, table.get(MockConnectionRequest.class.getName()));
107+
}
108+
109+
@Test
110+
void enumerateQueueReturnsSnapshot() throws Exception {
111+
Vector pending = getPendingQueue();
112+
ConnectionRequest first = new ConnectionRequest();
113+
ConnectionRequest second = new ConnectionRequest();
114+
pending.addElement(first);
115+
pending.addElement(second);
116+
117+
Enumeration enumeration = manager.enumurateQueue();
118+
assertTrue(enumeration.hasMoreElements());
119+
assertSame(first, enumeration.nextElement());
120+
assertSame(second, enumeration.nextElement());
121+
assertFalse(enumeration.hasMoreElements());
122+
}
123+
124+
@Test
125+
void isQueueIdleReflectsCurrentRequest() throws Exception {
126+
Vector pending = getPendingQueue();
127+
pending.clear();
128+
assertTrue(manager.isQueueIdle());
129+
130+
NetworkManager.NetworkThread thread = manager.new NetworkThread();
131+
Object array = Array.newInstance(thread.getClass(), 1);
132+
Array.set(array, 0, thread);
133+
Field threadsField = NetworkManager.class.getDeclaredField("networkThreads");
134+
threadsField.setAccessible(true);
135+
threadsField.set(manager, array);
136+
assertTrue(manager.isQueueIdle());
137+
138+
Field currentRequestField = thread.getClass().getDeclaredField("currentRequest");
139+
currentRequestField.setAccessible(true);
140+
currentRequestField.set(thread, new ConnectionRequest());
141+
assertFalse(manager.isQueueIdle());
142+
}
143+
144+
@Test
145+
void apDelegatesToImplementation() {
146+
when(implementation.isAPSupported()).thenReturn(true);
147+
when(implementation.getAPIds()).thenReturn(new String[]{"wifi"});
148+
when(implementation.getAPType("wifi")).thenReturn(NetworkManager.ACCESS_POINT_TYPE_WLAN);
149+
when(implementation.getAPName("wifi")).thenReturn("WiFi");
150+
manager.setCurrentAccessPoint("wifi");
151+
assertTrue(manager.isAPSupported());
152+
assertArrayEquals(new String[]{"wifi"}, manager.getAPIds());
153+
assertEquals(NetworkManager.ACCESS_POINT_TYPE_WLAN, manager.getAPType("wifi"));
154+
assertEquals("WiFi", manager.getAPName("wifi"));
155+
}
156+
157+
private Vector getPendingQueue() throws Exception {
158+
Field pendingField = NetworkManager.class.getDeclaredField("pending");
159+
pendingField.setAccessible(true);
160+
return (Vector) pendingField.get(manager);
161+
}
162+
163+
private void resetManagerState() throws Exception {
164+
Field runningField = NetworkManager.class.getDeclaredField("running");
165+
runningField.setAccessible(true);
166+
runningField.setBoolean(manager, false);
167+
168+
Field networkThreadsField = NetworkManager.class.getDeclaredField("networkThreads");
169+
networkThreadsField.setAccessible(true);
170+
networkThreadsField.set(manager, null);
171+
172+
Field errorField = NetworkManager.class.getDeclaredField("errorListeners");
173+
errorField.setAccessible(true);
174+
errorField.set(manager, null);
175+
176+
Field progressField = NetworkManager.class.getDeclaredField("progressListeners");
177+
progressField.setAccessible(true);
178+
progressField.set(manager, null);
179+
180+
Field autoDetectedField = NetworkManager.class.getDeclaredField("autoDetected");
181+
autoDetectedField.setAccessible(true);
182+
autoDetectedField.setBoolean(manager, false);
183+
184+
Field threadAssignments = NetworkManager.class.getDeclaredField("threadAssignements");
185+
threadAssignments.setAccessible(true);
186+
((java.util.Hashtable) threadAssignments.get(manager)).clear();
187+
188+
Field timeoutField = NetworkManager.class.getDeclaredField("timeout");
189+
timeoutField.setAccessible(true);
190+
timeoutField.setInt(manager, 300000);
191+
192+
getPendingQueue().clear();
193+
}
194+
195+
private void bootstrapDisplayThread() throws Exception {
196+
Display display = Display.getInstance();
197+
Field edtField = Display.class.getDeclaredField("edt");
198+
edtField.setAccessible(true);
199+
edtField.set(display, Thread.currentThread());
200+
}
201+
202+
private static class MockConnectionRequest extends ConnectionRequest {
203+
}
204+
}

0 commit comments

Comments
 (0)