GTest test-cases for HIDL safe unions

This change implements test-cases to exercise correctness of the HIDL
safe_union construct.

Bug: 79878527
Test: Ran the hidl_test suite (including new tests) on a real device.
Change-Id: I8131ee5453ee65952b08a95c0cb8bc698ce888d5
diff --git a/test/hidl_test/Android.bp b/test/hidl_test/Android.bp
index 60982d4..ea0ba1f 100644
--- a/test/hidl_test/Android.bp
+++ b/test/hidl_test/Android.bp
@@ -35,6 +35,7 @@
         "android.hardware.tests.memory@1.0",
         "android.hardware.tests.multithread@1.0",
         "android.hardware.tests.trie@1.0",
+        "android.hardware.tests.safeunion@1.0",
     ],
 
     // impls should never be static, these are used only for testing purposes
@@ -50,6 +51,7 @@
         "android.hardware.tests.memory@1.0-impl",
         "android.hardware.tests.multithread@1.0-impl",
         "android.hardware.tests.trie@1.0-impl",
+        "android.hardware.tests.safeunion@1.0-impl",
     ],
 
     group_static_libs: true,
diff --git a/test/hidl_test/hidl_test.h b/test/hidl_test/hidl_test.h
index 0ad17ef..c90521e 100644
--- a/test/hidl_test/hidl_test.h
+++ b/test/hidl_test/hidl_test.h
@@ -10,6 +10,7 @@
 #include <android/hardware/tests/multithread/1.0/IMultithread.h>
 #include <android/hardware/tests/pointer/1.0/IGraph.h>
 #include <android/hardware/tests/pointer/1.0/IPointer.h>
+#include <android/hardware/tests/safeunion/1.0/ISafeUnion.h>
 #include <android/hardware/tests/trie/1.0/ITrie.h>
 
 template <template <typename Type> class Service>
@@ -23,6 +24,7 @@
     using ::android::hardware::tests::multithread::V1_0::IMultithread;
     using ::android::hardware::tests::pointer::V1_0::IGraph;
     using ::android::hardware::tests::pointer::V1_0::IPointer;
+    using ::android::hardware::tests::safeunion::V1_0::ISafeUnion;
     using ::android::hardware::tests::trie::V1_0::ITrie;
 
     Service<IMemoryTest>::run("memory");
@@ -35,6 +37,7 @@
     Service<IPointer>::run("pointer");
     Service<IMultithread>::run("multithread");
     Service<ITrie>::run("trie");
+    Service<ISafeUnion>::run("safeunion");
 }
 
 #endif  // HIDL_TEST_H_
diff --git a/test/hidl_test/hidl_test_client.cpp b/test/hidl_test/hidl_test_client.cpp
index 620f197..57e25fa 100644
--- a/test/hidl_test/hidl_test_client.cpp
+++ b/test/hidl_test/hidl_test_client.cpp
@@ -32,6 +32,7 @@
 #include <android/hardware/tests/multithread/1.0/IMultithread.h>
 #include <android/hardware/tests/pointer/1.0/IGraph.h>
 #include <android/hardware/tests/pointer/1.0/IPointer.h>
+#include <android/hardware/tests/safeunion/1.0/ISafeUnion.h>
 #include <android/hardware/tests/trie/1.0/ITrie.h>
 
 #include <gtest/gtest.h>
@@ -51,6 +52,7 @@
 #include <condition_variable>
 #include <fstream>
 #include <future>
+#include <limits>
 #include <mutex>
 #include <random>
 #include <set>
@@ -91,52 +93,57 @@
 
 static HidlEnvironment *gHidlEnvironment = nullptr;
 
+using ::android::Condition;
+using ::android::DELAY_NS;
+using ::android::DELAY_S;
 using ::android::FQName;
