root/ppapi/shared_impl/var_value_conversions_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Equals
  2. ConvertVarAndVerify
  3. ConvertValueAndVerify
  4. SetUp
  5. TearDown
  6. TEST_F
  7. TEST_F
  8. TEST_F
  9. TEST_F

// Copyright (c) 2013 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/shared_impl/var_value_conversions.h"

#include <cmath>
#include <cstring>

#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/scoped_pp_var.h"
#include "ppapi/shared_impl/test_globals.h"
#include "ppapi/shared_impl/var.h"
#include "ppapi/shared_impl/var_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ppapi {
namespace {

bool Equals(const base::Value& value, const PP_Var& var) {
  switch (value.GetType()) {
    case base::Value::TYPE_NULL: {
      return var.type == PP_VARTYPE_NULL || var.type == PP_VARTYPE_UNDEFINED;
    }
    case base::Value::TYPE_BOOLEAN: {
      bool result = false;
      return var.type == PP_VARTYPE_BOOL && value.GetAsBoolean(&result) &&
             result == PP_ToBool(var.value.as_bool);
    }
    case base::Value::TYPE_INTEGER: {
      int result = 0;
      return var.type == PP_VARTYPE_INT32 && value.GetAsInteger(&result) &&
             result == var.value.as_int;
    }
    case base::Value::TYPE_DOUBLE: {
      double result = 0;
      return var.type == PP_VARTYPE_DOUBLE && value.GetAsDouble(&result) &&
             fabs(result - var.value.as_double) < 1.0e-4;
    }
    case base::Value::TYPE_STRING: {
      std::string result;
      StringVar* string_var = StringVar::FromPPVar(var);
      return string_var && value.GetAsString(&result) &&
             result == string_var->value();
    }
    case base::Value::TYPE_BINARY: {
      const base::BinaryValue& binary_value =
          static_cast<const base::BinaryValue&>(value);
      ArrayBufferVar* array_buffer_var = ArrayBufferVar::FromPPVar(var);
      if (!array_buffer_var ||
          binary_value.GetSize() != array_buffer_var->ByteLength()) {
        return false;
      }

      bool result = !memcmp(binary_value.GetBuffer(),
                            array_buffer_var->Map(),
                            binary_value.GetSize());
      array_buffer_var->Unmap();
      return result;
    }
    case base::Value::TYPE_DICTIONARY: {
      const base::DictionaryValue& dict_value =
          static_cast<const base::DictionaryValue&>(value);
      DictionaryVar* dict_var = DictionaryVar::FromPPVar(var);
      if (!dict_var)
        return false;

      size_t count = 0;
      for (DictionaryVar::KeyValueMap::const_iterator iter =
               dict_var->key_value_map().begin();
           iter != dict_var->key_value_map().end();
           ++iter) {
        if (iter->second.get().type == PP_VARTYPE_UNDEFINED ||
            iter->second.get().type == PP_VARTYPE_NULL) {
          continue;
        }

        ++count;
        const base::Value* sub_value = NULL;
        if (!dict_value.GetWithoutPathExpansion(iter->first, &sub_value) ||
            !Equals(*sub_value, iter->second.get())) {
          return false;
        }
      }
      return count == dict_value.size();
    }
    case base::Value::TYPE_LIST: {
      const base::ListValue& list_value =
          static_cast<const base::ListValue&>(value);
      ArrayVar* array_var = ArrayVar::FromPPVar(var);
      if (!array_var || list_value.GetSize() != array_var->elements().size())
        return false;

      base::ListValue::const_iterator value_iter = list_value.begin();
      ArrayVar::ElementVector::const_iterator var_iter =
          array_var->elements().begin();
      for (; value_iter != list_value.end() &&
                 var_iter != array_var->elements().end();
           ++value_iter, ++var_iter) {
        if (!Equals(**value_iter, var_iter->get()))
          return false;
      }
      return true;
    }
  }
  NOTREACHED();
  return false;
}

bool ConvertVarAndVerify(const PP_Var& var) {
  scoped_ptr<base::Value> value(CreateValueFromVar(var));
  if (value.get())
    return Equals(*value, var);
  return false;
}

bool ConvertValueAndVerify(const base::Value& value) {
  ScopedPPVar var(ScopedPPVar::PassRef(), CreateVarFromValue(value));
  if (var.get().type != PP_VARTYPE_UNDEFINED)
    return Equals(value, var.get());
  return false;
}

class VarValueConversionsTest : public testing::Test {
 public:
  VarValueConversionsTest() {}
  virtual ~VarValueConversionsTest() {}

