changeset 590:6251ec285570

C++ unit testing: Add a tracked-allocation implementation of operator-new Two tests that catch memory leaks are also added. For the purposes of verifying the patch works, the fix is in a separate changeset.
author Adam Domurad <adomurad@redhat.com>
date Thu, 20 Dec 2012 11:28:28 -0500
parents 18ee1b8417d8
children 2b2073cc9a19
files ChangeLog tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc tests/cpp-unit-tests/browser_mock.cc tests/cpp-unit-tests/checked_allocations.cc tests/cpp-unit-tests/checked_allocations.h tests/cpp-unit-tests/main.cc
diffstat 6 files changed, 256 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Dec 20 17:10:38 2012 +0100
+++ b/ChangeLog	Thu Dec 20 11:28:28 2012 -0500
@@ -1,4 +1,19 @@
-2012-12-18  Jiri Vanek <jvanek@redhat.com>
+2012-12-20  Adam Domurad  <adomurad@redhat.com>
+
+	* tests/cpp-unit-tests/browser_mock.cc
+	(mock_retainobject): New, mocks behaviour of NPAPI retainobject
+	(mock_releaseobject): New, mocks behaviour of NPAPI releaseobject
+	* tests/cpp-unit-tests/main.cc: Add warning of memory leak based on 
+	operator-new.
+	* tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc: New, tests
+	for memory leak in (IcedTeaScriptableJavaObject::deAllocate) and
+	(IcedTeaScriptableJavaPackageObject::deAllocate)
+	* tests/cpp-unit-tests/checked_allocations.h: Defines set that does not
+	use operator-new, to prevent recursion in overloaded operator-new
+	* tests/cpp-unit-tests/checked_allocations.cc: Operator new overload 
+	that has allocation-set for querying live allocations.
+
+2012-12-20  Jiri Vanek <jvanek@redhat.com>
 
 	Added and applied Remote annotation, added Remote tests:
 	* tests/report-styles/jreport.xsl: and
@@ -13,9 +28,7 @@
 	launcher for remote tests.
 	* tests/test-extensions/net/sourceforge/jnlp/annotations/Remote.java:
 	Implementation of Remote annotation
-	
-
-	
+
 2012-12-18  Jiri Vanek <jvanek@redhat.com>
 
 	Cleaned unit-tests:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/cpp-unit-tests/IcedTeaScriptablePluginObjectTest.cc	Thu Dec 20 11:28:28 2012 -0500