+using ::android::MultiDimensionalToString;
+using ::android::Mutex;
+using ::android::ONEWAY_TOLERANCE_NS;
+using ::android::sp;
+using ::android::to_string;
+using ::android::TOLERANCE_NS;
+using ::android::wp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::HidlMemory;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::tests::bar::V1_0::IBar;
+using ::android::hardware::tests::bar::V1_0::IComplicated;
+using ::android::hardware::tests::baz::V1_0::IBaz;
 using ::android::hardware::tests::foo::V1_0::Abc;
 using ::android::hardware::tests::foo::V1_0::IFoo;
 using ::android::hardware::tests::foo::V1_0::IFooCallback;
 using ::android::hardware::tests::foo::V1_0::ISimple;
 using ::android::hardware::tests::foo::V1_0::implementation::FooCallback;
-using ::android::hardware::tests::bar::V1_0::IBar;
-using ::android::hardware::tests::bar::V1_0::IComplicated;
-using ::android::hardware::tests::baz::V1_0::IBaz;
 using ::android::hardware::tests::hash::V1_0::IHash;
+using ::android::hardware::tests::inheritance::V1_0::IChild;
 using ::android::hardware::tests::inheritance::V1_0::IFetcher;
 using ::android::hardware::tests::inheritance::V1_0::IGrandparent;
 using ::android::hardware::tests::inheritance::V1_0::IParent;
-using ::android::hardware::tests::inheritance::V1_0::IChild;
-using ::android::hardware::tests::pointer::V1_0::IGraph;
-using ::android::hardware::tests::pointer::V1_0::IPointer;
 using ::android::hardware::tests::memory::V1_0::IMemoryTest;
 using ::android::hardware::tests::multithread::V1_0::IMultithread;
+using ::android::hardware::tests::pointer::V1_0::IGraph;
+using ::android::hardware::tests::pointer::V1_0::IPointer;
+using ::android::hardware::tests::safeunion::V1_0::ISafeUnion;
+using ::android::hardware::tests::safeunion::V1_0::J;
+using ::android::hardware::tests::safeunion::V1_0::LargeSafeUnion;
+using ::android::hardware::tests::safeunion::V1_0::MiscTypesSafeUnion;
+using ::android::hardware::tests::safeunion::V1_0::SmallSafeUnion;
 using ::android::hardware::tests::trie::V1_0::ITrie;
 using ::android::hardware::tests::trie::V1_0::TrieNode;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_death_recipient;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::HidlMemory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
 using ::android::hidl::allocator::V1_0::IAllocator;
 using ::android::hidl::base::V1_0::IBase;
 using ::android::hidl::manager::V1_0::IServiceNotification;
 using ::android::hidl::manager::V1_2::IServiceManager;
-using ::android::hidl::memory::V1_0::IMemory;
-using ::android::hidl::memory::token::V1_0::IMemoryToken;
 using ::android::hidl::memory::block::V1_0::MemoryBlock;
+using ::android::hidl::memory::token::V1_0::IMemoryToken;
+using ::android::hidl::memory::V1_0::IMemory;
 using ::android::hidl::token::V1_0::ITokenManager;
-using ::android::sp;
-using ::android::wp;
-using ::android::to_string;
-using ::android::Mutex;
-using ::android::MultiDimensionalToString;
-using ::android::Condition;
-using ::android::DELAY_S;
-using ::android::DELAY_NS;
-using ::android::TOLERANCE_NS;
-using ::android::ONEWAY_TOLERANCE_NS;
 using std::to_string;
 
 template <typename T>
@@ -317,6 +324,7 @@
     sp<IPointer> validationPointerInterface;
     sp<IMultithread> multithreadInterface;
     sp<ITrie> trieInterface;
+    sp<ISafeUnion> safeunionInterface;
     TestMode mode;
     bool enableDelayMeasurementTests;
     HidlEnvironment(TestMode mode, bool enableDelayMeasurementTests) :
@@ -355,8 +363,8 @@
         ASSERT_EQ(foo->isRemote(), mode == BINDERIZED);
 
         dyingBaz = IBaz::getService("dyingBaz", mode == PASSTHROUGH /* getStub */);