  // 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_;
};

}  // namespace

TEST_F(VarValueConversionsTest, CreateValueFromVar) {
  {
    // Var holding a ref to itself is not a valid input.
    scoped_refptr<DictionaryVar> dict_var(new DictionaryVar());
    ScopedPPVar var_1(ScopedPPVar::PassRef(), dict_var->GetPPVar());
    scoped_refptr<ArrayVar> array_var(new ArrayVar());
    ScopedPPVar var_2(ScopedPPVar::PassRef(), array_var->GetPPVar());

    ASSERT_TRUE(dict_var->SetWithStringKey("key_1", var_2.get()));
    ASSERT_TRUE(ConvertVarAndVerify(var_1.get()));

    ASSERT_TRUE(array_var->Set(0, var_1.get()));
    scoped_ptr<base::Value> value(CreateValueFromVar(var_1.get()));
    ASSERT_EQ(NULL, value.get());

    // Make sure |var_1| doesn't indirectly hold a ref to itself, otherwise it
    // is leaked.
    dict_var->DeleteWithStringKey("key_1");
  }

  // Vars of null or undefined type are converted to null values.
  {
    ASSERT_TRUE(ConvertVarAndVerify(PP_MakeNull()));
    ASSERT_TRUE(ConvertVarAndVerify(PP_MakeUndefined()));
  }

  {
    // Test empty dictionary.
    scoped_refptr<DictionaryVar> dict_var(new DictionaryVar());
    ScopedPPVar var(ScopedPPVar::PassRef(), dict_var->GetPPVar());

    ASSERT_TRUE(ConvertVarAndVerify(var.get()));
  }

  {
    // Key-value pairs whose value is undefined or null are ignored.
    scoped_refptr<DictionaryVar> dict_var(new DictionaryVar());
    ASSERT_TRUE(dict_var->SetWithStringKey("key_1", PP_MakeUndefined()));
    ASSERT_TRUE(dict_var->SetWithStringKey("key_2", PP_MakeInt32(1)));
    ASSERT_TRUE(dict_var->SetWithStringKey("key_3", PP_MakeNull()));
    ScopedPPVar var(ScopedPPVar::PassRef(), dict_var->GetPPVar());

    ASSERT_TRUE(ConvertVarAndVerify(var.get()));
  }

  {
    // The same PP_Var is allowed to appear multiple times.
    scoped_refptr<DictionaryVar> dict_var_1(new DictionaryVar());
    ScopedPPVar dict_pp_var_1(ScopedPPVar::PassRef(), dict_var_1->GetPPVar());
    scoped_refptr<DictionaryVar> dict_var_2(new DictionaryVar());
    ScopedPPVar dict_pp_var_2(ScopedPPVar::PassRef(), dict_var_2->GetPPVar());
    scoped_refptr<StringVar> string_var(new StringVar("string_value"));
    ScopedPPVar string_pp_var(ScopedPPVar::PassRef(), string_var->GetPPVar());

    ASSERT_TRUE(dict_var_1->SetWithStringKey("key_1", dict_pp_var_2.get()));
    ASSERT_TRUE(dict_var_1->SetWithStringKey("key_2", dict_pp_var_2.get()));
    ASSERT_TRUE(dict_var_1->SetWithStringKey("key_3", string_pp_var.get()));
    ASSERT_TRUE(dict_var_2->SetWithStringKey("key_4", string_pp_var.get()));

    ASSERT_TRUE(ConvertVarAndVerify(dict_pp_var_1.get()));
  }

  {
    // Test basic cases for array.
    scoped_refptr<ArrayVar> array_var(new ArrayVar());
    ScopedPPVar var(ScopedPPVar::PassRef(), array_var->GetPPVar());

    ASSERT_TRUE(ConvertVarAndVerify(var.get()));

    ASSERT_TRUE(array_var->Set(0, PP_MakeDouble(1)));

    ASSERT_TRUE(ConvertVarAndVerify(var.get()));
  }

  {
    // Test more complex inputs.
    scoped_refptr<DictionaryVar> dict_var_1(new DictionaryVar());
    ScopedPPVar dict_pp_var_1(ScopedPPVar::PassRef(), dict_var_1->GetPPVar());
    scoped_refptr<DictionaryVar> dict_var_2(new DictionaryVar());
    ScopedPPVar dict_pp_var_2(ScopedPPVar::PassRef(), dict_var_2->GetPPVar());
    scoped_refptr<ArrayVar> array_var(new ArrayVar());
    ScopedPPVar array_pp_var(ScopedPPVar::PassRef(), array_var->GetPPVar());
    scoped_refptr<StringVar> string_var(new StringVar("string_value"));
    ScopedPPVar string_pp_var(ScopedPPVar::PassRef(), string_var->GetPPVar());

    ASSERT_TRUE(dict_var_1->SetWithStringKey("null_key", PP_MakeNull()));
    ASSERT_TRUE(
        dict_var_1->SetWithStringKey("string_key", string_pp_var.get()));
    ASSERT_TRUE(dict_var_1->SetWithStringKey("dict_key", dict_pp_var_2.get()));

    ASSERT_TRUE(
        dict_var_2->SetWithStringKey("undefined_key", PP_MakeUndefined()));
    ASSERT_TRUE(dict_var_2->SetWithStringKey("double_key", PP_MakeDouble(1)));
    ASSERT_TRUE(dict_var_2->SetWithStringKey("array_key", array_pp_var.get()));

    ASSERT_TRUE(array_var->Set(0, PP_MakeInt32(2)));
    ASSERT_TRUE(array_var->Set(1, PP_MakeBool(PP_TRUE)));
    ASSERT_TRUE(array_var->SetLength(4));

    ASSERT_TRUE(ConvertVarAndVerify(dict_pp_var_1.get()));
  }

  {
    // Test that dictionary keys containing '.' are handled correctly.
    scoped_refptr<DictionaryVar> dict_var(new DictionaryVar());
    ScopedPPVar dict_pp_var(ScopedPPVar::PassRef(), dict_var->GetPPVar());

    ASSERT_TRUE(dict_var->SetWithStringKey("double.key", PP_MakeDouble(1)));
    ASSERT_TRUE(dict_var->SetWithStringKey("int.key..name", PP_MakeInt32(2)));

    ASSERT_TRUE(ConvertVarAndVerify(dict_pp_var.get()));
  }
}

