// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This test is POSIX only.

#include "ipc/ipc_message_attachment_set.h"

#include <fcntl.h>
#include <stddef.h>
#include <unistd.h>

#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
#include "ipc/ipc_platform_file_attachment_posix.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace IPC {
namespace {

// Get a safe file descriptor for test purposes.
int GetSafeFd() {
  return open("/dev/null", O_RDONLY);
}

// Returns true if fd was already closed.  Closes fd if not closed.
bool VerifyClosed(int fd) {
  const int duped = HANDLE_EINTR(dup(fd));
  if (duped != -1) {
    EXPECT_NE(IGNORE_EINTR(close(duped)), -1);
    EXPECT_NE(IGNORE_EINTR(close(fd)), -1);
    return false;
  }
  return true;
}

int GetFdAt(MessageAttachmentSet* set, int id) {
  return static_cast<internal::PlatformFileAttachment&>(
             *set->GetAttachmentAt(id))
      .TakePlatformFile();
}

// The MessageAttachmentSet will try and close some of the descriptor numbers
// which we given it. This is the base descriptor value. It's great enough such
// that no real descriptor will accidently be closed.
static const int kFDBase = 50000;

TEST(MessageAttachmentSet, BasicAdd) {
  scoped_refptr<MessageAttachmentSet> set(new MessageAttachmentSet);

  ASSERT_EQ(set->size(), 0u);
  ASSERT_TRUE(set->empty());
  ASSERT_TRUE(
      set->AddAttachment(new internal::PlatformFileAttachment(kFDBase)));
  ASSERT_EQ(set->size(), 1u);
  ASSERT_TRUE(!set->empty());

  // Empties the set and stops a warning about deleting a set with unconsumed
  // descriptors
  set->CommitAllDescriptors();
}

TEST(MessageAttachmentSet, BasicAddAndClose) {
  scoped_refptr<MessageAttachmentSet> set(new MessageAttachmentSet);

  ASSERT_EQ(set->size(), 0u);
  ASSERT_TRUE(set->empty());
  const int fd = GetSafeFd();
  ASSERT_TRUE(set->AddAttachment(
      new internal::PlatformFileAttachment(base::ScopedFD(fd))));
  ASSERT_EQ(set->size(), 1u);
  ASSERT_TRUE(!set->empty());

  set->CommitAllDescriptors();

  ASSERT_TRUE(VerifyClosed(fd));
}
TEST(MessageAttachmentSet, MaxSize) {
  scoped_refptr<MessageAttachmentSet> set(new MessageAttachmentSet);

  for (size_t i = 0; i < MessageAttachmentSet::kMaxDescriptorsPerMessage; ++i)
    ASSERT_TRUE(set->AddAttachment(
        new internal::PlatformFileAttachment(kFDBase + 1 + i)));

  ASSERT_TRUE(
      !set->AddAttachment(new internal::PlatformFileAttachment(kFDBase)));

  set->CommitAllDescriptors();
}

TEST(MessageAttachmentSet, WalkInOrder) {
  scoped_refptr<MessageAttachmentSet> set(new MessageAttachmentSet);

  ASSERT_TRUE(
      set->AddAttachment(new internal::PlatformFileAttachment(kFDBase)));
  ASSERT_TRUE(
      set->AddAttachment(new internal::PlatformFileAttachment(kFDBase + 1)));
  ASSERT_TRUE(
      set->AddAttachment(new internal::PlatformFileAttachment(kFDBase + 2)));

  ASSERT_EQ(GetFdAt(set.get(), 0), kFDBase);
  ASSERT_EQ(GetFdAt(set.get(), 1), kFDBase + 1);
  ASSERT_EQ(GetFdAt(set.get(), 2), kFDBase + 2);
  ASSERT_FALSE(set->GetAttachmentAt(0));

  set->CommitAllDescriptors();
}

TEST(MessageAttachmentSet, WalkWrongOrder) {
  scoped_refptr<MessageAttachmentSet> set(new MessageAttachmentSet);

  ASSERT_TRUE(
      set->AddAttachment(new internal::PlatformFileAttachment(kFDBase)));
  ASSERT_TRUE(
      set->AddAttachment(new internal::PlatformFileAttachment(kFDBase + 1)));
  ASSERT_TRUE(
      set->AddAttachment(new internal::PlatformFileAttachment(kFDBase + 2)));

  ASSERT_EQ(GetFdAt(set.get(), 0), kFDBase);
  ASSERT_FALSE(set->GetAttachmentAt(2));

  set->CommitAllDescriptors();
}

#if defined(OS_ANDROID)
#define MAYBE_DontClose DISABLED_DontClose
#else
#define MAYBE_DontClose DontClose
#endif
TEST(MessageAttachmentSet, MAYBE_DontClose) {
  scoped_refptr<MessageAttachmentSet> set(new MessageAttachmentSet);

  const int fd = GetSafeFd();
  ASSERT_TRUE(set->AddAttachment(new internal::PlatformFileAttachment(fd)));
  set->CommitAllDescriptors();

  ASSERT_FALSE(VerifyClosed(fd));
}

TEST(MessageAttachmentSet, DoClose) {
  scoped_refptr<MessageAttachmentSet> set(new MessageAttachmentSet);

  const int fd = GetSafeFd();
  ASSERT_TRUE(set->AddAttachment(
      new internal::PlatformFileAttachment(base::ScopedFD(fd))));
  set->CommitAllDescriptors();

  ASSERT_TRUE(VerifyClosed(fd));
}

}  // namespace
}  // namespace IPC
