chromium/ppapi/proxy/raw_var_data_unittest.cc

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

#include "ppapi/proxy/raw_var_data.h"

#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "base/functional/bind.h"
#include "base/memory/ref_counted.h"
#include "base/test/task_environment.h"
#include "base/values.h"
#include "ipc/ipc_message.h"
#include "ppapi/c/pp_bool.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/shared_impl/array_var.h"
#include "ppapi/shared_impl/dictionary_var.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/proxy_lock.h"
#include "ppapi/shared_impl/resource_var.h"
#include "ppapi/shared_impl/scoped_pp_var.h"
#include "ppapi/shared_impl/test_globals.h"
#include "ppapi/shared_impl/test_utils.h"
#include "ppapi/shared_impl/var.h"
#include "ppapi/shared_impl/var_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ppapi {
namespace proxy {

namespace {

void DefaultHandleWriter(base::Pickle* m, const SerializedHandle& handle) {
  IPC::ParamTraits<SerializedHandle>::Write(m, handle);
}

class RawVarDataTest : public testing::Test {
 public:
  RawVarDataTest() {}
  ~RawVarDataTest() {}

  // testing::Test implementation.
  virtual void SetUp() {
    ProxyLock::Acquire();
  }
  virtual void TearDown() {
    ASSERT_TRUE(PpapiGlobals::Get()->GetVarTracker()->GetLiveVars().empty());
    ProxyLock::Release();
  }

 private:
  base::test::SingleThreadTaskEnvironment
      task_environment_;  // Required to receive callbacks.
  TestGlobals globals_;
};

bool WriteAndRead(const PP_Var& var, PP_Var* result) {
  PP_Instance dummy_instance = 1234;
  std::unique_ptr<RawVarDataGraph> expected_data(
      RawVarDataGraph::Create(var, dummy_instance));
  if (!expected_data)
    return false;
  IPC::Message m;
  expected_data->Write(&m, base::BindRepeating(&DefaultHandleWriter));
  base::PickleIterator iter(m);
  std::unique_ptr<RawVarDataGraph> actual_data(
      RawVarDataGraph::Read(&m, &iter));
  *result = actual_data->CreatePPVar(dummy_instance);
  return true;
}

// Assumes a ref for var.
bool WriteReadAndCompare(const PP_Var& var) {
  ScopedPPVar expected(ScopedPPVar::PassRef(), var);
  PP_Var result;
  bool success = WriteAndRead(expected.get(), &result);
  if (!success)
    return false;
  ScopedPPVar actual(ScopedPPVar::PassRef(), result);
  return TestEqual(expected.get(), actual.get(), true);
}

}  // namespace

TEST_F(RawVarDataTest, SimpleTest) {
  EXPECT_TRUE(WriteReadAndCompare(PP_MakeUndefined()));
  EXPECT_TRUE(WriteReadAndCompare(PP_MakeNull()));
  EXPECT_TRUE(WriteReadAndCompare(PP_MakeInt32(100)));
  EXPECT_TRUE(WriteReadAndCompare(PP_MakeBool(PP_TRUE)));
  EXPECT_TRUE(WriteReadAndCompare(PP_MakeDouble(53.75)));
  PP_Var object;
  object.type = PP_VARTYPE_OBJECT;
  object.value.as_id = 10;
  EXPECT_TRUE(WriteReadAndCompare(object));
}

TEST_F(RawVarDataTest, StringTest) {
  EXPECT_TRUE(WriteReadAndCompare(StringVar::StringToPPVar("")));
  EXPECT_TRUE(WriteReadAndCompare(StringVar::StringToPPVar("hello world!")));
}

TEST_F(RawVarDataTest, ArrayBufferTest) {
  std::string data = "hello world!";
  PP_Var var = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
      static_cast<uint32_t>(data.size()), data.data());
  EXPECT_TRUE(WriteReadAndCompare(var));
  var = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
      0, static_cast<void*>(NULL));
  EXPECT_TRUE(WriteReadAndCompare(var));
  // TODO(raymes): add tests for shmem type array buffers.
}

