From b0d29d3868c07c4e03e2ba527c09b96c3cd138d4 Mon Sep 17 00:00:00 2001 From: summerji Date: Wed, 29 Jul 2020 01:09:04 -0700 Subject: [PATCH 1/8] Add SuperObjectValue and unit test --- .../engine/ast/SuperObjectValue.java | 53 +++++++++++++++++++ .../api/generator/engine/ast/BUILD.bazel | 1 + .../engine/ast/SuperObjectValueTest.java | 52 ++++++++++++++++++ .../engine/writer/JavaWriterVisitorTest.java | 31 +++++++++++ 4 files changed, 137 insertions(+) create mode 100644 src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java create mode 100644 src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java diff --git a/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java new file mode 100644 index 0000000000..50cd6d7637 --- /dev/null +++ b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java @@ -0,0 +1,53 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.engine.ast; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; + +@AutoValue +public abstract class SuperObjectValue implements ObjectValue { + private static final String SUPER_VALUE = "super"; + + public abstract TypeNode type(); + + @Override + public String value() { + return SUPER_VALUE; + } + + private static Builder builder() { + return new AutoValue_SuperObjectValue.Builder(); + } + + public static SuperObjectValue withType(TypeNode type) { + return builder().setType(type).build(); + } + + @AutoValue.Builder + abstract static class Builder { + abstract Builder setType(TypeNode type); + + abstract SuperObjectValue autoBuild(); + + private SuperObjectValue build() { + SuperObjectValue superObjectValue = autoBuild(); + Preconditions.checkState( + TypeNode.isReferenceType(superObjectValue.type()), + "super can only refer to object types"); + return superObjectValue; + } + } +} diff --git a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel index 70e53dd433..cca51b1d56 100644 --- a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel +++ b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel @@ -25,6 +25,7 @@ TESTS = [ "VariableTest", "VaporReferenceTest", "MethodInvocationExprTest", + "SuperObjectValueTest", ] filegroup( diff --git a/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java new file mode 100644 index 0000000000..3abeacc96f --- /dev/null +++ b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java @@ -0,0 +1,52 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.engine.ast; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public class SuperObjectValueTest { + + @Test + public void validSuperObjectValue_basic() { + VaporReference ref = + VaporReference.builder() + .setName("Student") + .setPakkage("com.google.example.examples.v1") + .build(); + TypeNode typeNode = TypeNode.withReference(ref); + SuperObjectValue.withType(typeNode); + // No exception thrown, we're good. + } + + @Test + public void invalidSuperObjectValue_NonReferenceType() { + assertThrows( + IllegalStateException.class, + () -> { + SuperObjectValue.withType(TypeNode.DOUBLE); + }); + } + + @Test + public void invalidSuperObjectValue_NullType() { + assertThrows( + IllegalStateException.class, + () -> { + SuperObjectValue.withType(TypeNode.NULL); + }); + } +} diff --git a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java index 0188c74797..523dcd17b3 100644 --- a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java @@ -43,6 +43,7 @@ import com.google.api.generator.engine.ast.ScopeNode; import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.ast.StringObjectValue; +import com.google.api.generator.engine.ast.SuperObjectValue; import com.google.api.generator.engine.ast.TernaryExpr; import com.google.api.generator.engine.ast.ThrowExpr; import com.google.api.generator.engine.ast.TryCatchStatement; @@ -1654,6 +1655,36 @@ public void writeClassDefinition_statementsAndMethods() { "}\n")); } + @Test + public void writeSuperObjectValue_asscessFieldAndInvokeMethod() { + VaporReference ref = + VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build(); + TypeNode classType = TypeNode.withReference(ref); + SuperObjectValue superObjectValue = SuperObjectValue.withType(classType); + ValueExpr superValueExpr = ValueExpr.withValue(superObjectValue); + Variable subVariable = Variable.builder().setName("name").setType(TypeNode.STRING).build(); + VariableExpr superVariableExpr = + VariableExpr.builder() + .setVariable(subVariable) + .setExprReferenceExpr(superValueExpr) + .build(); + + MethodInvocationExpr methodExpr = + MethodInvocationExpr.builder() + .setMethodName("getName") + .setExprReferenceExpr(ValueExpr.withValue(superObjectValue)) + .setReturnType(TypeNode.STRING) + .build(); + AssignmentExpr assignmentExpr = + AssignmentExpr.builder() + .setVariableExpr(superVariableExpr) + .setValueExpr(methodExpr) + .build(); + + assignmentExpr.accept(writerVisitor); + assertThat(writerVisitor.write()).isEqualTo("super.name = super.getName()"); + } + private static String createLines(int numLines) { return new String(new char[numLines]).replace("\0", "%s"); } From bb29ab3ca8bd0957de264fd30330c4b13e20760a Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Thu, 30 Jul 2020 11:26:49 -0700 Subject: [PATCH 2/8] fix: clean up composer methods (#138) From f4dab9146b1ca97942212783ade084edb8897b98 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Thu, 30 Jul 2020 11:27:09 -0700 Subject: [PATCH 3/8] [ggj] feat: add starter for ServiceClientTest generation (#139) * feat: support templated method definitions * fix!: MethodInvocationExpr: use TypeNode instead of names for static references * feat: mix IdNode and TypeNode in template vars for methods * feat: generate GrpcServiceCallableFactory class * fix: clean up composer methods * feat: add starter for ServiceClientTest generation --- .../generator/engine/ast/AnnotationNode.java | 4 + .../api/generator/gapic/composer/BUILD.bazel | 1 + .../generator/gapic/composer/Composer.java | 1 + .../ServiceClientTestClassComposer.java | 617 ++++++++++++++++++ .../generator/gapic/protowriter/Writer.java | 2 +- .../api/generator/gapic/composer/BUILD.bazel | 1 + .../ServiceClientTestClassComposerTest.java | 111 ++++ 7 files changed, 736 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java create mode 100644 src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java diff --git a/src/main/java/com/google/api/generator/engine/ast/AnnotationNode.java b/src/main/java/com/google/api/generator/engine/ast/AnnotationNode.java index 1b510c0c93..e7b9e05d2f 100644 --- a/src/main/java/com/google/api/generator/engine/ast/AnnotationNode.java +++ b/src/main/java/com/google/api/generator/engine/ast/AnnotationNode.java @@ -48,6 +48,10 @@ public static AnnotationNode withSuppressWarnings(String description) { .build(); } + public static AnnotationNode withType(TypeNode type) { + return builder().setType(type).build(); + } + public static Builder builder() { return new AutoValue_AnnotationNode.Builder(); } diff --git a/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel b/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel index 3d2dbdbfcc..93dcf721c9 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel +++ b/src/main/java/com/google/api/generator/gapic/composer/BUILD.bazel @@ -25,5 +25,6 @@ java_library( "@io_grpc_java//api", "@io_grpc_java//protobuf", "@io_grpc_java//stub", + "@junit_junit//jar", ], ) diff --git a/src/main/java/com/google/api/generator/gapic/composer/Composer.java b/src/main/java/com/google/api/generator/gapic/composer/Composer.java index 15c6fc47a0..381fe6c7b5 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/Composer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/Composer.java @@ -68,6 +68,7 @@ public static List generateMocksAndTestClasses( List clazzes = new ArrayList<>(); clazzes.add(MockServiceClassComposer.instance().generate(service, messageTypes)); clazzes.add(MockServiceImplClassComposer.instance().generate(service, messageTypes)); + clazzes.add(ServiceClientTestClassComposer.instance().generate(service, messageTypes)); return clazzes; } diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java new file mode 100644 index 0000000000..16531e389b --- /dev/null +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java @@ -0,0 +1,617 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.gapic.composer; + +import com.google.api.gax.core.GoogleCredentialsProvider; +import com.google.api.gax.core.InstantiatingExecutorProvider; +import com.google.api.gax.core.NoCredentialsProvider; +import com.google.api.gax.grpc.GaxGrpcProperties; +import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; +import com.google.api.gax.rpc.ApiClientHeaderProvider; +import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.BidiStreamingCallable; +import com.google.api.gax.rpc.InvalidArgumentException; +import com.google.api.gax.rpc.OperationCallSettings; +import com.google.api.gax.rpc.PagedCallSettings; +import com.google.api.gax.rpc.ServerStreamingCallSettings; +import com.google.api.gax.rpc.ServerStreamingCallable; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.rpc.StreamingCallSettings; +import com.google.api.gax.rpc.UnaryCallSettings; +import com.google.api.generator.engine.ast.AnnotationNode; +import com.google.api.generator.engine.ast.AssignmentExpr; +import com.google.api.generator.engine.ast.ClassDefinition; +import com.google.api.generator.engine.ast.ConcreteReference; +import com.google.api.generator.engine.ast.Expr; +import com.google.api.generator.engine.ast.ExprStatement; +import com.google.api.generator.engine.ast.MethodDefinition; +import com.google.api.generator.engine.ast.MethodInvocationExpr; +import com.google.api.generator.engine.ast.Reference; +import com.google.api.generator.engine.ast.ScopeNode; +import com.google.api.generator.engine.ast.Statement; +import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.engine.ast.VaporReference; +import com.google.api.generator.engine.ast.Variable; +import com.google.api.generator.engine.ast.VariableExpr; +import com.google.api.generator.gapic.model.GapicClass; +import com.google.api.generator.gapic.model.GapicClass.Kind; +import com.google.api.generator.gapic.model.Message; +import com.google.api.generator.gapic.model.Method; +import com.google.api.generator.gapic.model.Service; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.longrunning.Operation; +import com.google.protobuf.AbstractMessage; +import com.google.protobuf.Any; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Generated; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ServiceClientTestClassComposer implements ClassComposer { + private static final String CHANNEL_PROVIDER_VAR_NAME = "channelProvider"; + private static final String CLASS_NAME_PATTERN = "%sClientTest"; + private static final String CLIENT_VAR_NAME = "client"; + private static final String GRPC_TESTING_PACKAGE = "com.google.api.gax.grpc.testing"; + private static final String MOCK_SERVICE_CLASS_NAME_PATTERN = "Mock%s"; + private static final String MOCK_SERVICE_VAR_NAME_PATTERN = "mock%s"; + private static final String SERVICE_CLIENT_CLASS_NAME_PATTERN = "%sClient"; + private static final String SERVICE_HELPER_VAR_NAME = "mockServiceHelper"; + private static final String SERVICE_SETTINGS_CLASS_NAME_PATTERN = "%sSettings"; + private static final String STUB_SETTINGS_PATTERN = "%sSettings"; + private static final String PAGED_RESPONSE_TYPE_NAME_PATTERN = "%sPagedResponse"; + + private static final ServiceClientTestClassComposer INSTANCE = + new ServiceClientTestClassComposer(); + + private static final Map staticTypes = createStaticTypes(); + + private ServiceClientTestClassComposer() {} + + public static ServiceClientTestClassComposer instance() { + return INSTANCE; + } + + @Override + public GapicClass generate(Service service, Map ignore) { + String pakkage = service.pakkage(); + Map types = createDynamicTypes(service); + String className = String.format("%sClientTest", service.name()); + GapicClass.Kind kind = Kind.MAIN; + + Map classMemberVarExprs = createClassMemberVarExprs(service, types); + + ClassDefinition classDef = + ClassDefinition.builder() + .setPackageString(pakkage) + .setAnnotations(createClassAnnotations()) + .setScope(ScopeNode.PUBLIC) + .setName(className) + .setStatements(createClassMemberFieldDecls(classMemberVarExprs)) + .setMethods(createClassMethods(service, classMemberVarExprs, types)) + .build(); + return GapicClass.create(kind, classDef); + } + + private static List createClassAnnotations() { + return Arrays.asList( + AnnotationNode.builder() + .setType(staticTypes.get("Generated")) + .setDescription("by gapic-generator-java") + .build()); + } + + private static Map createClassMemberVarExprs( + Service service, Map types) { + BiFunction varExprFn = + (name, type) -> + VariableExpr.withVariable(Variable.builder().setName(name).setType(type).build()); + Map fields = new LinkedHashMap<>(); + fields.put( + getMockServiceVarName(service.name()), + types.get(String.format(MOCK_SERVICE_CLASS_NAME_PATTERN, service.name()))); + fields.put(SERVICE_HELPER_VAR_NAME, staticTypes.get("MockServiceHelper")); + fields.put(CLIENT_VAR_NAME, types.get(getClientClassName(service.name()))); + fields.put(CHANNEL_PROVIDER_VAR_NAME, staticTypes.get("LocalChannelProvider")); + return fields.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey(), e -> varExprFn.apply(e.getKey(), e.getValue()))); + } + + private static List createClassMemberFieldDecls( + Map classMemberVarExprs) { + return classMemberVarExprs.values().stream() + .map( + v -> + ExprStatement.withExpr( + v.toBuilder() + .setIsDecl(true) + .setScope(ScopeNode.PUBLIC) + .setIsStatic(v.type().reference().name().startsWith("Mock")) + .build())) + .collect(Collectors.toList()); + } + + private static List createClassMethods( + Service service, Map classMemberVarExprs, Map types) { + List javaMethods = new ArrayList<>(); + javaMethods.addAll(createTestAdminMethods(service, classMemberVarExprs, types)); + // TODO(miraleung): FIll this in. + /* + for (Method protoMethod : service.methods()) { + javaMethods.add(createTestMethod(protoMethod, classMemberVarExprs, types)); + javaMethods.add(createExceptionTestMethod(protoMethod, classMemberVarExprs, types)); + } + */ + return javaMethods; + } + + private static List createTestAdminMethods( + Service service, Map classMemberVarExprs, Map types) { + List javaMethods = new ArrayList<>(); + javaMethods.add(createStartStaticServerMethod(service, classMemberVarExprs)); + javaMethods.add(createStopServerMethod(service, classMemberVarExprs)); + javaMethods.add(createSetUpMethod(service, classMemberVarExprs, types)); + javaMethods.add(createTearDownMethod(service, classMemberVarExprs)); + return javaMethods; + } + + private static MethodDefinition createStartStaticServerMethod( + Service service, Map classMemberVarExprs) { + VariableExpr mockServiceVarExpr = + classMemberVarExprs.get(getMockServiceVarName(service.name())); + VariableExpr serviceHelperVarExpr = classMemberVarExprs.get(SERVICE_HELPER_VAR_NAME); + // TODO(miraleung): Actually intantiate this. + Expr initMockServiceExpr = + AssignmentExpr.builder() + .setVariableExpr(mockServiceVarExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setMethodName("newMockTodo") + .setReturnType(mockServiceVarExpr.type()) + .build()) + .build(); + + MethodInvocationExpr firstArg = + MethodInvocationExpr.builder() + .setStaticReferenceType(staticTypes.get("UUID")) + .setMethodName("randomUUID") + .build(); + firstArg = + MethodInvocationExpr.builder() + .setExprReferenceExpr(firstArg) + .setMethodName("toString") + .build(); + + MethodInvocationExpr secondArg = + MethodInvocationExpr.builder() + .setStaticReferenceType(staticTypes.get("Arrays")) + .setGenerics(Arrays.asList(staticTypes.get("MockGrpcService").reference())) + .setMethodName("asList") + .setArguments(Arrays.asList(mockServiceVarExpr)) + .build(); + // TODO(miraleung): Actually intantiate this. + Expr initServiceHelperExpr = + AssignmentExpr.builder() + .setVariableExpr(serviceHelperVarExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setMethodName("newMockServiceHelperTodo") + .setArguments(Arrays.asList(firstArg, secondArg)) + .setReturnType(serviceHelperVarExpr.type()) + .build()) + .build(); + + Expr startServiceHelperExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(serviceHelperVarExpr) + .setMethodName("start") + .build(); + + return MethodDefinition.builder() + .setAnnotations(Arrays.asList(AnnotationNode.withType(staticTypes.get("BeforeClass")))) + .setScope(ScopeNode.PUBLIC) + .setIsStatic(true) + .setReturnType(TypeNode.VOID) + .setName("startStaticServer") + .setBody( + Arrays.asList(initMockServiceExpr, initServiceHelperExpr, startServiceHelperExpr) + .stream() + .map(e -> ExprStatement.withExpr(e)) + .collect(Collectors.toList())) + .build(); + } + + private static MethodDefinition createStopServerMethod( + Service service, Map classMemberVarExprs) { + return MethodDefinition.builder() + .setAnnotations(Arrays.asList(AnnotationNode.withType(staticTypes.get("AfterClass")))) + .setScope(ScopeNode.PUBLIC) + .setIsStatic(true) + .setReturnType(TypeNode.VOID) + .setName("stopServer") + .setBody( + Arrays.asList( + ExprStatement.withExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(classMemberVarExprs.get(SERVICE_HELPER_VAR_NAME)) + .setMethodName("stop") + .build()))) + .build(); + } + + private static MethodDefinition createSetUpMethod( + Service service, Map classMemberVarExprs, Map types) { + VariableExpr clientVarExpr = classMemberVarExprs.get(CLIENT_VAR_NAME); + VariableExpr serviceHelperVarExpr = classMemberVarExprs.get(SERVICE_HELPER_VAR_NAME); + VariableExpr channelProviderVarExpr = classMemberVarExprs.get(CHANNEL_PROVIDER_VAR_NAME); + + Expr resetServiceHelperExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(serviceHelperVarExpr) + .setMethodName("reset") + .build(); + Expr channelProviderInitExpr = + AssignmentExpr.builder() + .setVariableExpr(channelProviderVarExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(serviceHelperVarExpr) + .setMethodName("createChannelProvider") + .setReturnType(channelProviderVarExpr.type()) + .build()) + .build(); + + TypeNode settingsType = types.get(getServiceSettingsClassName(service.name())); + VariableExpr localSettingsVarExpr = + VariableExpr.withVariable( + Variable.builder().setName("settings").setType(settingsType).build()); + + Expr settingsBuilderExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(settingsType) + .setMethodName("newBuilder") + .build(); + Function> methodBuilderFn = + methodExpr -> + (mName, argExpr) -> + MethodInvocationExpr.builder() + .setExprReferenceExpr(methodExpr) + .setMethodName(mName) + .setArguments(Arrays.asList(argExpr)) + .build(); + settingsBuilderExpr = + methodBuilderFn + .apply(settingsBuilderExpr) + .apply( + "setTransportChannelProvider", classMemberVarExprs.get(CHANNEL_PROVIDER_VAR_NAME)); + settingsBuilderExpr = + methodBuilderFn + .apply(settingsBuilderExpr) + .apply( + "setCredentialsProvider", + MethodInvocationExpr.builder() + .setStaticReferenceType(staticTypes.get("NoCredentialsProvider")) + .setMethodName("create") + .build()); + settingsBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(settingsBuilderExpr) + .setMethodName("build") + .setReturnType(localSettingsVarExpr.type()) + .build(); + + Expr initLocalSettingsExpr = + AssignmentExpr.builder() + .setVariableExpr(localSettingsVarExpr) + .setValueExpr(settingsBuilderExpr) + .build(); + + Expr initClientExpr = + AssignmentExpr.builder() + .setVariableExpr(clientVarExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setStaticReferenceType(types.get(getClientClassName(service.name()))) + .setMethodName("create") + .setArguments(Arrays.asList(localSettingsVarExpr)) + .setReturnType(clientVarExpr.type()) + .build()) + .build(); + + return MethodDefinition.builder() + .setAnnotations(Arrays.asList(AnnotationNode.withType(staticTypes.get("Before")))) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.VOID) + .setName("setUp") + .setThrowsExceptions(Arrays.asList(staticTypes.get("IOException"))) + .setBody( + Arrays.asList( + resetServiceHelperExpr, + channelProviderInitExpr, + initLocalSettingsExpr, + initClientExpr) + .stream() + .map(e -> ExprStatement.withExpr(e)) + .collect(Collectors.toList())) + .build(); + } + + private static MethodDefinition createTearDownMethod( + Service service, Map classMemberVarExprs) { + return MethodDefinition.builder() + .setAnnotations(Arrays.asList(AnnotationNode.withType(staticTypes.get("After")))) + .setScope(ScopeNode.PUBLIC) + .setIsStatic(true) + .setReturnType(TypeNode.VOID) + .setName("tearDown") + .setThrowsExceptions( + Arrays.asList(TypeNode.withReference(ConcreteReference.withClazz(Exception.class)))) + .setBody( + Arrays.asList( + ExprStatement.withExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(classMemberVarExprs.get(CLIENT_VAR_NAME)) + .setMethodName("close") + .build()))) + .build(); + } + + private static void createTestMethod( + Method protoMethod, + Map classMemberVarExprs, + Map types) { + // TODO(miraleung): Fill this in. + } + + private static void createExceptionTestMethod( + Method protoMethod, + Map classMemberVarExprs, + Map types) { + // TODO(miraleung): Fill this in. + } + + private static Map createStaticTypes() { + List concreteClazzes = + Arrays.asList( + AbstractMessage.class, + After.class, + AfterClass.class, + Any.class, + ApiClientHeaderProvider.class, + ApiStreamObserver.class, + Arrays.class, + Assert.class, + Before.class, + BeforeClass.class, + BidiStreamingCallable.class, + ExecutionException.class, + GaxGrpcProperties.class, + Generated.class, + IOException.class, + InvalidArgumentException.class, + List.class, + Lists.class, + NoCredentialsProvider.class, + Operation.class, + ServerStreamingCallable.class, + Status.class, + StatusCode.class, + StatusRuntimeException.class, + Test.class, + UUID.class); + Map staticTypes = + concreteClazzes.stream() + .collect( + Collectors.toMap( + c -> c.getSimpleName(), + c -> TypeNode.withReference(ConcreteReference.withClazz(c)))); + + staticTypes.putAll( + Arrays.asList( + "LocalChannelProvider", + "MockGrpcService", + "MockServiceHelper", + "MockStreamObserver") + .stream() + .collect( + Collectors.toMap( + n -> n, + n -> + TypeNode.withReference( + VaporReference.builder() + .setName(n) + .setPakkage(GRPC_TESTING_PACKAGE) + .build())))); + return staticTypes; + } + + private static Map createDefaultMethodNamesToTypes() { + Function typeMakerFn = + c -> TypeNode.withReference(ConcreteReference.withClazz(c)); + Map javaMethodNameToReturnType = new LinkedHashMap<>(); + javaMethodNameToReturnType.put( + "defaultExecutorProviderBuilder", + typeMakerFn.apply(InstantiatingExecutorProvider.Builder.class)); + javaMethodNameToReturnType.put("getDefaultEndpoint", TypeNode.STRING); + javaMethodNameToReturnType.put( + "getDefaultServiceScopes", + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics(Arrays.asList(TypeNode.STRING.reference())) + .build())); + javaMethodNameToReturnType.put( + "defaultCredentialsProviderBuilder", + typeMakerFn.apply(GoogleCredentialsProvider.Builder.class)); + javaMethodNameToReturnType.put( + "defaultGrpcTransportProviderBuilder", + typeMakerFn.apply(InstantiatingGrpcChannelProvider.Builder.class)); + javaMethodNameToReturnType.put( + "defaultTransportChannelProvider", staticTypes.get("TransportChannelProvider")); + return javaMethodNameToReturnType; + } + + private static Map createDynamicTypes(Service service) { + Map types = new HashMap<>(); + + // ServiceStubSettings class. + String stubSettingsClassName = String.format(STUB_SETTINGS_PATTERN, service.name()); + types.put( + stubSettingsClassName, + TypeNode.withReference( + VaporReference.builder() + .setName(stubSettingsClassName) + .setPakkage(String.format("%s.stub", service.pakkage())) + .build())); + + // Classes in the same package. + types.putAll( + Arrays.asList( + SERVICE_CLIENT_CLASS_NAME_PATTERN, + SERVICE_SETTINGS_CLASS_NAME_PATTERN, + MOCK_SERVICE_CLASS_NAME_PATTERN) + .stream() + .collect( + Collectors.toMap( + p -> String.format(p, service.name()), + p -> + TypeNode.withReference( + VaporReference.builder() + .setName(String.format(p, service.name())) + .setPakkage(service.pakkage()) + .build())))); + + // Pagination types. + types.putAll( + service.methods().stream() + .filter(m -> m.isPaged()) + .collect( + Collectors.toMap( + m -> String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, m.name()), + m -> + TypeNode.withReference( + VaporReference.builder() + .setName(String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, m.name())) + .setPakkage(service.pakkage()) + .setEnclosingClassName(getClientClassName(service.name())) + .setIsStaticImport(true) + .build())))); + return types; + } + + private static TypeNode getOperationCallSettingsType(Method protoMethod) { + return getOperationCallSettingsTypeHelper(protoMethod, false); + } + + private static TypeNode getOperationCallSettingsBuilderType(Method protoMethod) { + return getOperationCallSettingsTypeHelper(protoMethod, true); + } + + private static TypeNode getOperationCallSettingsTypeHelper( + Method protoMethod, boolean isBuilder) { + Preconditions.checkState( + protoMethod.hasLro(), + String.format("Cannot get OperationCallSettings on non-LRO method %s", protoMethod.name())); + Class callSettingsClazz = + isBuilder ? OperationCallSettings.Builder.class : OperationCallSettings.class; + return TypeNode.withReference( + ConcreteReference.builder() + .setClazz(callSettingsClazz) + .setGenerics( + Arrays.asList( + protoMethod.inputType().reference(), + protoMethod.lro().responseType().reference(), + protoMethod.lro().metadataType().reference())) + .build()); + } + + private static TypeNode getCallSettingsType(Method protoMethod, Map types) { + return getCallSettingsTypeHelper(protoMethod, types, false); + } + + private static TypeNode getCallSettingsBuilderType( + Method protoMethod, Map types) { + return getCallSettingsTypeHelper(protoMethod, types, true); + } + + private static TypeNode getCallSettingsTypeHelper( + Method protoMethod, Map types, boolean isBuilder) { + Class callSettingsClazz = isBuilder ? UnaryCallSettings.Builder.class : UnaryCallSettings.class; + if (protoMethod.isPaged()) { + callSettingsClazz = isBuilder ? PagedCallSettings.Builder.class : PagedCallSettings.class; + } else { + switch (protoMethod.stream()) { + case CLIENT: + // Fall through. + case BIDI: + callSettingsClazz = + isBuilder ? StreamingCallSettings.Builder.class : StreamingCallSettings.class; + break; + case SERVER: + callSettingsClazz = + isBuilder + ? ServerStreamingCallSettings.Builder.class + : ServerStreamingCallSettings.class; + break; + case NONE: + // Fall through + default: + // Fall through + } + } + + List generics = new ArrayList<>(); + generics.add(protoMethod.inputType().reference()); + generics.add(protoMethod.outputType().reference()); + if (protoMethod.isPaged()) { + generics.add( + types + .get(String.format(PAGED_RESPONSE_TYPE_NAME_PATTERN, protoMethod.name())) + .reference()); + } + + return TypeNode.withReference( + ConcreteReference.builder().setClazz(callSettingsClazz).setGenerics(generics).build()); + } + + private static String getClientClassName(String serviceName) { + return String.format(SERVICE_CLIENT_CLASS_NAME_PATTERN, serviceName); + } + + private static String getServiceSettingsClassName(String serviceName) { + return String.format(SERVICE_SETTINGS_CLASS_NAME_PATTERN, serviceName); + } + + private static String getMockServiceVarName(String serviceName) { + return String.format(MOCK_SERVICE_VAR_NAME_PATTERN, serviceName); + } +} diff --git a/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java b/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java index e1eeb29ca2..91c35f1cac 100644 --- a/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java +++ b/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java @@ -80,7 +80,7 @@ public static CodeGeneratorResponse writeCode(List clazzes, String o private static String getPath(String pakkage, String className) { String path = pakkage.replaceAll("\\.", "/"); - if (className.substring(0, 3).equals("Mock") || className.endsWith("Test")) { + if (className.startsWith("Mock") || className.endsWith("Test")) { path = "test/" + path; } // TODO(miraleung): Add path for resource name classes. diff --git a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel index cfd7955482..1e83bcc3b6 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel +++ b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel @@ -6,6 +6,7 @@ TESTS = [ "MockServiceClassComposerTest", "MockServiceImplClassComposerTest", "ServiceClientClassComposerTest", + "ServiceClientTestClassComposerTest", "ServiceStubClassComposerTest", ] diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java new file mode 100644 index 0000000000..ab1925fa41 --- /dev/null +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposerTest.java @@ -0,0 +1,111 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.gapic.composer; + +import static junit.framework.Assert.assertEquals; + +import com.google.api.generator.engine.writer.JavaWriterVisitor; +import com.google.api.generator.gapic.model.GapicClass; +import com.google.api.generator.gapic.model.Message; +import com.google.api.generator.gapic.model.Service; +import com.google.api.generator.gapic.protoparser.Parser; +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.ServiceDescriptor; +import com.google.showcase.v1beta1.EchoOuterClass; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; + +public class ServiceClientTestClassComposerTest { + private ServiceDescriptor echoService; + private FileDescriptor echoFileDescriptor; + + @Before + public void setUp() { + echoFileDescriptor = EchoOuterClass.getDescriptor(); + echoService = echoFileDescriptor.getServices().get(0); + assertEquals(echoService.getName(), "Echo"); + } + + @Test + public void generateServiceClasses() { + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + List services = Parser.parseService(echoFileDescriptor, messageTypes); + Service echoProtoService = services.get(0); + GapicClass clazz = + ServiceClientTestClassComposer.instance().generate(echoProtoService, messageTypes); + + JavaWriterVisitor visitor = new JavaWriterVisitor(); + clazz.classDefinition().accept(visitor); + assertEquals(EXPECTED_CLASS_STRING, visitor.write()); + } + + // TODO(miraleung): Update this when a file-diffing test mechanism is in place. + private static final String EXPECTED_CLASS_STRING = + "package com.google.showcase.v1beta1;\n" + + "\n" + + "import com.google.api.gax.core.NoCredentialsProvider;\n" + + "import com.google.api.gax.grpc.testing.LocalChannelProvider;\n" + + "import com.google.api.gax.grpc.testing.MockGrpcService;\n" + + "import com.google.api.gax.grpc.testing.MockServiceHelper;\n" + + "import java.io.IOException;\n" + + "import java.util.Arrays;\n" + + "import java.util.UUID;\n" + + "import javax.annotation.Generated;\n" + + "import org.junit.After;\n" + + "import org.junit.AfterClass;\n" + + "import org.junit.Before;\n" + + "import org.junit.BeforeClass;\n" + + "\n" + + "@Generated(\"by gapic-generator-java\")\n" + + "public class EchoClientTest {\n" + + " public static MockServiceHelper mockServiceHelper;\n" + + " public static MockEcho mockEcho;\n" + + " public EchoClient client;\n" + + " public LocalChannelProvider channelProvider;\n" + + "\n" + + " @BeforeClass\n" + + " public static void startStaticServer() {\n" + + " mockEcho = newMockTodo();\n" + + " mockServiceHelper =\n" + + " newMockServiceHelperTodo(\n" + + " UUID.randomUUID().toString(), Arrays.asList(mockEcho));\n" + + " mockServiceHelper.start();\n" + + " }\n" + + "\n" + + " @AfterClass\n" + + " public static void stopServer() {\n" + + " mockServiceHelper.stop();\n" + + " }\n" + + "\n" + + " @Before\n" + + " public void setUp() throws IOException {\n" + + " mockServiceHelper.reset();\n" + + " channelProvider = mockServiceHelper.createChannelProvider();\n" + + " settings =\n" + + " EchoSettings.newBuilder()\n" + + " .setTransportChannelProvider(channelProvider)\n" + + " .setCredentialsProvider(NoCredentialsProvider.create())\n" + + " .build();\n" + + " client = EchoClient.create(settings);\n" + + " }\n" + + "\n" + + " @After\n" + + " public static void tearDown() throws Exception {\n" + + " client.close();\n" + + " }\n" + + "}\n"; +} From 5382ec9d3515bc064916f1e17d6503c3ba654072 Mon Sep 17 00:00:00 2001 From: summerji Date: Thu, 30 Jul 2020 20:36:31 -0700 Subject: [PATCH 4/8] correct test name and add overwrite --- .../com/google/api/generator/engine/ast/SuperObjectValue.java | 1 + .../google/api/generator/engine/ast/SuperObjectValueTest.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java index 50cd6d7637..9500dbb88c 100644 --- a/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java +++ b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java @@ -21,6 +21,7 @@ public abstract class SuperObjectValue implements ObjectValue { private static final String SUPER_VALUE = "super"; + @Override public abstract TypeNode type(); @Override diff --git a/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java index 3abeacc96f..3c7a264ca9 100644 --- a/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java +++ b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java @@ -33,7 +33,7 @@ public void validSuperObjectValue_basic() { } @Test - public void invalidSuperObjectValue_NonReferenceType() { + public void invalidSuperObjectValue_nonReferenceType() { assertThrows( IllegalStateException.class, () -> { @@ -42,7 +42,7 @@ public void invalidSuperObjectValue_NonReferenceType() { } @Test - public void invalidSuperObjectValue_NullType() { + public void invalidSuperObjectValue_nullType() { assertThrows( IllegalStateException.class, () -> { From 7154d99201ae9897ab77467854feea15e60f0b82 Mon Sep 17 00:00:00 2001 From: Summer Ji Date: Fri, 31 Jul 2020 21:28:01 -0700 Subject: [PATCH 5/8] feat: add ThisObjectValue (#137) --- .../generator/engine/ast/ThisObjectValue.java | 54 +++++++++++++++++++ .../api/generator/engine/ast/BUILD.bazel | 1 + .../engine/ast/ThisObjectValueTest.java | 51 ++++++++++++++++++ .../engine/writer/JavaWriterVisitorTest.java | 49 +++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 src/main/java/com/google/api/generator/engine/ast/ThisObjectValue.java create mode 100644 src/test/java/com/google/api/generator/engine/ast/ThisObjectValueTest.java diff --git a/src/main/java/com/google/api/generator/engine/ast/ThisObjectValue.java b/src/main/java/com/google/api/generator/engine/ast/ThisObjectValue.java new file mode 100644 index 0000000000..e72f291936 --- /dev/null +++ b/src/main/java/com/google/api/generator/engine/ast/ThisObjectValue.java @@ -0,0 +1,54 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.engine.ast; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; + +@AutoValue +public abstract class ThisObjectValue implements ObjectValue { + private static final String THIS_VALUE = "this"; + + @Override + public abstract TypeNode type(); + + @Override + public String value() { + return THIS_VALUE; + } + + public static ThisObjectValue withType(TypeNode type) { + return builder().setType(type).build(); + } + + private static Builder builder() { + return new AutoValue_ThisObjectValue.Builder(); + } + + @AutoValue.Builder + abstract static class Builder { + abstract Builder setType(TypeNode type); + + abstract ThisObjectValue autoBuild(); + + private ThisObjectValue build() { + ThisObjectValue thisObjectValue = autoBuild(); + Preconditions.checkState( + TypeNode.isReferenceType(thisObjectValue.type()), + "The \"this\" object can only refer to object types"); + return thisObjectValue; + } + } +} diff --git a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel index 70e53dd433..88ed8a8fdd 100644 --- a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel +++ b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel @@ -25,6 +25,7 @@ TESTS = [ "VariableTest", "VaporReferenceTest", "MethodInvocationExprTest", + "ThisObjectValueTest", ] filegroup( diff --git a/src/test/java/com/google/api/generator/engine/ast/ThisObjectValueTest.java b/src/test/java/com/google/api/generator/engine/ast/ThisObjectValueTest.java new file mode 100644 index 0000000000..83e7c75ab8 --- /dev/null +++ b/src/test/java/com/google/api/generator/engine/ast/ThisObjectValueTest.java @@ -0,0 +1,51 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.engine.ast; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public class ThisObjectValueTest { + @Test + public void validThisObjectValue_basic() { + VaporReference ref = + VaporReference.builder() + .setName("Student") + .setPakkage("com.google.example.examples.v1") + .build(); + TypeNode typeNode = TypeNode.withReference(ref); + ThisObjectValue.withType(typeNode); + // No exception thrown, we're good. + } + + @Test + public void invalidThisObjectValue_nonReferenceType() { + assertThrows( + IllegalStateException.class, + () -> { + ThisObjectValue.withType(TypeNode.DOUBLE); + }); + } + + @Test + public void invalidThisObjectValue_nullType() { + assertThrows( + IllegalStateException.class, + () -> { + ThisObjectValue.withType(TypeNode.NULL); + }); + } +} diff --git a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java index 0188c74797..d3f9fb4412 100644 --- a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java @@ -44,6 +44,7 @@ import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.ast.StringObjectValue; import com.google.api.generator.engine.ast.TernaryExpr; +import com.google.api.generator.engine.ast.ThisObjectValue; import com.google.api.generator.engine.ast.ThrowExpr; import com.google.api.generator.engine.ast.TryCatchStatement; import com.google.api.generator.engine.ast.TypeNode; @@ -1654,6 +1655,54 @@ public void writeClassDefinition_statementsAndMethods() { "}\n")); } + @Test + public void writeThisObjectValue_methodReturn() { + VaporReference ref = + VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build(); + TypeNode classType = TypeNode.withReference(ref); + MethodDefinition methodDefinition = + MethodDefinition.builder() + .setName("apply") + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.withReference(ref)) + .setReturnExpr( + ValueExpr.builder().setValue(ThisObjectValue.withType(classType)).build()) + .build(); + methodDefinition.accept(writerVisitor); + assertEquals( + writerVisitor.write(), + String.format("public Student apply() {\n" + "return this;\n" + "}\n")); + } + + @Test + public void writeThisObjectValue_accessFieldAndInvokeMethod() { + VaporReference ref = + VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build(); + TypeNode classType = TypeNode.withReference(ref); + ThisObjectValue thisObjectValue = ThisObjectValue.withType(classType); + ValueExpr thisValueExpr = ValueExpr.withValue(thisObjectValue); + VariableExpr varExpr = + VariableExpr.builder() + .setVariable(Variable.builder().setName("id").setType(TypeNode.STRING).build()) + .build(); + Variable subVariable = Variable.builder().setName("name").setType(TypeNode.STRING).build(); + VariableExpr thisVariableExpr = + VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(thisValueExpr).build(); + + MethodInvocationExpr methodExpr = + MethodInvocationExpr.builder() + .setMethodName("getName") + .setExprReferenceExpr(ValueExpr.withValue(thisObjectValue)) + .setArguments(Arrays.asList(varExpr)) + .setReturnType(TypeNode.STRING) + .build(); + AssignmentExpr assignmentExpr = + AssignmentExpr.builder().setVariableExpr(thisVariableExpr).setValueExpr(methodExpr).build(); + + assignmentExpr.accept(writerVisitor); + assertThat(writerVisitor.write()).isEqualTo("this.name = this.getName(id)"); + } + private static String createLines(int numLines) { return new String(new char[numLines]).replace("\0", "%s"); } From 612dcd518103eef3295d396a76cfaae0ad52b73a Mon Sep 17 00:00:00 2001 From: summerji Date: Sat, 1 Aug 2020 00:43:27 -0700 Subject: [PATCH 6/8] rebase master --- .../engine/ast/SuperObjectValue.java | 53 +++++++++++++++++++ .../api/generator/engine/ast/BUILD.bazel | 1 + .../engine/ast/SuperObjectValueTest.java | 52 ++++++++++++++++++ .../engine/writer/JavaWriterVisitorTest.java | 32 ++++++++++- 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java create mode 100644 src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java diff --git a/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java new file mode 100644 index 0000000000..50cd6d7637 --- /dev/null +++ b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java @@ -0,0 +1,53 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.engine.ast; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; + +@AutoValue +public abstract class SuperObjectValue implements ObjectValue { + private static final String SUPER_VALUE = "super"; + + public abstract TypeNode type(); + + @Override + public String value() { + return SUPER_VALUE; + } + + private static Builder builder() { + return new AutoValue_SuperObjectValue.Builder(); + } + + public static SuperObjectValue withType(TypeNode type) { + return builder().setType(type).build(); + } + + @AutoValue.Builder + abstract static class Builder { + abstract Builder setType(TypeNode type); + + abstract SuperObjectValue autoBuild(); + + private SuperObjectValue build() { + SuperObjectValue superObjectValue = autoBuild(); + Preconditions.checkState( + TypeNode.isReferenceType(superObjectValue.type()), + "super can only refer to object types"); + return superObjectValue; + } + } +} diff --git a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel index 88ed8a8fdd..82bcfe0c6c 100644 --- a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel +++ b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel @@ -26,6 +26,7 @@ TESTS = [ "VaporReferenceTest", "MethodInvocationExprTest", "ThisObjectValueTest", + "SuperObjectValueTest", ] filegroup( diff --git a/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java new file mode 100644 index 0000000000..3abeacc96f --- /dev/null +++ b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java @@ -0,0 +1,52 @@ +// Copyright 2020 Google LLC +// +// Licensed 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 com.google.api.generator.engine.ast; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public class SuperObjectValueTest { + + @Test + public void validSuperObjectValue_basic() { + VaporReference ref = + VaporReference.builder() + .setName("Student") + .setPakkage("com.google.example.examples.v1") + .build(); + TypeNode typeNode = TypeNode.withReference(ref); + SuperObjectValue.withType(typeNode); + // No exception thrown, we're good. + } + + @Test + public void invalidSuperObjectValue_NonReferenceType() { + assertThrows( + IllegalStateException.class, + () -> { + SuperObjectValue.withType(TypeNode.DOUBLE); + }); + } + + @Test + public void invalidSuperObjectValue_NullType() { + assertThrows( + IllegalStateException.class, + () -> { + SuperObjectValue.withType(TypeNode.NULL); + }); + } +} diff --git a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java index d3f9fb4412..0bbabb7e0d 100644 --- a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java @@ -43,6 +43,7 @@ import com.google.api.generator.engine.ast.ScopeNode; import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.ast.StringObjectValue; +import com.google.api.generator.engine.ast.SuperObjectValue; import com.google.api.generator.engine.ast.TernaryExpr; import com.google.api.generator.engine.ast.ThisObjectValue; import com.google.api.generator.engine.ast.ThrowExpr; @@ -1688,7 +1689,6 @@ public void writeThisObjectValue_accessFieldAndInvokeMethod() { Variable subVariable = Variable.builder().setName("name").setType(TypeNode.STRING).build(); VariableExpr thisVariableExpr = VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(thisValueExpr).build(); - MethodInvocationExpr methodExpr = MethodInvocationExpr.builder() .setMethodName("getName") @@ -1703,6 +1703,36 @@ public void writeThisObjectValue_accessFieldAndInvokeMethod() { assertThat(writerVisitor.write()).isEqualTo("this.name = this.getName(id)"); } + @Test + public void writeSuperObjectValue_asscessFieldAndInvokeMethod() { + VaporReference ref = + VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build(); + TypeNode classType = TypeNode.withReference(ref); + SuperObjectValue superObjectValue = SuperObjectValue.withType(classType); + ValueExpr superValueExpr = ValueExpr.withValue(superObjectValue); + Variable subVariable = Variable.builder().setName("name").setType(TypeNode.STRING).build(); + VariableExpr superVariableExpr = + VariableExpr.builder() + .setVariable(subVariable) + .setExprReferenceExpr(superValueExpr) + .build(); + + MethodInvocationExpr methodExpr = + MethodInvocationExpr.builder() + .setMethodName("getName") + .setExprReferenceExpr(ValueExpr.withValue(superObjectValue)) + .setReturnType(TypeNode.STRING) + .build(); + AssignmentExpr assignmentExpr = + AssignmentExpr.builder() + .setVariableExpr(superVariableExpr) + .setValueExpr(methodExpr) + .build(); + + assignmentExpr.accept(writerVisitor); + assertThat(writerVisitor.write()).isEqualTo("super.name = super.getName()"); + } + private static String createLines(int numLines) { return new String(new char[numLines]).replace("\0", "%s"); } From a5b9837d5e93f81a2719504e6d89a7982006bf8b Mon Sep 17 00:00:00 2001 From: summerji Date: Thu, 30 Jul 2020 20:36:31 -0700 Subject: [PATCH 7/8] correct test name and add overwrite --- .../com/google/api/generator/engine/ast/SuperObjectValue.java | 1 + .../google/api/generator/engine/ast/SuperObjectValueTest.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java index 50cd6d7637..9500dbb88c 100644 --- a/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java +++ b/src/main/java/com/google/api/generator/engine/ast/SuperObjectValue.java @@ -21,6 +21,7 @@ public abstract class SuperObjectValue implements ObjectValue { private static final String SUPER_VALUE = "super"; + @Override public abstract TypeNode type(); @Override diff --git a/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java index 3abeacc96f..3c7a264ca9 100644 --- a/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java +++ b/src/test/java/com/google/api/generator/engine/ast/SuperObjectValueTest.java @@ -33,7 +33,7 @@ public void validSuperObjectValue_basic() { } @Test - public void invalidSuperObjectValue_NonReferenceType() { + public void invalidSuperObjectValue_nonReferenceType() { assertThrows( IllegalStateException.class, () -> { @@ -42,7 +42,7 @@ public void invalidSuperObjectValue_NonReferenceType() { } @Test - public void invalidSuperObjectValue_NullType() { + public void invalidSuperObjectValue_nullType() { assertThrows( IllegalStateException.class, () -> { From 21c98a198cb554ddd836265c105438dc49ac144a Mon Sep 17 00:00:00 2001 From: summerji Date: Sat, 1 Aug 2020 01:40:43 -0700 Subject: [PATCH 8/8] Add import write test --- .../writer/ImportWriterVisitorTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java index 52bfefb6d1..96e24e8577 100644 --- a/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java @@ -33,8 +33,10 @@ import com.google.api.generator.engine.ast.NewObjectExpr; import com.google.api.generator.engine.ast.Reference; import com.google.api.generator.engine.ast.ScopeNode; +import com.google.api.generator.engine.ast.SuperObjectValue; import com.google.api.generator.engine.ast.ThrowExpr; import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.engine.ast.ValueExpr; import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.engine.ast.Variable; import com.google.api.generator.engine.ast.VariableExpr; @@ -635,6 +637,25 @@ public void writeMethodDefinitionImports_templatedMixedNamesAndTypes() { "import java.util.Map;\n\n")); } + @Test + public void writeSuperObjectValueImports() { + VaporReference ref = + VaporReference.builder() + .setName("Student") + .setPakkage("com.google.example.examples.v1") + .build(); + TypeNode typeNode = TypeNode.withReference(ref); + SuperObjectValue superObjectValue = SuperObjectValue.withType(typeNode); + MethodInvocationExpr methodExpr = + MethodInvocationExpr.builder() + .setMethodName("getName") + .setExprReferenceExpr(ValueExpr.withValue(superObjectValue)) + .setReturnType(TypeNode.STRING) + .build(); + methodExpr.accept(writerVisitor); + assertEquals(writerVisitor.write(), "import com.google.example.examples.v1.Student;\n\n"); + } + private static TypeNode createType(Class clazz) { return TypeNode.withReference(ConcreteReference.withClazz(clazz)); }