Merge "docs: Address comments on concurrent-tracing-sessions.md" into main
diff --git a/Android.bp b/Android.bp
index dec60eb..698fe51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -10718,6 +10718,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10737,6 +10739,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10757,6 +10761,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/library.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.gen.cc",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.gen.cc",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/test_messages.gen.cc",
         "external/perfetto/src/protozero/test/example_proto/upper_import.gen.cc",
     ],
@@ -10777,6 +10783,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.gen.h",
         "external/perfetto/src/protozero/test/example_proto/library.gen.h",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.gen.h",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.gen.h",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.gen.h",
         "external/perfetto/src/protozero/test/example_proto/test_messages.gen.h",
         "external/perfetto/src/protozero/test/example_proto/upper_import.gen.h",
     ],
@@ -10793,6 +10801,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10812,6 +10822,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/library.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pb.cc",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pb.cc",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pb.cc",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pb.cc",
     ],
@@ -10831,6 +10843,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pb.h",
         "external/perfetto/src/protozero/test/example_proto/library.pb.h",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pb.h",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pb.h",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pb.h",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pb.h",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pb.h",
     ],
@@ -10847,6 +10861,8 @@
         "src/protozero/test/example_proto/extensions.proto",
         "src/protozero/test/example_proto/library.proto",
         "src/protozero/test/example_proto/library_internals/galaxies.proto",
+        "src/protozero/test/example_proto/other_package/test_messages.proto",
+        "src/protozero/test/example_proto/subpackage/test_messages.proto",
         "src/protozero/test/example_proto/test_messages.proto",
         "src/protozero/test/example_proto/upper_import.proto",
     ],
@@ -10867,6 +10883,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/library.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pbzero.cc",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pbzero.cc",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pbzero.cc",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pbzero.cc",
     ],
@@ -10887,6 +10905,8 @@
         "external/perfetto/src/protozero/test/example_proto/extensions.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/library.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/library_internals/galaxies.pbzero.h",
+        "external/perfetto/src/protozero/test/example_proto/other_package/test_messages.pbzero.h",
+        "external/perfetto/src/protozero/test/example_proto/subpackage/test_messages.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/test_messages.pbzero.h",
         "external/perfetto/src/protozero/test/example_proto/upper_import.pbzero.h",
     ],
diff --git a/src/protozero/BUILD.gn b/src/protozero/BUILD.gn
index 4d940bc..796a125 100644
--- a/src/protozero/BUILD.gn
+++ b/src/protozero/BUILD.gn
@@ -93,6 +93,8 @@
     "test/example_proto/library_internals/galaxies.proto",
     "test/example_proto/test_messages.proto",
     "test/example_proto/upper_import.proto",
+    "test/example_proto/other_package/test_messages.proto",
+    "test/example_proto/subpackage/test_messages.proto",
   ]
   proto_path = perfetto_root_path
 }
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index f134caa..4257613 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -95,7 +95,13 @@
     return full_type;
   }
 
+  template <class T>
+  bool HasSamePackage(const T* descriptor) const {
+    return descriptor->file()->package() == package_;
+  }
+
   mutable std::string wrapper_namespace_;
+  mutable std::string package_;
 };
 
 CppObjGenerator::CppObjGenerator() = default;
@@ -116,6 +122,8 @@
     }
   }
 
+  package_ = file->package();
+
   auto get_file_name = [](const FileDescriptor* proto) {
     return StripSuffix(proto->name(), ".proto") + ".gen";
   };
@@ -372,10 +380,16 @@
       return constref ? "const std::string&" : "std::string";
     case FieldDescriptor::TYPE_MESSAGE:
       assert(!field->options().lazy());
-      return constref ? "const " + GetFullName(field->message_type()) + "&"
-                      : GetFullName(field->message_type());
+      return constref
+                 ? "const " +
+                       GetFullName(field->message_type(),
+                                   !HasSamePackage(field->message_type())) +
+                       "&"
+                 : GetFullName(field->message_type(),
+                               !HasSamePackage(field->message_type()));
     case FieldDescriptor::TYPE_ENUM:
-      return GetFullName(field->enum_type());
+      return GetFullName(field->enum_type(),
+                         !HasSamePackage(field->enum_type()));
     case FieldDescriptor::TYPE_GROUP:
       abort();
   }
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index 7d46a50..5b08292 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -42,6 +42,7 @@
 using google::protobuf::compiler::GeneratorContext;
 using google::protobuf::io::Printer;
 using google::protobuf::io::ZeroCopyOutputStream;
