Skip to content

Commit 16c7c28

Browse files
committed
feat: optimize long link test
1 parent 619876e commit 16c7c28

7 files changed

Lines changed: 70 additions & 61 deletions

File tree

trpc-core/src/test/java/com/tencent/trpc/core/cluster/RpcClusterClientManagerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public void testObserveHealthHealthyClient() throws Exception {
207207
BackendConfig backendConfig = new BackendConfig();
208208
backendConfig.setNamingUrl("ip://127.0.0.1:9005");
209209
ProtocolConfigTest config = new ProtocolConfigTest();
210-
RpcClient client = RpcClusterClientManager.getOrCreateClient(backendConfig, config);
210+
final RpcClient client = RpcClusterClientManager.getOrCreateClient(backendConfig, config);
211211
invokeObserveHealth();
212212
// Healthy client must not be evicted.
213213
Field field = RpcClusterClientManager.class.getDeclaredField("CLUSTER_MAP");
@@ -230,7 +230,7 @@ public void testObserveHealthUnavailableDoesNotEvict() throws Exception {
230230
backendConfig.setNamingUrl("ip://127.0.0.1:9006");
231231
ProtocolConfigTest config = new ProtocolConfigTest();
232232
config.available = false;
233-
RpcClient client = RpcClusterClientManager.getOrCreateClient(backendConfig, config);
233+
final RpcClient client = RpcClusterClientManager.getOrCreateClient(backendConfig, config);
234234

235235
Field field = RpcClusterClientManager.class.getDeclaredField("CLUSTER_MAP");
236236
field.setAccessible(true);

trpc-core/src/test/java/com/tencent/trpc/core/cluster/def/DefClusterInvokerCloseFutureTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public void testGetInvokerReturnsCachedAvailable() throws Exception {
7070
String key = "127.0.0.1:18001:tcp";
7171

7272
TestRpcClient client = new TestRpcClient();
73-
ConsumerInvokerProxy<GenericClient> proxy = new ConsumerInvokerProxy<>(
73+
final ConsumerInvokerProxy<GenericClient> proxy = new ConsumerInvokerProxy<>(
7474
stubInvoker(client.getProtocolConfig()), client);
7575
cache.put(key, proxy);
7676

@@ -149,7 +149,7 @@ public void testCasRemoveDoesNotEvictNewerEntry() throws Exception {
149149
@Test
150150
public void testProxyIsAvailableTracksUnderlyingClient() {
151151
TestRpcClient client = new TestRpcClient();
152-
ConsumerInvokerProxy<GenericClient> proxy = new ConsumerInvokerProxy<>(
152+
final ConsumerInvokerProxy<GenericClient> proxy = new ConsumerInvokerProxy<>(
153153
stubInvoker(client.getProtocolConfig()), client);
154154
Assert.assertTrue(proxy.isAvailable());
155155
client.available.set(false);
@@ -163,7 +163,7 @@ public void testProxyIsAvailableTracksUnderlyingClient() {
163163
@Test
164164
public void testProxyInvokeFillsCallInfoAndReports() {
165165
TestRpcClient client = new TestRpcClient();
166-
ConsumerInvokerProxy<GenericClient> proxy = new ConsumerInvokerProxy<>(
166+
final ConsumerInvokerProxy<GenericClient> proxy = new ConsumerInvokerProxy<>(
167167
stubInvoker(client.getProtocolConfig()), client);
168168

169169
com.tencent.trpc.core.rpc.def.DefRequest request = new com.tencent.trpc.core.rpc.def.DefRequest();

trpc-core/src/test/java/com/tencent/trpc/core/transport/AbstractClientTransportTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ public void testEnsureChannelActiveDoesNotStorm() throws Exception {
8989
start.await();
9090
m.invoke(transport, 0);
9191
} catch (Exception ignore) {
92+
// concurrent invocations may race; the test only asserts on the
93+
// aggregate openCalls counter below
9294
} finally {
9395
done.countDown();
9496
}

trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/HttpMultiPortNamingUrlConcurrentTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ public class HttpMultiPortNamingUrlConcurrentTest {
7171

7272
private static ServerConfig serverConfig;
7373

74+
/**
75+
* Spin up {@value #SERVER_COUNT} HTTP providers on contiguous ports. Each provider returns
76+
* its own port number so the concurrent test can assert that requests are dispatched across
77+
* every endpoint resolved from the multi-port {@code ip://} naming URL.
78+
*/
7479
@BeforeClass
7580
public static void startHttpServers() {
7681
ConfigManager.stopTest();

trpc-transport/trpc-transport-netty/src/test/java/com/tencent/trpc/transport/netty/NettyAbstractClientTransportTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,15 @@ public void tearDown() throws Exception {
6363
*/
6464
@Test
6565
public void testNioSharedGroupReferenceCounting() {
66-
ProtocolConfig c1 = newConfig(true, false);
67-
ProtocolConfig c2 = newConfig(true, false);
68-
66+
final ProtocolConfig c1 = newConfig(true, false);
6967
NoopTransport t1 = new NoopTransport(c1);
7068
t1.open();
7169
assertEquals(1, NettyAbstractClientTransport.SHARE_NIO_USED_NUMS.get());
7270
assertNotNull("first constructor must lazily create the NIO group",
7371
NettyAbstractClientTransport.SHARE_NIO_GROUP);
7472
EventLoopGroup grp = NettyAbstractClientTransport.SHARE_NIO_GROUP;
7573

74+
final ProtocolConfig c2 = newConfig(true, false);
7675
NoopTransport t2 = new NoopTransport(c2);
7776
t2.open();
7877
assertEquals(2, NettyAbstractClientTransport.SHARE_NIO_USED_NUMS.get());

trpc-transport/trpc-transport-netty/src/test/java/com/tencent/trpc/transport/netty/NettyTcpClientIdleCloseTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,12 @@ public void idleTimeoutClosesChannelAndInvalidatesSlot() throws Exception {
113113
try {
114114
client.close();
115115
} catch (Throwable ignore) {
116+
// best-effort cleanup
116117
}
117118
try {
118119
server.close();
119120
} catch (Throwable ignore) {
121+
// best-effort cleanup
120122
}
121123
acceptor.interrupt();
122124
}

trpc-transport/trpc-transport-netty/src/test/java/com/tencent/trpc/transport/netty/NettyTcpClientTransportTest.java

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,15 @@ public void testApplyTcpKeepAliveTuningSkipsNonPositive() throws Exception {
121121

122122
/**
123123
* When {@code idleTimeout > 0}, {@code doOpen} must register both
124-
* {@code idleState} and {@code idleClose} pipeline handlers. Driven via a real TCP
125-
* connect against a throwaway {@link java.net.ServerSocket} so netty actually runs
126-
* the {@code ChannelInitializer} and the assertions inspect a populated pipeline.
124+
* {@code idleState} and {@code idleClose} pipeline handlers. Driven offline by
125+
* reflecting on the {@link io.netty.channel.ChannelInitializer} captured in the
126+
* bootstrap and invoking its {@code initChannel(Channel)} on a fresh
127+
* {@link io.netty.channel.embedded.EmbeddedChannel}; no real socket / EventLoop is
128+
* involved so the test is fully deterministic and isolated from JVM-level concurrency.
127129
*/
128130
@Test
129131
public void testDoOpenInstallsIdlePipelineWhenEnabled() throws Exception {
130-
ChannelPipeline pipeline = pipelineAfterRealConnect(180_000);
132+
ChannelPipeline pipeline = pipelineAfterDoOpen(180_000);
131133
assertNotNull("idleState handler must be installed when idleTimeout > 0",
132134
pipeline.get("idleState"));
133135
assertNotNull("idleClose handler must be installed when idleTimeout > 0",
@@ -140,7 +142,7 @@ public void testDoOpenInstallsIdlePipelineWhenEnabled() throws Exception {
140142
*/
141143
@Test
142144
public void testDoOpenSkipsIdlePipelineWhenDisabled() throws Exception {
143-
ChannelPipeline pipeline = pipelineAfterRealConnect(0);
145+
ChannelPipeline pipeline = pipelineAfterDoOpen(0);
144146
assertNull("idleState handler must NOT be installed when idleTimeout = 0",
145147
pipeline.get("idleState"));
146148
assertNull("idleClose handler must NOT be installed when idleTimeout = 0",
@@ -195,68 +197,67 @@ private static boolean invokeUseChannelPool(NettyTcpClientTransport t) throws Ex
195197
}
196198

197199
/**
198-
* Stand up a throwaway plain {@link java.net.ServerSocket}, point a {@link NettyTcpClientTransport}
199-
* at it with the given {@code idleTimeout}, force the lazy connect, and return the
200-
* resulting pipeline of the connected netty channel. The server socket immediately
201-
* accepts the connection and never writes anything, so READ_IDLE handlers (when
202-
* installed) would eventually fire — but the test inspects the pipeline before that.
200+
* Drive {@code doOpen} on a transport configured with the given {@code idleTimeout},
201+
* pull the {@link io.netty.channel.ChannelInitializer} captured in the bootstrap and
202+
* reflectively run its {@code initChannel(Channel)} against a fresh
203+
* {@link io.netty.channel.embedded.EmbeddedChannel}. The resulting pipeline reflects
204+
* exactly what production code would install on a real connected channel — no real
205+
* socket / EventLoop / connect attempt is involved, so the result is deterministic
206+
* regardless of host load or other tests running in the same JVM.
203207
*/
204-
private static ChannelPipeline pipelineAfterRealConnect(int idleTimeout) throws Exception {
205-
java.net.ServerSocket server = new java.net.ServerSocket(0);
206-
server.setSoTimeout(2000);
207-
Thread accept = new Thread(() -> {
208-
try {
209-
java.net.Socket s = server.accept();
210-
// Hold the socket; do not write anything. Closed implicitly when the
211-
// accept thread exits.
212-
Thread.sleep(1500);
213-
try {
214-
s.close();
215-
} catch (Throwable ignore) {
216-
}
217-
} catch (Throwable ignore) {
218-
// Test may finish quickly and leave accept blocked — that's fine.
219-
}
220-
}, "test-accept");
221-
accept.setDaemon(true);
222-
accept.start();
223-
208+
private static ChannelPipeline pipelineAfterDoOpen(int idleTimeout) throws Exception {
224209
ProtocolConfig config = new ProtocolConfig();
225-
config.setIp("127.0.0.1");
226-
config.setPort(server.getLocalPort());
210+
config.setIp(NetUtils.LOCAL_HOST);
211+
// Port number is irrelevant — we never actually connect.
212+
config.setPort(NetUtils.getAvailablePort());
227213
config.setNetwork("tcp");
228214
config.setConnsPerAddr(1);
229-
config.setLazyinit(false);
215+
// lazyinit=true: open() must NOT fire a real connect; we only need doOpen() so the
216+
// bootstrap is configured with our ChannelInitializer.
217+
config.setLazyinit(true);
230218
config.setKeepAlive(true);
231219
config.setIoThreadGroupShare(false);
232220
config.setIdleTimeout(idleTimeout);
233221

234-
NettyTcpClientTransport client = new NettyTcpClientTransport(config,
222+
NettyTcpClientTransport transport = new NettyTcpClientTransport(config,
235223
new ChannelHandlerAdapter(), new TransportClientCodecTest());
236224
try {
237-
client.open();
238-
// Drive the connect synchronously and capture the connected netty channel.
239-
com.tencent.trpc.core.transport.Channel wrapper =
240-
client.getChannel().toCompletableFuture().get(2, java.util.concurrent.TimeUnit.SECONDS);
241-
assertNotNull(wrapper);
242-
assertTrue("channel must be connected", wrapper.isConnected());
243-
// Pull the underlying io.netty.channel.Channel via reflection so we can grab
244-
// the pipeline. NettyChannel keeps it as a private field "ioChannel".
245-
java.lang.reflect.Field ioChannelField = wrapper.getClass()
246-
.getDeclaredField("ioChannel");
247-
ioChannelField.setAccessible(true);
248-
io.netty.channel.Channel ioChannel = (io.netty.channel.Channel) ioChannelField.get(wrapper);
249-
return ioChannel.pipeline();
250-
} finally {
251-
try {
252-
client.close();
253-
} catch (Throwable ignore) {
225+
// open() runs through LifecycleObj → doOpen(); with lazyinit=true no connect
226+
// is attempted. After this the bootstrap has our ChannelInitializer registered.
227+
transport.open();
228+
io.netty.channel.ChannelHandler initializer = transport.getBootstrap().config().handler();
229+
assertNotNull("doOpen must register a ChannelInitializer", initializer);
230+
231+
// Use a fresh EmbeddedChannel as the target for the ChannelInitializer's
232+
// initChannel(Channel) — we invoke it reflectively rather than via netty's
233+
// own pipeline.add()/register() path so the result is fully deterministic and
234+
// independent of any other tests / EventLoop state in the same JVM.
235+
io.netty.channel.embedded.EmbeddedChannel ch = new io.netty.channel.embedded.EmbeddedChannel();
236+
// The anonymous ChannelInitializer subclass declares exactly one
237+
// initChannel(Channel) method — non-synthetic, non-bridge.
238+
Method initChannel = null;
239+
for (Method m : initializer.getClass().getDeclaredMethods()) {
240+
if ("initChannel".equals(m.getName()) && m.getParameterCount() == 1
241+
&& io.netty.channel.Channel.class.isAssignableFrom(m.getParameterTypes()[0])
242+
&& !m.isSynthetic() && !m.isBridge()) {
243+
initChannel = m;
244+
break;
245+
}
254246
}
247+
assertNotNull("ChannelInitializer must declare an initChannel(Channel) method",
248+
initChannel);
249+
initChannel.setAccessible(true);
250+
initChannel.invoke(initializer, ch);
251+
252+
// Return the live pipeline. NOTE: do NOT close the embedded channel — close()
253+
// detaches every handler, which would invalidate {@code pipeline.get(name)}.
254+
return ch.pipeline();
255+
} finally {
255256
try {
256-
server.close();
257+
transport.close();
257258
} catch (Throwable ignore) {
259+
// best-effort cleanup; the assertions above already captured the result
258260
}
259-
accept.interrupt();
260261
}
261262
}
262263

0 commit comments

Comments
 (0)