This is an automated email from the ASF dual-hosted git repository.
guohao pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.0 by this push:
new 94e26b4 Add unit test for rpc-triple module (#9203)
94e26b4 is described below
commit 94e26b4c4a2379ec01fa29ee2d9daf0baad41dcc
Author: 灼华 <[email protected]>
AuthorDate: Fri Nov 5 10:48:32 2021 +0800
Add unit test for rpc-triple module (#9203)
* Add unit test for rpc-triple module
* Fix build
* Fix and add unit test
Co-authored-by: guohao <[email protected]>
---
.../threadpool/serial/SerializingExecutor.java | 2 +-
.../exchange/PortUnificationExchanger.java | 5 +-
.../apache/dubbo/rpc/CancellationContextTest.java | 61 ++++++++++
.../apache/dubbo/rpc/protocol/tri/GrpcStatus.java | 2 +-
.../rpc/protocol/tri/SingleProtobufUtils.java | 17 ++-
.../dubbo/rpc/protocol/tri/TripleProtocol.java | 3 -
.../dubbo/rpc/protocol/tri/UnaryClientStream.java | 2 +-
.../dubbo/rpc/protocol/tri/UnaryServerStream.java | 2 +-
.../protocol/tri/service/TriBuiltinService.java | 2 +-
.../rpc/protocol/tri/service/TriHealthImpl.java | 2 +-
.../rpc/protocol/tri/GrpcDataDecoderTest.java | 101 ++++++++++++++++
.../rpc/protocol/tri/SingleProtobufUtilsTest.java | 77 ++++++++++++
.../tri/TripleHttp2ClientResponseHandlerTest.java | 107 +++++++++++++++++
.../tri/service/TriBuiltinServiceTest.java | 43 +++++++
.../protocol/tri/service/TriHealthImplTest.java | 129 +++++++++++++++++++++
15 files changed, 534 insertions(+), 21 deletions(-)
diff --git
a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/serial/SerializingExecutor.java
b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/serial/SerializingExecutor.java
index e106523..bc82f26 100644
---
a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/serial/SerializingExecutor.java
+++
b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/serial/SerializingExecutor.java
@@ -80,7 +80,7 @@ public final class SerializingExecutor implements Executor,
Runnable {
// ConcurrentLinkedQueue claims that null elements are
not allowed, but seems to not
// throw if the item to remove is null. If removable
is present in the queue twice,
// the wrong one may be removed. It doesn't seem
possible for this case to exist today.
- // This is important to run in case of
RejectedExectuionException, so that future calls
+ // This is important to run in case of
RejectedExecutionException, so that future calls
// to execute don't succeed and accidentally run a
previous runnable.
runQueue.remove(removable);
}
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
index 3b0fd38..fc00d4c 100644
---
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
@@ -21,15 +21,14 @@ import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.remoting.api.PortUnificationServer;
-import io.netty.util.internal.PlatformDependent;
-
import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class PortUnificationExchanger {
private static final Logger log =
LoggerFactory.getLogger(PortUnificationExchanger.class);
- private static final ConcurrentMap<String, PortUnificationServer> servers
= PlatformDependent.newConcurrentHashMap();
+ private static final ConcurrentMap<String, PortUnificationServer> servers
= new ConcurrentHashMap<>();
public static void bind(URL url) {
servers.computeIfAbsent(url.getAddress(), addr -> {
diff --git
a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/CancellationContextTest.java
b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/CancellationContextTest.java
new file mode 100644
index 0000000..aeffbcb
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/CancellationContextTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.rpc;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * {@link CancellationContext}
+ */
+public class CancellationContextTest {
+
+ @Test
+ public void test() throws Exception {
+ CancellationContext cancellationContext = new CancellationContext();
+
+ CountDownLatch latch = new CountDownLatch(2);
+ cancellationContext.addListener(rpcServiceContext -> {
+ latch.countDown();
+ });
+ cancellationContext.addListener(rpcServiceContext -> {
+ latch.countDown();
+ });
+ RuntimeException runtimeException = new RuntimeException();
+ cancellationContext.cancel(runtimeException);
+ latch.await();
+ Assertions.assertNull(cancellationContext.getListeners());
+ Assertions.assertTrue(cancellationContext.isCancelled());
+ Assertions.assertEquals(cancellationContext.getCancellationCause(),
runtimeException);
+ }
+
+ @Test
+ public void testAddListenerAfterCancel() throws Exception {
+ CancellationContext cancellationContext = new CancellationContext();
+
+ cancellationContext.cancel(null);
+ CountDownLatch latch = new CountDownLatch(1);
+ cancellationContext.addListener(rpcServiceContext -> {
+ latch.countDown();
+ });
+ latch.await();
+ }
+
+}
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java
index 56a3a21..b1eab70 100644
---
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java
+++
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java
@@ -150,7 +150,7 @@ public class GrpcStatus {
}
public static String fromMessage(String raw) {
- if (raw == null || raw.isEmpty()) {
+ if (StringUtils.isEmpty(raw)) {
return "";
}
return QueryStringDecoder.decodeComponent(raw);
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtils.java
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtils.java
index aca2b09..ab73a52 100644
---
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtils.java
+++
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtils.java
@@ -40,10 +40,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class SingleProtobufUtils {
- private static final ConcurrentHashMap<Class<?>, Message> instCache = new
ConcurrentHashMap<>();
- private static final ExtensionRegistryLite globalRegistry =
- ExtensionRegistryLite.getEmptyRegistry();
- private static final ConcurrentMap<Class<?>, SingleMessageMarshaller<?>>
marshallers = new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<Class<?>, Message> INST_CACHE = new
ConcurrentHashMap<>();
+ private static final ExtensionRegistryLite GLOBAL_REGISTRY =
ExtensionRegistryLite.getEmptyRegistry();
+ private static final ConcurrentMap<Class<?>, SingleMessageMarshaller<?>>
MARSHALLER_CACHE = new ConcurrentHashMap<>();
static {
// Built-in types need to be registered in advance
@@ -67,12 +66,12 @@ public class SingleProtobufUtils {
}
public static <T extends MessageLite> void marshaller(T defaultInstance) {
- marshallers.put(defaultInstance.getClass(), new
SingleMessageMarshaller<>(defaultInstance));
+ MARSHALLER_CACHE.put(defaultInstance.getClass(), new
SingleMessageMarshaller<>(defaultInstance));
}
@SuppressWarnings("all")
public static Message defaultInst(Class<?> clz) {
- Message defaultInst = instCache.get(clz);
+ Message defaultInst = INST_CACHE.get(clz);
if (defaultInst != null) {
return defaultInst;
}
@@ -81,7 +80,7 @@ public class SingleProtobufUtils {
} catch (IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
throw new RuntimeException("Create default protobuf instance
failed ", e);
}
- instCache.put(clz, defaultInst);
+ INST_CACHE.put(clz, defaultInst);
return defaultInst;
}
@@ -110,7 +109,7 @@ public class SingleProtobufUtils {
}
private static SingleMessageMarshaller<?> getMarshaller(Class<?> clz) {
- return marshallers.computeIfAbsent(clz, k -> new
SingleMessageMarshaller(k));
+ return MARSHALLER_CACHE.computeIfAbsent(clz, k -> new
SingleMessageMarshaller(k));
}
public static final class SingleMessageMarshaller<T extends MessageLite> {
@@ -128,7 +127,7 @@ public class SingleProtobufUtils {
}
public T parse(InputStream stream) throws
InvalidProtocolBufferException {
- return parser.parseFrom(stream, globalRegistry);
+ return parser.parseFrom(stream, GLOBAL_REGISTRY);
}
}
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
index d2b124a..f9d1187 100644
---
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
+++
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java
@@ -37,9 +37,6 @@ import grpc.health.v1.HealthCheckResponse;
import static
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CLIENT_THREADPOOL;
import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
-/**
- *
- */
public class TripleProtocol extends AbstractProtocol implements Protocol {
private static final Logger logger =
LoggerFactory.getLogger(TripleProtocol.class);
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java
index 30e8b94..f1eae16 100644
---
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java
+++
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java
@@ -69,7 +69,7 @@ public class UnaryClientStream extends AbstractClientStream
implements Stream {
return map;
}
- private class ClientUnaryInboundTransportObserver extends
ServerUnaryInboundTransportObserver implements TransportObserver {
+ private class ClientUnaryInboundTransportObserver extends
ServerUnaryInboundTransportObserver {
@Override
public void onComplete() {
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java
index 21c418a..688091c 100644
---
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java
+++
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java
@@ -45,7 +45,7 @@ public class UnaryServerStream extends AbstractServerStream
implements Stream {
return new UnaryServerTransportObserver();
}
- private class UnaryServerTransportObserver extends
ServerUnaryInboundTransportObserver implements TransportObserver {
+ private class UnaryServerTransportObserver extends
ServerUnaryInboundTransportObserver {
@Override
public void onError(GrpcStatus status) {
transportError(status);
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java
index a0d6a6c..72143a4 100644
---
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java
+++
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java
@@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
/**
- * tri internal service like grpc internal service
+ * tri internal service like grpc internal service
**/
public class TriBuiltinService {
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImpl.java
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImpl.java
index 59bda55..2ec9e06 100644
---
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImpl.java
+++
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImpl.java
@@ -44,7 +44,7 @@ public class TriHealthImpl implements Health {
private final Object watchLock = new Object();
// Technically a Multimap<String, StreamObserver<HealthCheckResponse>>.
The Boolean value is not
- // used. The StreamObservers need to be kept in a identity-equality set,
to make sure
+ // used. The StreamObservers need to be kept in an identity-equality set,
to make sure
// user-defined equals() doesn't confuse our book-keeping of the
StreamObservers. Constructing
// such Multimap would require extra lines and the end result is not
significantly simpler, thus I
// would rather not have the Guava collections dependency.
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/GrpcDataDecoderTest.java
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/GrpcDataDecoderTest.java
new file mode 100644
index 0000000..ff4772f
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/GrpcDataDecoderTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.rpc.protocol.tri;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.Attribute;
+import org.apache.dubbo.rpc.RpcException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * {@link GrpcDataDecoder}
+ */
+public class GrpcDataDecoderTest {
+
+ @Test
+ public void test() throws Exception {
+ GrpcDataDecoder grpcDataDecoder = new GrpcDataDecoder(1024, true);
+ ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class);
+ Channel channel = Mockito.mock(Channel.class);
+ Mockito.when(ctx.channel()).thenReturn(channel);
+ ByteBuf in = ByteBufAllocator.DEFAULT.buffer();
+ List<Object> out = new ArrayList<>();
+
+ // test frame header malformed
+ in.writeByte(0xFE);
+ try {
+ grpcDataDecoder.decode(ctx, in, out);
+ Assertions.fail();
+ } catch (Exception e) {
+ Assertions.assertTrue(e instanceof RpcException);
+ RpcException rpcException = (RpcException) e;
+ Assertions.assertEquals(rpcException.getCode(),
GrpcStatus.Code.INTERNAL.code);
+ Assertions.assertEquals(rpcException.getMessage(), "gRPC frame
header malformed: reserved bits not zero");
+ }
+ in.clear();
+
+ // test message exceeds maximum size
+ in.writeByte(0);
+ in.writeInt(2048);
+ try {
+ grpcDataDecoder.decode(ctx, in, out);
+ } catch (Exception e) {
+ Assertions.assertTrue(e instanceof RpcException);
+ RpcException rpcException = (RpcException) e;
+ Assertions.assertEquals(rpcException.getCode(),
GrpcStatus.Code.RESOURCE_EXHAUSTED.code);
+ Assertions.assertEquals(rpcException.getMessage(), "gRPC message
exceeds maximum size 1024: 2048");
+ }
+
+ // test normal
+ in.writeByte(1);
+ in.writeInt(1024);
+ byte[] bytes = new byte[1024];
+ Arrays.fill(bytes, (byte) 1);
+ in.writeBytes(bytes);
+ in.markReaderIndex();
+ AbstractClientStream stream = Mockito.mock(AbstractClientStream.class);
+ Mockito.when(stream.getDeCompressor()).thenReturn(Compressor.NONE);
+ Attribute<AbstractClientStream> attribute =
Mockito.mock(Attribute.class);
+ Mockito.when(attribute.get()).thenReturn(stream);
+
Mockito.when(channel.attr(TripleConstant.CLIENT_STREAM_KEY)).thenReturn(attribute);
+ grpcDataDecoder.decode(ctx, in, out);
+ Assertions.assertFalse(out.isEmpty());
+ Assertions.assertArrayEquals((byte[]) out.get(0), bytes);
+
+ // test compressor not found
+ in.resetReaderIndex();
+ Mockito.when(stream.getDeCompressor()).thenReturn(null);
+ try {
+ grpcDataDecoder.decode(ctx, in, out);
+ } catch (Exception e) {
+ Assertions.assertTrue(e instanceof RpcException);
+ RpcException rpcException = (RpcException) e;
+ Assertions.assertEquals(rpcException.getCode(),
GrpcStatus.Code.UNIMPLEMENTED.code);
+ Assertions.assertEquals(rpcException.getMessage(), "gRPC message
compressor not found");
+ }
+
+ }
+}
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtilsTest.java
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtilsTest.java
new file mode 100644
index 0000000..b74342e
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/SingleProtobufUtilsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.rpc.protocol.tri;
+
+import com.google.protobuf.BoolValue;
+import com.google.protobuf.BytesValue;
+import com.google.protobuf.DoubleValue;
+import com.google.protobuf.Empty;
+import com.google.protobuf.EnumValue;
+import com.google.protobuf.FloatValue;
+import com.google.protobuf.Int32Value;
+import com.google.protobuf.Int64Value;
+import com.google.protobuf.ListValue;
+import com.google.protobuf.Message;
+import com.google.protobuf.Parser;
+import com.google.protobuf.StringValue;
+import grpc.health.v1.HealthCheckRequest;
+import grpc.health.v1.HealthCheckResponse;
+import org.apache.dubbo.triple.TripleWrapper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * {@link SingleProtobufUtils}
+ */
+public class SingleProtobufUtilsTest {
+
+ @Test
+ public void test() throws IOException {
+
Assertions.assertFalse(SingleProtobufUtils.isSupported(SingleProtobufUtilsTest.class));
+ Assertions.assertTrue(SingleProtobufUtils.isSupported(Empty.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(BoolValue.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(Int32Value.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(Int64Value.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(FloatValue.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(DoubleValue.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(BytesValue.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(StringValue.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(EnumValue.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(ListValue.class));
+
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(HealthCheckResponse.class));
+
Assertions.assertTrue(SingleProtobufUtils.isSupported(HealthCheckRequest.class));
+
+ Message message =
SingleProtobufUtils.defaultInst(HealthCheckRequest.class);
+ Assertions.assertNotNull(message);
+ Parser<HealthCheckRequest> parser =
SingleProtobufUtils.getParser(HealthCheckRequest.class);
+ Assertions.assertNotNull(parser);
+
+ TripleWrapper.TripleRequestWrapper requestWrapper =
TripleWrapper.TripleRequestWrapper.newBuilder()
+ .setSerializeType("hessian4").build();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ SingleProtobufUtils.serialize(requestWrapper,bos);
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ TripleWrapper.TripleRequestWrapper tripleRequestWrapper =
SingleProtobufUtils.deserialize(bis, TripleWrapper.TripleRequestWrapper.class);
+
Assertions.assertEquals(tripleRequestWrapper.getSerializeType(),"hessian4");
+ }
+}
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2ClientResponseHandlerTest.java
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2ClientResponseHandlerTest.java
new file mode 100644
index 0000000..a7660dd
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2ClientResponseHandlerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.rpc.protocol.tri;
+
+
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http2.DefaultHttp2GoAwayFrame;
+import io.netty.handler.codec.http2.DefaultHttp2Headers;
+import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
+import io.netty.handler.codec.http2.DefaultHttp2ResetFrame;
+import io.netty.handler.codec.http2.Http2Error;
+import io.netty.handler.codec.http2.Http2GoAwayFrame;
+import io.netty.handler.codec.http2.Http2Headers;
+import io.netty.util.Attribute;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.RpcException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+/**
+ * {@link TripleHttp2ClientResponseHandler }
+ */
+public class TripleHttp2ClientResponseHandlerTest {
+ private TripleHttp2ClientResponseHandler handler;
+ private ChannelHandlerContext ctx;
+ private AbstractClientStream stream;
+ private InboundTransportObserver transportObserver;
+ private StreamObserver streamObserver;
+
+
+ @BeforeEach
+ public void init() {
+ handler = new TripleHttp2ClientResponseHandler();
+
+ ctx = Mockito.mock(ChannelHandlerContext.class);
+ Channel channel = Mockito.mock(Channel.class);
+ Mockito.when(ctx.channel()).thenReturn(channel);
+
+ stream = Mockito.mock(AbstractClientStream.class);
+ transportObserver = Mockito.mock(InboundTransportObserver.class);
+ streamObserver = Mockito.mock(StreamObserver.class);
+
Mockito.when(stream.inboundTransportObserver()).thenReturn(transportObserver);
+
Mockito.when(stream.inboundMessageObserver()).thenReturn(streamObserver);
+
Mockito.when(stream.getUrl()).thenReturn(URL.valueOf("test://127.0.0.1/test"));
+ Mockito.when(stream.getDeCompressor()).thenReturn(Compressor.NONE);
+
+ Attribute<AbstractClientStream> attribute =
Mockito.mock(Attribute.class);
+ Mockito.when(attribute.get()).thenReturn(stream);
+
Mockito.when(channel.attr(TripleConstant.CLIENT_STREAM_KEY)).thenReturn(attribute);
+ }
+
+ @Test
+ public void testUserEventTriggered() throws Exception {
+ // test Http2GoAwayFrame
+ Http2GoAwayFrame goAwayFrame = new
DefaultHttp2GoAwayFrame(Http2Error.NO_ERROR, ByteBufUtil
+ .writeAscii(ByteBufAllocator.DEFAULT, "app_requested"));
+ handler.userEventTriggered(ctx, goAwayFrame);
+ Mockito.verify(ctx, Mockito.times(1)).close();
+
+ // test Http2ResetFrame
+ DefaultHttp2ResetFrame resetFrame = new
DefaultHttp2ResetFrame(Http2Error.CANCEL);
+ handler.userEventTriggered(ctx, resetFrame);
+ Mockito.verify(ctx, Mockito.times(2)).close();
+ Mockito.verify(stream, Mockito.times(1)).cancelByRemote();
+ }
+
+ @Test
+ public void testChannelRead0() throws Exception {
+ final Http2Headers headers = new DefaultHttp2Headers(true);
+ headers.set(TripleHeaderEnum.GRPC_ENCODING.getHeader(), "gzip");
+ DefaultHttp2HeadersFrame headersFrame = new
DefaultHttp2HeadersFrame(headers, true);
+ handler.channelRead0(ctx, headersFrame);
+ Mockito.verify(transportObserver,
Mockito.times(1)).onMetadata(Mockito.any(), Mockito.anyBoolean());
+ Mockito.verify(transportObserver, Mockito.times(1)).onComplete();
+ }
+
+ @Test
+ public void testExceptionCaught() {
+ RuntimeException exception = new RuntimeException();
+ handler.exceptionCaught(ctx, exception);
+ ArgumentCaptor<RpcException> rpcExceptionArgumentCaptor =
ArgumentCaptor.forClass(RpcException.class);
+ Mockito.verify(streamObserver,
Mockito.times(1)).onError(rpcExceptionArgumentCaptor.capture());
+
Assertions.assertEquals(rpcExceptionArgumentCaptor.getValue().getCode(),
GrpcStatus.Code.INTERNAL.code);
+ Mockito.verify(ctx, Mockito.times(1)).close();
+ }
+}
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java
new file mode 100644
index 0000000..747686c
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinServiceTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.rpc.protocol.tri.service;
+
+import grpc.health.v1.Health;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.protocol.tri.PathResolver;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * {@link TriBuiltinService}
+ */
+public class TriBuiltinServiceTest {
+
+ @Test
+ public void test() {
+ FrameworkModel frameworkModel = new FrameworkModel();
+ TriBuiltinService triBuiltinService = new
TriBuiltinService(frameworkModel);
+ Assertions.assertNotNull(triBuiltinService.getHealthStatusManager());
+ PathResolver pathResolver =
frameworkModel.getExtensionLoader(PathResolver.class).getDefaultExtension();
+ Assertions.assertNotNull(pathResolver.resolve(Health.class.getName()));
+
+ ModuleServiceRepository repository =
frameworkModel.getInternalApplicationModel().getInternalModule().getServiceRepository();
+ Assertions.assertFalse(repository.getAllServices().isEmpty());
+ Assertions.assertFalse(repository.getExportedServices().isEmpty());
+ }
+}
diff --git
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImplTest.java
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImplTest.java
new file mode 100644
index 0000000..e62ee62
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/service/TriHealthImplTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.dubbo.rpc.protocol.tri.service;
+
+import grpc.health.v1.HealthCheckRequest;
+import grpc.health.v1.HealthCheckResponse;
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+
+/**
+ * {@link TriHealthImpl}
+ */
+public class TriHealthImplTest {
+
+ @Test
+ public void testCheck() {
+ TriHealthImpl triHealth = new TriHealthImpl();
+
+ HealthCheckRequest request = HealthCheckRequest.newBuilder().build();
+ HealthCheckResponse response = triHealth.check(request);
+ Assertions.assertEquals(response.getStatus(),
HealthCheckResponse.ServingStatus.SERVING);
+
+ HealthCheckRequest badRequest =
HealthCheckRequest.newBuilder().setService("test").build();
+ Assertions.assertThrows(RpcException.class, () ->
triHealth.check(badRequest));
+ }
+
+ @Test
+ public void testWatch() throws Exception {
+ TriHealthImpl triHealth = new TriHealthImpl();
+
+ HealthCheckRequest request = HealthCheckRequest.newBuilder().build();
+ StreamObserver<HealthCheckResponse> streamObserver = new
MockStreamObserver();
+
+ // test watch
+ triHealth.watch(request, streamObserver);
+ MockStreamObserver mockStreamObserver = (MockStreamObserver)
streamObserver;
+
Assertions.assertNotNull(RpcContext.getCancellationContext().getListeners());
+ HashMap<String, IdentityHashMap<StreamObserver<HealthCheckResponse>,
Boolean>> watches = getWatches(triHealth);
+ Assertions.assertTrue(watches.containsKey(request.getService()));
+
Assertions.assertTrue(watches.get(request.getService()).containsKey(streamObserver));
+
Assertions.assertTrue(watches.get(request.getService()).get(streamObserver));
+ Assertions.assertEquals(mockStreamObserver.getCount(), 1);
+ Assertions.assertEquals(mockStreamObserver.getResponse().getStatus(),
HealthCheckResponse.ServingStatus.SERVING);
+
+ // test setStatus
+ triHealth.setStatus(request.getService(),
HealthCheckResponse.ServingStatus.SERVICE_UNKNOWN);
+ Assertions.assertEquals(mockStreamObserver.getCount(), 2);
+ Assertions.assertEquals(mockStreamObserver.getResponse().getStatus(),
HealthCheckResponse.ServingStatus.SERVICE_UNKNOWN);
+
+ triHealth.enterTerminalState();
+ Assertions.assertEquals(mockStreamObserver.getCount(), 3);
+ Assertions.assertEquals(mockStreamObserver.getResponse().getStatus(),
HealthCheckResponse.ServingStatus.NOT_SERVING);
+
+ // test clearStatus
+ turnOffTerminal(triHealth);
+ triHealth.clearStatus(request.getService());
+ Assertions.assertEquals(mockStreamObserver.getCount(), 4);
+ Assertions.assertEquals(mockStreamObserver.getResponse().getStatus(),
HealthCheckResponse.ServingStatus.SERVICE_UNKNOWN);
+
+ // test listener
+ RpcContext.getCancellationContext().close();
+ Assertions.assertTrue(watches.isEmpty());
+ }
+
+ private void turnOffTerminal(TriHealthImpl triHealth) throws
NoSuchFieldException, IllegalAccessException {
+ Field terminalField =
triHealth.getClass().getDeclaredField("terminal");
+ terminalField.setAccessible(true);
+ terminalField.set(triHealth, false);
+ }
+
+ private HashMap<String,
IdentityHashMap<StreamObserver<HealthCheckResponse>, Boolean>>
getWatches(TriHealthImpl triHealth) throws Exception {
+ Field watchersField =
triHealth.getClass().getDeclaredField("watchers");
+ watchersField.setAccessible(true);
+ HashMap<String, IdentityHashMap<StreamObserver<HealthCheckResponse>,
Boolean>> watches =
+ (HashMap<String,
IdentityHashMap<StreamObserver<HealthCheckResponse>, Boolean>>)
watchersField.get(triHealth);
+ return watches;
+ }
+
+ class MockStreamObserver implements StreamObserver<HealthCheckResponse> {
+
+ private int count = 0;
+ private HealthCheckResponse response;
+
+ @Override
+ public void onNext(HealthCheckResponse data) {
+ count++;
+ response = data;
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+
+ }
+
+ @Override
+ public void onCompleted() {
+
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public HealthCheckResponse getResponse() {
+ return response;
+ }
+ }
+}