Skip to content

Commit a0d4de5

Browse files
authored
[ggj][codegen][test] feat: add rpcExceptionTest for RPCs w/o overloads, support LRO (#349)
* fix!: refactor field into MethodArgument, add enum/msg flags * feat: partial isAssignableFrom VaporRef support, enable full-name type usage * feat: support negative numeric literals * fix: separate resname tokenizing from class composer * feat: add per-type default value composer * feat: add ServiceClientTest.methodExceptionTests codegen * feat: add rpcExceptionTest for RPCs w/o overloads, support LRO * fix: CI merge
1 parent d221f8b commit a0d4de5

7 files changed

Lines changed: 358 additions & 25 deletions

File tree

BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
package(default_visibility = ["//visibility:public"])
2-
31
load(
42
"//:gapic_generator_java.bzl",
53
"google_java_format",
64
"google_java_format_verification",
75
)
86

7+
package(default_visibility = ["//visibility:public"])
8+
99
JAVA_SRCS = [
1010
"//src/main/java/com/google/api/generator:generator_files",
1111
"//src/main/java/com/google/api/generator/engine:engine_files",

src/main/java/com/google/api/generator/gapic/composer/DefaultValueComposer.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.api.generator.engine.ast.TypeNode;
2424
import com.google.api.generator.engine.ast.ValueExpr;
2525
import com.google.api.generator.gapic.model.Field;
26+
import com.google.api.generator.gapic.model.Message;
2627
import com.google.api.generator.gapic.model.MethodArgument;
2728
import com.google.api.generator.gapic.model.ResourceName;
2829
import com.google.api.generator.gapic.utils.JavaStyle;
@@ -186,4 +187,54 @@ static Expr createDefaultValue(ResourceName resourceName, List<ResourceName> res
186187
.setReturnType(resourceNameJavaType)
187188
.build();
188189
}
190+
191+
static Expr createSimpleMessageBuilderExpr(
192+
Message message, Map<String, ResourceName> resourceNames, Map<String, Message> messageTypes) {
193+
MethodInvocationExpr builderExpr =
194+
MethodInvocationExpr.builder()
195+
.setStaticReferenceType(message.type())
196+
.setMethodName("newBuilder")
197+
.build();
198+
for (Field field : message.fields()) {
199+
if (field.isContainedInOneof() // Avoid colliding fields.
200+
|| ((field.isMessage() || field.isEnum()) // Avoid importing unparsed messages.
201+
&& !field.isRepeated()
202+
&& !messageTypes.containsKey(field.type().reference().name()))) {
203+
continue;
204+
}
205+
String setterMethodNamePattern = "set%s";
206+
if (field.isRepeated()) {
207+
setterMethodNamePattern = field.isMap() ? "putAll%s" : "addAll%s";
208+
}
209+
Expr defaultExpr = null;
210+
if (field.hasResourceReference()
211+
&& resourceNames.get(field.resourceReference().resourceTypeString()) != null) {
212+
defaultExpr =
213+
createDefaultValue(
214+
resourceNames.get(field.resourceReference().resourceTypeString()),
215+
resourceNames.values().stream().collect(Collectors.toList()));
216+
defaultExpr =
217+
MethodInvocationExpr.builder()
218+
.setExprReferenceExpr(defaultExpr)
219+
.setMethodName("toString")
220+
.setReturnType(TypeNode.STRING)
221+
.build();
222+
} else {
223+
defaultExpr = createDefaultValue(field);
224+
}
225+
builderExpr =
226+
MethodInvocationExpr.builder()
227+
.setExprReferenceExpr(builderExpr)
228+
.setMethodName(
229+
String.format(setterMethodNamePattern, JavaStyle.toUpperCamelCase(field.name())))
230+
.setArguments(defaultExpr)
231+
.build();
232+
}
233+
234+
return MethodInvocationExpr.builder()
235+
.setExprReferenceExpr(builderExpr)
236+
.setMethodName("build")
237+
.setReturnType(message.type())
238+
.build();
239+
}
189240
}

src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java

Lines changed: 155 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.google.api.gax.rpc.UnaryCallSettings;
3333
import com.google.api.generator.engine.ast.AnnotationNode;
3434
import com.google.api.generator.engine.ast.AssignmentExpr;
35+
import com.google.api.generator.engine.ast.CastExpr;
3536
import com.google.api.generator.engine.ast.ClassDefinition;
3637
import com.google.api.generator.engine.ast.CommentStatement;
3738
import com.google.api.generator.engine.ast.ConcreteReference;
@@ -69,6 +70,7 @@
6970
import java.io.IOException;
7071
import java.util.ArrayList;
7172
import java.util.Arrays;
73+
import java.util.Collections;
7274
import java.util.HashMap;
7375
import java.util.LinkedHashMap;
7476
import java.util.List;
@@ -403,15 +405,26 @@ private static List<MethodDefinition> createTestMethods(
403405
Map<String, Message> messageTypes) {
404406
List<MethodDefinition> javaMethods = new ArrayList<>();
405407
for (Method method : service.methods()) {
406-
for (int i = 0; i < method.methodSignatures().size(); i++) {
408+
if (method.methodSignatures().isEmpty()) {
407409
javaMethods.add(
408410
createRpcExceptionTestMethod(
409411
method,
410-
method.methodSignatures().get(i),
411-
i,
412+
Collections.emptyList(),
413+
0,
412414
classMemberVarExprs,
413415
resourceNames,
414416
messageTypes));
417+
} else {
418+
for (int i = 0; i < method.methodSignatures().size(); i++) {
419+
javaMethods.add(
420+
createRpcExceptionTestMethod(
421+
method,
422+
method.methodSignatures().get(i),
423+
i,
424+
classMemberVarExprs,
425+
resourceNames,
426+
messageTypes));
427+
}
415428
}
416429
}
417430
return javaMethods;
@@ -462,25 +475,74 @@ private static MethodDefinition createRpcExceptionTestMethod(
462475

463476
List<VariableExpr> argVarExprs = new ArrayList<>();
464477
List<Expr> tryBodyExprs = new ArrayList<>();
465-
for (MethodArgument methodArg : methodSignature) {
478+
if (methodSignature.isEmpty()) {
479+
// Construct the actual request.
466480
VariableExpr varExpr =
467481
VariableExpr.withVariable(
468-
Variable.builder().setType(methodArg.type()).setName(methodArg.name()).build());
482+
Variable.builder().setType(method.inputType()).setName("request").build());
469483
argVarExprs.add(varExpr);
470-
Expr valExpr = DefaultValueComposer.createDefaultValue(methodArg, resourceNames);
484+
Message requestMessage = messageTypes.get(method.inputType().reference().name());
485+
Preconditions.checkNotNull(requestMessage);
486+
Expr valExpr =
487+
DefaultValueComposer.createSimpleMessageBuilderExpr(
488+
requestMessage, resourceNames, messageTypes);
471489
tryBodyExprs.add(
472490
AssignmentExpr.builder()
473491
.setVariableExpr(varExpr.toBuilder().setIsDecl(true).build())
474492
.setValueExpr(valExpr)
475493
.build());
476-
// TODO(miraleung): Empty line here.
494+
} else {
495+
for (MethodArgument methodArg : methodSignature) {
496+
VariableExpr varExpr =
497+
VariableExpr.withVariable(
498+
Variable.builder().setType(methodArg.type()).setName(methodArg.name()).build());
499+
argVarExprs.add(varExpr);
500+
Expr valExpr = DefaultValueComposer.createDefaultValue(methodArg, resourceNames);
501+
tryBodyExprs.add(
502+
AssignmentExpr.builder()
503+
.setVariableExpr(varExpr.toBuilder().setIsDecl(true).build())
504+
.setValueExpr(valExpr)
505+
.build());
506+
// TODO(miraleung): Empty line here.
507+
}
477508
}
478-
tryBodyExprs.add(
509+
String rpcJavaName = JavaStyle.toLowerCamelCase(method.name());
510+
if (method.hasLro()) {
511+
rpcJavaName += "Async";
512+
}
513+
MethodInvocationExpr rpcJavaMethodInvocationExpr =
479514
MethodInvocationExpr.builder()
480515
.setExprReferenceExpr(classMemberVarExprs.get("client"))
481-
.setMethodName(JavaStyle.toLowerCamelCase(method.name()))
516+
.setMethodName(rpcJavaName)
482517
.setArguments(argVarExprs.stream().map(e -> (Expr) e).collect(Collectors.toList()))
483-
.build());
518+
.build();
519+
if (method.hasLro()) {
520+
rpcJavaMethodInvocationExpr =
521+
MethodInvocationExpr.builder()
522+
.setExprReferenceExpr(rpcJavaMethodInvocationExpr)
523+
.setMethodName("get")
524+
.build();
525+
}
526+
tryBodyExprs.add(rpcJavaMethodInvocationExpr);
527+
528+
VariableExpr catchExceptionVarExpr =
529+
VariableExpr.builder()
530+
.setVariable(
531+
Variable.builder()
532+
.setType(
533+
TypeNode.withExceptionClazz(
534+
method.hasLro()
535+
? ExecutionException.class
536+
: InvalidArgumentException.class))
537+
.setName("e")
538+
.build())
539+
.build();
540+
541+
List<Statement> catchBody =
542+
method.hasLro()
543+
? createRpcLroExceptionTestCatchBody(catchExceptionVarExpr)
544+
: Arrays.asList(
545+
CommentStatement.withComment(LineComment.withComment("Expected exception.")));
484546

485547
// Assert a failure if no exception was raised.
486548
tryBodyExprs.add(
@@ -496,18 +558,8 @@ private static MethodDefinition createRpcExceptionTestMethod(
496558
tryBodyExprs.stream()
497559
.map(e -> ExprStatement.withExpr(e))
498560
.collect(Collectors.toList()))
499-
.setCatchVariableExpr(
500-
VariableExpr.builder()
501-
.setVariable(
502-
Variable.builder()
503-
.setType(TypeNode.withExceptionClazz(InvalidArgumentException.class))
504-
.setName("e")
505-
.build())
506-
.setIsDecl(true)
507-
.build())
508-
.setCatchBody(
509-
Arrays.asList(
510-
CommentStatement.withComment(LineComment.withComment("Expected exception."))))
561+
.setCatchVariableExpr(catchExceptionVarExpr.toBuilder().setIsDecl(true).build())
562+
.setCatchBody(catchBody)
511563
.build();
512564

513565
return MethodDefinition.builder()
@@ -524,6 +576,87 @@ private static MethodDefinition createRpcExceptionTestMethod(
524576
.build();
525577
}
526578

579+
private static List<Statement> createRpcLroExceptionTestCatchBody(VariableExpr exceptionExpr) {
580+
List<Expr> catchBodyExprs = new ArrayList<>();
581+
582+
Expr testExpectedValueExpr =
583+
VariableExpr.builder()
584+
.setVariable(
585+
Variable.builder()
586+
.setType(TypeNode.withReference(ConcreteReference.withClazz(Class.class)))
587+
.setName("class")
588+
.build())
589+
.setStaticReferenceType(STATIC_TYPES.get("InvalidArgumentException"))
590+
.build();
591+
Expr getCauseExpr =
592+
MethodInvocationExpr.builder()
593+
.setExprReferenceExpr(exceptionExpr)
594+
.setMethodName("getCause")
595+
.setReturnType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
596+
.build();
597+
Expr testActualValueExpr =
598+
MethodInvocationExpr.builder()
599+
.setExprReferenceExpr(getCauseExpr)
600+
.setMethodName("getClass")
601+
.build();
602+
603+
// Constructs `Assert.assertEquals(InvalidArgumentException.class, e.getCaus().getClass());`.
604+
catchBodyExprs.add(
605+
MethodInvocationExpr.builder()
606+
.setStaticReferenceType(STATIC_TYPES.get("Assert"))
607+
.setMethodName("assertEquals")
608+
.setArguments(testExpectedValueExpr, testActualValueExpr)
609+
.build());
610+
611+
// Construct the apiException variable.
612+
VariableExpr apiExceptionVarExpr =
613+
VariableExpr.withVariable(
614+
Variable.builder()
615+
.setType(STATIC_TYPES.get("InvalidArgumentException"))
616+
.setName("apiException")
617+
.build());
618+
Expr castedCauseExpr =
619+
CastExpr.builder()
620+
.setType(STATIC_TYPES.get("InvalidArgumentException"))
621+
.setExpr(getCauseExpr)
622+
.build();
623+
catchBodyExprs.add(
624+
AssignmentExpr.builder()
625+
.setVariableExpr(apiExceptionVarExpr.toBuilder().setIsDecl(true).build())
626+
.setValueExpr(castedCauseExpr)
627+
.build());
628+
629+
// Construct the last assert statement.
630+
testExpectedValueExpr =
631+
EnumRefExpr.builder()
632+
.setType(
633+
TypeNode.withReference(
634+
ConcreteReference.builder()
635+
.setClazz(StatusCode.Code.class)
636+
.setIsStaticImport(false)
637+
.build()))
638+
.setName("INVALID_ARGUMENT")
639+
.build();
640+
testActualValueExpr =
641+
MethodInvocationExpr.builder()
642+
.setExprReferenceExpr(apiExceptionVarExpr)
643+
.setMethodName("getStatusCode")
644+
.build();
645+
testActualValueExpr =
646+
MethodInvocationExpr.builder()
647+
.setExprReferenceExpr(testActualValueExpr)
648+
.setMethodName("getCode")
649+
.build();
650+
catchBodyExprs.add(
651+
MethodInvocationExpr.builder()
652+
.setStaticReferenceType(STATIC_TYPES.get("Assert"))
653+
.setMethodName("assertEquals")
654+
.setArguments(testExpectedValueExpr, testActualValueExpr)
655+
.build());
656+
657+
return catchBodyExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList());
658+
}
659+
527660
/* =========================================
528661
* Type creator methods.
529662
* =========================================

src/main/java/com/google/api/generator/gapic/model/Field.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public abstract class Field {
3232

3333
public abstract boolean isMap();
3434

35+
public abstract boolean isContainedInOneof();
36+
3537
@Nullable
3638
public abstract ResourceReference resourceReference();
3739

@@ -51,7 +53,8 @@ public static Builder builder() {
5153
.setIsMessage(false)
5254
.setIsEnum(false)
5355
.setIsRepeated(false)
54-
.setIsMap(false);
56+
.setIsMap(false)
57+
.setIsContainedInOneof(false);
5558
}
5659

5760
@AutoValue.Builder
@@ -68,6 +71,8 @@ public abstract static class Builder {
6871

6972
public abstract Builder setIsMap(boolean isMap);
7073

74+
public abstract Builder setIsContainedInOneof(boolean isContainedInOneof);
75+
7176
public abstract Builder setResourceReference(ResourceReference resourceReference);
7277

7378
public abstract Builder setDescription(String description);

src/main/java/com/google/api/generator/gapic/protoparser/Parser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ private static Field parseField(FieldDescriptor fieldDescriptor, Descriptor mess
404404
.setType(TypeParser.parseType(fieldDescriptor))
405405
.setIsMessage(fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE)
406406
.setIsEnum(fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.ENUM)
407+
.setIsContainedInOneof(fieldDescriptor.getContainingOneof() != null)
407408
.setIsRepeated(fieldDescriptor.isRepeated())
408409
.setIsMap(fieldDescriptor.isMapField())
409410
.setResourceReference(resourceReference)

0 commit comments

Comments
 (0)