// 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.
#include <vector>
#include "base/compiler_specific.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
#include "webkit/glue/cpp_variant.h"
using WebKit::WebBindings;
// Creates a std::string from an NPVariant of string type. If the NPVariant
// is not a string, empties the std::string.
void MakeStdString(const NPVariant& np, std::string* std_string) {
if (np.type == NPVariantType_String) {
const char* chars =
reinterpret_cast<const char*>(np.value.stringValue.UTF8Characters);
(*std_string).assign(chars, np.value.stringValue.UTF8Length);
} else {
(*std_string).clear();
}
}
// Verifies that the actual NPVariant is a string and that its value matches
// the expected_str.
void CheckString(const std::string& expected_str, const NPVariant& actual) {
EXPECT_EQ(NPVariantType_String, actual.type);
std::string actual_str;
MakeStdString(actual, &actual_str);
EXPECT_EQ(expected_str, actual_str);
}
// Verifies that both the actual and the expected NPVariants are strings and
// that their values match.
void CheckString(const NPVariant& expected, const NPVariant& actual) {
EXPECT_EQ(NPVariantType_String, expected.type);
std::string expected_str;
MakeStdString(expected, &expected_str);
CheckString(expected_str, actual);
}
int g_allocate_call_count = 0;
int g_deallocate_call_count = 0;
void CheckObject(const NPVariant& actual) {
EXPECT_EQ(NPVariantType_Object, actual.type);
EXPECT_TRUE(actual.value.objectValue);
EXPECT_EQ(1U, actual.value.objectValue->referenceCount);
EXPECT_EQ(1, g_allocate_call_count);
EXPECT_EQ(0, g_deallocate_call_count);
}
NPObject* MockNPAllocate(NPP npp, NPClass* aClass) {
// This is a mock allocate method that mimics the behavior
// of WebBindings::createObject when allocate() is NULL
++g_allocate_call_count;
// Ignore npp and NPClass
return reinterpret_cast<NPObject*>(malloc(sizeof(NPObject)));
}
void MockNPDeallocate(NPObject* npobj) {
// This is a mock deallocate method that mimics the behavior
// of NPN_DeallocateObject when deallocate() is NULL
++g_deallocate_call_count;
free(npobj);
}
static NPClass void_class = { NP_CLASS_STRUCT_VERSION,
MockNPAllocate,
MockNPDeallocate,
0, 0, 0, 0, 0, 0, 0, 0, 0 };
NPObject* MakeVoidObject() {
g_allocate_call_count = 0;
g_deallocate_call_count = 0;
return WebBindings::createObject(NULL, &void_class);
}
TEST(CppVariantTest, NewVariantHasNullType) {
CppVariant value;
EXPECT_EQ(NPVariantType_Null, value.type);
}
TEST(CppVariantTest, SetNullSetsType) {
CppVariant value;
value.Set(17);
value.SetNull();
EXPECT_EQ(NPVariantType_Null, value.type);
}
TEST(CppVariantTest, CopyConstructorDoesDeepCopy) {
CppVariant source;
source.Set("test string");
CppVariant dest = source;
EXPECT_EQ(NPVariantType_String, dest.type);
EXPECT_EQ(NPVariantType_String, source.type);
// Ensure that the string was copied, not just the pointer.
EXPECT_NE(source.value.stringValue.UTF8Characters,
dest.value.stringValue.UTF8Characters);
CheckString(source, dest);
}
TEST(CppVariantTest, CopyConstructorIncrementsRefCount) {
CppVariant source;
NPObject *object = MakeVoidObject();
source.Set(object);
// 2 references so far.
EXPECT_EQ(2U, source.value.objectValue->referenceCount);
CppVariant dest = source;
EXPECT_EQ(3U, dest.value.objectValue->referenceCount);
EXPECT_EQ(1, g_allocate_call_count);
WebBindings::releaseObject(object);
source.SetNull();
CheckObject(dest);
}
TEST(CppVariantTest, AssignmentDoesDeepCopy) {
CppVariant source;
source.Set("test string");
CppVariant dest;
dest = source;
EXPECT_EQ(NPVariantType_String, dest.type);
EXPECT_EQ(NPVariantType_String, source.type);
// Ensure that the string was copied, not just the pointer.
EXPECT_NE(source.value.stringValue.UTF8Characters,
dest.value.stringValue.UTF8Characters);
CheckString(source, dest);
}
TEST(CppVariantTest, AssignmentIncrementsRefCount) {
CppVariant source;
NPObject *object = MakeVoidObject();
source.Set(object);
// 2 references so far.
EXPECT_EQ(2U, source.value.objectValue->referenceCount);
CppVariant dest;
dest = source;
EXPECT_EQ(3U, dest.value.objectValue->referenceCount);
EXPECT_EQ(1, g_allocate_call_count);
WebBindings::releaseObject(object);
source.SetNull();
CheckObject(dest);
}
TEST(CppVariantTest, DestroyingCopyDoesNotCorruptSource) {
CppVariant source;
source.Set("test string");
std::string before;
MakeStdString(source, &before);
{
CppVariant dest = source;
}
CheckString(before, source);
NPObject *object = MakeVoidObject();
source.Set(object);
{
CppVariant dest2 = source;
}
WebBindings::releaseObject(object);
CheckObject(source);
}
TEST(CppVariantTest, CopiesTypeAndValueToNPVariant) {
NPVariant np;
CppVariant cpp;
cpp.Set(true);
cpp.CopyToNPVariant(&np);
EXPECT_EQ(cpp.type, np.type);
EXPECT_EQ(cpp.value.boolValue, np.value.boolValue);
WebBindings::releaseVariantValue(&np);
cpp.Set(17);
cpp.CopyToNPVariant(&np);
EXPECT_EQ(cpp.type, np.type);
EXPECT_EQ(cpp.value.intValue, np.value.intValue);
WebBindings::releaseVariantValue(&np);
cpp.Set(3.1415);
cpp.CopyToNPVariant(&np);
EXPECT_EQ(cpp.type, np.type);
EXPECT_EQ(cpp.value.doubleValue, np.value.doubleValue);
WebBindings::releaseVariantValue(&np);
cpp.Set("test value");
cpp.CopyToNPVariant(&np);
CheckString("test value", np);
WebBindings::releaseVariantValue(&np);
cpp.SetNull();
cpp.CopyToNPVariant(&np);
EXPECT_EQ(cpp.type, np.type);
WebBindings::releaseVariantValue(&np);
NPObject *object = MakeVoidObject();
cpp.Set(object);
cpp.CopyToNPVariant(&np);
WebBindings::releaseObject(object);
cpp.SetNull();
CheckObject(np);
WebBindings::releaseVariantValue(&np);
}
TEST(CppVariantTest, SetsTypeAndValueFromNPVariant) {
NPVariant np;
CppVariant cpp;
VOID_TO_NPVARIANT(np);
cpp.Set(np);
EXPECT_EQ(np.type, cpp.type);
WebBindings::releaseVariantValue(&np);
NULL_TO_NPVARIANT(np);
cpp.Set(np);
EXPECT_EQ(np.type, cpp.type);
WebBindings::releaseVariantValue(&np);
BOOLEAN_TO_NPVARIANT(true, np);
cpp.Set(np);
EXPECT_EQ(np.type, cpp.type);
EXPECT_EQ(np.value.boolValue, cpp.value.boolValue);
WebBindings::releaseVariantValue(&np);
INT32_TO_NPVARIANT(15, np);
cpp.Set(np);
EXPECT_EQ(np.type, cpp.type);
EXPECT_EQ(np.value.intValue, cpp.value.intValue);
WebBindings::releaseVariantValue(&np);
DOUBLE_TO_NPVARIANT(2.71828, np);
cpp.Set(np);
EXPECT_EQ(np.type, cpp.type);
EXPECT_EQ(np.value.doubleValue, cpp.value.doubleValue);
WebBindings::releaseVariantValue(&np);
NPString np_ascii_str = { "1st test value",
static_cast<uint32_t>(strlen("1st test value")) };
WebBindings::initializeVariantWithStringCopy(&np, &np_ascii_str);
cpp.Set(np);
CheckString("1st test value", cpp);
WebBindings::releaseVariantValue(&np);
// Test characters represented in 2/3/4 bytes in UTF-8
// Greek alpha, Chinese number 1 (horizontal bar),
// Deseret letter (similar to 'O')
NPString np_intl_str = { "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84",
static_cast<uint32_t>(strlen(
"\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84")) };
WebBindings::initializeVariantWithStringCopy(&np, &np_intl_str);
cpp.Set(np);
CheckString("\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84", cpp);
WebBindings::releaseVariantValue(&np);
NPObject *obj = MakeVoidObject();
OBJECT_TO_NPVARIANT(obj, np); // Doesn't make a copy.
cpp.Set(np);
// Use this or WebBindings::releaseObject but NOT both.
WebBindings::releaseVariantValue(&np);
CheckObject(cpp);
}
TEST(CppVariantTest, SetsSimpleTypesAndValues) {
CppVariant cpp;
cpp.Set(true);
EXPECT_EQ(NPVariantType_Bool, cpp.type);
EXPECT_TRUE(cpp.value.boolValue);
cpp.Set(5);
EXPECT_EQ(NPVariantType_Int32, cpp.type);
EXPECT_EQ(5, cpp.value.intValue);
cpp.Set(1.234);
EXPECT_EQ(NPVariantType_Double, cpp.type);
EXPECT_EQ(1.234, cpp.value.doubleValue);
// C string
cpp.Set("1st test string");
CheckString("1st test string", cpp);
// std::string
std::string source("std test string");
cpp.Set(source);
CheckString("std test string", cpp);
// NPString
NPString np_ascii_str = { "test NPString",
static_cast<uint32_t>(strlen("test NPString")) };
cpp.Set(np_ascii_str);
std::string expected("test NPString");
CheckString(expected, cpp);
// Test characters represented in 2/3/4 bytes in UTF-8
// Greek alpha, Chinese number 1 (horizontal bar),
// Deseret letter (similar to 'O')
NPString np_intl_str = { "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84",
static_cast<uint32_t>(strlen(
"\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84")) };
cpp.Set(np_intl_str);
expected = std::string("\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84");
CheckString(expected, cpp);
NPObject* obj = MakeVoidObject();
cpp.Set(obj);
WebBindings::releaseObject(obj);
CheckObject(cpp);
}
TEST(CppVariantTest, FreeDataSetsToVoid) {
CppVariant cpp;
EXPECT_EQ(NPVariantType_Null, cpp.type);
cpp.Set(12);
EXPECT_EQ(NPVariantType_Int32, cpp.type);
cpp.FreeData();
EXPECT_EQ(NPVariantType_Void, cpp.type);
}
TEST(CppVariantTest, FreeDataReleasesObject) {
CppVariant cpp;
NPObject* object = MakeVoidObject();
cpp.Set(object);
EXPECT_EQ(2U, object->referenceCount);
cpp.FreeData();
EXPECT_EQ(1U, object->referenceCount);
EXPECT_EQ(0, g_deallocate_call_count);
cpp.Set(object);
WebBindings::releaseObject(object);
EXPECT_EQ(0, g_deallocate_call_count);
cpp.FreeData();
EXPECT_EQ(1, g_deallocate_call_count);
}
TEST(CppVariantTest, IsTypeFunctionsWork) {
CppVariant cpp;
// These should not happen in practice, since voids are not supported
// This test must be first since it just clobbers internal data without
// releasing.
VOID_TO_NPVARIANT(cpp);
EXPECT_FALSE(cpp.isBool());
EXPECT_FALSE(cpp.isInt32());
EXPECT_FALSE(cpp.isDouble());
EXPECT_FALSE(cpp.isNumber());
EXPECT_FALSE(cpp.isString());
EXPECT_TRUE(cpp.isVoid());
EXPECT_FALSE(cpp.isNull());
EXPECT_TRUE(cpp.isEmpty());
cpp.Set(true);
EXPECT_TRUE(cpp.isBool());
EXPECT_FALSE(cpp.isInt32());
EXPECT_FALSE(cpp.isDouble());
EXPECT_FALSE(cpp.isNumber());
EXPECT_FALSE(cpp.isString());
EXPECT_FALSE(cpp.isVoid());
EXPECT_FALSE(cpp.isNull());
EXPECT_FALSE(cpp.isEmpty());
EXPECT_FALSE(cpp.isObject());
cpp.Set(12);
EXPECT_FALSE(cpp.isBool());
EXPECT_TRUE(cpp.isInt32());
EXPECT_FALSE(cpp.isDouble());
EXPECT_TRUE(cpp.isNumber());
EXPECT_FALSE(cpp.isString());
EXPECT_FALSE(cpp.isVoid());
EXPECT_FALSE(cpp.isNull());
EXPECT_FALSE(cpp.isEmpty());
EXPECT_FALSE(cpp.isObject());
cpp.Set(3.1415);
EXPECT_FALSE(cpp.isBool());
EXPECT_FALSE(cpp.isInt32());
EXPECT_TRUE(cpp.isDouble());
EXPECT_TRUE(cpp.isNumber());
EXPECT_FALSE(cpp.isString());
EXPECT_FALSE(cpp.isVoid());
EXPECT_FALSE(cpp.isNull());
EXPECT_FALSE(cpp.isEmpty());
EXPECT_FALSE(cpp.isObject());
cpp.Set("a string");
EXPECT_FALSE(cpp.isBool());
EXPECT_FALSE(cpp.isInt32());
EXPECT_FALSE(cpp.isDouble());
EXPECT_FALSE(cpp.isNumber());
EXPECT_TRUE(cpp.isString());
EXPECT_FALSE(cpp.isVoid());
EXPECT_FALSE(cpp.isNull());
EXPECT_FALSE(cpp.isEmpty());
EXPECT_FALSE(cpp.isObject());
cpp.SetNull();
EXPECT_FALSE(cpp.isBool());
EXPECT_FALSE(cpp.isInt32());
EXPECT_FALSE(cpp.isDouble());
EXPECT_FALSE(cpp.isNumber());
EXPECT_FALSE(cpp.isString());
EXPECT_FALSE(cpp.isVoid());
EXPECT_TRUE(cpp.isNull());
EXPECT_TRUE(cpp.isEmpty());
EXPECT_FALSE(cpp.isObject());
NPObject *obj = MakeVoidObject();
cpp.Set(obj);
EXPECT_FALSE(cpp.isBool());
EXPECT_FALSE(cpp.isInt32());
EXPECT_FALSE(cpp.isDouble());
EXPECT_FALSE(cpp.isNumber());
EXPECT_FALSE(cpp.isString());
EXPECT_FALSE(cpp.isVoid());
EXPECT_FALSE(cpp.isNull());
EXPECT_FALSE(cpp.isEmpty());
EXPECT_TRUE(cpp.isObject());
WebBindings::releaseObject(obj);
CheckObject(cpp);
}
bool MockNPHasPropertyFunction(NPObject *npobj, NPIdentifier name) {
return true;
}
bool MockNPGetPropertyFunction(NPObject *npobj, NPIdentifier name,
NPVariant *result) {
if (WebBindings::getStringIdentifier("length") == name) {
DOUBLE_TO_NPVARIANT(4, *result);
} else if (WebBindings::getIntIdentifier(0) == name) {
DOUBLE_TO_NPVARIANT(0, *result);
} else if (WebBindings::getIntIdentifier(1) == name) {
BOOLEAN_TO_NPVARIANT(true, *result);
} else if (WebBindings::getIntIdentifier(2) == name) {
NULL_TO_NPVARIANT(*result);
} else if (WebBindings::getIntIdentifier(3) == name) {
const char* s = "string";
size_t length = strlen(s);
char* mem = static_cast<char*>(malloc(length + 1));
base::strlcpy(mem, s, length + 1);
STRINGZ_TO_NPVARIANT(mem, *result);
}
return true;
}
TEST(CppVariantTest, ToVector) {
NPClass array_like_class = {
NP_CLASS_STRUCT_VERSION,
0, // NPAllocateFunctionPtr allocate;
0, // NPDeallocateFunctionPtr deallocate;
0, // NPInvalidateFunctionPtr invalidate;
0, // NPHasMethodFunctionPtr hasMethod;
0, // NPInvokeFunctionPtr invoke;
0, // NPInvokeDefaultFunctionPtr invokeDefault;
MockNPHasPropertyFunction, // NPHasPropertyFunctionPtr hasProperty;
MockNPGetPropertyFunction, // NPGetPropertyFunctionPtr getProperty;
0, // NPSetPropertyFunctionPtr setProperty;
0, // NPRemovePropertyFunctionPtr removeProperty;
0, // NPEnumerationFunctionPtr enumerate;
0 // NPConstructFunctionPtr construct;
};
NPObject* obj = WebBindings::createObject(NULL, &array_like_class);
CppVariant cpp;
cpp.Set(obj);
std::vector<CppVariant> cpp_vector = cpp.ToVector();
EXPECT_EQ(4u, cpp_vector.size());
EXPECT_TRUE(cpp_vector[0].isDouble());
EXPECT_EQ(0, cpp_vector[0].ToDouble());
EXPECT_TRUE(cpp_vector[1].isBool());
EXPECT_EQ(true, cpp_vector[1].ToBoolean());
EXPECT_TRUE(cpp_vector[2].isNull());
EXPECT_TRUE(cpp_vector[3].isString());
CheckString("string", cpp_vector[3]);
WebBindings::releaseObject(obj);
}