Browse Source

Re-Enable unittests

Re-enabling the unit test infrastructure, adding new tests
for URI and linking existing tests into it. Some other tests
are not yet linked (aren't cppunit or aren't in a shared lib)
and will be done in the furure.

There is a good documentation on how to extend, and about each
part of the infrastructure. The remote tests are also a good
example on how to create more API/TDD unit tests in the future.
Renato Golin 13 years ago
parent
commit
c3b0149ee7

+ 1 - 0
CMakeLists.txt

@@ -119,6 +119,7 @@ if ( NOT MAKE_DOCS_ONLY )
     HPCC_ADD_SUBDIRECTORY (services "PLATFORM")
     HPCC_ADD_SUBDIRECTORY (system)
     HPCC_ADD_SUBDIRECTORY (thorlcr "PLATFORM")
+    HPCC_ADD_SUBDIRECTORY (testing)
 endif()
 HPCC_ADD_SUBDIRECTORY (docs "PLATFORM")
 if (APPLE)

+ 1 - 1
common/remote/uri.cpp

@@ -37,7 +37,7 @@ URI::URI(const char* path)
 }
 
 // Helper, to validate URI before creating object
-bool isURI(const char *path)
+bool URI::isURI(const char *path)
 {
     UriParserStateA state;
     UriUriA uri;

+ 15 - 4
system/jlib/jutil.cpp

@@ -332,10 +332,21 @@ HINSTANCE LoadSharedObject(const char *name, bool isGlobal, bool raiseOnError)
     HINSTANCE h = dlopen((char *)name, isGlobal ? RTLD_NOW|RTLD_GLOBAL : RTLD_NOW);
     if(h == NULL)
     {
-        StringBuffer dlErrorMsg(dlerror());
-        DBGLOG("Error loading %s: %s", name, dlErrorMsg.str());
-        if (raiseOnError)
-            throw MakeStringException(0, "Error loading %s: %s", name, dlErrorMsg.str());
+        // Try again, with .so extension if necessary
+        if (strncmp(".so", name+(strlen(name)-3), 3) != 0)
+        {
+            // Assume if there's no .so, there's also no lib at the beginning
+            StringBuffer nameBuf;
+            nameBuf.append("lib").append(name).append(".so");
+            h = dlopen((char *)nameBuf.str(), isGlobal ? RTLD_NOW|RTLD_GLOBAL : RTLD_NOW);
+        }
+        if (h == NULL)
+        {
+            StringBuffer dlErrorMsg(dlerror());
+            DBGLOG("Error loading %s: %s", name, dlErrorMsg.str());
+            if (raiseOnError)
+                throw MakeStringException(0, "Error loading %s: %s", name, dlErrorMsg.str());
+        }
     }
 
 #endif

+ 19 - 0
testing/CMakeLists.txt

@@ -0,0 +1,19 @@
+################################################################################
+#    Copyright (C) 2012 HPCC Systems.
+#
+#    This program is free software: you can redistribute it and/or All rights
+#    reserved. This program is NOT PRESENTLY free software: you can NOT
+#    redistribute
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program 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 Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+################################################################################
+HPCC_ADD_SUBDIRECTORY (unittests)

+ 49 - 0
testing/unittests/CMakeLists.txt

@@ -0,0 +1,49 @@
+################################################################################
+#    Copyright (C) 2012 HPCC Systems.
+#
+#    All rights reserved. This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program 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 Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+################################################################################
+
+
+# Component: unittests
+#####################################################
+# Description:
+# ------------
+#    Cmake Input File for unittests
+#####################################################
+
+project( unitests )
+
+set (    SRCS
+         unittests.cpp
+         remotetests.cpp
+    )
+
+include_directories (
+         .
+         ./../../system/include
+         ./../../system/jlib
+         ./../../common/remote
+    )
+
+ADD_DEFINITIONS( -D_CONSOLE )
+
+HPCC_ADD_EXECUTABLE ( unittests ${SRCS} )
+
+install ( TARGETS unittests DESTINATION ${OSSDIR}/bin )
+target_link_libraries ( unittests
+         jlib
+         remote
+         cppunit
+    )

+ 111 - 0
testing/unittests/remotetests.cpp

@@ -0,0 +1,111 @@
+/*##############################################################################
+
+    Copyright (C) 2012 HPCC Systems.
+
+    All rights reserved. This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as
+    published by the Free Software Foundation, either version 3 of the
+    License, or (at your option) any later version.
+
+    This program 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 Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+############################################################################## */
+
+#ifdef _USE_CPPUNIT
+#include "jlib.hpp"
+#include "jlog.hpp"
+#include "uri.hpp"
+
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+#include <cppunit/extensions/HelperMacros.h>
+// CPPUNIT_ASSERT is too slow, even when not matching failure
+#define ASSERT(a) { if (!(a)) CPPUNIT_ASSERT(a); }
+
+// =============================================================== jURI - URI parser
+class URITests : public CppUnit::TestFixture
+{
+    CPPUNIT_TEST_SUITE( URITests );
+        CPPUNIT_TEST(testURIError);
+        CPPUNIT_TEST(testURIUnknwon);
+        CPPUNIT_TEST(testURILocal);
+        CPPUNIT_TEST(testURIDali);
+    CPPUNIT_TEST_SUITE_END();
+    const IContextLogger &logctx;
+
+    void test_uri(const char * str, bool shouldBeURI, URISchemeType scheme=URIScheme_error, const char * server=NULL, const char * path=NULL)
+    {
+        bool isURI = URI::isURI(str);
+        ASSERT(isURI == shouldBeURI);
+        if (!isURI)
+            return;
+
+        // Now, validate URI
+        try
+        {
+            URI res(str);
+            ASSERT(res.getScheme() == scheme);
+            // No need to validate the rest
+            if (scheme == URIScheme_error)
+                return;
+            StringBuffer response;
+            res.appendServerStr(response);
+            ASSERT(strcmp(response.str(), server) == 0);
+            response.clear();
+            res.appendPathStr(response);
+            ASSERT(strcmp(response.str(), path) == 0);
+        }
+        catch (IException *e)
+        {
+            StringBuffer buf;
+            logctx.CTXLOG("Exception: %s", e->errorMessage(buf).str());
+            e->Release();
+            ASSERT(false); // Check exception log
+        }
+    }
+
+public:
+    URITests() : logctx(queryDummyContextLogger()) {}
+
+    void testURIError() {
+        test_uri("You, shall not, pass!", false);
+        test_uri("http://almost there...", false);
+    }
+
+    void testURIUnknwon() {
+        test_uri("ftp://www.hpccsystems.com/", true);
+        test_uri("gopher://www.hpccsystems.com/", true);
+        test_uri("https://www.hpccsystems.com:443/", true);
+        test_uri("http://user:passwd@www.hpccsystems.com:8080/my/path?is=full#of-stuff", true);
+    }
+
+    void testURILocal() {
+        test_uri("file:///opt/HPCCSystems/examples/IMDB/ActorsInMovies.ecl", true, URIScheme_file, "", "/opt/HPCCSystems/examples/IMDB/ActorsInMovies.ecl");
+    }
+
+    void testURIDali() {
+        // Dali file types
+        test_uri("hpcc://mydali/path/to/file", true, URIScheme_hpcc, "mydali", "path/to/file");
+        test_uri("hpcc://mydali/path/to/superfile?super", true, URIScheme_hpcc, "mydali", "path/to/superfile?super");
+        test_uri("hpcc://mydali/path/to/superfile?super#subname", true, URIScheme_hpcc, "mydali", "path/to/superfile?super#subname");
+        test_uri("hpcc://mydali/path/to/streamfile?stream", true, URIScheme_hpcc, "mydali", "path/to/streamfile?stream");
+        test_uri("hpcc://mydali/path/to/streamfile?stream#047", true, URIScheme_hpcc, "mydali", "path/to/streamfile?stream#47");
+
+        // Variations in Dali location
+        test_uri("hpcc://mydali:7070/path/to/file", true, URIScheme_hpcc, "mydali:7070", "path/to/file");
+        test_uri("hpcc://user@mydali:7070/path/to/file", true, URIScheme_hpcc, "user@mydali:7070", "path/to/file");
+        test_uri("hpcc://user@mydali/path/to/file", true, URIScheme_hpcc, "user@mydali", "path/to/file");
+        test_uri("hpcc://user:passwd@mydali:7070/path/to/file", true, URIScheme_hpcc, "user:passwd@mydali:7070", "path/to/file");
+        test_uri("hpcc://user:passwd@mydali/path/to/file", true, URIScheme_hpcc, "user:passwd@mydali", "path/to/file");
+    }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( URITests );
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( URITests, "URITests" );
+
+#endif // _USE_CPPUNIT

+ 24 - 1
testing/unittests/sourcedoc.xml

@@ -22,6 +22,29 @@
     <title>testing/unittests</title>
 
     <para>
-        The testing/unittests directory contains the sources for the testing/unittests library.
+        The testing/unittests directory contains the sources for the unittests.
+    </para>
+    <para>
+        For internal unit tests, code the CPPUNIT test inside the CPP file that
+        declares/uses it and include the library in which they end up in here,
+        in unittest.cpp's loadDLLs().
+    </para>
+    <para>
+        For API tests, functionality checks and Test-Driven-Developement, create
+        a new cpp file in this directory with the set of tests and include it
+        in the CMakeLists.txt.
+    </para>
+    <para>
+        The difference between internal and API tests is that the former tests
+        have access to the internal classes, and can change states that other
+        classes don't, while the latter is seeing the classes as the rest of
+        HPCC does. So, the API tests also server as documentation on how to
+        use the API and to expose API problems.
+    </para>
+    <para>
+        Other binaries that include unit tests, and that ultimatelly should be
+        included in this framework, are: datest, daregress, daunittest, eclagent,
+        roxie. Test ncbd (which is part of both eclagent and roxie) is already in
+        thorhelper.
     </para>
 </section>

+ 47 - 10
testing/unittests/unittests.cpp

@@ -27,21 +27,58 @@
 
 #define ASSERT(a) { if (!(a)) CPPUNIT_ASSERT(a); }
 
-void loadDll(const char *name)
-{
-    SharedObject *so = new SharedObject;
-    so->load(name, true);
-}
+/*
+ * This is the main unittest driver for HPCC. From here,
+ * all unit tests, be them internal or external (API).
+ *
+ * All internal unit tests, written on the same source
+ * files as the implementation they're testing, can be
+ * dynamically linked via the helper class below.
+ *
+ * All external unit tests (API tests, test-driven
+ * development, interface documentation and general
+ * usability tests) should be implemented as source
+ * files within the same directory as this file, and
+ * statically linked together.
+ *
+ * CPPUnit will automaticall recognise and run them all.
+ */
 
-void loadDlls()
-{
-    loadDll("jhtree");
-}
+/*
+ * Helper class to unload libraries at the end
+ * and make sure the SharedObject gets deleted
+ * correctly.
+ *
+ * This is important to run valgrind tests and not
+ * having to care about which memory leaks are "good"
+ * and which are not.
+ */
+class LoadedObject : public IInterface, CInterface {
+    SharedObject *so;
+public:
+    IMPLEMENT_IINTERFACE;
+
+    LoadedObject(const char * name)
+    {
+        so = new SharedObject;
+        so->load(name, true);
+    }
+    ~LoadedObject()
+    {
+        so->unload();
+        delete so;
+    }
+};
 
 int main(int argc, char* argv[])
 {
-    loadDlls();
     InitModuleObjects();
+    // These are the internal unit tests covered by other modules and libraries
+    Array objects;
+    objects.append(*(new LoadedObject ("jhtree")));
+    objects.append(*(new LoadedObject ("roxiemem")));
+    objects.append(*(new LoadedObject ("thorhelper")));
+
     queryStderrLogMsgHandler()->setMessageFields(MSGFIELD_time);
     CppUnit::TextUi::TestRunner runner;
     if (argc==1)