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