diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/cpp-unit-tests/IcedTeaJavaRequestProcessorTest.cc | 416 | ||||
-rw-r--r-- | tests/cpp-unit-tests/IcedTeaNPPluginTest.cc | 32 | ||||
-rw-r--r-- | tests/cpp-unit-tests/IcedTeaPluginUtilsTest.cc | 4 | ||||
-rw-r--r-- | tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc | 33 | ||||
-rw-r--r-- | tests/cpp-unit-tests/MemoryLeakDetector.h | 2 | ||||
-rw-r--r-- | tests/cpp-unit-tests/browser_mock.cc | 56 | ||||
-rw-r--r-- | tests/cpp-unit-tests/browser_mock.h | 4 | ||||
-rw-r--r-- | tests/cpp-unit-tests/browser_mock_npidentifier.cc | 117 | ||||
-rw-r--r-- | tests/cpp-unit-tests/browser_mock_npidentifier.h | 57 | ||||
-rw-r--r-- | tests/cpp-unit-tests/checked_allocations.h | 6 | ||||
-rw-r--r-- | tests/cpp-unit-tests/main.cc | 27 |
11 files changed, 731 insertions, 23 deletions
diff --git a/tests/cpp-unit-tests/IcedTeaJavaRequestProcessorTest.cc b/tests/cpp-unit-tests/IcedTeaJavaRequestProcessorTest.cc new file mode 100644 index 0000000..517d5d0 --- /dev/null +++ b/tests/cpp-unit-tests/IcedTeaJavaRequestProcessorTest.cc @@ -0,0 +1,416 @@ +/* Copyright (C) 2013 Red Hat + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +#include <cstdio> + +#include <vector> +#include <string> + +#include <npapi.h> + +#include <UnitTest++.h> + +#include "MemoryLeakDetector.h" + +#include "IcedTeaJavaRequestProcessor.h" + +/****************************************************************************** + * Simple helper methods to keep the tests clean. * + ******************************************************************************/ + +static const char* TEST_SOURCE = "[System]"; + +static std::string checked_return(JavaResultData* result) { + CHECK(!result->error_occurred); + return *result->return_string; +} + +// Packages + +static bool jrp_has_package(std::string package_name) { + JavaRequestProcessor processor; + JavaResultData* result = processor.hasPackage(0, package_name); + CHECK(!result->error_occurred); + return (result->return_identifier != 0); +} + +// Classes + +static std::string jrp_find_class(std::string name) { + return checked_return( + JavaRequestProcessor().findClass(0, name) + ); +} + +// Object creation + +static std::string jrp_new_object_with_constructor(std::string class_id, + std::string method_id, + std::vector<std::string> args = std::vector<std::string>()) { + return checked_return( + JavaRequestProcessor().newObjectWithConstructor(TEST_SOURCE, + class_id, method_id, args) + ); +} + +static std::string jrp_new_array(std::string class_id, std::string len) { + return checked_return( + JavaRequestProcessor().newArray(class_id, len) + ); +} + +static std::string jrp_new_string(std::string str) { + return checked_return( + JavaRequestProcessor().newString(str) + ); +} + +static std::string jrp_new_object(std::string class_id, + std::vector<std::string> args = std::vector<std::string>()) { + return checked_return( + JavaRequestProcessor().newObject(TEST_SOURCE, class_id, args) + ); +} + +static std::string jrp_get_value(std::string object_id) { + return checked_return( + JavaRequestProcessor().getValue(object_id) + ); +} + +// Inheritance + +static bool jrp_is_instance_of(std::string object_id, std::string class_id) { + JavaRequestProcessor processor; + JavaResultData* result = processor.isInstanceOf(object_id, class_id); + + CHECK(!result->error_occurred); + return (result->return_identifier != 0); +} + + +// Java methods operations. + +static std::string jrp_get_method_id(std::string class_id, + std::string method_name, + std::vector<std::string> args = std::vector<std::string>()) { + return checked_return( + JavaRequestProcessor().getMethodID(class_id, + browser_functions.getstringidentifier(method_name.c_str()), args) + ); +} + +static std::string jrp_get_static_method_id(std::string class_id, + std::string method_name, + std::vector<std::string> args = std::vector<std::string>()) { + return checked_return( + JavaRequestProcessor().getStaticMethodID(class_id, + browser_functions.getstringidentifier(method_name.c_str()), args) + ); +} + +static std::string jrp_call_method(std::string object_id, + std::string method_name, std::vector<std::string> args = std::vector<std::string>()) { + return checked_return( + JavaRequestProcessor().callMethod(TEST_SOURCE, object_id, + method_name, args) + ); +} + +static std::string jrp_call_static_method(std::string class_id, + std::string method_name, std::vector<std::string> args = std::vector<std::string>()) { + return checked_return( + JavaRequestProcessor().callStaticMethod(TEST_SOURCE, class_id, + method_name, args) + ); +} + +static std::string jrp_get_string(std::string object_id) { + return checked_return( + JavaRequestProcessor().getString(object_id) + ); +} + +static std::string jrp_get_class_id(std::string object_id) { + return checked_return( + JavaRequestProcessor().getClassID(object_id) + ); +} + + +// Java field operations. + +static std::string jrp_get_field(std::string object_id, + std::string field_name) { + return checked_return( + JavaRequestProcessor().getField(TEST_SOURCE, + jrp_get_class_id(object_id), object_id, field_name) + ); +} + +static std::string jrp_get_field_id(std::string class_id, + std::string field_name) { + return checked_return( + JavaRequestProcessor().getFieldID(class_id, field_name) + ); +} + +static std::string jrp_get_static_field_id(std::string class_id, + std::string field_name) { + return checked_return( + JavaRequestProcessor().getStaticFieldID(class_id, field_name) + ); +} + +static std::string jrp_get_static_field(std::string class_id, + std::string field_name) { + return checked_return( + JavaRequestProcessor().getStaticField(TEST_SOURCE, class_id, field_name) + ); +} + +static std::string jrp_set_field(std::string object_id, std::string field_name, + std::string value_id) { + return checked_return( + JavaRequestProcessor().setField(TEST_SOURCE, + jrp_get_class_id(object_id), object_id, field_name, value_id) + ); +} + +static std::string jrp_set_static_field(std::string class_id, std::string field_name, + std::string value_id) { + return checked_return( + JavaRequestProcessor().setStaticField(TEST_SOURCE, class_id, field_name, value_id) + ); +} + +// Java array operations. + +static std::string jrp_set_slot(std::string object_id, std::string index, + std::string value_id) { + return checked_return( + JavaRequestProcessor().setSlot(object_id, index, value_id) + ); +} + +static std::string jrp_get_slot(std::string object_id, std::string index) { + return checked_return( + JavaRequestProcessor().getSlot(object_id, index) + ); +} + +static std::string jrp_get_array_length(std::string object_id) { + return checked_return( + JavaRequestProcessor().getArrayLength(object_id) + ); +} + +// Result of toString() + +static std::string jrp_get_to_string_value(std::string object_id) { + return checked_return( + JavaRequestProcessor().getToStringValue(object_id) + ); +} + +/****************************************************************************** + * Compound helper methods. * + ******************************************************************************/ + +static NPP_t dummy_npp = {0,0}; + +static std::string create_java_integer(int value) { + // Prepare a java integer object with the value 1 + NPVariant integer_variant; + std::string integer_id; + INT32_TO_NPVARIANT(value, integer_variant); + createJavaObjectFromVariant(&dummy_npp, integer_variant, &integer_id); + return integer_id; +} + +static std::string create_null() { + // Prepare a null object + NPVariant null_variant; + std::string null_id; + NULL_TO_NPVARIANT(null_variant); + createJavaObjectFromVariant(&dummy_npp, null_variant, &null_id); + return null_id; +} + +static NPVariant java_result_to_variant(std::string object_id) { + NPVariant variant; + IcedTeaPluginUtilities::javaResultToNPVariant(&dummy_npp, &object_id, &variant); + return variant; +} + +/* Call the no-argument constructor of an object */ +static std::string jrp_noarg_construct(std::string classname) { + std::string class_id = jrp_find_class(classname); + std::string constructor_id = jrp_get_method_id(class_id, "<init>"); + return jrp_new_object_with_constructor(class_id, constructor_id); +} + + +/****************************************************************************** + * Test cases. Note that the tests exercise a variety of functions to first * + * create the appropriate conditions for the intended test. * + ******************************************************************************/ + +SUITE(JavaRequestProcessor) { + + TEST(callMethod) { + std::string object_id = jrp_noarg_construct("java.lang.Object"); + std::string tostring_result = jrp_get_string( + jrp_call_method(object_id, "toString")); + const char substr[] = "java.lang.Object@"; + // Check that the result of toString is as expected + CHECK(strncmp(tostring_result.c_str(), substr, strlen(substr)) == 0); + } + + /* Create a java.awt.Point, since it is one of the few standard classes with public fields. */ + TEST(getField_and_setField) { + std::string object_id = jrp_noarg_construct("java.awt.Point"); + + // Set the field 'x' to 1 + jrp_set_field(object_id, "x", create_java_integer(1)); + + // Get the field 'x' + NPVariant field_value = java_result_to_variant(jrp_get_field(object_id, "x")); + + // Ensure that the received field is 1 + CHECK(NPVARIANT_IS_INT32(field_value) && NPVARIANT_TO_INT32(field_value) == 1); + } + + TEST(getStaticField_and_setStaticField) { + // One of the few classes with a public & non-final static field that we can tinker with. + // If it moves, this test will fail, in which-case this test should be updated to another appropriate field. + std::string class_id = jrp_find_class("net.sourceforge.jnlp.controlpanel.DebuggingPanel"); + std::string properties_id = jrp_get_static_field(class_id, "properties"); + + // Check that the field is initially a non-null object + NPVariant sh_variant = java_result_to_variant(properties_id); + CHECK(!NPVARIANT_IS_NULL(sh_variant) && NPVARIANT_IS_OBJECT(sh_variant)); + browser_functions.releasevariantvalue(&sh_variant); + + jrp_set_static_field(class_id, "properties", create_null()); + sh_variant = java_result_to_variant(jrp_get_static_field(class_id, "properties")); + CHECK(NPVARIANT_IS_NULL(sh_variant)); + + // Reset the field to its original contents + jrp_set_static_field(class_id, "properties", properties_id); + sh_variant = java_result_to_variant(jrp_get_static_field(class_id, "properties")); + CHECK(!NPVARIANT_IS_NULL(sh_variant) && NPVARIANT_IS_OBJECT(sh_variant)); + browser_functions.releasevariantvalue(&sh_variant); + } + + TEST(arrayIndexing) { + const int ARRAY_LEN = 1; + + // We will create an Integer array of ARRAY_LEN + std::vector<std::string> args; + args.push_back(jrp_find_class("java.lang.Integer")); + args.push_back(create_java_integer(ARRAY_LEN)); + + // Create an array 'the hard way' (ie not using 'newArray') to test more of the API + std::string array_id = jrp_call_static_method(jrp_find_class("java.lang.reflect.Array"), "newInstance", args); + + // Attempt to set the first element to 1 + jrp_set_slot(array_id, "0", create_java_integer(1)); + // Note we get an integer _object_, not a plain int literal + std::string integer_id = jrp_get_slot(array_id, "0"); + NPVariant unboxed_slot_value = java_result_to_variant(jrp_call_method(integer_id, "intValue")); + + // Ensure that the received slot is 1 + CHECK(NPVARIANT_IS_INT32(unboxed_slot_value) && NPVARIANT_TO_INT32(unboxed_slot_value) == 1); + } + + // Also exercises 'getToStringValue' + TEST(newObject) { + std::string object_id = jrp_new_object(jrp_find_class("java.lang.Object")); + const char substr[] = "java.lang.Object@"; + // Check that the result of toString is as expected + CHECK(strncmp(jrp_get_to_string_value(object_id).c_str(), substr, strlen(substr)) == 0); + } + + TEST(hasPackage) { + CHECK(jrp_has_package("java.lang")); + CHECK(!jrp_has_package("not.an.icedtea_web.package")); + } + + TEST(newArray) { + const char ARRAY_LEN[] = "10"; + std::string array_id = jrp_new_array(jrp_find_class("java.lang.Integer"), ARRAY_LEN); + CHECK_EQUAL(ARRAY_LEN, jrp_get_array_length(array_id)); + } + + TEST(newString) { + const char TEST_STRING[] = "foobar"; + std::string string_id = jrp_new_string(TEST_STRING); + CHECK_EQUAL(TEST_STRING, jrp_get_string(string_id)); + CHECK_EQUAL(TEST_STRING, jrp_get_to_string_value(string_id)); + } + + // Can only really do sanity checks with given API + TEST(getFieldID_getStaticFieldID) { + CHECK(!jrp_get_field_id(jrp_find_class("java.awt.Point"), "x").empty()); + CHECK(!jrp_get_static_field_id(jrp_find_class("java.lang.Integer"), "MAX_VALUE").empty()); + } + + // Can only really do sanity checks with given API + TEST(getStaticMethodID) { + std::string class_id = jrp_find_class("java.lang.Integer"); + + std::vector<std::string> argtypes; + argtypes.push_back("Ljava.lang.String;"); + + CHECK(!jrp_get_static_method_id(class_id, "valueOf", argtypes).empty()); + } + + TEST(isInstanceOf) { + std::string point_id = jrp_noarg_construct("java.awt.Point"); + std::string object_class_id = jrp_find_class("java.lang.Object"); + CHECK(jrp_is_instance_of(point_id, object_class_id)); + } + + // Wasn't sure what the point of this method is to be honest, but it is used. + // Here it simply returns back a passed string object. + TEST(getValue) { + const char TEST_STRING[] = "foobar"; + + std::string str = jrp_get_string(jrp_get_value(jrp_new_string(TEST_STRING))); + CHECK_EQUAL(TEST_STRING, str); + } +} diff --git a/tests/cpp-unit-tests/IcedTeaNPPluginTest.cc b/tests/cpp-unit-tests/IcedTeaNPPluginTest.cc index 1633d6b..6fc0bf9 100644 --- a/tests/cpp-unit-tests/IcedTeaNPPluginTest.cc +++ b/tests/cpp-unit-tests/IcedTeaNPPluginTest.cc @@ -40,7 +40,10 @@ #include <UnitTest++.h> +#include "MemoryLeakDetector.h" + #include "IcedTeaNPPlugin.h" +#include "IcedTeaScriptablePluginObject.h" #include "IcedTeaPluginUtils.h" TEST(NP_GetMIMEDescription) { @@ -51,19 +54,38 @@ TEST(NP_GetMIMEDescription) { /* Not normally exposed */ std::vector<std::string*>* get_jvm_args(); -extern gchar* in_pipe_name; -extern gchar* out_pipe_name; TEST(get_jvm_args) { - in_pipe_name = (gchar*)"inpipe"; - out_pipe_name = (gchar*)"outpipe"; - std::vector<std::string*>* args = get_jvm_args(); CHECK(args != NULL); IcedTeaPluginUtilities::freeStringPtrVector(args); } + +static IcedTeaScriptableJavaPackageObject* get_scriptable_package_object() { + NPP_t instance = { /*Plugin data*/plugin_data_new(), /* Browser data*/0 }; + + /* Get the packages object (since instance.pdata->is_applet_instance == false) */ + NPObject* obj = get_scriptable_object(&instance); + + /* Make sure we got an IcedTeaScriptableJavaPackageObject */ + CHECK(obj->_class->deallocate == IcedTeaScriptableJavaPackageObject::deAllocate); + + plugin_data_destroy(&instance); + return (IcedTeaScriptableJavaPackageObject*)obj; +} + +TEST(get_scriptable_object) { + MemoryLeakDetector leak_detector; + // We test without an applet context, pending mocking of applet instances. + IcedTeaScriptableJavaPackageObject* obj = get_scriptable_package_object(); // Calls get_scriptable_object + + browser_functions.releaseobject(obj); + + CHECK(leak_detector.memory_leaks() == 0); +} + TEST(NP_GetValue) { void* __unused = NULL; gchar* char_value = NULL; diff --git a/tests/cpp-unit-tests/IcedTeaPluginUtilsTest.cc b/tests/cpp-unit-tests/IcedTeaPluginUtilsTest.cc index 7df0a1e..ac0e0d3 100644 --- a/tests/cpp-unit-tests/IcedTeaPluginUtilsTest.cc +++ b/tests/cpp-unit-tests/IcedTeaPluginUtilsTest.cc @@ -86,11 +86,11 @@ TEST(NPVariantStringCopy) { } TEST(NPIdentifierAsString) { - // NB: Mocked definition of 'utf8fromidentifier' simply reads NPIdentifier as a char* string. const char test_string[] = "foobar"; MemoryLeakDetector leak_detector; /* Ensure destruction */{ - std::string str = IcedTeaPluginUtilities::NPIdentifierAsString((NPIdentifier)test_string); + std::string str = IcedTeaPluginUtilities::NPIdentifierAsString( + browser_functions.getstringidentifier(test_string)); CHECK_EQUAL(test_string, str); } CHECK_EQUAL(0, leak_detector.memory_leaks()); diff --git a/tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc b/tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc index 9de0bbd..bb2b376 100644 --- a/tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc +++ b/tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc @@ -95,4 +95,37 @@ SUITE(IcedTeaScriptableJavaPackageObject) { browser_functions.releaseobject(obj); CHECK(leak_detector.memory_leaks() == 0); } + + static NPVariant np_checked_get(NPObject* obj, const char* identifier) { + NPVariant result; + CHECK(browser_functions.getproperty(&dummy_npp, obj, browser_functions.getstringidentifier(identifier), &result)); + return result; + } + + /* NOTICE: Requires icedtea-web Java-side to be running! + * Loads java.lang.Integer.MAX_VALUE */ + TEST(getProperty) { + MemoryLeakDetector leak_detector; + /* Ensure destruction */{ + /* Get the 'root' package */ + NPObject* obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_package_object(&dummy_npp, ""); + // Look up java.lang.Integer.MAX_VALUE + NPVariant java_result = np_checked_get(obj, "java"); + NPVariant lang_result = np_checked_get(NPVARIANT_TO_OBJECT(java_result), "lang"); + NPVariant integer_result = np_checked_get(NPVARIANT_TO_OBJECT(lang_result), "Integer"); + NPVariant max_value_result = np_checked_get(NPVARIANT_TO_OBJECT(integer_result), "MAX_VALUE"); + + // Check that it is indeed equal to 2147483647 + CHECK(NPVARIANT_IS_INT32(max_value_result)); + CHECK_EQUAL(2147483647, NPVARIANT_TO_INT32(max_value_result)); + + browser_functions.releasevariantvalue(&java_result); + browser_functions.releasevariantvalue(&lang_result); + browser_functions.releasevariantvalue(&integer_result); + browser_functions.releasevariantvalue(&max_value_result); + + browser_functions.releaseobject(obj); + } + CHECK(leak_detector.memory_leaks() == 0); + } } diff --git a/tests/cpp-unit-tests/MemoryLeakDetector.h b/tests/cpp-unit-tests/MemoryLeakDetector.h index 2bd5e9c..d457cb7 100644 --- a/tests/cpp-unit-tests/MemoryLeakDetector.h +++ b/tests/cpp-unit-tests/MemoryLeakDetector.h @@ -42,6 +42,7 @@ #define MEMORYLEAKDETECTOR_H_ #include <cstdio> +#include "browser_mock.h" #include "checked_allocations.h" #include "IcedTeaPluginUtils.h" @@ -69,7 +70,6 @@ public: return cpp_leaks + npapi_leaks; } -private: static void reset_global_state() { /* Clears allocations caused by storeInstanceID */ IcedTeaPluginUtilities::clearInstanceIDs(); diff --git a/tests/cpp-unit-tests/browser_mock.cc b/tests/cpp-unit-tests/browser_mock.cc index 8724687..32027f3 100644 --- a/tests/cpp-unit-tests/browser_mock.cc +++ b/tests/cpp-unit-tests/browser_mock.cc @@ -36,17 +36,26 @@ // Browser mock functions. Add more as needed. +#include <new> +#include <map> #include <cstring> + +#include <npapi.h> + #include "checked_allocations.h" #include "UnitTest++.h" #include "browser_mock.h" +#include "browser_mock_npidentifier.h" -#include "IcedTeaNPPlugin.h" +// 'Browser' global data +// Stores NPAPI allocations static AllocationSet __allocations; +// Mocked functions + // It is expected that these will only run during a unit test static void* mock_memalloc(uint32_t size) { void* mem = malloc(size); @@ -78,6 +87,15 @@ static void mock_releaseobject(NPObject* obj) { } } +static void mock_releasevariantvalue(NPVariant* variant) { + if (variant->type == NPVariantType_String) { + /* Only string and object values require freeing */ + mock_memfree((void*)variant->value.stringValue.UTF8Characters); + } else if (variant->type == NPVariantType_Object) { + mock_releaseobject(variant->value.objectValue); + } +} + static NPObject* mock_createobject(NPP instance, NPClass* np_class) { NPObject* obj; if (np_class->allocate) { @@ -90,17 +108,26 @@ static NPObject* mock_createobject(NPP instance, NPClass* np_class) { return obj; } -static NPUTF8* mock_utf8fromidentifier(NPIdentifier id) { - // Treat NPIdentifier (== void pointer) as a pointer to characters for simplicity - const NPUTF8* str = (const NPUTF8*) id; - // We expect this string to be freed with 'memfree' - NPUTF8* copy = (NPUTF8*) mock_memalloc(strlen(str) + 1); - memcpy(copy, str, strlen(str) + 1); - return copy; +static bool mock_getproperty(NPP npp, NPObject* obj, NPIdentifier property_name, NPVariant* result) { + if (obj->_class->getProperty) { + return obj->_class->getProperty(obj, property_name, result); + } else { + return false; + } } -void browsermock_setup_functions() { +static bool mock_setproperty(NPP npp, NPObject* obj, NPIdentifier property_name, const NPVariant* value) { + if (obj->_class->setProperty) { + return obj->_class->setProperty(obj, property_name, value); + } else { + return false; + } +} + +NPNetscapeFuncs browsermock_create_table() { + NPNetscapeFuncs browser_functions; memset(&browser_functions, 0, sizeof(NPNetscapeFuncs)); + browser_functions.size = sizeof(NPNetscapeFuncs); browser_functions.memalloc = &mock_memalloc; browser_functions.memfree = &mock_memfree; @@ -108,7 +135,16 @@ void browsermock_setup_functions() { browser_functions.createobject = &mock_createobject; browser_functions.retainobject = &mock_retainobject; browser_functions.releaseobject = &mock_releaseobject; - browser_functions.utf8fromidentifier = &mock_utf8fromidentifier; + browser_functions.releasevariantvalue = &mock_releasevariantvalue; + + browser_functions.setproperty = &mock_setproperty; + browser_functions.getproperty = &mock_getproperty; + + browser_functions.utf8fromidentifier = &browsermock_utf8fromidentifier; + browser_functions.getstringidentifier = &browsermock_getstringidentifier; + browser_functions.identifierisstring = &browsermock_identifierisstring; + + return browser_functions; } void browsermock_clear_state() { diff --git a/tests/cpp-unit-tests/browser_mock.h b/tests/cpp-unit-tests/browser_mock.h index d936215..ac38813 100644 --- a/tests/cpp-unit-tests/browser_mock.h +++ b/tests/cpp-unit-tests/browser_mock.h @@ -43,7 +43,9 @@ #ifndef __BROWSER_MOCK_H__ #define __BROWSER_MOCK_H__ -void browsermock_setup_functions(); +#include <npfunctions.h> + +NPNetscapeFuncs browsermock_create_table(); void browsermock_clear_state(); int browsermock_unfreed_allocations(); diff --git a/tests/cpp-unit-tests/browser_mock_npidentifier.cc b/tests/cpp-unit-tests/browser_mock_npidentifier.cc new file mode 100644 index 0000000..736d9a5 --- /dev/null +++ b/tests/cpp-unit-tests/browser_mock_npidentifier.cc @@ -0,0 +1,117 @@ +/* Copyright (C) 2013 Red Hat + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +#include <cstdlib> + +#include <map> + +#include "IcedTeaNPPlugin.h" +#include "checked_allocations.h" +#include "browser_mock_npidentifier.h" + +struct MockedNPIdentifier_t; // foward declare +typedef std::basic_string<char, std::char_traits<char>, SafeAllocator> SafeString; +typedef std::map<int, MockedNPIdentifier_t*, std::less<int>, SafeAllocator> SafeIntToIDMap; +typedef std::map<SafeString, MockedNPIdentifier_t*, std::less<SafeString>, SafeAllocator> SafeStringToIDMap; + + +// Handles creation of NPIdentifier + +// Carefully avoids operator new so as not to interfere with leak detection. +// This mimics browser internal state. +struct MockedNPIdentifier_t { + SafeString string; + int integer; + bool is_integer; // If false, it is a string + + // Carefully avoids operator new so as not to interfere with leak detection + static MockedNPIdentifier_t* safe_allocate(SafeString str) { + MockedNPIdentifier_t* mem = (MockedNPIdentifier_t*)malloc(sizeof(MockedNPIdentifier_t)); + new (&mem->string) SafeString(str); + mem->integer = -1; + mem->is_integer = false; + return mem; + } + + // Carefully avoids operator new so as not to interfere with leak detection + static MockedNPIdentifier_t* safe_allocate(int i) { + MockedNPIdentifier_t* mem = (MockedNPIdentifier_t*) malloc( + sizeof(MockedNPIdentifier_t)); + new (&mem->string) SafeString(); + mem->integer = i; + mem->is_integer = true; + return mem; + } +}; + +// Mimics global browser data. OK if not cleared in-between tests, does not change semantics. +// Used to ensure NPIdentifiers are unique. Never freed. +static SafeIntToIDMap __np_int_identifiers; +static SafeStringToIDMap __np_string_identifiers; + +// Carefully avoids operator new so as not to interfere with leak detection +NPIdentifier browsermock_getstringidentifier(const NPUTF8* name) { + SafeString safe_copy(name); + if (__np_string_identifiers.find(safe_copy) == __np_string_identifiers.end()) { + __np_string_identifiers[safe_copy] = MockedNPIdentifier_t::safe_allocate(safe_copy); + } + return __np_string_identifiers[safe_copy]; +} + +// Carefully avoids operator new so as not to interfere with leak detection +NPIdentifier browsermock_getintidentifier(int i) { + if (__np_int_identifiers.find(i) == __np_int_identifiers.end()) { + __np_int_identifiers[i] = MockedNPIdentifier_t::safe_allocate(i); + } + return __np_int_identifiers[i]; +} + +bool browsermock_identifierisstring(NPIdentifier identifier) { + MockedNPIdentifier_t* contents = (MockedNPIdentifier_t*)identifier; + return !contents->is_integer; +} + +NPUTF8* browsermock_utf8fromidentifier(NPIdentifier identifier) { + MockedNPIdentifier_t* contents = (MockedNPIdentifier_t*)identifier; + if (contents->is_integer) { + return NULL; + } + + // We expect this string to be freed with 'memfree' + NPUTF8* copy = (NPUTF8*) browser_functions.memalloc(contents->string.size() + 1); + memcpy(copy, contents->string.c_str(), contents->string.size() + 1); + return copy; +} diff --git a/tests/cpp-unit-tests/browser_mock_npidentifier.h b/tests/cpp-unit-tests/browser_mock_npidentifier.h new file mode 100644 index 0000000..d477fef --- /dev/null +++ b/tests/cpp-unit-tests/browser_mock_npidentifier.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2013 Red Hat + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +/* + * browser_mock_npidentifier.h: + * Handles NPAPI functions related to NPIdentifier, NPIdentifier has the following constraints: + * - Unique for each integer & string + * - Should not interfere with leak detection, so use malloc-based allocators (specified by templates) everywhere. + */ + +#ifndef __BROWSER_MOCK_NPIDENTIFIER_H__ +#define __BROWSER_MOCK_NPIDENTIFIER_H__ + +#include <string> +#include <npfunctions.h> + +NPIdentifier browsermock_getstringidentifier(const NPUTF8* name); +NPIdentifier browsermock_getintidentifier(int i); + +bool browsermock_identifierisstring(NPIdentifier identifier); + +NPUTF8* browsermock_utf8fromidentifier(NPIdentifier identifier); + +#endif // __BROWSER_MOCK_NPIDENTIFIER_H__ diff --git a/tests/cpp-unit-tests/checked_allocations.h b/tests/cpp-unit-tests/checked_allocations.h index 0c9ce04..299c769 100644 --- a/tests/cpp-unit-tests/checked_allocations.h +++ b/tests/cpp-unit-tests/checked_allocations.h @@ -39,6 +39,7 @@ #ifndef CHECKED_ALLOCATIONS_H_ #define CHECKED_ALLOCATIONS_H_ +#include <string> #include <set> #include <cstdio> #include <exception> @@ -46,8 +47,9 @@ #include <cstdlib> #include <ext/malloc_allocator.h> //GNU extension -// Plays nice with custom-defined operator new -typedef std::set<void*, std::less<void*>, __gnu_cxx::malloc_allocator<void*> > AllocationSet; +// Classes that play nice with custom-defined operator new by using 'vanilla' malloc +typedef __gnu_cxx::malloc_allocator<void*> SafeAllocator; +typedef std::set<void*, std::less<void*>, SafeAllocator> AllocationSet; int cpp_unfreed_allocations(); diff --git a/tests/cpp-unit-tests/main.cc b/tests/cpp-unit-tests/main.cc index d7e71f6..c050bbb 100644 --- a/tests/cpp-unit-tests/main.cc +++ b/tests/cpp-unit-tests/main.cc @@ -42,7 +42,11 @@ #include <UnitTest++.h> #include <TestReporter.h> +#include <npfunctions.h> + +#include "IcedTeaNPPlugin.h" #include "browser_mock.h" +#include "MemoryLeakDetector.h" #include "checked_allocations.h" using namespace UnitTest; @@ -56,6 +60,12 @@ static std::string full_testname(const TestDetails& details) { } } +// Important for testing purposes of eg leaks between tests +static void reset_global_state() { + browsermock_clear_state(); + MemoryLeakDetector::reset_global_state(); +} + class IcedteaWebUnitTestReporter: public TestReporter { public: @@ -66,7 +76,7 @@ public: } virtual void ReportTestStart(const TestDetails& test) { - browsermock_clear_state(); + reset_global_state(); pretest_allocs = cpp_unfreed_allocations(); did_finish_correctly = true; } @@ -84,6 +94,7 @@ public: virtual void ReportTestFinish(const TestDetails& details, float secondsElapsed) { + reset_global_state(); int posttest_allocs = cpp_unfreed_allocations(); std::string testname = full_testname(details); @@ -126,8 +137,20 @@ static int run_icedtea_web_unit_tests() { True() /*All tests*/, 0 /*No time limit*/); } +/* Spawns the Java-side of the plugin, create request processing threads, + * and sets up a mocked 'browser' environment. */ +static void initialize_plugin_components() { + NPNetscapeFuncs mocked_browser_functions = browsermock_create_table(); + NPPluginFuncs unused_plugin_functions; + memset(&unused_plugin_functions, 0, sizeof(NPPluginFuncs)); + unused_plugin_functions.size = sizeof(NPPluginFuncs); + + NP_Initialize (&mocked_browser_functions, &unused_plugin_functions); + start_jvm_if_needed(); +} + int main() { - browsermock_setup_functions(); + initialize_plugin_components(); int exitcode = run_icedtea_web_unit_tests(); |