From c0b56462a1550c3720a3e5d0b11dcc1a21add7ef Mon Sep 17 00:00:00 2001 From: Diego Marquez Date: Mon, 10 Jun 2024 17:22:54 -0400 Subject: [PATCH] feat: gapic-generator-java to perform a no-op when no services are detected (#2460) Fixes https://github.com/googleapis/sdk-platform-java/issues/2050 Adds behavior to gracefully perform a NOOP if no services are contained in the generation request. ## Confimation in hermetic build scripts From `generate_library.sh` against `google/cloud/alloydb/connectors/v1` ``` + /usr/local/google/home/diegomarquezp/Desktop/sdk2/sdk3/sdk-platform-java/library_generation/output/protobuf-25.2/bin/protoc --experimental_allow_proto3_optional --plugin=protoc-gen-java_gapic=/usr/local/google/home/diegomarquezp/.pyenv/versions/3.11.0/lib/python3.11/site-packages/library_generation/gapic-generator-java-wrapper --java_gapic_out=metadata:/usr/local/google/home/diegomarquezp/Desktop/sdk2/sdk3/sdk-platform-java/library_generation/output/temp_preprocessed/java_gapic_srcjar_raw.srcjar.zip --java_gapic_opt=transport=grpc,rest-numeric-enums,grpc-service-config=,gapic-config=,api-service-config=google/cloud/alloydb/connectors/v1/connectors_v1.yaml google/cloud/alloydb/connectors/v1/resources.proto google/cloud/common_resources.proto Apr 05, 2024 9:33:22 PM com.google.api.generator.gapic.protoparser.Parser parse WARNING: No services found to generate. This will produce an empty zip file Apr 05, 2024 9:33:22 PM com.google.api.generator.gapic.composer.ClientLibraryPackageInfoComposer generatePackageInfo WARNING: Generating empty package info since no services were found + did_generate_gapic=true + zipinfo -t /usr/local/google/home/diegomarquezp/Desktop/sdk2/sdk3/sdk-platform-java/library_generation/output/temp_preprocessed/java_gapic_srcjar_raw.srcjar.zip Empty zipfile. + did_generate_gapic=false + [[ false == \t\r\u\e ]] ``` I made some changes to library_generation but I moved them to a follow up PR (https://github.com/googleapis/sdk-platform-java/pull/2599) so the checks can pass here. --- .../java/com/google/api/generator/Main.java | 6 +- .../ClientLibraryPackageInfoComposer.java | 11 ++- .../generator/gapic/composer/Composer.java | 3 + .../generator/gapic/model/GapicContext.java | 15 ++++ .../generator/gapic/protoparser/Parser.java | 25 ++++--- .../generator/gapic/protowriter/Writer.java | 52 ++++++++----- .../ClientLibraryPackageInfoComposerTest.java | 7 ++ .../gapic/composer/ComposerTest.java | 48 ++++++++---- .../gapic/protoparser/ParserTest.java | 23 ++++-- .../gapic/protowriter/WriterTest.java | 75 +++++++++++++++++-- 10 files changed, 206 insertions(+), 59 deletions(-) diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/Main.java b/gapic-generator-java/src/main/java/com/google/api/generator/Main.java index 2ad75c19ba..027eedaf81 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/Main.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/Main.java @@ -14,6 +14,8 @@ package com.google.api.generator; +import static com.google.api.generator.gapic.protowriter.Writer.EMPTY_RESPONSE; + import com.google.api.generator.gapic.Generator; import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; @@ -26,6 +28,8 @@ public static void main(String[] args) throws IOException { ProtoRegistry.registerAllExtensions(registry); CodeGeneratorRequest request = CodeGeneratorRequest.parseFrom(System.in, registry); CodeGeneratorResponse response = Generator.generateGapic(request); - response.writeTo(System.out); + if (response != EMPTY_RESPONSE) { + response.writeTo(System.out); + } } } diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposer.java index cb794b4230..5bd24bdbd0 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposer.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposer.java @@ -29,11 +29,15 @@ import com.google.api.generator.gapic.model.GapicPackageInfo; import com.google.api.generator.gapic.model.Sample; import com.google.api.generator.gapic.model.Service; -import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import java.util.logging.Logger; import javax.annotation.Generated; public class ClientLibraryPackageInfoComposer { + + private static final Logger LOGGER = + Logger.getLogger(ClientLibraryPackageInfoComposer.class.getName()); + private static final String DIVIDER = "======================="; private static final String PACKAGE_INFO_DESCRIPTION = @@ -44,7 +48,10 @@ public class ClientLibraryPackageInfoComposer { private static final String SERVICE_DESCRIPTION_HEADER_PATTERN = "Service Description: %s"; public static GapicPackageInfo generatePackageInfo(GapicContext context) { - Preconditions.checkState(!context.services().isEmpty(), "No services found to generate"); + if (!context.containsServices()) { + LOGGER.warning("Generating empty package info since no services were found"); + return null; + } // Pick some service's package, as we assume they are all the same. String libraryPakkage = context.services().get(0).pakkage(); diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java index f2ffa56016..51da8f919a 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/Composer.java @@ -53,6 +53,9 @@ public static List composeServiceClasses(GapicContext context) { } public static GapicPackageInfo composePackageInfo(GapicContext context) { + if (!context.containsServices()) { + return null; + } return addApacheLicense(ClientLibraryPackageInfoComposer.generatePackageInfo(context)); } diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/GapicContext.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/GapicContext.java index 8fdba8d3ee..780890c664 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/GapicContext.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/model/GapicContext.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; @@ -32,6 +33,16 @@ public abstract class GapicContext { // it iteratively as we generate client methods. private GapicMetadata gapicMetadata = defaultGapicMetadata(); + public static final GapicContext EMPTY = + builder() + .setServices(Collections.emptyList()) + .setMessages(Collections.emptyMap()) + .setServiceConfig(GapicServiceConfig.create(Optional.empty())) + .setResourceNames(Collections.emptyMap()) + .setHelperResourceNames(Collections.emptySet()) + .setTransport(Transport.GRPC) + .build(); + // Maps the message name (as it appears in the protobuf) to Messages. public abstract ImmutableMap messages(); @@ -59,6 +70,10 @@ public GapicMetadata gapicMetadata() { @Nullable public abstract com.google.api.Service serviceYamlProto(); + public boolean containsServices() { + return !services().isEmpty(); + } + public boolean hasServiceYamlProto() { return serviceYamlProto() != null; } diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java index 747faa25c3..e7c6bd8967 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java @@ -79,14 +79,18 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.IntStream; public class Parser { + + private static final Logger LOGGER = Logger.getLogger(Parser.class.getName()); private static final String COMMA = ","; private static final String COLON = ":"; private static final String DEFAULT_PORT = "443"; @@ -175,7 +179,10 @@ public static GapicContext parse(CodeGeneratorRequest request) { mixinServices, transport); - Preconditions.checkState(!services.isEmpty(), "No services found to generate"); + if (services.isEmpty()) { + LOGGER.warning("No services found to generate. This will cause a no-op (no files generated)"); + return GapicContext.EMPTY; + } // TODO(vam-google): Figure out whether we should keep this allowlist or bring // back the unused resource names for all APIs. @@ -1102,7 +1109,8 @@ private static Map getFilesToGenerate(CodeGeneratorReque return fileDescriptors; } - private static String parseServiceJavaPackage(CodeGeneratorRequest request) { + @VisibleForTesting + static String parseServiceJavaPackage(CodeGeneratorRequest request) { Map javaPackageCount = new HashMap<>(); Map fileDescriptors = getFilesToGenerate(request); for (String fileToGenerate : request.getFileToGenerateList()) { @@ -1135,13 +1143,12 @@ private static String parseServiceJavaPackage(CodeGeneratorRequest request) { processedJavaPackageCount = javaPackageCount; } - String finalJavaPackage = - processedJavaPackageCount.entrySet().stream() - .max(Map.Entry.comparingByValue()) - .get() - .getKey(); - Preconditions.checkState( - !Strings.isNullOrEmpty(finalJavaPackage), "No service Java package found"); + String finalJavaPackage = ""; + Optional> finalPackageEntry = + processedJavaPackageCount.entrySet().stream().max(Map.Entry.comparingByValue()); + if (finalPackageEntry.isPresent()) { + finalJavaPackage = finalPackageEntry.get().getKey(); + } return finalJavaPackage; } diff --git a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java index f0390ff6ea..79c9cbf349 100644 --- a/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java +++ b/gapic-generator-java/src/main/java/com/google/api/generator/gapic/protowriter/Writer.java @@ -36,29 +36,29 @@ import java.util.jar.JarOutputStream; public class Writer { - static class GapicWriterException extends RuntimeException { - public GapicWriterException(String errorMessage) { - super(errorMessage); - } + static class GapicWriterException extends RuntimeException { public GapicWriterException(String errorMessage, Throwable cause) { super(errorMessage, cause); } } - public static CodeGeneratorResponse write( + public static final CodeGeneratorResponse EMPTY_RESPONSE = null; + + @VisibleForTesting + protected static CodeGeneratorResponse write( GapicContext context, List clazzes, GapicPackageInfo gapicPackageInfo, List reflectConfigInfo, - String outputFilePath) { - ByteString.Output output = ByteString.newOutput(); + String outputFilePath, + JarOutputStream jos, + ByteString.Output output) + throws IOException { JavaWriterVisitor codeWriter = new JavaWriterVisitor(); - JarOutputStream jos; - try { - jos = new JarOutputStream(output); - } catch (IOException e) { - throw new GapicWriterException(e.getMessage(), e); + + if (!context.containsServices()) { + return EMPTY_RESPONSE; } for (GapicClass gapicClazz : clazzes) { @@ -72,12 +72,8 @@ public static CodeGeneratorResponse write( writeMetadataFile(context, writePackageInfo(gapicPackageInfo, codeWriter, jos), jos); writeReflectConfigFile(gapicPackageInfo.packageInfo().pakkage(), reflectConfigInfo, jos); - try { - jos.finish(); - jos.flush(); - } catch (IOException e) { - throw new GapicWriterException(e.getMessage(), e); - } + jos.finish(); + jos.flush(); CodeGeneratorResponse.Builder response = CodeGeneratorResponse.newBuilder(); response @@ -88,6 +84,23 @@ public static CodeGeneratorResponse write( return response.build(); } + public static CodeGeneratorResponse write( + GapicContext context, + List clazzes, + GapicPackageInfo gapicPackageInfo, + List reflectConfigInfo, + String outputFilePath) { + ByteString.Output output = ByteString.newOutput(); + CodeGeneratorResponse response; + try (JarOutputStream jos = new JarOutputStream(output)) { + response = + write(context, clazzes, gapicPackageInfo, reflectConfigInfo, outputFilePath, jos, output); + } catch (IOException e) { + throw new GapicWriterException(e.getMessage(), e); + } + return response; + } + @VisibleForTesting static void writeReflectConfigFile( String pakkage, List reflectConfigInfo, JarOutputStream jos) { @@ -167,7 +180,8 @@ private static void writeSamples( } } - private static String writePackageInfo( + @VisibleForTesting + static String writePackageInfo( GapicPackageInfo gapicPackageInfo, JavaWriterVisitor codeWriter, JarOutputStream jos) { PackageInfoDefinition packageInfo = gapicPackageInfo.packageInfo(); packageInfo.accept(codeWriter); diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposerTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposerTest.java index df828d5119..5110e3efaf 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposerTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ClientLibraryPackageInfoComposerTest.java @@ -14,6 +14,8 @@ package com.google.api.generator.gapic.composer; +import static org.junit.jupiter.api.Assertions.assertNull; + import com.google.api.generator.engine.writer.JavaWriterVisitor; import com.google.api.generator.gapic.model.GapicContext; import com.google.api.generator.gapic.model.GapicPackageInfo; @@ -39,4 +41,9 @@ void composePackageInfo_showcase() { GoldenFileWriter.getGoldenDir(this.getClass()), "ShowcaseWithEchoPackageInfo.golden"); Assert.assertCodeEquals(goldenFilePath, visitor.write()); } + + @Test + void testGeneratePackageInfo_noServices_returnsNullPackageInfo() { + assertNull(ClientLibraryPackageInfoComposer.generatePackageInfo(GapicContext.EMPTY)); + } } diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java index 1d2053944f..ad370307c1 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java @@ -14,8 +14,10 @@ package com.google.api.generator.gapic.composer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.api.generator.engine.ast.ClassDefinition; import com.google.api.generator.engine.ast.ScopeNode; @@ -35,6 +37,7 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.List; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ComposerTest { @@ -53,8 +56,13 @@ class ComposerTest { .build(); private List ListofSamples = Arrays.asList(new Sample[] {sample}); + @BeforeEach + void initialSanityCheck() { + assertTrue(context.containsServices()); + } + @Test - void gapicClass_addApacheLicense() { + public void gapicClass_addApacheLicense_validInput_succeeds() { ClassDefinition classDef = ClassDefinition.builder() .setPackageString("com.google.showcase.v1beta1.stub") @@ -84,14 +92,14 @@ void composeSamples_showcase() { assertFalse(composedSamples.isEmpty()); for (Sample sample : composedSamples) { assertEquals( - "File header should be APACHE", Arrays.asList(CommentComposer.APACHE_LICENSE_COMMENT), - sample.fileHeader()); + sample.fileHeader(), + "File header should be APACHE"); assertEquals( - "ApiShortName should be Localhost7469", "Localhost7469", - sample.regionTag().apiShortName()); - assertEquals("ApiVersion should be V1Beta1", "V1Beta1", sample.regionTag().apiVersion()); + sample.regionTag().apiShortName(), + "ApiShortName should be Localhost7469"); + assertEquals("V1Beta1", sample.regionTag().apiVersion(), "ApiVersion should be V1Beta1"); } } @@ -120,10 +128,10 @@ void composeSamples_parseProtoPackage() { for (Sample sample : composedSamples) { assertEquals( - "ApiShortName should be Accessapproval", + "Accessapproval", sample.regionTag().apiShortName(), - "Accessapproval"); - assertEquals("ApiVersion should be V1", sample.regionTag().apiVersion(), "V1"); + "ApiShortName should be Accessapproval"); + assertEquals("V1", sample.regionTag().apiVersion(), "ApiVersion should be V1"); } protoPack = "google.cloud.vision.v1p1beta1"; @@ -136,8 +144,8 @@ void composeSamples_parseProtoPackage() { assertFalse(composedSamples.isEmpty()); for (Sample sample : composedSamples) { - assertEquals("ApiShortName should be Vision", sample.regionTag().apiShortName(), "Vision"); - assertEquals("ApiVersion should be V1P1Beta1", sample.regionTag().apiVersion(), "V1P1Beta1"); + assertEquals("Vision", sample.regionTag().apiShortName(), "ApiShortName should be Vision"); + assertEquals("V1P1Beta1", sample.regionTag().apiVersion(), "ApiVersion should be V1P1Beta1"); } protoPack = "google.cloud.vision"; @@ -149,11 +157,21 @@ void composeSamples_parseProtoPackage() { assertFalse(composedSamples.isEmpty()); for (Sample sample : composedSamples) { - assertEquals("ApiShortName should be Vision", sample.regionTag().apiShortName(), "Vision"); - assertEquals("ApiVersion should be empty", sample.regionTag().apiVersion(), ""); + assertEquals("Vision", sample.regionTag().apiShortName(), "ApiShortName should be Vision"); + assertTrue(sample.regionTag().apiVersion().isEmpty(), "ApiVersion should be empty"); } } + @Test + void testEmptyGapicContext_doesNotThrow() { + assertTrue(Composer.composeServiceClasses(GapicContext.EMPTY).isEmpty()); + } + + @Test + void testComposePackageInfo_emptyGapicContext_returnsNull() { + assertNull(Composer.composePackageInfo(GapicContext.EMPTY)); + } + private List getTestClassListFromService(Service testService) { GapicClass testClass = GrpcServiceCallableFactoryClassComposer.instance() diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java index 66f2bf49a9..93c4eb3599 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java @@ -15,11 +15,11 @@ package com.google.api.generator.gapic.protoparser; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.api.FieldInfo.Format; import com.google.api.MethodSettings; @@ -30,6 +30,7 @@ import com.google.api.generator.engine.ast.TypeNode; import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.gapic.model.Field; +import com.google.api.generator.gapic.model.GapicContext; import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; import com.google.api.generator.gapic.model.MethodArgument; @@ -44,6 +45,7 @@ import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.MethodDescriptor; import com.google.protobuf.Descriptors.ServiceDescriptor; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; import com.google.showcase.v1beta1.EchoOuterClass; import com.google.showcase.v1beta1.TestingOuterClass; import com.google.testgapic.v1beta1.LockerProto; @@ -609,6 +611,17 @@ void parseNestedProtoTypeName() { "google.ads.googleads.v3.resources.MutateJob.MutateJobMetadata")); } + @Test + void testParse_noServices_returnsEmptyGapicContext() { + GapicContext result = Parser.parse(CodeGeneratorRequest.newBuilder().build()); + assertEquals(GapicContext.EMPTY, result); + } + + @Test + void testParseServiceJavaPackage_emptyRequest_noop() { + assertThat(Parser.parseServiceJavaPackage(CodeGeneratorRequest.newBuilder().build())).isEmpty(); + } + @Test void parseServiceApiVersionTest() { FileDescriptor apiVersionFileDescriptor = ApiVersionTestingOuterClass.getDescriptor(); diff --git a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protowriter/WriterTest.java b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protowriter/WriterTest.java index 04d0dfa7de..c366d2085e 100644 --- a/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protowriter/WriterTest.java +++ b/gapic-generator-java/src/test/java/com/google/api/generator/gapic/protowriter/WriterTest.java @@ -1,12 +1,21 @@ package com.google.api.generator.gapic.protowriter; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import com.google.api.generator.engine.ast.PackageInfoDefinition; +import com.google.api.generator.gapic.model.GapicClass; +import com.google.api.generator.gapic.model.GapicContext; +import com.google.api.generator.gapic.model.GapicPackageInfo; import com.google.api.generator.gapic.model.ReflectConfig; +import com.google.common.collect.ImmutableList; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; +import com.google.protobuf.ByteString; +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; @@ -39,6 +48,12 @@ void createJarOutputStream() throws IOException { file = path.toFile(); } + private void closeJarOutputStream() throws IOException { + jarOutputStream.finish(); + jarOutputStream.flush(); + jarOutputStream.close(); + } + @AfterEach void assertJarOutputStream_isClosed() { assertThrows( @@ -49,9 +64,7 @@ void assertJarOutputStream_isClosed() { void reflectConfig_notWritten_ifEmptyInput() throws IOException { Writer.writeReflectConfigFile("com.google", Collections.emptyList(), jarOutputStream); - jarOutputStream.finish(); - jarOutputStream.flush(); - jarOutputStream.close(); + closeJarOutputStream(); try (JarFile jarFile = new JarFile(file)) { assertThat(jarFile.entries().hasMoreElements()).isFalse(); @@ -65,9 +78,7 @@ void reflectConfig_isWritten() throws IOException { Collections.singletonList(new ReflectConfig("com.google.Class")), jarOutputStream); - jarOutputStream.finish(); - jarOutputStream.flush(); - jarOutputStream.close(); + closeJarOutputStream(); try (JarFile jarFile = new JarFile(file)) { Enumeration entries = jarFile.entries(); @@ -85,4 +96,52 @@ void reflectConfig_isWritten() throws IOException { } } } + + @Test + void write_emptyGapicContext_writesNoBytes() throws IOException { + ByteString.Output output = ByteString.newOutput(); + CodeGeneratorResponse response = + Writer.write( + GapicContext.EMPTY, + Collections.emptyList(), + null, + Collections.emptyList(), + "temp-codegen.srcjar", + jarOutputStream, + output); + assertTrue(output.size() == 0); + closeJarOutputStream(); + } + + @Test + void write_emptyGapicContextAndFilledPackageInfo_succeeds() throws IOException { + ByteString.Output output = ByteString.newOutput(); + CodeGeneratorResponse response = + Writer.write( + GapicContext.EMPTY, + ImmutableList.of(GapicClass.createNonGeneratedGapicClass()), + GapicPackageInfo.with(PackageInfoDefinition.builder().setPakkage("com.test").build()), + Collections.emptyList(), + "temp-codegen.srcjar", + jarOutputStream, + output); + assertTrue(output.size() == 0); + closeJarOutputStream(); + } + + @Test + void productionWrite_emptyGapicContext_succeeds() throws IOException { + // This is a special case test to confirm the production function works as expected. + // We don't need the output stream + jarOutputStream.close(); + + CodeGeneratorResponse result = + Writer.write( + GapicContext.EMPTY, + ImmutableList.of(GapicClass.createNonGeneratedGapicClass()), + GapicPackageInfo.with(PackageInfoDefinition.builder().setPakkage("com.test").build()), + Collections.emptyList(), + "temp-codegen.srcjar"); + assertNull(result); + } }