TEST_F(RawVarDataTest, DictionaryArrayTest) {
  // Empty array.
  scoped_refptr<ArrayVar> array(new ArrayVar);
  ScopedPPVar release_array(ScopedPPVar::PassRef(), array->GetPPVar());
  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));

  size_t index = 0;

  // Array with primitives.
  array->Set(static_cast<uint32_t>(index++), PP_MakeUndefined());
  array->Set(static_cast<uint32_t>(index++), PP_MakeNull());
  array->Set(static_cast<uint32_t>(index++), PP_MakeInt32(100));
  array->Set(static_cast<uint32_t>(index++), PP_MakeBool(PP_FALSE));
  array->Set(static_cast<uint32_t>(index++), PP_MakeDouble(0.123));
  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));

  // Array with 2 references to the same string.
  ScopedPPVar release_string(
      ScopedPPVar::PassRef(), StringVar::StringToPPVar("abc"));
  array->Set(static_cast<uint32_t>(index++), release_string.get());
  array->Set(static_cast<uint32_t>(index++), release_string.get());
  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));

  // Array with nested array that references the same string.
  scoped_refptr<ArrayVar> array2(new ArrayVar);
  ScopedPPVar release_array2(ScopedPPVar::PassRef(), array2->GetPPVar());
  array2->Set(0, release_string.get());
  array->Set(static_cast<uint32_t>(index++), release_array2.get());
  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));

  // Empty dictionary.
  scoped_refptr<DictionaryVar> dictionary(new DictionaryVar);
  ScopedPPVar release_dictionary(ScopedPPVar::PassRef(),
                                 dictionary->GetPPVar());
  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));

  // Dictionary with primitives.
  dictionary->SetWithStringKey("1", PP_MakeUndefined());
  dictionary->SetWithStringKey("2", PP_MakeNull());
  dictionary->SetWithStringKey("3", PP_MakeInt32(-100));
  dictionary->SetWithStringKey("4", PP_MakeBool(PP_TRUE));
  dictionary->SetWithStringKey("5", PP_MakeDouble(-103.52));
  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));

  // Dictionary with 2 references to the same string.
  dictionary->SetWithStringKey("6", release_string.get());
  dictionary->SetWithStringKey("7", release_string.get());
  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));

  // Dictionary with nested dictionary that references the same string.
  scoped_refptr<DictionaryVar> dictionary2(new DictionaryVar);
  ScopedPPVar release_dictionary2(ScopedPPVar::PassRef(),
                                  dictionary2->GetPPVar());
  dictionary2->SetWithStringKey("abc", release_string.get());
  dictionary->SetWithStringKey("8", release_dictionary2.get());
  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));

  // Array with dictionary.
  array->Set(static_cast<uint32_t>(index++), release_dictionary.get());
  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));

  // Array with dictionary with array.
  array2->Set(0, PP_MakeInt32(100));
  dictionary->SetWithStringKey("9", release_array2.get());
  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));

  // Array <-> dictionary cycle.
  dictionary->SetWithStringKey("10", release_array.get());
  PP_Var result;
  ASSERT_FALSE(WriteAndRead(release_dictionary.get(), &result));
  // Break the cycle.
  // TODO(raymes): We need some better machinery for releasing vars with
  // cycles. Remove the code below once we have that.
  dictionary->DeleteWithStringKey("10");

  // Array with self references.
  array->Set(static_cast<uint32_t>(index), release_array.get());
  ASSERT_FALSE(WriteAndRead(release_array.get(), &result));
  // Break the self reference.
  array->Set(static_cast<uint32_t>(index), PP_MakeUndefined());
}

TEST_F(RawVarDataTest, ResourceTest) {
  // TODO(mgiuca): This test passes trivially, since GetVarTracker() returns a
  // TestVarTracker which returns a null PP_Var.
  ScopedPPVar resource(
      ScopedPPVar::PassRef(),
      PpapiGlobals::Get()->GetVarTracker()->MakeResourcePPVar(34));
  EXPECT_TRUE(WriteReadAndCompare(resource.get()));

  // TODO(mgiuca): Test a host resource with an IPC::Message. It is currently a
  // checkfail to deserialize such a resource.
}

}  // namespace proxy
}  // namespace ppapi