TEST_F(VarValueConversionsTest, CreateVarFromValue) {
  {
    // Test basic cases for dictionary.
    base::DictionaryValue dict_value;
    ASSERT_TRUE(ConvertValueAndVerify(dict_value));

    dict_value.SetInteger("int_key", 1);
    ASSERT_TRUE(ConvertValueAndVerify(dict_value));
  }

  {
    // Test basic cases for array.
    base::ListValue list_value;
    ASSERT_TRUE(ConvertValueAndVerify(list_value));

    list_value.AppendInteger(1);
    ASSERT_TRUE(ConvertValueAndVerify(list_value));
  }

  {
    // Test more complex inputs.
    base::DictionaryValue dict_value;
    dict_value.SetString("string_key", "string_value");
    dict_value.SetDouble("dict_key.double_key", 1);

    scoped_ptr<base::ListValue> list_value(new base::ListValue());
    list_value->AppendInteger(2);
    list_value->AppendBoolean(true);
    list_value->Append(base::Value::CreateNullValue());

    dict_value.Set("dict_key.array_key", list_value.release());

    ASSERT_TRUE(ConvertValueAndVerify(dict_value));
  }
}

TEST_F(VarValueConversionsTest, CreateListValueFromVarVector) {
  {
    // Test empty var vector.
    scoped_ptr<base::ListValue> list_value(
        CreateListValueFromVarVector(std::vector<PP_Var>()));
    ASSERT_TRUE(list_value.get());
    ASSERT_EQ(0u, list_value->GetSize());
  }

  {
    // Test more complex inputs.
    scoped_refptr<StringVar> string_var(new StringVar("string_value"));
    ScopedPPVar string_pp_var(ScopedPPVar::PassRef(), string_var->GetPPVar());

    scoped_refptr<DictionaryVar> dict_var(new DictionaryVar());
    ScopedPPVar dict_pp_var(ScopedPPVar::PassRef(), dict_var->GetPPVar());
    ASSERT_TRUE(dict_var->SetWithStringKey("null_key", PP_MakeNull()));
    ASSERT_TRUE(dict_var->SetWithStringKey("string_key", string_pp_var.get()));

    scoped_refptr<ArrayVar> array_var(new ArrayVar());
    ScopedPPVar array_pp_var(ScopedPPVar::PassRef(), array_var->GetPPVar());
    ASSERT_TRUE(array_var->Set(0, PP_MakeInt32(2)));
    ASSERT_TRUE(array_var->Set(1, PP_MakeBool(PP_TRUE)));
    ASSERT_TRUE(array_var->SetLength(4));

    std::vector<PP_Var> vars;
    vars.push_back(dict_pp_var.get());
    vars.push_back(string_pp_var.get());
    vars.push_back(array_pp_var.get());
    vars.push_back(PP_MakeDouble(1));
    vars.push_back(PP_MakeUndefined());
    vars.push_back(PP_MakeNull());

    scoped_ptr<base::ListValue> list_value(CreateListValueFromVarVector(vars));

    ASSERT_TRUE(list_value.get());
    ASSERT_EQ(vars.size(), list_value->GetSize());

    for (size_t i = 0; i < list_value->GetSize(); ++i) {
      const base::Value* value = NULL;
      ASSERT_TRUE(list_value->Get(i, &value));
      ASSERT_TRUE(Equals(*value, vars[i]));
    }
  }
}