@@ -0,0 +1,66 @@
+/* Copyright (C) 2012 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 <UnitTest++.h>
+
+#include <npapi.h>
+
+#include "browser_mock.h"
+#include "checked_allocations.h"
+
+#include "IcedTeaScriptablePluginObject.h"
+
+SUITE(IcedTeaScriptableJavaObject) {
+    TEST(deallocate) {
+        int pre_allocations = cpp_unfreed_allocations();
+        IcedTeaScriptableJavaObject* obj = new IcedTeaScriptableJavaObject(NULL);
+        IcedTeaScriptableJavaObject::deAllocate(obj);
+        int post_allocations = cpp_unfreed_allocations();
+
+        CHECK(pre_allocations == post_allocations);
+    }
+}
+
+SUITE(IcedTeaScriptableJavaPackageObject) {
+    TEST(deallocate) {
+        int pre_allocations = cpp_unfreed_allocations();
+        IcedTeaScriptableJavaPackageObject* obj = new IcedTeaScriptableJavaPackageObject(NULL);
+        IcedTeaScriptableJavaPackageObject::deAllocate(obj);
+        int post_allocations = cpp_unfreed_allocations();
+
+        CHECK(pre_allocations == post_allocations);
+    }
+}
--- a/tests/cpp-unit-tests/browser_mock.cc	Thu Dec 20 17:10:38 2012 +0100
+++ b/tests/cpp-unit-tests/browser_mock.cc	Thu Dec 20 11:28:28 2012 -0500
@@ -37,7 +37,7 @@
 // Browser mock functions. Add more as needed.
 
 #include <cstring>
-#include <set>
+#include "checked_allocations.h"
 
 #include "UnitTest++.h"
 
@@ -45,7 +45,7 @@
 
 #include "IcedTeaNPPlugin.h"
 
-static std::set<void*> __allocations;
+static AllocationSet __allocations;
 
 // It is expected that these will only run during a unit test
 static void* mock_memalloc(uint32_t size) {
@@ -63,11 +63,29 @@
     }
 }
 
+static NPObject* mock_retainobject(NPObject* obj) {
+    obj->referenceCount++;
+    return obj;
+}
+
+static void mock_releaseobject(NPObject* obj) {
+    if (--(obj->referenceCount) == 0) {
+        if (obj->_class->deallocate) {
+            obj->_class->deallocate(obj);
+        } else {
+            free(obj);
+        }
+    }
+}
+
 void browsermock_setup_functions() {
     memset(&browser_functions, 0, sizeof(NPNetscapeFuncs));
 
     browser_functions.memalloc = &mock_memalloc;
     browser_functions.memfree = &mock_memfree;
+
+    browser_functions.retainobject = &mock_retainobject;
+    browser_functions.releaseobject= &mock_releaseobject;
 }
 
 void browsermock_clear_state() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/cpp-unit-tests/checked_allocations.cc	Thu Dec 20 11:28:28 2012 -0500
@@ -0,0 +1,77 @@
+/* Copyright (C) 2012 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. */
+
+//  Overrides global 'new' operator with one that does error checking.
+
+#include <new>
+
+#include <UnitTest++.h>
+#include "checked_allocations.h"
+
+// We keep a set of allocations, that, for obvious reasons, does not itself use the 'new' operator.
+static AllocationSet* __allocations = NULL;
+
+// Override global definition of new and delete!
+void* operator new(size_t size) throw (std::bad_alloc) {
+    if (!__allocations) {
+        // This uses placement-new, which calls the constructor on a specific memory location
+        // This is needed because we cannot call 'new' in this context, nor can we rely on static-initialization
+        // for the set to occur before any call to 'new'!
+        void* memory = malloc(sizeof(AllocationSet));
+        __allocations = new (memory) AllocationSet();
+    }
+
+    void* mem = malloc(size);
+    if (mem == 0) {
+        throw std::bad_alloc(); // ANSI/ISO compliant behavior
+    }
+    __allocations->insert(mem);
+    return mem;
+}
+
+void operator delete(void* ptr) throw () {
+    if (__allocations->erase(ptr)) {
+        free(ptr);
+    } else {
+        printf(
+                "Attempt to free memory with operator 'delete' that was not allocated by 'new'!\n");
+        CHECK(false);
+    }
+}
+
+int cpp_unfreed_allocations() {
+    return __allocations->size();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/cpp-unit-tests/checked_allocations.h	Thu Dec 20 11:28:28 2012 -0500
@@ -0,0 +1,54 @@
+/* Copyright (C) 2012 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. */
+
+//  Overrides global 'new' operator with one that does error checking.
+
+#ifndef CHECKED_ALLOCATIONS_H_
+#define CHECKED_ALLOCATIONS_H_
+
+#include <set>
+#include <cstdio>
+#include <exception>
+#include <memory>
+#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;
+
+int cpp_unfreed_allocations();
+
+#endif /* CHECKED_ALLOCATIONS_H_ */
--- a/tests/cpp-unit-tests/main.cc	Thu Dec 20 17:10:38 2012 +0100
+++ b/tests/cpp-unit-tests/main.cc	Thu Dec 20 11:28:28 2012 -0500
@@ -43,9 +43,19 @@
 #include <TestReporter.h>
 
 #include "browser_mock.h"
+#include "checked_allocations.h"
 
 using namespace UnitTest;
 
+static std::string full_testname(const TestDetails& details) {
+    std::string suite = details.suiteName;
+    if (suite == "DefaultSuite") {
+        return details.testName;
+    } else {
+        return suite + "." + details.testName;
+    }
+}
+
 class IcedteaWebUnitTestReporter: public TestReporter {
 public:
 
@@ -57,13 +67,15 @@
 
     virtual void ReportTestStart(const TestDetails& test) {
         browsermock_clear_state();
+        pretest_allocs = cpp_unfreed_allocations();
         did_finish_correctly = true;
     }
 
     virtual void ReportFailure(const TestDetails& details,
             char const* failure) {
+        std::string testname = full_testname(details);
 
-        printf("FAILED: %s line %d (%s)\n", details.testName,
+        printf("FAILED: %s line %d (%s)\n", testname.c_str(),
                 details.lineNumber, failure);
 
         did_finish_correctly = false;
@@ -72,13 +84,20 @@
     virtual void ReportTestFinish(const TestDetails& details,
             float secondsElapsed) {
 
+        int posttest_allocs = cpp_unfreed_allocations();
+
         if (browsermock_unfreed_allocations() > 0) {
             printf("*** WARNING: Memory leak! %d more NPAPI allocations than frees!\n",
                     browsermock_unfreed_allocations());
         }
+        if (posttest_allocs > pretest_allocs) {
+            printf("*** WARNING: Memory leak! %d more operator 'new' allocations than 'delete's!\n",
+                    posttest_allocs - pretest_allocs);
+        }
 
         if (did_finish_correctly) {
-            printf("Passed: %s\n", details.testName);
+            std::string testname = full_testname(details);
+            printf("Passed: %s\n", testname.c_str());
         }
     }
 
@@ -95,6 +114,7 @@
     }
 
 private:
+    int pretest_allocs;
     bool did_finish_correctly;
 };