chromium/mojo/public/cpp/platform/tests/platform_handle_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "mojo/public/cpp/platform/platform_handle.h"

#include <tuple>

#include "base/check.h"
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/platform_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/notreached.h"
#include "base/rand_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "mojo/public/c/system/platform_handle.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_MAC)
#include <mach/mach_vm.h>
#endif

#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_handle.h"
#else
#include "base/files/scoped_file.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include "base/android/binder.h"
#endif

namespace mojo {
namespace {

// Different types of platform handles are supported on different platforms.
// We run all PlatformHandle once for each type of handle available on the
// target platform.
enum class HandleType {};

// Different types of test modes we support in order to exercise the available
// handles types. Fuchsia zx_handle tests and Mac Mach port tests use shared
// memory for setup and validation. Everything else uses platform files.
// See |SetUp()| below.
enum class TestType {};

const std::string kTestData =;

class PlatformHandleTest : public testing::Test,
                           public testing::WithParamInterface<HandleType> {};

TEST_P(PlatformHandleTest, BasicConstruction) {}

TEST_P(PlatformHandleTest, Move) {}

TEST_P(PlatformHandleTest, Reset) {}

TEST_P(PlatformHandleTest, Clone) {}

// This is really testing system library stuff, but we conveniently have all
// this handle type parameterization already in place here.
TEST_P(PlatformHandleTest, CStructConversion) {}

#if BUILDFLAG(IS_ANDROID)
DEFINE_BINDER_CLASS(IncrementerInterface);
class Incrementer : public base::android::SupportsBinder<IncrementerInterface> {
 public:
  using Proxy = IncrementerInterface::BinderRef;

  Incrementer() = default;

  static constexpr transaction_code_t kDoIncrement = 42;

  static int32_t DoIncrement(Proxy& proxy, int32_t value) {
    const auto reply = *proxy.Transact(
        kDoIncrement,
        [value](const auto& parcel) { return parcel.WriteInt32(value); });
    return *reply.reader().ReadInt32();
  }

  void WaitForDisconnect() { disconnect_.Wait(); }

 private:
  ~Incrementer() override = default;

  // base::android::SupportsBinder<IncrementerInterface>
  base::android::BinderStatusOr<void> OnBinderTransaction(
      transaction_code_t code,
      const base::android::ParcelReader& in,
      const base::android::ParcelWriter& out) override {
    EXPECT_EQ(kDoIncrement, code);
    return out.WriteInt32(*in.ReadInt32() + 1);
  }

  void OnBinderDestroyed() override { disconnect_.Signal(); }

  base::WaitableEvent disconnect_;
};

TEST(PlatformHandleBinderTest, HandleTypeAndValidity) {
  if (!base::android::IsNativeBinderAvailable()) {
    GTEST_SKIP() << "This test is only valid with native Binder support (Q+)";
  }

  auto handle = PlatformHandle(base::android::BinderRef());
  EXPECT_TRUE(handle.is_binder());
  EXPECT_FALSE(handle.is_valid_binder());
  EXPECT_FALSE(handle.is_fd());
  EXPECT_FALSE(handle.is_valid_fd());
  EXPECT_FALSE(handle.is_valid());

  auto incrementer = base::MakeRefCounted<Incrementer>();
  handle = PlatformHandle(incrementer->GetBinder());
  EXPECT_TRUE(handle.is_binder());
  EXPECT_TRUE(handle.is_valid_binder());
  EXPECT_FALSE(handle.is_fd());
  EXPECT_FALSE(handle.is_valid_fd());
  EXPECT_TRUE(handle.is_valid());

  auto proxy = Incrementer::Proxy(handle.GetBinder());
  EXPECT_EQ(3, Incrementer::DoIncrement(proxy, 2));

  PlatformHandle other = std::move(handle);
  EXPECT_FALSE(handle.is_binder());
  EXPECT_FALSE(handle.is_valid_binder());
  EXPECT_FALSE(handle.is_valid());
  EXPECT_FALSE(handle.GetBinder());
  EXPECT_FALSE(handle.TakeBinder());
  EXPECT_TRUE(other.is_binder());
  EXPECT_TRUE(other.is_valid_binder());
  EXPECT_TRUE(other.is_valid());
  EXPECT_TRUE(other.GetBinder());

  auto binder = other.TakeBinder();
  EXPECT_TRUE(binder);
  EXPECT_FALSE(other.is_binder());
  EXPECT_FALSE(other.is_valid());

  proxy = Incrementer::Proxy(binder);
  EXPECT_EQ(7, Incrementer::DoIncrement(proxy, 6));
  proxy.reset();
  binder.reset();

  // Ensure we haven't leaked any binder refs through all this.
  incrementer->WaitForDisconnect();
}

TEST(PlatformHandleBinderTest, WrapUnwrap) {
  if (!base::android::IsNativeBinderAvailable()) {
    GTEST_SKIP() << "This test is only valid with native Binder support (Q+)";
  }

  Incrementer::Proxy proxy;
  auto handle = WrapPlatformHandle(PlatformHandle(std::move(proxy)));
  EXPECT_FALSE(handle.is_valid());

  auto incrementer = base::MakeRefCounted<Incrementer>();
  proxy = Incrementer::Proxy(incrementer->GetBinder());
  EXPECT_EQ(43, Incrementer::DoIncrement(proxy, 42));

  handle = WrapPlatformHandle(PlatformHandle(std::move(proxy)));
  EXPECT_TRUE(handle.is_valid());
  proxy =
      Incrementer::Proxy(UnwrapPlatformHandle(std::move(handle)).TakeBinder());
  EXPECT_EQ(9001, Incrementer::DoIncrement(proxy, 9000));
}
#endif

INSTANTIATE_TEST_SUITE_P();

}  // namespace
}  // namespace mojo