-        ASSERT_NE(foo, nullptr);
-        ASSERT_EQ(foo->isRemote(), mode == BINDERIZED);
+        ASSERT_NE(dyingBaz, nullptr);
+        ASSERT_EQ(dyingBaz->isRemote(), mode == BINDERIZED);
 
         bar = IBar::getService("foo", mode == PASSTHROUGH /* getStub */);
         ASSERT_NE(bar, nullptr);
@@ -382,6 +390,10 @@
         trieInterface = ITrie::getService("trie", mode == PASSTHROUGH /* getStub */);
         ASSERT_NE(trieInterface, nullptr);
         ASSERT_EQ(trieInterface->isRemote(), mode == BINDERIZED);
+
+        safeunionInterface = ISafeUnion::getService("safeunion", mode == PASSTHROUGH /* getStub */);
+        ASSERT_NE(safeunionInterface, nullptr);
+        ASSERT_EQ(safeunionInterface->isRemote(), mode == BINDERIZED);
     }
 
     virtual void SetUp() {
@@ -405,6 +417,7 @@
     sp<IPointer> pointerInterface;
     sp<IPointer> validationPointerInterface;
     sp<ITrie> trieInterface;
+    sp<ISafeUnion> safeunionInterface;
     TestMode mode = TestMode::PASSTHROUGH;
 
     virtual void SetUp() override {
@@ -421,6 +434,7 @@
         pointerInterface = gHidlEnvironment->pointerInterface;
         validationPointerInterface = gHidlEnvironment->validationPointerInterface;
         trieInterface = gHidlEnvironment->trieInterface;
+        safeunionInterface = gHidlEnvironment->safeunionInterface;
         mode = gHidlEnvironment->mode;
         ALOGI("Test setup complete");
     }
@@ -1814,6 +1828,144 @@
     });
 }
 
