From 6ca20ea95e28e25b8c4b47f4992fa9de024b31a9 Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Tue, 6 Feb 2024 17:04:21 +0000 Subject: Add option to only compile certain Proto messages Used to save space in the framework.jar which isn't r8 optimized to remove unused constants. Bug: 321668269 Test: abtd running v2/android-crystalball-eng/health/microbench/bootup/idle/aosp/system-processes-memory and checking pinner_system_/system/framework/framework.jar_bytes metric value Change-Id: I32d16f80364e3446799498318e40844bc8adfc5a --- tools/streaming_proto/Android.bp | 56 +++- .../java/java_proto_stream_code_generator.cpp | 339 +++++++++++++++++++++ .../java/java_proto_stream_code_generator.h | 29 ++ tools/streaming_proto/java/main.cpp | 278 +---------------- tools/streaming_proto/test/imported.proto | 26 -- .../test/integration/imported.proto | 26 ++ .../src/com/android/streaming_proto_test/Main.java | 23 ++ tools/streaming_proto/test/integration/test.proto | 124 ++++++++ .../src/com/android/streaming_proto_test/Main.java | 7 - tools/streaming_proto/test/test.proto | 124 -------- .../test/unit/streaming_proto_java.cpp | 191 ++++++++++++ 11 files changed, 794 insertions(+), 429 deletions(-) create mode 100644 tools/streaming_proto/java/java_proto_stream_code_generator.cpp create mode 100644 tools/streaming_proto/java/java_proto_stream_code_generator.h delete mode 100644 tools/streaming_proto/test/imported.proto create mode 100644 tools/streaming_proto/test/integration/imported.proto create mode 100644 tools/streaming_proto/test/integration/src/com/android/streaming_proto_test/Main.java create mode 100644 tools/streaming_proto/test/integration/test.proto delete mode 100644 tools/streaming_proto/test/src/com/android/streaming_proto_test/Main.java delete mode 100644 tools/streaming_proto/test/test.proto create mode 100644 tools/streaming_proto/test/unit/streaming_proto_java.cpp diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp index b18bdff7263f..b1b314fcdb19 100644 --- a/tools/streaming_proto/Android.bp +++ b/tools/streaming_proto/Android.bp @@ -17,6 +17,7 @@ // ========================================================== // Build the host executable: protoc-gen-javastream // ========================================================== + package { // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import @@ -41,6 +42,32 @@ cc_defaults { static_libs: ["libprotoc"], } +// ========================================================== +// Build the host static library: java_streaming_proto_lib +// ========================================================== + +cc_library_host_static { + name: "java_streaming_proto_lib", + defaults: ["protoc-gen-stream-defaults"], + target: { + darwin: { + cflags: ["-D_DARWIN_UNLIMITED_STREAMS"], + }, + }, + cflags: [ + "-Wno-format-y2k", + "-DSTATIC_ANDROIDFW_FOR_TOOLS", + ], + + srcs: [ + "java/java_proto_stream_code_generator.cpp", + ], +} + +// ========================================================== +// Build the host executable: protoc-gen-javastream +// ========================================================== + cc_binary_host { name: "protoc-gen-javastream", srcs: [ @@ -48,8 +75,13 @@ cc_binary_host { ], defaults: ["protoc-gen-stream-defaults"], + static_libs: ["java_streaming_proto_lib"], } +// ========================================================== +// Build the host executable: protoc-gen-cppstream +// ========================================================== + cc_binary_host { name: "protoc-gen-cppstream", srcs: [ @@ -59,14 +91,32 @@ cc_binary_host { defaults: ["protoc-gen-stream-defaults"], } +// ========================================================== +// Build the host tests: StreamingProtoTest +// ========================================================== + +cc_test_host { + name: "StreamingProtoTest", + defaults: ["protoc-gen-stream-defaults"], + srcs: [ + "test/unit/**/*.cpp", + ], + static_libs: [ + "java_streaming_proto_lib", + "libgmock", + "libgtest", + ], +} + // ========================================================== // Build the java test // ========================================================== + java_library { - name: "StreamingProtoTest", + name: "StreamingProtoJavaIntegrationTest", srcs: [ - "test/**/*.java", - "test/**/*.proto", + "test/integration/**/*.java", + "test/integration/**/*.proto", ], proto: { type: "stream", diff --git a/tools/streaming_proto/java/java_proto_stream_code_generator.cpp b/tools/streaming_proto/java/java_proto_stream_code_generator.cpp new file mode 100644 index 000000000000..9d61111fb5bd --- /dev/null +++ b/tools/streaming_proto/java/java_proto_stream_code_generator.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * 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. + */ + +#include "java_proto_stream_code_generator.h" + +#include + +#include +#include +#include +#include +#include + +#include "Errors.h" + +using namespace android::stream_proto; +using namespace google::protobuf::io; +using namespace std; + +/** + * If the descriptor gives us a class name, use that. Otherwise make one up from + * the filename of the .proto file. + */ +static string make_outer_class_name(const FileDescriptorProto& file_descriptor) { + string name = file_descriptor.options().java_outer_classname(); + if (name.size() == 0) { + name = to_camel_case(file_base_name(file_descriptor.name())); + if (name.size() == 0) { + ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE, + "Unable to make an outer class name for file: %s", + file_descriptor.name().c_str()); + name = "Unknown"; + } + } + return name; +} + +/** + * Figure out the package name that we are generating. + */ +static string make_java_package(const FileDescriptorProto& file_descriptor) { + if (file_descriptor.options().has_java_package()) { + return file_descriptor.options().java_package(); + } else { + return file_descriptor.package(); + } +} + +/** + * Figure out the name of the file we are generating. + */ +static string make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name) { + string const package = make_java_package(file_descriptor); + string result; + if (package.size() > 0) { + result = replace_string(package, '.', '/'); + result += '/'; + } + + result += class_name; + result += ".java"; + + return result; +} + +static string indent_more(const string& indent) { + return indent + INDENT; +} + +/** + * Write the constants for an enum. + */ +static void write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent) { + const int N = enu.value_size(); + text << indent << "// enum " << enu.name() << endl; + for (int i = 0; i < N; i++) { + const EnumValueDescriptorProto& value = enu.value(i); + text << indent << "public static final int " << make_constant_name(value.name()) << " = " + << value.number() << ";" << endl; + } + text << endl; +} + +/** + * Write a field. + */ +static void write_field(stringstream& text, const FieldDescriptorProto& field, + const string& indent) { + string optional_comment = + field.label() == FieldDescriptorProto::LABEL_OPTIONAL ? "optional " : ""; + string repeated_comment = + field.label() == FieldDescriptorProto::LABEL_REPEATED ? "repeated " : ""; + string proto_type = get_proto_type(field); + string packed_comment = field.options().packed() ? " [packed=true]" : ""; + text << indent << "// " << optional_comment << repeated_comment << proto_type << ' ' + << field.name() << " = " << field.number() << packed_comment << ';' << endl; + + text << indent << "public static final long " << make_constant_name(field.name()) << " = 0x"; + + ios::fmtflags fmt(text.flags()); + text << setfill('0') << setw(16) << hex << get_field_id(field); + text.flags(fmt); + + text << "L;" << endl; + + text << endl; +} + +/** + * Write a Message constants class. + */ +static void write_message(stringstream& text, const DescriptorProto& message, + const string& indent) { + int N; + const string indented = indent_more(indent); + + text << indent << "// message " << message.name() << endl; + text << indent << "public final class " << message.name() << " {" << endl; + text << endl; + + // Enums + N = message.enum_type_size(); + for (int i = 0; i < N; i++) { + write_enum(text, message.enum_type(i), indented); + } + + // Nested classes + N = message.nested_type_size(); + for (int i = 0; i < N; i++) { + write_message(text, message.nested_type(i), indented); + } + + // Fields + N = message.field_size(); + for (int i = 0; i < N; i++) { + write_field(text, message.field(i), indented); + } + + text << indent << "}" << endl; + text << endl; +} + +/** + * Write the contents of a file. + * + * If there are enums and generate_outer is false, invalid java code will be generated. + */ +static void write_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor, + const string& filename, bool generate_outer, + const vector& enums, + const vector& messages) { + stringstream text; + + string const package_name = make_java_package(file_descriptor); + string const outer_class_name = make_outer_class_name(file_descriptor); + + text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl; + text << "// source: " << file_descriptor.name() << endl << endl; + + if (package_name.size() > 0) { + if (package_name.size() > 0) { + text << "package " << package_name << ";" << endl; + text << endl; + } + } + + // This bit of policy is android api rules specific: Raw proto classes + // must never be in the API + text << "/** @hide */" << endl; + // text << "@android.annotation.TestApi" << endl; + + if (generate_outer) { + text << "public final class " << outer_class_name << " {" << endl; + text << endl; + } + + size_t N; + const string indented = generate_outer ? indent_more("") : string(); + + N = enums.size(); + for (size_t i = 0; i < N; i++) { + write_enum(text, enums[i], indented); + } + + N = messages.size(); + for (size_t i = 0; i < N; i++) { + write_message(text, messages[i], indented); + } + + if (generate_outer) { + text << "}" << endl; + } + + CodeGeneratorResponse::File* file_response = response->add_file(); + file_response->set_name(filename); + file_response->set_content(text.str()); +} + +/** + * Write one file per class. Put all of the enums into the "outer" class. + */ +static void write_multiple_files(CodeGeneratorResponse* response, + const FileDescriptorProto& file_descriptor, + set messages_to_compile) { + // If there is anything to put in the outer class file, create one + if (file_descriptor.enum_type_size() > 0) { + vector enums; + int N = file_descriptor.enum_type_size(); + for (int i = 0; i < N; i++) { + auto enum_full_name = + file_descriptor.package() + "." + file_descriptor.enum_type(i).name(); + if (!messages_to_compile.empty() && !messages_to_compile.count(enum_full_name)) { + continue; + } + enums.push_back(file_descriptor.enum_type(i)); + } + + vector messages; + + if (messages_to_compile.empty() || !enums.empty()) { + write_file(response, file_descriptor, + make_file_name(file_descriptor, make_outer_class_name(file_descriptor)), + true, enums, messages); + } + } + + // For each of the message types, make a file + int N = file_descriptor.message_type_size(); + for (int i = 0; i < N; i++) { + vector enums; + + vector messages; + + auto message_full_name = + file_descriptor.package() + "." + file_descriptor.message_type(i).name(); + if (!messages_to_compile.empty() && !messages_to_compile.count(message_full_name)) { + continue; + } + messages.push_back(file_descriptor.message_type(i)); + + if (messages_to_compile.empty() || !messages.empty()) { + write_file(response, file_descriptor, + make_file_name(file_descriptor, file_descriptor.message_type(i).name()), + false, enums, messages); + } + } +} + +static void write_single_file(CodeGeneratorResponse* response, + const FileDescriptorProto& file_descriptor, + set messages_to_compile) { + int N; + + vector enums; + N = file_descriptor.enum_type_size(); + for (int i = 0; i < N; i++) { + auto enum_full_name = file_descriptor.package() + "." + file_descriptor.enum_type(i).name(); + if (!messages_to_compile.empty() && !messages_to_compile.count(enum_full_name)) { + continue; + } + + enums.push_back(file_descriptor.enum_type(i)); + } + + vector messages; + N = file_descriptor.message_type_size(); + for (int i = 0; i < N; i++) { + auto message_full_name = + file_descriptor.package() + "." + file_descriptor.message_type(i).name(); + + if (!messages_to_compile.empty() && !messages_to_compile.count(message_full_name)) { + continue; + } + + messages.push_back(file_descriptor.message_type(i)); + } + + if (messages_to_compile.empty() || !enums.empty() || !messages.empty()) { + write_file(response, file_descriptor, + make_file_name(file_descriptor, make_outer_class_name(file_descriptor)), true, + enums, messages); + } +} + +static void parse_args_string(stringstream args_string_stream, + set* messages_to_compile_out) { + string line; + while (getline(args_string_stream, line, ';')) { + stringstream line_ss(line); + string arg_name; + getline(line_ss, arg_name, ':'); + if (arg_name == "include_filter") { + string full_message_name; + while (getline(line_ss, full_message_name, ',')) { + messages_to_compile_out->insert(full_message_name); + } + } else { + ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE, "Unexpected argument '%s'.", arg_name.c_str()); + } + } +} + +CodeGeneratorResponse generate_java_protostream_code(CodeGeneratorRequest request) { + CodeGeneratorResponse response; + + set messages_to_compile; + auto request_params = request.parameter(); + if (!request_params.empty()) { + parse_args_string(stringstream(request_params), &messages_to_compile); + } + + // Build the files we need. + const int N = request.proto_file_size(); + for (int i = 0; i < N; i++) { + const FileDescriptorProto& file_descriptor = request.proto_file(i); + if (should_generate_for_file(request, file_descriptor.name())) { + if (file_descriptor.options().java_multiple_files()) { + write_multiple_files(&response, file_descriptor, messages_to_compile); + } else { + write_single_file(&response, file_descriptor, messages_to_compile); + } + } + } + + return response; +} diff --git a/tools/streaming_proto/java/java_proto_stream_code_generator.h b/tools/streaming_proto/java/java_proto_stream_code_generator.h new file mode 100644 index 000000000000..d2492f75d383 --- /dev/null +++ b/tools/streaming_proto/java/java_proto_stream_code_generator.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * 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. + */ + +#ifndef AOSP_MAIN_FRAMEWORKS_BASE_JAVAPROTOSTREAMCODEGENERATOR_H +#define AOSP_MAIN_FRAMEWORKS_BASE_JAVAPROTOSTREAMCODEGENERATOR_H + +#include "stream_proto_utils.h" +#include "string_utils.h" + +using namespace android::stream_proto; +using namespace google::protobuf::io; +using namespace std; + +CodeGeneratorResponse generate_java_protostream_code(CodeGeneratorRequest request); + +#endif // AOSP_MAIN_FRAMEWORKS_BASE_JAVAPROTOSTREAMCODEGENERATOR_H \ No newline at end of file diff --git a/tools/streaming_proto/java/main.cpp b/tools/streaming_proto/java/main.cpp index c9c50a561a04..5b35504865f8 100644 --- a/tools/streaming_proto/java/main.cpp +++ b/tools/streaming_proto/java/main.cpp @@ -1,268 +1,21 @@ -#include "Errors.h" -#include "stream_proto_utils.h" -#include "string_utils.h" - #include + #include #include -#include #include +#include +#include + +#include "Errors.h" +#include "java_proto_stream_code_generator.h" +#include "stream_proto_utils.h" using namespace android::stream_proto; using namespace google::protobuf::io; using namespace std; /** - * If the descriptor gives us a class name, use that. Otherwise make one up from - * the filename of the .proto file. - */ -static string -make_outer_class_name(const FileDescriptorProto& file_descriptor) -{ - string name = file_descriptor.options().java_outer_classname(); - if (name.size() == 0) { - name = to_camel_case(file_base_name(file_descriptor.name())); - if (name.size() == 0) { - ERRORS.Add(UNKNOWN_FILE, UNKNOWN_LINE, - "Unable to make an outer class name for file: %s", - file_descriptor.name().c_str()); - name = "Unknown"; - } - } - return name; -} - -/** - * Figure out the package name that we are generating. - */ -static string -make_java_package(const FileDescriptorProto& file_descriptor) { - if (file_descriptor.options().has_java_package()) { - return file_descriptor.options().java_package(); - } else { - return file_descriptor.package(); - } -} - -/** - * Figure out the name of the file we are generating. - */ -static string -make_file_name(const FileDescriptorProto& file_descriptor, const string& class_name) -{ - string const package = make_java_package(file_descriptor); - string result; - if (package.size() > 0) { - result = replace_string(package, '.', '/'); - result += '/'; - } - - result += class_name; - result += ".java"; - - return result; -} - -static string -indent_more(const string& indent) -{ - return indent + INDENT; -} - -/** - * Write the constants for an enum. - */ -static void -write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent) -{ - const int N = enu.value_size(); - text << indent << "// enum " << enu.name() << endl; - for (int i=0; i& enums, const vector& messages) -{ - stringstream text; - - string const package_name = make_java_package(file_descriptor); - string const outer_class_name = make_outer_class_name(file_descriptor); - - text << "// Generated by protoc-gen-javastream. DO NOT MODIFY." << endl; - text << "// source: " << file_descriptor.name() << endl << endl; - - if (package_name.size() > 0) { - if (package_name.size() > 0) { - text << "package " << package_name << ";" << endl; - text << endl; - } - } - - // This bit of policy is android api rules specific: Raw proto classes - // must never be in the API - text << "/** @hide */" << endl; -// text << "@android.annotation.TestApi" << endl; - - if (generate_outer) { - text << "public final class " << outer_class_name << " {" << endl; - text << endl; - } - - size_t N; - const string indented = generate_outer ? indent_more("") : string(); - - N = enums.size(); - for (size_t i=0; iadd_file(); - file_response->set_name(filename); - file_response->set_content(text.str()); -} - -/** - * Write one file per class. Put all of the enums into the "outer" class. - */ -static void -write_multiple_files(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor) -{ - // If there is anything to put in the outer class file, create one - if (file_descriptor.enum_type_size() > 0) { - vector enums; - int N = file_descriptor.enum_type_size(); - for (int i=0; i messages; - - write_file(response, file_descriptor, - make_file_name(file_descriptor, make_outer_class_name(file_descriptor)), - true, enums, messages); - } - - // For each of the message types, make a file - int N = file_descriptor.message_type_size(); - for (int i=0; i enums; - - vector messages; - messages.push_back(file_descriptor.message_type(i)); - - write_file(response, file_descriptor, - make_file_name(file_descriptor, file_descriptor.message_type(i).name()), - false, enums, messages); - } -} - -static void -write_single_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor) -{ - int N; - - vector enums; - N = file_descriptor.enum_type_size(); - for (int i=0; i messages; - N = file_descriptor.message_type_size(); - for (int i=0; i +#include + +#include "java/java_proto_stream_code_generator.h" + +using ::testing::HasSubstr; +using ::testing::Not; + +static void add_my_test_proto_file(CodeGeneratorRequest* request) { + request->add_file_to_generate("MyTestProtoFile"); + + FileDescriptorProto* file_desc = request->add_proto_file(); + file_desc->set_name("MyTestProtoFile"); + file_desc->set_package("test.package"); + + auto* file_options = file_desc->mutable_options(); + file_options->set_java_multiple_files(false); + + auto* message = file_desc->add_message_type(); + message->set_name("MyTestMessage"); + + auto* field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("my_test_field"); + + field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("my_other_test_field"); + + field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("my_other_test_message"); +} + +static void add_my_other_test_proto_file(CodeGeneratorRequest* request) { + request->add_file_to_generate("MyOtherTestProtoFile"); + + FileDescriptorProto* file_desc = request->add_proto_file(); + file_desc->set_name("MyOtherTestProtoFile"); + file_desc->set_package("test.package"); + + auto* file_options = file_desc->mutable_options(); + file_options->set_java_multiple_files(false); + + auto* message = file_desc->add_message_type(); + message->set_name("MyOtherTestMessage"); + + auto* field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("a_test_field"); + + field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("another_test_field"); +} + +static CodeGeneratorRequest create_simple_two_file_request() { + CodeGeneratorRequest request; + + add_my_test_proto_file(&request); + add_my_other_test_proto_file(&request); + + return request; +} + +static CodeGeneratorRequest create_simple_multi_file_request() { + CodeGeneratorRequest request; + + request.add_file_to_generate("MyMultiMessageTestProtoFile"); + + FileDescriptorProto* file_desc = request.add_proto_file(); + file_desc->set_name("MyMultiMessageTestProtoFile"); + file_desc->set_package("test.package"); + + auto* file_options = file_desc->mutable_options(); + file_options->set_java_multiple_files(true); + + auto* message = file_desc->add_message_type(); + message->set_name("MyTestMessage"); + + auto* field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("my_test_field"); + + field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("my_other_test_field"); + + field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("my_other_test_message"); + + message = file_desc->add_message_type(); + message->set_name("MyOtherTestMessage"); + + field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("a_test_field"); + + field = message->add_field(); + field->set_label(FieldDescriptorProto::LABEL_OPTIONAL); + field->set_name("another_test_field"); + + return request; +} + +TEST(StreamingProtoJavaTest, NoFilter) { + CodeGeneratorRequest request = create_simple_two_file_request(); + CodeGeneratorResponse response = generate_java_protostream_code(request); + + auto generated_file_count = response.file_size(); + EXPECT_EQ(generated_file_count, 2); + + EXPECT_EQ(response.file(0).name(), "test/package/MyTestProtoFile.java"); + EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestProtoFile")); + EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD")); + + EXPECT_EQ(response.file(1).name(), "test/package/MyOtherTestProtoFile.java"); + EXPECT_THAT(response.file(1).content(), HasSubstr("class MyOtherTestProtoFile")); + EXPECT_THAT(response.file(1).content(), HasSubstr("class MyOtherTestMessage")); + EXPECT_THAT(response.file(1).content(), HasSubstr("long A_TEST_FIELD")); + EXPECT_THAT(response.file(1).content(), HasSubstr("long ANOTHER_TEST_FIELD")); +} + +TEST(StreamingProtoJavaTest, WithFilter) { + CodeGeneratorRequest request = create_simple_two_file_request(); + request.set_parameter("include_filter:test.package.MyTestMessage"); + CodeGeneratorResponse response = generate_java_protostream_code(request); + + auto generated_file_count = response.file_size(); + EXPECT_EQ(generated_file_count, 1); + + EXPECT_EQ(response.file(0).name(), "test/package/MyTestProtoFile.java"); + EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestProtoFile")); + EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD")); +} + +TEST(StreamingProtoJavaTest, WithoutFilter_MultipleJavaFiles) { + CodeGeneratorRequest request = create_simple_multi_file_request(); + CodeGeneratorResponse response = generate_java_protostream_code(request); + + auto generated_file_count = response.file_size(); + EXPECT_EQ(generated_file_count, 2); + + EXPECT_EQ(response.file(0).name(), "test/package/MyTestMessage.java"); + EXPECT_THAT(response.file(0).content(), Not(HasSubstr("class MyTestProtoFile"))); + EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD")); + + EXPECT_EQ(response.file(1).name(), "test/package/MyOtherTestMessage.java"); + EXPECT_THAT(response.file(1).content(), Not(HasSubstr("class MyOtherTestProtoFile"))); + EXPECT_THAT(response.file(1).content(), HasSubstr("class MyOtherTestMessage")); + EXPECT_THAT(response.file(1).content(), HasSubstr("long A_TEST_FIELD")); + EXPECT_THAT(response.file(1).content(), HasSubstr("long ANOTHER_TEST_FIELD")); +} + +TEST(StreamingProtoJavaTest, WithFilter_MultipleJavaFiles) { + CodeGeneratorRequest request = create_simple_multi_file_request(); + request.set_parameter("include_filter:test.package.MyTestMessage"); + CodeGeneratorResponse response = generate_java_protostream_code(request); + + auto generated_file_count = response.file_size(); + EXPECT_EQ(generated_file_count, 1); + + EXPECT_EQ(response.file(0).name(), "test/package/MyTestMessage.java"); + EXPECT_THAT(response.file(0).content(), Not(HasSubstr("class MyTestProtoFile"))); + EXPECT_THAT(response.file(0).content(), HasSubstr("class MyTestMessage")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_TEST_FIELD")); + EXPECT_THAT(response.file(0).content(), HasSubstr("long MY_OTHER_TEST_FIELD")); +} -- cgit v1.2.3