+using perfetto::base::ReplaceAll;
 using perfetto::base::SplitString;
 using perfetto::base::StripChars;
 using perfetto::base::StripPrefix;
@@ -126,14 +127,9 @@
       error_ = reason;
   }
 
-  // Get full name (including outer descriptors) of proto descriptor.
   template <class T>
-  inline std::string GetDescriptorName(const T* descriptor) {
-    if (!package_.empty()) {
-      return StripPrefix(descriptor->full_name(), package_ + ".");
-    } else {
-      return descriptor->full_name();
-    }
+  bool HasSamePackage(const T* descriptor) const {
+    return descriptor->file()->package() == package_;
   }
 
   // Get C++ class name corresponding to proto descriptor.
@@ -141,9 +137,28 @@
   // prohibited but not recommended in order to avoid name collisions.
   template <class T>
   inline std::string GetCppClassName(const T* descriptor, bool full = false) {
-    std::string name = StripChars(GetDescriptorName(descriptor), ".", '_');
-    if (full)
-      name = full_namespace_prefix_ + name;
+    std::string package = descriptor->file()->package();
+    std::string name = StripPrefix(descriptor->full_name(), package + ".");
+    name = StripChars(name, ".", '_');
+
+    if (full && !package.empty()) {
+      auto get_full_namespace = [&]() {
+        std::vector<std::string> namespaces = SplitString(package, ".");
+        if (!wrapper_namespace_.empty())
+          namespaces.push_back(wrapper_namespace_);
+
+        std::string result = "";
+        for (const std::string& ns : namespaces) {
+          result += "::";
+          result += ns;
+        }
+        return result;
+      };
+
+      std::string namespaces = ReplaceAll(package, ".", "::");
+      name = get_full_namespace() + "::" + name;
+    }
+
     return name;
   }
 
@@ -305,12 +320,14 @@
       case FieldDescriptor::TYPE_DOUBLE:
         return "double";
       case FieldDescriptor::TYPE_ENUM:
-        return GetCppClassName(field->enum_type(), true);
+        return GetCppClassName(field->enum_type(),
+                               !HasSamePackage(field->enum_type()));
       case FieldDescriptor::TYPE_STRING:
       case FieldDescriptor::TYPE_BYTES:
         return "std::string";
       case FieldDescriptor::TYPE_MESSAGE:
-        return GetCppClassName(field->message_type());
+        return GetCppClassName(field->message_type(),
+                               !HasSamePackage(field->message_type()));
       case FieldDescriptor::TYPE_GROUP:
         Abort("Groups not supported.");
         return "";
@@ -405,11 +422,6 @@
     while (!stack.empty()) {
       const FileDescriptor* import = stack.back();
       stack.pop_back();
-      // Having imports under different packages leads to unnecessary
-      // complexity with namespaces.
-      if (import->package() != package_)
-        Abort("Imported proto must be in the same package.");
-
       for (int i = 0; i < import->public_dependency_count(); ++i) {
         stack.push_back(import->public_dependency(i));
       }
@@ -504,32 +516,70 @@
     }
     stub_h_->Print("\n");
 
+    PrintForwardDeclarations();
+
     // Print namespaces.
     for (const std::string& ns : namespaces_) {
       stub_h_->Print("namespace $ns$ {\n", "ns", ns);
     }
     stub_h_->Print("\n");
+  }
 