+TEST_F(HidlTest, SafeUnionNoInitTest) {
+    EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& safeUnion) {
+        EXPECT_EQ(LargeSafeUnion::hidl_discriminator::hidl_no_init, safeUnion.getDiscriminator());
+    }));
+}
+
+TEST_F(HidlTest, SafeUnionSimpleTest) {
+    EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& safeUnion) {
+        EXPECT_OK(safeunionInterface->setA(safeUnion, -5, [&](const LargeSafeUnion& safeUnion) {
+            EXPECT_EQ(LargeSafeUnion::hidl_discriminator::a, safeUnion.getDiscriminator());
+            EXPECT_EQ(-5, safeUnion.a());
+
+            uint64_t max = std::numeric_limits<uint64_t>::max();
+            EXPECT_OK(
+                safeunionInterface->setD(safeUnion, max, [&](const LargeSafeUnion& safeUnion) {
+                    EXPECT_EQ(LargeSafeUnion::hidl_discriminator::d, safeUnion.getDiscriminator());
+                    EXPECT_EQ(max, safeUnion.d());
+                }));
+        }));
+    }));
+}
+
+TEST_F(HidlTest, SafeUnionArrayLikeTypesTest) {
+    const std::array<int64_t, 5> testArray{1, -2, 3, -4, 5};
+    const hidl_vec<uint64_t> testVector{std::numeric_limits<uint64_t>::max()};
+
+    EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& safeUnion) {
+        EXPECT_OK(
+            safeunionInterface->setF(safeUnion, testArray, [&](const LargeSafeUnion& safeUnion) {
+                EXPECT_EQ(LargeSafeUnion::hidl_discriminator::f, safeUnion.getDiscriminator());
+
+                for (size_t i = 0; i < testArray.size(); i++) {
+                    EXPECT_EQ(testArray[i], safeUnion.f()[i]);
+                }
+            }));
+
+        EXPECT_OK(
+            safeunionInterface->setI(safeUnion, testVector, [&](const LargeSafeUnion& safeUnion) {
+                EXPECT_EQ(LargeSafeUnion::hidl_discriminator::i, safeUnion.getDiscriminator());
+                EXPECT_EQ(testVector, safeUnion.i());
+            }));
+    }));
+}
+
+TEST_F(HidlTest, SafeUnionStringTypeTest) {
+    const std::string testString =
+        "This is an inordinately long test string to exercise hidl_string types in safe unions.";
+
+    EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& safeUnion) {
+        EXPECT_OK(safeunionInterface->setG(
+            safeUnion, hidl_string(testString), [&](const LargeSafeUnion& safeUnion) {
+                EXPECT_EQ(LargeSafeUnion::hidl_discriminator::g, safeUnion.getDiscriminator());
+                EXPECT_EQ(testString, std::string(safeUnion.g()));
+            }));
+    }));
+}
+
+TEST_F(HidlTest, SafeUnionCopyTest) {
+    const hidl_vec<bool> testVector{true, false, true, false, false, false, true,  false,
+                                    true, true,  true, false, false, true,  false, true};
+
+    EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& safeUnion) {
+        EXPECT_OK(
+            safeunionInterface->setH(safeUnion, testVector, [&](const LargeSafeUnion& safeUnion) {
+                LargeSafeUnion safeUnionCopy(safeUnion);
+
+                EXPECT_EQ(LargeSafeUnion::hidl_discriminator::h, safeUnionCopy.getDiscriminator());
+                EXPECT_EQ(testVector, safeUnionCopy.h());
+            }));
+    }));
+}
+
+TEST_F(HidlTest, SafeUnionMutateTest) {
+    const std::array<int64_t, 5> testArray{-1, -2, -3, -4, -5};
+    const std::string testString = "Test string";
+    LargeSafeUnion safeUnion;
+
+    safeUnion.f(testArray);
+    safeUnion.f()[0] += 10;
+    EXPECT_EQ(testArray[0] + 10, safeUnion.f()[0]);
+
+    safeUnion.j(J());
+    safeUnion.j().j3 = testString;
+    EXPECT_EQ(testString, std::string(safeUnion.j().j3));
+}
+
+TEST_F(HidlTest, SafeUnionNestedTest) {
+    SmallSafeUnion smallSafeUnion;
+    smallSafeUnion.a(1);
+
+    EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& safeUnion) {
+        EXPECT_OK(safeunionInterface->setL(
+            safeUnion, smallSafeUnion, [&](const LargeSafeUnion& safeUnion) {
+                EXPECT_EQ(LargeSafeUnion::hidl_discriminator::l, safeUnion.getDiscriminator());
+
+                EXPECT_EQ(SmallSafeUnion::hidl_discriminator::a, safeUnion.l().getDiscriminator());
+                EXPECT_EQ(1, safeUnion.l().a());
+            }));
+    }));
+}
+
+TEST_F(HidlTest, SafeUnionEqualityTest) {
+    EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& one) {
+        EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& two) {
+            EXPECT_FALSE(one == two);
+            EXPECT_TRUE(one != two);
+        }));
+
+        EXPECT_OK(safeunionInterface->setA(one, 1, [&](const LargeSafeUnion& one) {
+            EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& two) {
+                EXPECT_FALSE(one == two);
+                EXPECT_TRUE(one != two);
+            }));
+
+            EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& two) {
+                EXPECT_OK(safeunionInterface->setB(two, 1, [&](const LargeSafeUnion& two) {
+                    EXPECT_FALSE(one == two);
+                    EXPECT_TRUE(one != two);
+                }));
+            }));
+
+            EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& two) {
+                EXPECT_OK(safeunionInterface->setA(two, 2, [&](const LargeSafeUnion& two) {
+                    EXPECT_FALSE(one == two);
+                    EXPECT_TRUE(one != two);
+                }));
+            }));
+
+            EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& two) {
+                EXPECT_OK(safeunionInterface->setA(two, 1, [&](const LargeSafeUnion& two) {
+                    EXPECT_TRUE(one == two);
+                    EXPECT_FALSE(one != two);
+                }));
+            }));
+        }));
+    }));
+}
+
 class HidlMultithreadTest : public ::testing::Test {
    public:
     sp<IMultithread> multithreadInterface;