// Copyright (c) 2012 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.
#include "ppapi/proxy/raw_var_data.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.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/unittest_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(IPC::Message* 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::EnableLockingOnThreadForTest();
ProxyLock::Acquire();
}
virtual void TearDown() {
ASSERT_TRUE(PpapiGlobals::Get()->GetVarTracker()->GetLiveVars().empty());
ProxyLock::Release();
}
private:
TestGlobals globals_;
};
bool WriteAndRead(const PP_Var& var, PP_Var* result) {
PP_Instance dummy_instance = 1234;
scoped_ptr<RawVarDataGraph> expected_data(RawVarDataGraph::Create(
var, dummy_instance));
if (!expected_data)
return false;
IPC::Message m;
expected_data->Write(&m, base::Bind(&DefaultHandleWriter));
PickleIterator iter(m);
scoped_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());
}
} // 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(
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(index++, PP_MakeUndefined());
array->Set(index++, PP_MakeNull());
array->Set(index++, PP_MakeInt32(100));
array->Set(index++, PP_MakeBool(PP_FALSE));
array->Set(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(index++, release_string.get());
array->Set(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(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(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(index, release_array.get());
ASSERT_FALSE(WriteAndRead(release_array.get(), &result));
// Break the self reference.
array->Set(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