-    // Print forward declarations.
+  void PrintForwardDeclarations() {
+    struct Descriptors {
+      std::vector<const Descriptor*> messages_;
+      std::vector<const EnumDescriptor*> enums_;
+    };
+    std::map<std::string, Descriptors> package_to_descriptors;
+
     for (const Descriptor* message : referenced_messages_) {
-      stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
+      package_to_descriptors[message->file()->package()].messages_.push_back(
+          message);
     }
-    for (const EnumDescriptor* enumeration : referenced_enums_) {
-      if (enumeration->containing_type()) {
-        stub_h_->Print("namespace $namespace_name$ {\n", "namespace_name",
-                       GetNamespaceNameForInnerEnum(enumeration));
-      }
-      stub_h_->Print("enum $class$ : int32_t;\n", "class", enumeration->name());
 
-      if (enumeration->containing_type()) {
-        stub_h_->Print("}  // namespace $namespace_name$\n", "namespace_name",
-                       GetNamespaceNameForInnerEnum(enumeration));
-        stub_h_->Print("using $alias$ = $namespace_name$::$short_name$;\n",
-                       "alias", GetCppClassName(enumeration), "namespace_name",
-                       GetNamespaceNameForInnerEnum(enumeration), "short_name",
+    for (const EnumDescriptor* enumeration : referenced_enums_) {
+      package_to_descriptors[enumeration->file()->package()].enums_.push_back(
+          enumeration);
+    }
+
+    for (const auto& [package, descriptors] : package_to_descriptors) {
+      std::vector<std::string> namespaces = SplitString(package, ".");
+      namespaces.push_back(wrapper_namespace_);
+
+      // open namespaces
+      for (const auto& ns : namespaces) {
+        stub_h_->Print("namespace $ns$ {\n", "ns", ns);
+      }
+
+      for (const Descriptor* message : descriptors.messages_) {
+        stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
+      }
+
+      for (const EnumDescriptor* enumeration : descriptors.enums_) {
+        if (enumeration->containing_type()) {
+          stub_h_->Print("namespace $namespace_name$ {\n", "namespace_name",
+                         GetNamespaceNameForInnerEnum(enumeration));
+        }
+        stub_h_->Print("enum $class$ : int32_t;\n", "class",
                        enumeration->name());
+
+        if (enumeration->containing_type()) {
+          stub_h_->Print("}  // namespace $namespace_name$\n", "namespace_name",
+                         GetNamespaceNameForInnerEnum(enumeration));
+          stub_h_->Print("using $alias$ = $namespace_name$::$short_name$;\n",
+                         "alias", GetCppClassName(enumeration),
+                         "namespace_name",
+                         GetNamespaceNameForInnerEnum(enumeration),
+                         "short_name", enumeration->name());
+        }
+      }
+
+      // close namespaces
+      for (auto it = namespaces.crbegin(); it != namespaces.crend(); ++it) {
+        stub_h_->Print("} // Namespace $ns$.\n", "ns", *it);
       }
     }
+
     stub_h_->Print("\n");
   }
 
@@ -675,7 +725,8 @@
 
   void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
     std::string action = field->is_repeated() ? "add" : "set";
-    std::string inner_class = GetCppClassName(field->message_type());
+    std::string inner_class = GetCppClassName(
+        field->message_type(), !HasSamePackage(field->message_type()));
     stub_h_->Print(
         "template <typename T = $inner_class$> T* $action$_$name$() {\n"
         "  return BeginNestedMessage<T>($id$);\n"
diff --git a/src/protozero/test/cppgen_conformance_unittest.cc b/src/protozero/test/cppgen_conformance_unittest.cc
index 5ac43aa..d8d284e 100644
--- a/src/protozero/test/cppgen_conformance_unittest.cc
+++ b/src/protozero/test/cppgen_conformance_unittest.cc
@@ -24,14 +24,20 @@
 
 // Autogenerated headers in out/*/gen/
 #include "src/protozero/test/example_proto/library.gen.h"
+#include "src/protozero/test/example_proto/other_package/test_messages.gen.h"
+#include "src/protozero/test/example_proto/subpackage/test_messages.gen.h"
 #include "src/protozero/test/example_proto/test_messages.gen.h"
 #include "src/protozero/test/example_proto/test_messages.pb.h"
 
 // Generated by the cppgen compiler.
 namespace pbtest = protozero::test::protos::gen;
+namespace pbtest_subpackage = protozero::test::protos::subpackage::gen;
+namespace pbtest_otherpackage = other_package::gen;
 
 // Generated by the official protobuf compiler.
 namespace pbgold = protozero::test::protos;
+namespace pbgold_subpackage = protozero::test::protos::subpackage;
+namespace pbgold_other_package = other_package;
 
 namespace protozero {
 namespace {
@@ -339,5 +345,57 @@
     ASSERT_THAT(msg.field_sfixed64(), ElementsAreArray(exp_sfixed64));
   }
 }
+
+TEST(ProtoCppConformanceTest, DifferentPackages) {
+  pbtest::DifferentPackages msg;
+
+  // Pupulate fields defined in "protozero.test.protos.subpackage"
+  pbtest_subpackage::Message* msgSubpackage = msg.mutable_subpackage_message();
+  msgSubpackage->set_field_int32(1);
+  msgSubpackage->set_field_enum(pbtest_subpackage::Enum::A);
+  msgSubpackage->set_field_nested_enum(pbtest_subpackage::Message_NestedEnum_C);
+  msg.mutable_subpackage_nested_message()->set_field_int32(2);
+  msg.set_subpackage_enum(pbtest_subpackage::Enum::B);
+  msg.set_subpackage_nested_enum(pbtest_subpackage::Message_NestedEnum_D);
+
+  // Pupulate fields defined in "other_package"
+  pbtest_otherpackage::Message* msgOtherPackage =
+      msg.mutable_otherpackage_message();
+  msgOtherPackage->set_field_int32(11);
+  msgOtherPackage->set_field_enum(pbtest_otherpackage::Enum::A);
+  msgOtherPackage->set_field_nested_enum(
+      pbtest_otherpackage::Message_NestedEnum_C);
+  msg.mutable_otherpackage_nested_message()->set_field_int32(12);
+  msg.set_otherpackage_enum(pbtest_otherpackage::Enum::B);
+  msg.set_otherpackage_nested_enum(pbtest_otherpackage::Message_NestedEnum_D);
+
+  // Deserialize into golden proto
+  std::string serialized = msg.SerializeAsString();
+  pbgold::DifferentPackages gold_msg;
+  gold_msg.ParseFromString(serialized);
+  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSizeLong()));
+
+  // Check fields defined in "protozero.test.protos.subpackage"
+  EXPECT_EQ(1, gold_msg.subpackage_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::A,
+            gold_msg.subpackage_message().field_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_C,
+            gold_msg.subpackage_message().field_nested_enum());
+  EXPECT_EQ(2, gold_msg.subpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::B, gold_msg.subpackage_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_D,
+            gold_msg.subpackage_nested_enum());
+
+  // Check fields defined in "other_package"
+  EXPECT_EQ(11, gold_msg.otherpackage_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::A,
+            gold_msg.otherpackage_message().field_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_C,
+            gold_msg.otherpackage_message().field_nested_enum());
+  EXPECT_EQ(12, gold_msg.otherpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::B, gold_msg.otherpackage_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_D,
+            gold_msg.otherpackage_nested_enum());
+}
 }  // namespace
 }  // namespace protozero