TEST_F(VarValueConversionsTest, CreateVarVectorFromListValue) {
  {
    // Test empty list.
    base::ListValue list_value;
    std::vector<PP_Var> vars;
    ASSERT_TRUE(CreateVarVectorFromListValue(list_value, &vars));
    ASSERT_EQ(0u, vars.size());
  }

  {
    // Test more complex inputs.
    base::ListValue list_value;

    scoped_ptr<base::DictionaryValue> dict_value(new base::DictionaryValue());
    dict_value->SetString("string_key", "string_value");

    scoped_ptr<base::ListValue> sub_list_value(new base::ListValue());
    sub_list_value->AppendInteger(2);
    sub_list_value->AppendBoolean(true);

    list_value.Append(dict_value.release());
    list_value.AppendString("string_value");
    list_value.Append(sub_list_value.release());
    list_value.AppendDouble(1);
    list_value.Append(base::Value::CreateNullValue());

    std::vector<PP_Var> vars;
    ASSERT_TRUE(CreateVarVectorFromListValue(list_value, &vars));

    ASSERT_EQ(list_value.GetSize(), vars.size());

    for (size_t i = 0; i < list_value.GetSize(); ++i) {
      const base::Value* value = NULL;
      ASSERT_TRUE(list_value.Get(i, &value));
      ASSERT_TRUE(Equals(*value, vars[i]));

      PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(vars[i]);
    }
  }
}

}  // namespace ppapi

/* [<][>][^][v][top][bottom][index][help] */