Skip to content

Commit ea69320

Browse files
committed
Merge branch 'master' into ExpectedException-api-core
2 parents 3f799ce + a57c14a commit ea69320

File tree

28 files changed

+942
-716
lines changed

28 files changed

+942
-716
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2025 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
import static com.google.common.base.Preconditions.checkState;
21+
22+
import org.mockito.ArgumentMatcher;
23+
24+
/**
25+
* Mockito matcher for {@link Status}.
26+
*/
27+
public final class StatusMatcher implements ArgumentMatcher<Status> {
28+
public static StatusMatcher statusHasCode(ArgumentMatcher<Status.Code> codeMatcher) {
29+
return new StatusMatcher(codeMatcher, null);
30+
}
31+
32+
public static StatusMatcher statusHasCode(Status.Code code) {
33+
return statusHasCode(new EqualsMatcher<>(code));
34+
}
35+
36+
private final ArgumentMatcher<Status.Code> codeMatcher;
37+
private final ArgumentMatcher<String> descriptionMatcher;
38+
39+
private StatusMatcher(
40+
ArgumentMatcher<Status.Code> codeMatcher,
41+
ArgumentMatcher<String> descriptionMatcher) {
42+
this.codeMatcher = checkNotNull(codeMatcher, "codeMatcher");
43+
this.descriptionMatcher = descriptionMatcher;
44+
}
45+
46+
public StatusMatcher andDescription(ArgumentMatcher<String> descriptionMatcher) {
47+
checkState(this.descriptionMatcher == null, "Already has a description matcher");
48+
return new StatusMatcher(codeMatcher, descriptionMatcher);
49+
}
50+
51+
public StatusMatcher andDescription(String description) {
52+
return andDescription(new EqualsMatcher<>(description));
53+
}
54+
55+
public StatusMatcher andDescriptionContains(String substring) {
56+
return andDescription(new StringContainsMatcher(substring));
57+
}
58+
59+
@Override
60+
public boolean matches(Status status) {
61+
return status != null
62+
&& codeMatcher.matches(status.getCode())
63+
&& (descriptionMatcher == null || descriptionMatcher.matches(status.getDescription()));
64+
}
65+
66+
@Override
67+
public String toString() {
68+
StringBuilder sb = new StringBuilder();
69+
sb.append("{code=");
70+
sb.append(codeMatcher);
71+
if (descriptionMatcher != null) {
72+
sb.append(", description=");
73+
sb.append(descriptionMatcher);
74+
}
75+
sb.append("}");
76+
return sb.toString();
77+
}
78+
79+
// Use instead of lambda for better error message.
80+
static final class EqualsMatcher<T> implements ArgumentMatcher<T> {
81+
private final T obj;
82+
83+
EqualsMatcher(T obj) {
84+
this.obj = checkNotNull(obj, "obj");
85+
}
86+
87+
@Override
88+
public boolean matches(Object other) {
89+
return obj.equals(other);
90+
}
91+
92+
@Override
93+
public String toString() {
94+
return obj.toString();
95+
}
96+
}
97+
98+
static final class StringContainsMatcher implements ArgumentMatcher<String> {
99+
private final String needle;
100+
101+
StringContainsMatcher(String needle) {
102+
this.needle = checkNotNull(needle, "needle");
103+
}
104+
105+
@Override
106+
public boolean matches(String haystack) {
107+
if (haystack == null) {
108+
return false;
109+
}
110+
return haystack.contains(needle);
111+
}
112+
113+
@Override
114+
public String toString() {
115+
return "contains " + needle;
116+
}
117+
}
118+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2025 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
21+
import org.mockito.ArgumentMatcher;
22+
23+
/**
24+
* Mockito matcher for {@link StatusOr}.
25+
*/
26+
public final class StatusOrMatcher<T> implements ArgumentMatcher<StatusOr<T>> {
27+
public static <T> StatusOrMatcher<T> hasValue(ArgumentMatcher<T> valueMatcher) {
28+
return new StatusOrMatcher<T>(checkNotNull(valueMatcher, "valueMatcher"), null);
29+
}
30+
31+
public static <T> StatusOrMatcher<T> hasStatus(ArgumentMatcher<Status> statusMatcher) {
32+
return new StatusOrMatcher<T>(null, checkNotNull(statusMatcher, "statusMatcher"));
33+
}
34+
35+
private final ArgumentMatcher<T> valueMatcher;
36+
private final ArgumentMatcher<Status> statusMatcher;
37+
38+
private StatusOrMatcher(ArgumentMatcher<T> valueMatcher, ArgumentMatcher<Status> statusMatcher) {
39+
this.valueMatcher = valueMatcher;
40+
this.statusMatcher = statusMatcher;
41+
}
42+
43+
@Override
44+
public boolean matches(StatusOr<T> statusOr) {
45+
if (statusOr == null) {
46+
return false;
47+
}
48+
if (statusOr.hasValue() != (valueMatcher != null)) {
49+
return false;
50+
}
51+
if (valueMatcher != null) {
52+
return valueMatcher.matches(statusOr.getValue());
53+
} else {
54+
return statusMatcher.matches(statusOr.getStatus());
55+
}
56+
}
57+
58+
@Override
59+
public String toString() {
60+
if (valueMatcher != null) {
61+
return "{value=" + valueMatcher + "}";
62+
} else {
63+
return "{status=" + statusMatcher + "}";
64+
}
65+
}
66+
}

binder/src/main/java/io/grpc/binder/internal/PingTracker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ private final class Ping {
9999
private synchronized void fail(Status status) {
100100
if (!done) {
101101
done = true;
102-
executor.execute(() -> callback.onFailure(status.asException()));
102+
executor.execute(() -> callback.onFailure(status));
103103
}
104104
}
105105

binder/src/test/java/io/grpc/binder/internal/PingTrackerTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private static final class TestCallback implements ClientTransport.PingCallback
9696
private int numCallbacks;
9797
private boolean success;
9898
private boolean failure;
99-
private Throwable failureException;
99+
private Status failureStatus;
100100
private long roundtripTimeNanos;
101101

102102
@Override
@@ -107,10 +107,10 @@ public synchronized void onSuccess(long roundtripTimeNanos) {
107107
}
108108

109109
@Override
110-
public synchronized void onFailure(Throwable failureException) {
110+
public synchronized void onFailure(Status failureStatus) {
111111
numCallbacks += 1;
112112
failure = true;
113-
this.failureException = failureException;
113+
this.failureStatus = failureStatus;
114114
}
115115

116116
public void assertNotCalled() {
@@ -130,13 +130,13 @@ public void assertSuccess(long expectRoundTripTimeNanos) {
130130
public void assertFailure(Status status) {
131131
assertThat(numCallbacks).isEqualTo(1);
132132
assertThat(failure).isTrue();
133-
assertThat(((StatusException) failureException).getStatus()).isSameInstanceAs(status);
133+
assertThat(failureStatus).isSameInstanceAs(status);
134134
}
135135

136136
public void assertFailure(Status.Code statusCode) {
137137
assertThat(numCallbacks).isEqualTo(1);
138138
assertThat(failure).isTrue();
139-
assertThat(((StatusException) failureException).getStatus().getCode()).isEqualTo(statusCode);
139+
assertThat(failureStatus.getCode()).isEqualTo(statusCode);
140140
}
141141
}
142142
}

compiler/src/java_plugin/cpp/java_plugin.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323

2424
#include "java_generator.h"
2525
#include <google/protobuf/compiler/code_generator.h>
26+
#if GOOGLE_PROTOBUF_VERSION >= 5027000
27+
#include <google/protobuf/compiler/java/java_features.pb.h>
28+
#endif
2629
#include <google/protobuf/compiler/plugin.h>
2730
#include <google/protobuf/descriptor.h>
2831
#include <google/protobuf/io/zero_copy_stream.h>
@@ -57,6 +60,10 @@ class JavaGrpcGenerator : public protobuf::compiler::CodeGenerator {
5760
protobuf::Edition GetMaximumEdition() const override {
5861
return protobuf::Edition::EDITION_2023;
5962
}
63+
std::vector<const protobuf::FieldDescriptor*> GetFeatureExtensions()
64+
const override {
65+
return {GetExtensionReflection(pb::java)};
66+
}
6067
#else
6168
uint64_t GetSupportedFeatures() const override {
6269
return Feature::FEATURE_PROTO3_OPTIONAL;

core/src/main/java/io/grpc/internal/ClientTransport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.grpc.InternalInstrumented;
2323
import io.grpc.Metadata;
2424
import io.grpc.MethodDescriptor;
25+
import io.grpc.Status;
2526
import java.util.concurrent.Executor;
2627
import javax.annotation.concurrent.ThreadSafe;
2728

@@ -90,6 +91,6 @@ interface PingCallback {
9091
*
9192
* @param cause the cause of the ping failure
9293
*/
93-
void onFailure(Throwable cause);
94+
void onFailure(Status cause);
9495
}
9596
}

core/src/main/java/io/grpc/internal/FailingClientTransport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public ClientStream newStream(
5555
public void ping(final PingCallback callback, Executor executor) {
5656
executor.execute(new Runnable() {
5757
@Override public void run() {
58-
callback.onFailure(error.asException());
58+
callback.onFailure(error);
5959
}
6060
});
6161
}

core/src/main/java/io/grpc/internal/Http2Ping.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.common.base.Stopwatch;
2020
import com.google.errorprone.annotations.concurrent.GuardedBy;
21+
import io.grpc.Status;
2122
import io.grpc.internal.ClientTransport.PingCallback;
2223
import java.util.LinkedHashMap;
2324
import java.util.Map;
@@ -62,7 +63,7 @@ public class Http2Ping {
6263
/**
6364
* If non-null, indicates the ping failed.
6465
*/
65-
@GuardedBy("this") private Throwable failureCause;
66+
@GuardedBy("this") private Status failureCause;
6667

6768
/**
6869
* The round-trip time for the ping, in nanoseconds. This value is only meaningful when
@@ -144,7 +145,7 @@ public boolean complete() {
144145
*
145146
* @param failureCause the cause of failure
146147
*/
147-
public void failed(Throwable failureCause) {
148+
public void failed(Status failureCause) {
148149
Map<ClientTransport.PingCallback, Executor> callbacks;
149150
synchronized (this) {
150151
if (completed) {
@@ -167,7 +168,7 @@ public void failed(Throwable failureCause) {
167168
* @param executor the executor used to invoke the callback
168169
* @param cause the cause of failure
169170
*/
170-
public static void notifyFailed(PingCallback callback, Executor executor, Throwable cause) {
171+
public static void notifyFailed(PingCallback callback, Executor executor, Status cause) {
171172
doExecute(executor, asRunnable(callback, cause));
172173
}
173174

@@ -203,7 +204,7 @@ public void run() {
203204
* failure.
204205
*/
205206
private static Runnable asRunnable(final ClientTransport.PingCallback callback,
206-
final Throwable failureCause) {
207+
final Status failureCause) {
207208
return new Runnable() {
208209
@Override
209210
public void run() {

core/src/main/java/io/grpc/internal/KeepAliveManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public void ping() {
275275
public void onSuccess(long roundTripTimeNanos) {}
276276

277277
@Override
278-
public void onFailure(Throwable cause) {
278+
public void onFailure(Status cause) {
279279
transport.shutdownNow(Status.UNAVAILABLE.withDescription(
280280
"Keepalive failed. The connection is likely gone"));
281281
}

core/src/test/java/io/grpc/internal/KeepAliveManagerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public void clientKeepAlivePinger_pingFailure() {
127127
verify(transport).ping(pingCallbackCaptor.capture(), isA(Executor.class));
128128
ClientTransport.PingCallback pingCallback = pingCallbackCaptor.getValue();
129129

130-
pingCallback.onFailure(new Throwable());
130+
pingCallback.onFailure(Status.UNAVAILABLE.withDescription("I must write descriptions"));
131131

132132
ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(Status.class);
133133
verify(transport).shutdownNow(statusCaptor.capture());

0 commit comments

Comments
 (0)