1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/proxy/raw_var_data.h"
6
7#include "base/logging.h"
8#include "base/memory/ref_counted.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/values.h"
11#include "ppapi/c/pp_bool.h"
12#include "ppapi/c/pp_var.h"
13#include "ppapi/shared_impl/array_var.h"
14#include "ppapi/shared_impl/dictionary_var.h"
15#include "ppapi/shared_impl/ppapi_globals.h"
16#include "ppapi/shared_impl/proxy_lock.h"
17#include "ppapi/shared_impl/resource_var.h"
18#include "ppapi/shared_impl/scoped_pp_var.h"
19#include "ppapi/shared_impl/test_globals.h"
20#include "ppapi/shared_impl/unittest_utils.h"
21#include "ppapi/shared_impl/var.h"
22#include "ppapi/shared_impl/var_tracker.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25namespace ppapi {
26namespace proxy {
27
28namespace {
29
30void DefaultHandleWriter(IPC::Message* m, const SerializedHandle& handle) {
31  IPC::ParamTraits<SerializedHandle>::Write(m, handle);
32}
33
34class RawVarDataTest : public testing::Test {
35 public:
36  RawVarDataTest() {}
37  ~RawVarDataTest() {}
38
39  // testing::Test implementation.
40  virtual void SetUp() {
41    ProxyLock::EnableLockingOnThreadForTest();
42    ProxyLock::Acquire();
43  }
44  virtual void TearDown() {
45    ASSERT_TRUE(PpapiGlobals::Get()->GetVarTracker()->GetLiveVars().empty());
46    ProxyLock::Release();
47  }
48
49 private:
50  TestGlobals globals_;
51};
52
53bool WriteAndRead(const PP_Var& var, PP_Var* result) {
54  PP_Instance dummy_instance = 1234;
55  scoped_ptr<RawVarDataGraph> expected_data(RawVarDataGraph::Create(
56      var, dummy_instance));
57  if (!expected_data)
58    return false;
59  IPC::Message m;
60  expected_data->Write(&m, base::Bind(&DefaultHandleWriter));
61  PickleIterator iter(m);
62  scoped_ptr<RawVarDataGraph> actual_data(RawVarDataGraph::Read(&m, &iter));
63  *result = actual_data->CreatePPVar(dummy_instance);
64  return true;
65}
66
67// Assumes a ref for var.
68bool WriteReadAndCompare(const PP_Var& var) {
69  ScopedPPVar expected(ScopedPPVar::PassRef(), var);
70  PP_Var result;
71  bool success = WriteAndRead(expected.get(), &result);
72  if (!success)
73    return false;
74  ScopedPPVar actual(ScopedPPVar::PassRef(), result);
75  return TestEqual(expected.get(), actual.get(), true);
76}
77
78}  // namespace
79
80TEST_F(RawVarDataTest, SimpleTest) {
81  EXPECT_TRUE(WriteReadAndCompare(PP_MakeUndefined()));
82  EXPECT_TRUE(WriteReadAndCompare(PP_MakeNull()));
83  EXPECT_TRUE(WriteReadAndCompare(PP_MakeInt32(100)));
84  EXPECT_TRUE(WriteReadAndCompare(PP_MakeBool(PP_TRUE)));
85  EXPECT_TRUE(WriteReadAndCompare(PP_MakeDouble(53.75)));
86  PP_Var object;
87  object.type = PP_VARTYPE_OBJECT;
88  object.value.as_id = 10;
89  EXPECT_TRUE(WriteReadAndCompare(object));
90}
91
92TEST_F(RawVarDataTest, StringTest) {
93  EXPECT_TRUE(WriteReadAndCompare(StringVar::StringToPPVar("")));
94  EXPECT_TRUE(WriteReadAndCompare(StringVar::StringToPPVar("hello world!")));
95}
96
97TEST_F(RawVarDataTest, ArrayBufferTest) {
98  std::string data = "hello world!";
99  PP_Var var = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
100      data.size(), data.data());
101  EXPECT_TRUE(WriteReadAndCompare(var));
102  var = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
103      0, static_cast<void*>(NULL));
104  EXPECT_TRUE(WriteReadAndCompare(var));
105  // TODO(raymes): add tests for shmem type array buffers.
106}
107
108TEST_F(RawVarDataTest, DictionaryArrayTest) {
109  // Empty array.
110  scoped_refptr<ArrayVar> array(new ArrayVar);
111  ScopedPPVar release_array(ScopedPPVar::PassRef(), array->GetPPVar());
112  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));
113
114  size_t index = 0;
115
116  // Array with primitives.
117  array->Set(index++, PP_MakeUndefined());
118  array->Set(index++, PP_MakeNull());
119  array->Set(index++, PP_MakeInt32(100));
120  array->Set(index++, PP_MakeBool(PP_FALSE));
121  array->Set(index++, PP_MakeDouble(0.123));
122  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));
123
124  // Array with 2 references to the same string.
125  ScopedPPVar release_string(
126      ScopedPPVar::PassRef(), StringVar::StringToPPVar("abc"));
127  array->Set(index++, release_string.get());
128  array->Set(index++, release_string.get());
129  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));
130
131  // Array with nested array that references the same string.
132  scoped_refptr<ArrayVar> array2(new ArrayVar);
133  ScopedPPVar release_array2(ScopedPPVar::PassRef(), array2->GetPPVar());
134  array2->Set(0, release_string.get());
135  array->Set(index++, release_array2.get());
136  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));
137
138  // Empty dictionary.
139  scoped_refptr<DictionaryVar> dictionary(new DictionaryVar);
140  ScopedPPVar release_dictionary(ScopedPPVar::PassRef(),
141                                 dictionary->GetPPVar());
142  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));
143
144  // Dictionary with primitives.
145  dictionary->SetWithStringKey("1", PP_MakeUndefined());
146  dictionary->SetWithStringKey("2", PP_MakeNull());
147  dictionary->SetWithStringKey("3", PP_MakeInt32(-100));
148  dictionary->SetWithStringKey("4", PP_MakeBool(PP_TRUE));
149  dictionary->SetWithStringKey("5", PP_MakeDouble(-103.52));
150  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));
151
152  // Dictionary with 2 references to the same string.
153  dictionary->SetWithStringKey("6", release_string.get());
154  dictionary->SetWithStringKey("7", release_string.get());
155  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));
156
157  // Dictionary with nested dictionary that references the same string.
158  scoped_refptr<DictionaryVar> dictionary2(new DictionaryVar);
159  ScopedPPVar release_dictionary2(ScopedPPVar::PassRef(),
160                                  dictionary2->GetPPVar());
161  dictionary2->SetWithStringKey("abc", release_string.get());
162  dictionary->SetWithStringKey("8", release_dictionary2.get());
163  EXPECT_TRUE(WriteReadAndCompare(dictionary->GetPPVar()));
164
165  // Array with dictionary.
166  array->Set(index++, release_dictionary.get());
167  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));
168
169  // Array with dictionary with array.
170  array2->Set(0, PP_MakeInt32(100));
171  dictionary->SetWithStringKey("9", release_array2.get());
172  EXPECT_TRUE(WriteReadAndCompare(array->GetPPVar()));
173
174  // Array <-> dictionary cycle.
175  dictionary->SetWithStringKey("10", release_array.get());
176  PP_Var result;
177  ASSERT_FALSE(WriteAndRead(release_dictionary.get(), &result));
178  // Break the cycle.
179  // TODO(raymes): We need some better machinery for releasing vars with
180  // cycles. Remove the code below once we have that.
181  dictionary->DeleteWithStringKey("10");
182
183  // Array with self references.
184  array->Set(index, release_array.get());
185  ASSERT_FALSE(WriteAndRead(release_array.get(), &result));
186  // Break the self reference.
187  array->Set(index, PP_MakeUndefined());
188}
189
190TEST_F(RawVarDataTest, ResourceTest) {
191  // TODO(mgiuca): This test passes trivially, since GetVarTracker() returns a
192  // TestVarTracker which returns a null PP_Var.
193  ScopedPPVar resource(
194      ScopedPPVar::PassRef(),
195      PpapiGlobals::Get()->GetVarTracker()->MakeResourcePPVar(34));
196  EXPECT_TRUE(WriteReadAndCompare(resource.get()));
197
198  // TODO(mgiuca): Test a host resource with an IPC::Message. It is currently a
199  // checkfail to deserialize such a resource.
200}
201
202}  // namespace proxy
203}  // namespace ppapi
204