Add test for trunk stable workaround

This test is an example of using the same source code for different
versions of the AIDL library.

Test: atest aidl_ndk_v1_trunk_stable_unittest
Test: atest aidl_ndk_v2_trunk_stable_unittest
Bug: none

Change-Id: I6d0872931287e4127c9fef213250d67cfee3bb4e
diff --git a/tests/trunk_stable_test/Android.bp b/tests/trunk_stable_test/Android.bp
new file mode 100644
index 0000000..64336e7
--- /dev/null
+++ b/tests/trunk_stable_test/Android.bp
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_tools_aidl_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_tools_aidl_license"],
+}
+
+aidl_interface {
+    name: "android.aidl.test.trunk",
+    srcs: ["android/aidl/test/trunk/*.aidl"],
+    flags: ["-Werror"],
+    backend: {
+        rust: {
+            enabled: true,
+        },
+    },
+    versions_with_info: [
+        {
+            version: "1",
+            imports: [],
+        },
+    ],
+    frozen: false,
+
+}
+
+// Test using the same client and server source code including different
+// versions of the generated AIDL library.
+// This is an example of a possible solution to dealing with different
+// configurations of the build without needing to modify the source code.
+// This is the version of the test that uses the V1 library with the
+// V2 code behind #ifdefs.
+cc_test {
+    name: "aidl_ndk_V1_trunk_stable_unittest",
+    srcs: ["trunk_ndk_unittest.cpp"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libbase",
+    ],
+    cflags: [
+        "-DAIDL_TEST_TRUNK_VER=1",
+    ],
+    static_libs: [
+        "android.aidl.test.trunk-V1-ndk",
+    ],
+    require_root: true,
+}
+
+// This is the version of the test that uses the latest V2 library.
+cc_test {
+    name: "aidl_ndk_V2_trunk_stable_unittest",
+    srcs: ["trunk_ndk_unittest.cpp"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libbase",
+    ],
+    cflags: [
+        "-DAIDL_TEST_TRUNK_VER=2",
+    ],
+    static_libs: [
+        "android.aidl.test.trunk-V2-ndk",
+    ],
+    require_root: true,
+}
diff --git a/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/1/.hash b/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/1/.hash
new file mode 100644
index 0000000..890f5f5
--- /dev/null
+++ b/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/1/.hash
@@ -0,0 +1 @@
+88311b9118fb6fe9eff4a2ca19121de0587f6d5f
diff --git a/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/1/android/aidl/test/trunk/ITrunkStableTest.aidl b/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/1/android/aidl/test/trunk/ITrunkStableTest.aidl
new file mode 100644
index 0000000..98bbd2f
--- /dev/null
+++ b/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/1/android/aidl/test/trunk/ITrunkStableTest.aidl
@@ -0,0 +1,43 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.aidl.test.trunk;
+interface ITrunkStableTest {
+  android.aidl.test.trunk.ITrunkStableTest.MyParcelable repeatParcelable(in android.aidl.test.trunk.ITrunkStableTest.MyParcelable input);
+  android.aidl.test.trunk.ITrunkStableTest.MyEnum repeatEnum(in android.aidl.test.trunk.ITrunkStableTest.MyEnum input);
+  android.aidl.test.trunk.ITrunkStableTest.MyUnion repeatUnion(in android.aidl.test.trunk.ITrunkStableTest.MyUnion input);
+  void callMyCallback(in android.aidl.test.trunk.ITrunkStableTest.IMyCallback cb);
+  parcelable MyParcelable {
+    int a;
+    int b;
+  }
+  enum MyEnum {
+    ZERO,
+    ONE,
+    TWO,
+  }
+  union MyUnion {
+    int a;
+    int b;
+  }
+  interface IMyCallback {
+    android.aidl.test.trunk.ITrunkStableTest.MyParcelable repeatParcelable(in android.aidl.test.trunk.ITrunkStableTest.MyParcelable input);
+    android.aidl.test.trunk.ITrunkStableTest.MyEnum repeatEnum(in android.aidl.test.trunk.ITrunkStableTest.MyEnum input);
+    android.aidl.test.trunk.ITrunkStableTest.MyUnion repeatUnion(in android.aidl.test.trunk.ITrunkStableTest.MyUnion input);
+  }
+}
diff --git a/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/current/android/aidl/test/trunk/ITrunkStableTest.aidl b/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/current/android/aidl/test/trunk/ITrunkStableTest.aidl
new file mode 100644
index 0000000..2aa3bb1
--- /dev/null
+++ b/tests/trunk_stable_test/aidl_api/android.aidl.test.trunk/current/android/aidl/test/trunk/ITrunkStableTest.aidl
@@ -0,0 +1,52 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.aidl.test.trunk;
+interface ITrunkStableTest {
+  android.aidl.test.trunk.ITrunkStableTest.MyParcelable repeatParcelable(in android.aidl.test.trunk.ITrunkStableTest.MyParcelable input);
+  android.aidl.test.trunk.ITrunkStableTest.MyEnum repeatEnum(in android.aidl.test.trunk.ITrunkStableTest.MyEnum input);
+  android.aidl.test.trunk.ITrunkStableTest.MyUnion repeatUnion(in android.aidl.test.trunk.ITrunkStableTest.MyUnion input);
+  void callMyCallback(in android.aidl.test.trunk.ITrunkStableTest.IMyCallback cb);
+  android.aidl.test.trunk.ITrunkStableTest.MyOtherParcelable repeatOtherParcelable(in android.aidl.test.trunk.ITrunkStableTest.MyOtherParcelable input);
+  parcelable MyParcelable {
+    int a;
+    int b;
+    int c;
+  }
+  enum MyEnum {
+    ZERO,
+    ONE,
+    TWO,
+    THREE,
+  }
+  union MyUnion {
+    int a;
+    int b;
+    int c;
+  }
+  interface IMyCallback {
+    android.aidl.test.trunk.ITrunkStableTest.MyParcelable repeatParcelable(in android.aidl.test.trunk.ITrunkStableTest.MyParcelable input);
+    android.aidl.test.trunk.ITrunkStableTest.MyEnum repeatEnum(in android.aidl.test.trunk.ITrunkStableTest.MyEnum input);
+    android.aidl.test.trunk.ITrunkStableTest.MyUnion repeatUnion(in android.aidl.test.trunk.ITrunkStableTest.MyUnion input);
+    android.aidl.test.trunk.ITrunkStableTest.MyOtherParcelable repeatOtherParcelable(in android.aidl.test.trunk.ITrunkStableTest.MyOtherParcelable input);
+  }
+  parcelable MyOtherParcelable {
+    int a;
+    int b;
+  }
+}
diff --git a/tests/trunk_stable_test/android/aidl/test/trunk/ITrunkStableTest.aidl b/tests/trunk_stable_test/android/aidl/test/trunk/ITrunkStableTest.aidl
new file mode 100644
index 0000000..7c4ce2b
--- /dev/null
+++ b/tests/trunk_stable_test/android/aidl/test/trunk/ITrunkStableTest.aidl
@@ -0,0 +1,41 @@
+package android.aidl.test.trunk;
+
+interface ITrunkStableTest {
+    parcelable MyParcelable {
+        int a;
+        int b;
+        // New in V2
+        int c;
+    }
+    enum MyEnum {
+        ZERO,
+        ONE,
+        TWO,
+        // New in V2
+        THREE,
+    }
+    union MyUnion {
+        int a;
+        int b;
+        // New in V3
+        int c;
+    }
+    interface IMyCallback {
+        MyParcelable repeatParcelable(in MyParcelable input);
+        MyEnum repeatEnum(in MyEnum input);
+        MyUnion repeatUnion(in MyUnion input);
+        MyOtherParcelable repeatOtherParcelable(in MyOtherParcelable input);
+    }
+
+    MyParcelable repeatParcelable(in MyParcelable input);
+    MyEnum repeatEnum(in MyEnum input);
+    MyUnion repeatUnion(in MyUnion input);
+    void callMyCallback(in IMyCallback cb);
+
+    // New in V2
+    parcelable MyOtherParcelable {
+        int a;
+        int b;
+    }
+    MyOtherParcelable repeatOtherParcelable(in MyOtherParcelable input);
+}
diff --git a/tests/trunk_stable_test/trunk_ndk_unittest.cpp b/tests/trunk_stable_test/trunk_ndk_unittest.cpp
new file mode 100644
index 0000000..2ac721d
--- /dev/null
+++ b/tests/trunk_stable_test/trunk_ndk_unittest.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2023, 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 "gtest/gtest.h"
+
+/* Shared Client/Service includes */
+#include <aidl/android/aidl/test/trunk/BnTrunkStableTest.h>
+#include <aidl/android/aidl/test/trunk/ITrunkStableTest.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <sys/prctl.h>
+
+/* AIDL Client includes */
+
+/* AIDL Service includes */
+#include <android/binder_process.h>
+
+#ifndef AIDL_TEST_TRUNK_VER
+#define AIDL_TEST_TRUNK_VER 2
+#endif
+
+using ::aidl::android::aidl::test::trunk::ITrunkStableTest;
+using ndk::ScopedAStatus;
+
+/* AIDL Client definition */
+class MyCallback : public ITrunkStableTest::BnMyCallback {
+ public:
+  ScopedAStatus repeatParcelable(const ITrunkStableTest::MyParcelable& in_input,
+                                 ITrunkStableTest::MyParcelable* _aidl_return) override {
+    *_aidl_return = in_input;
+    repeatParcelableCalled = true;
+    return ScopedAStatus::ok();
+  }
+  ScopedAStatus repeatEnum(const ITrunkStableTest::MyEnum in_input,
+                           ITrunkStableTest::MyEnum* _aidl_return) override {
+    *_aidl_return = in_input;
+    repeatEnumCalled = true;
+    return ScopedAStatus::ok();
+  }
+  ScopedAStatus repeatUnion(const ITrunkStableTest::MyUnion& in_input,
+                            ITrunkStableTest::MyUnion* _aidl_return) override {
+    *_aidl_return = in_input;
+    repeatUnionCalled = true;
+    return ScopedAStatus::ok();
+  }
+
+#if AIDL_TEST_TRUNK_VER >= 2
+  ScopedAStatus repeatOtherParcelable(const ITrunkStableTest::MyOtherParcelable& in_input,
+                                      ITrunkStableTest::MyOtherParcelable* _aidl_return) override {
+    *_aidl_return = in_input;
+    repeatOtherParcelableCalled = true;
+    return ScopedAStatus::ok();
+  }
+
+  bool repeatOtherParcelableCalled = false;
+#endif
+
+  bool repeatParcelableCalled = false;
+  bool repeatEnumCalled = false;
+  bool repeatUnionCalled = false;
+};
+
+class ClientTest : public testing::Test {
+ public:
+  void SetUp() override {
+    mService = ITrunkStableTest::fromBinder(
+        ndk::SpAIBinder(AServiceManager_waitForService(ITrunkStableTest::descriptor)));
+    ASSERT_NE(nullptr, mService);
+  }
+
+  std::shared_ptr<ITrunkStableTest> mService;
+};
+
+TEST_F(ClientTest, SanityCheck) {
+  ITrunkStableTest::MyParcelable a, b;
+  a.a = 12;
+  a.b = 13;
+#if AIDL_TEST_TRUNK_VER >= 2
+  a.c = 14;
+#endif
+  auto status = mService->repeatParcelable(a, &b);
+  EXPECT_TRUE(status.isOk());
+  EXPECT_EQ(a, b);
+}
+
+TEST_F(ClientTest, Callback) {
+  auto cb = ndk::SharedRefBase::make<MyCallback>();
+  auto status = mService->callMyCallback(cb);
+  EXPECT_TRUE(status.isOk());
+  EXPECT_TRUE(cb->repeatParcelableCalled);
+  EXPECT_TRUE(cb->repeatEnumCalled);
+  EXPECT_TRUE(cb->repeatUnionCalled);
+#if AIDL_TEST_TRUNK_VER >= 2
+  EXPECT_TRUE(cb->repeatOtherParcelableCalled);
+#endif
+}
+
+#if AIDL_TEST_TRUNK_VER >= 2
+TEST_F(ClientTest, CallV2Method) {
+  ITrunkStableTest::MyOtherParcelable a, b;
+  a.a = 12;
+  a.b = 13;
+  auto status = mService->repeatOtherParcelable(a, &b);
+  EXPECT_TRUE(status.isOk());
+  EXPECT_EQ(a, b);
+}
+#endif
+
+/* AIDL service definition */
+using ::aidl::android::aidl::test::trunk::BnTrunkStableTest;
+class TrunkStableTest : public BnTrunkStableTest {
+  ScopedAStatus repeatParcelable(const MyParcelable& in_input,
+                                 MyParcelable* _aidl_return) override {
+    *_aidl_return = in_input;
+    return ScopedAStatus::ok();
+  }
+  ScopedAStatus repeatEnum(const MyEnum in_input, MyEnum* _aidl_return) override {
+    *_aidl_return = in_input;
+    return ScopedAStatus::ok();
+  }
+  ScopedAStatus repeatUnion(const MyUnion& in_input, MyUnion* _aidl_return) override {
+    *_aidl_return = in_input;
+    return ScopedAStatus::ok();
+  }
+  ScopedAStatus callMyCallback(const std::shared_ptr<IMyCallback>& in_cb) override {
+    MyParcelable a, b;
+    MyEnum c = MyEnum::ZERO, d = MyEnum::ZERO;
+    MyUnion e, f;
+    auto status = in_cb->repeatParcelable(a, &b);
+    if (!status.isOk()) {
+      return status;
+    }
+    status = in_cb->repeatEnum(c, &d);
+    if (!status.isOk()) {
+      return status;
+    }
+    status = in_cb->repeatUnion(e, &f);
+#if AIDL_TEST_TRUNK_VER >= 2
+    if (!status.isOk()) {
+      return status;
+    }
+    MyOtherParcelable g, h;
+    status = in_cb->repeatOtherParcelable(g, &h);
+#endif
+    return status;
+  }
+#if AIDL_TEST_TRUNK_VER >= 2
+  ScopedAStatus repeatOtherParcelable(const ITrunkStableTest::MyOtherParcelable& in_input,
+                                      ITrunkStableTest::MyOtherParcelable* _aidl_return) override {
+    *_aidl_return = in_input;
+    return ScopedAStatus::ok();
+  }
+#endif
+};
+
+int run_service() {
+  auto trunk = ndk::SharedRefBase::make<TrunkStableTest>();
+  binder_status_t status =
+      AServiceManager_addService(trunk->asBinder().get(), TrunkStableTest::descriptor);
+  CHECK_EQ(status, STATUS_OK);
+
+  ABinderProcess_joinThreadPool();
+  return EXIT_FAILURE;  // should not reach
+}
+
+int main(int argc, char* argv[]) {
+  if (fork() == 0) {
+    prctl(PR_SET_PDEATHSIG, SIGHUP);
+    run_service();
+  }
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}