diff --git a/src/protozero/test/example_proto/other_package/test_messages.proto b/src/protozero/test/example_proto/other_package/test_messages.proto
new file mode 100644
index 0000000..d299082
--- /dev/null
+++ b/src/protozero/test/example_proto/other_package/test_messages.proto
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package other_package;
+
+enum Enum {
+  A = 10;
+  B = 11;
+}
+
+message Message {
+  message NestedMessage {
+    optional int32 field_int32 = 1;
+  }
+
+  enum NestedEnum {
+    C = 12;
+    D = 13;
+  }
+
+  optional int32 field_int32 = 1;
+  optional Enum field_enum = 2;
+  optional NestedEnum field_nested_enum = 3;
+  optional NestedMessage field_nested_message = 4;
+}
diff --git a/src/protozero/test/example_proto/subpackage/test_messages.proto b/src/protozero/test/example_proto/subpackage/test_messages.proto
new file mode 100644
index 0000000..6f7c0cc
--- /dev/null
+++ b/src/protozero/test/example_proto/subpackage/test_messages.proto
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package protozero.test.protos.subpackage;
+
+enum Enum {
+  A = 1;
+  B = 2;
+}
+
+message Message {
+  message NestedMessage {
+    optional int32 field_int32 = 1;
+  }
+
+  enum NestedEnum {
+    C = 3;
+    D = 4;
+  }
+
+  optional int32 field_int32 = 1;
+  optional Enum field_enum = 2;
+  optional NestedEnum field_nested_enum = 3;
+  optional NestedMessage field_nested_message = 4;
+}
diff --git a/src/protozero/test/example_proto/test_messages.proto b/src/protozero/test/example_proto/test_messages.proto
index e6ece33..b54d883 100644
--- a/src/protozero/test/example_proto/test_messages.proto
+++ b/src/protozero/test/example_proto/test_messages.proto
@@ -19,6 +19,8 @@
 package protozero.test.protos;
 
 import "src/protozero/test/example_proto/library.proto";
+import "src/protozero/test/example_proto/other_package/test_messages.proto";
+import "src/protozero/test/example_proto/subpackage/test_messages.proto";
 
 // This file contains comprehensive set of supported message structures and
 // data types. Unit tests depends on the plugin-processed version of this file.
@@ -179,3 +181,15 @@
   repeated Sub2_V2 sub2_rep = 12;
   optional Sub2_V2 sub2_lazy = 13 [lazy = true];
 }
+
+message DifferentPackages {
+  optional subpackage.Message subpackage_message = 1;
+  optional subpackage.Message.NestedMessage subpackage_nested_message = 2;
+  optional subpackage.Enum subpackage_enum = 3;
+  optional subpackage.Message.NestedEnum subpackage_nested_enum = 4;
+
+  optional .other_package.Message otherpackage_message = 5;
+  optional .other_package.Message.NestedMessage otherpackage_nested_message = 6;
+  optional .other_package.Enum otherpackage_enum = 7;
+  optional .other_package.Message.NestedEnum otherpackage_nested_enum = 8;
+}
diff --git a/src/protozero/test/protozero_conformance_unittest.cc b/src/protozero/test/protozero_conformance_unittest.cc
index 74e2204..3a4b7cf 100644
--- a/src/protozero/test/protozero_conformance_unittest.cc
+++ b/src/protozero/test/protozero_conformance_unittest.cc
@@ -27,14 +27,20 @@
 #include "src/protozero/test/example_proto/extensions.pb.h"
 #include "src/protozero/test/example_proto/extensions.pbzero.h"
 #include "src/protozero/test/example_proto/library.pbzero.h"
+#include "src/protozero/test/example_proto/other_package/test_messages.pbzero.h"
+#include "src/protozero/test/example_proto/subpackage/test_messages.pbzero.h"
 #include "src/protozero/test/example_proto/test_messages.pb.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
 
 // Generated by the protozero plugin.
 namespace pbtest = protozero::test::protos::pbzero;
+namespace pbtest_subpackage = protozero::test::protos::subpackage::pbzero;
+namespace pbtest_otherpackage = other_package::pbzero;
 
 // Generated by the official protobuf compiler.
 namespace pbgold = protozero::test::protos;
+namespace pbgold_subpackage = protozero::test::protos::subpackage;
+namespace pbgold_other_package = other_package;
 
 namespace protozero {
 namespace {
@@ -310,5 +316,58 @@
                "PING");
 }
 
+TEST(ProtoZeroConformanceTest, DifferentPackages) {
+  HeapBuffered<pbtest::DifferentPackages> msg{kChunkSize, kChunkSize};
+
+  // Pupulate fields defined in "protozero.test.protos.subpackage"
+  pbtest_subpackage::Message* msgSubpackage = msg->set_subpackage_message();
+  msgSubpackage->set_field_int32(1);
+  msgSubpackage->set_field_enum(pbtest_subpackage::Enum::A);
+  msgSubpackage->set_field_nested_enum(
+      pbtest_subpackage::Message::NestedEnum::C);
+  msg->set_subpackage_nested_message()->set_field_int32(2);
+  msg->set_subpackage_enum(pbtest_subpackage::Enum::B);
+  msg->set_subpackage_nested_enum(pbtest_subpackage::Message_NestedEnum::D);
+
+  // Pupulate fields defined in "other_package"
+  pbtest_otherpackage::Message* msgOtherPackage =
+      msg->set_otherpackage_message();
+  msgOtherPackage->set_field_int32(11);
+  msgOtherPackage->set_field_enum(pbtest_otherpackage::Enum::A);
+  msgOtherPackage->set_field_nested_enum(
+      pbtest_otherpackage::Message::NestedEnum::C);
+  msg->set_otherpackage_nested_message()->set_field_int32(12);
+  msg->set_otherpackage_enum(pbtest_otherpackage::Enum::B);
+  msg->set_otherpackage_nested_enum(pbtest_otherpackage::Message_NestedEnum::D);
+
+  // Deserialize into golden proto
+  std::string serialized = msg.SerializeAsString();
+  pbgold::DifferentPackages gold_msg;
+  gold_msg.ParseFromString(serialized);
+  EXPECT_EQ(serialized.size(), static_cast<size_t>(gold_msg.ByteSizeLong()));
+
+  // Check fields defined in "protozero.test.protos.subpackage"
+  EXPECT_EQ(1, gold_msg.subpackage_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::A,
+            gold_msg.subpackage_message().field_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_C,
+            gold_msg.subpackage_message().field_nested_enum());
+  EXPECT_EQ(2, gold_msg.subpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_subpackage::Enum::B, gold_msg.subpackage_enum());
+  EXPECT_EQ(pbgold_subpackage::Message_NestedEnum_D,
+            gold_msg.subpackage_nested_enum());
+
+  // Check fields defined in "other_package"
+  EXPECT_EQ(11, gold_msg.otherpackage_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::A,
+            gold_msg.otherpackage_message().field_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_C,
+            gold_msg.otherpackage_message().field_nested_enum());
+  EXPECT_EQ(12, gold_msg.otherpackage_nested_message().field_int32());
+  EXPECT_EQ(pbgold_other_package::Enum::B, gold_msg.otherpackage_enum());
+  EXPECT_EQ(pbgold_other_package::Message_NestedEnum_D,
+            gold_msg.otherpackage_nested_enum());
+}
+
 }  // namespace
 }  // namespace protozero