Code

CxxTest unit tests can now be built on Windows, also adds CxxTest versions of most...
authorjaspervdg <jaspervdg@users.sourceforge.net>
Tue, 1 Jul 2008 18:18:32 +0000 (18:18 +0000)
committerjaspervdg <jaspervdg@users.sourceforge.net>
Tue, 1 Jul 2008 18:18:32 +0000 (18:18 +0000)
build.xml
buildtool.cpp
src/display/bezier-utils-test.h [new file with mode: 0644]
src/helper/units-test.h [new file with mode: 0644]
src/xml/quote-test.h
src/xml/repr-action-test.h

index fdbc3c2e77e136146a6d6dd3807de38f271756e5..0355b98bc5049559d62f6adf5357b366b68fa25d 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -53,6 +53,7 @@
   <property name="arch"          value="mingw32-"/>
   <property name="archutil"      value=""/>
   <property name="devlibs"       location="c:/devlibs"/>
+  <property name="cxxtest"       location="cxxtest"/>
   <!-- -->
 
   <!-- Use these settings for the cross compiler -->  
   </target>
 
 
+  <!--
+  ########################################################################
+  ## T A R G E T    :    C X X T E S T
+  ########################################################################
+  -->
+  <target name="cxxtest" depends="init"
+        description="generate test files" >
+
+    <!-- Generate CxxTest files -->
+    <cxxtestpart command="python ${cxxtest}/cxxtestgen.py"
+                 out="${src}/test-src.cpp">
+        <fileset dir="${src}">
+            <include name="attributes-test.h"/>
+            <include name="color-profile-test.h"/>
+            <include name="dir-util-test.h"/>
+            <include name="extract-uri-test.h"/>
+            <include name="mod360-test.h"/>
+            <include name="round-test.h"/>
+            <include name="sp-gradient-test.h"/>
+            <include name="sp-style-elem-test.h"/>
+            <include name="syle-test.h"/>
+            <include name="test-helpers.h"/>
+            <include name="verbs-test.h"/>
+        </fileset>
+    </cxxtestpart>
+    <cxxtestpart command="python ${cxxtest}/cxxtestgen.py" 
+                 out="${src}/display/test-display.cpp">
+        <fileset dir="${src}/display">
+            <include name="bezier-utils-test.h"/>
+        </fileset>
+    </cxxtestpart>
+    <cxxtestpart command="python ${cxxtest}/cxxtestgen.py" 
+                 out="${src}/helper/test-helper.cpp">
+        <fileset dir="${src}/helper">
+            <include name="units-test.h"/>
+        </fileset>
+    </cxxtestpart>
+    <cxxtestpart command="python ${cxxtest}/cxxtestgen.py" 
+                 out="${src}/libnr/test-nr.cpp">
+        <fileset dir="${src}/libnr">
+            <include name="nr-types-test.h"/>
+            <include name="nr-translate-test.h"/>
+            <include name="nr-rotate-test.h"/>
+            <include name="nr-scale-test.h"/>
+            <include name="nr-point-fns-test.h"/>
+            <include name="nr-rotate-fns-test.h"/>
+            <include name="in-svg-plane-test.h"/>
+            <include name="nr-matrix-test.h"/>
+        </fileset>
+    </cxxtestpart>
+    <cxxtestpart command="python ${cxxtest}/cxxtestgen.py" 
+                 out="${src}/svg/test-svg.cpp">
+        <fileset dir="${src}/svg">
+            <include name="css-ostringstream-test.h"/>
+            <include name="stringstream-test.h"/>
+            <include name="svg-color-test.h"/>
+            <include name="svg-path-test.h"/>
+        </fileset>
+    </cxxtestpart>
+    <cxxtestpart command="python ${cxxtest}/cxxtestgen.py" 
+                 out="${src}/xml/test-xml.cpp">
+        <fileset dir="${src}/xml">
+            <include name="repr-action-test.h"/>
+            <include name="quote-test.h"/>
+        </fileset>
+    </cxxtestpart>
+    <cxxtestroot command="python ${cxxtest}/cxxtestgen.py" 
+                 out="${src}/test-main.cpp"
+                 template="${src}/selfname.tpl">
+        <fileset dir="${src}">
+            <include name="MultiPrinter.h"/>
+            <include name="PylogFormatter.h"/>
+            <include name="TRPIFormatter.h"/>
+        </fileset>
+    </cxxtestroot>
+  </target>
+
+
   <!--
   ########################################################################
   ## T A R G E T    :    C O M P I L E
   ########################################################################
   -->
-  <target name="compile" depends="init"
+  <target name="compile" depends="cxxtest"
         description="compile the source to .o" >
 
     <!-- Compile from source to build -->
             <exclude name="ast/.*"/>
             <exclude name="bonobo/.*"/>
             <exclude name="deptool.cpp"/>
-            <exclude name="test-all.cpp"/>
+            <!--<exclude name="test-main.cpp"/>-->
+            <!--<exclude name="test-src.cpp"/>-->
             <exclude name="display/testnr.cpp"/>
             <exclude name="display/bezier-utils-test.cpp"/>
             <exclude name="dom/work/.*"/>
             <exclude name="extract-uri-test.cpp"/>
             <exclude name="helper/units-test.cpp"/>
             <!-- exclude name="inkview.cpp"/-->
-            <exclude name="libnr/test-nr.cpp"/>
+            <!--<exclude name="libnr/test-nr.cpp"/>-->
             <exclude name="libnr/test-nr-main.cpp"/>
             <exclude name="libnr/testnr.cpp"/>
             <exclude name="libnr/in-svg-plane-test.cpp"/>
             <exclude name="trace/potrace/potest.cpp"/>
             <exclude name="round-test.cpp"/>
             <exclude name="sp-gradient-test.cpp"/>
+            <exclude name="style-test.cpp"/>
             <exclude name="svg/ftos.cpp"/>
-            <exclude name="svg/test-svg.cpp"/>
+            <!--<exclude name="svg/test-svg.cpp"/>-->
             <exclude name="svg/test-svg-main.cpp"/>
+            <exclude name="svg/test-stubs.cpp"/>
             <exclude name="utest/.*"/>
+            <exclude name="util/list-container-test.cpp"/>
             <exclude name="widgets/test-widgets.cpp"/>
             <exclude name="xml/quote-test.cpp"/>
             <exclude name="xml/repr-action-test.cpp"/>
-            <exclude name="xml/test-xml.cpp"/>
+            <!--<exclude name="xml/test-xml.cpp"/>-->
             <exclude name="xml/test-xml-main.cpp"/>
             <exclude name="io/streamtest.cpp"/>
             <!--JABBER-->
             ${pcc.poppler}
             -I${devlibs}/include/gc
             ${pcc.libwpg-0.1} ${pcc.libwpg-stream-0.1}
+            -I${cxxtest}
             <!-- PERL -->
             <!-- -Wno-comment -I${devlibs}/perl/lib/CORE -->
             <!-- PYTHON -->
            <exclude name="main.o"/>
            <exclude name="winmain.o"/>
            <exclude name="inkview.o"/>
+           <!-- CxxTest -->
+           <exclude name="obj/test-main.o"/>
+           <exclude name="obj/test-src.o"/>
+           <exclude name="obj/display/test-display.o"/>
+           <exclude name="obj/helper/test-helper.o"/>
+           <exclude name="obj/libnr/test-nr.o"/>
+           <exclude name="obj/svg/test-svg.o"/>
+           <exclude name="obj/xml/test-xml.o"/>
        </fileset>
     </staticlib>
   </target>
 
 
 
+  <!--
+  ########################################################################
+  ## T A R G E T    :    L I N K C X X T E S T S
+  ########################################################################
+  -->
+  <target name="linkcxxtests" depends="lib"
+      description="link objects and library to create executable">
+
+    <link command="${arch}g++" out="${build}/cxxtests.exe"
+              strip="true" symfile="${build}/cxxtests.dbg"
+              stripcommand="${archutil}strip"
+              objcopycommand="${archutil}objcopy">
+       <flags>
+       </flags>
+       <fileset dir="${build}">
+           <include name="obj/test-main.o"/>
+           <include name="obj/test-src.o"/>
+           <include name="obj/display/test-display.o"/>
+           <include name="obj/helper/test-helper.o"/>
+           <include name="obj/libnr/test-nr.o"/>
+           <include name="obj/svg/test-svg.o"/>
+           <include name="obj/xml/test-xml.o"/>
+           <include name="libinkscape.a"/>
+       </fileset>
+       <libs>
+           -L${devlibs}/lib
+           ${pcl.poppler-cairo} ${pcl.poppler-glib} ${pcl.poppler} 
+           ${pcl.gtkmm-2.4}  ${pcl.pangoft2} ${pcl.gthread-2.0}
+           ${devlibs}/bin/libxml2.dll
+           ${devlibs}/bin/libxslt.dll
+           ${pcl.cairo} ${pcl.cairomm-1.0}
+           ${pcl.libwpg-0.1} ${pcl.libwpg-stream-0.1}
+           ${devlibs}/lib/iconv.lib
+           ${pcl.ImageMagick++}
+           ${pcl.fontconfig} ${pcl.freetype2}
+           -lssl -lcrypto
+           ${pcl.lcms}
+           ${pcl.gsl}
+           -lpng -ljpeg.dll -ltiff.dll -lpopt ${devlibs}/lib/zdll.lib
+           -lgc
+           -lws2_32 -lintl -lgdi32 -lcomdlg32 -lm
+       </libs>
+    </link>
+  </target>
+
+
   <!--
   ########################################################################
   ## T A R G E T    :    D I S T
     <delete file="build.dep"/>
     <delete file="config.h"/>
 
+    <delete file="${src}/test-main.cpp"/>
+    <delete file="${src}/test-src.cpp"/>
+    <delete file="${src}/display/test-display.cpp"/>
+    <delete file="${src}/helper/test-helper.cpp"/>
+    <delete file="${src}/libnr/test-nr.cpp"/>
+    <delete file="${src}/svg/test-svg.cpp"/>
+    <delete file="${src}/xml/test-xml.cpp"/>
+
   </target>
 
 
index 852e651247c4548088af574053ddebe0e4781d4c..c3b0a29530cc9b8e079104e02b387fca4b9ac9b3 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Authors:
  *   Bob Jamison
+ *   Jasper van de Gronde
  *
  * Copyright (C) 2006-2008 Bob Jamison
  *
@@ -2778,7 +2779,7 @@ public:
     /**
      *
      */
-    String getDirectory()
+    String getDirectory() const
         { return directory; }
         
     /**
@@ -2796,7 +2797,7 @@ public:
     /**
      *
      */
-    std::vector<String> getFiles()
+    std::vector<String> getFiles() const
         { return files; }
         
     /**
@@ -2808,7 +2809,7 @@ public:
     /**
      *
      */
-    std::vector<String> getIncludes()
+    std::vector<String> getIncludes() const
         { return includes; }
         
     /**
@@ -2820,19 +2821,19 @@ public:
     /**
      *
      */
-    std::vector<String> getExcludes()
+    std::vector<String> getExcludes() const
         { return excludes; }
         
     /**
      *
      */
-    unsigned int size()
+    unsigned int size() const
         { return files.size(); }
         
     /**
      *
      */
-    String operator[](int index)
+    String operator[](int index) const
         { return files[index]; }
         
     /**
@@ -6374,6 +6375,8 @@ public:
         TASK_NONE,
         TASK_CC,
         TASK_COPY,
+        TASK_CXXTEST_PART,
+        TASK_CXXTEST_ROOT,
         TASK_DELETE,
         TASK_ECHO,
         TASK_JAR,
@@ -7079,6 +7082,208 @@ private:
 };
 
 
+/**
+ * Generate CxxTest files
+ */
+class TaskCxxTestPart: public Task
+{
+public:
+
+    TaskCxxTestPart(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_PART;
+         name    = "cxxtestpart";
+         }
+
+    virtual ~TaskCxxTestPart()
+        {}
+
+    virtual bool execute()
+        {
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
+                
+        String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
+        String cmd = parent.eval(commandOpt, "cxxtestgen.py");
+        cmd.append(" --part -o ");
+        cmd.append(fullDest);
+
+        unsigned int newFiles = 0;
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
+            {
+            String fileName = fileSet[i];
+            if (getSuffix(fileName) != "h")
+                continue;
+            String sourcePath;
+            if (fileSetDir.size()>0)
+                {
+                sourcePath.append(fileSetDir);
+                sourcePath.append("/");
+                }
+            sourcePath.append(fileName);
+            String fullSource = parent.resolve(sourcePath);
+
+            cmd.append(" ");
+            cmd.append(fullSource);
+            if (isNewerThan(fullSource, fullDest)) newFiles++;
+            }
+        
+        if (newFiles>0) {
+            size_t const lastSlash = fullDest.find_last_of('/');
+            if (lastSlash != fullDest.npos) {
+                String directory(fullDest, 0, lastSlash);
+                if (!createDirectory(directory))
+                    return false;
+            }
+
+            String outString, errString;
+            if (!executeCommand(cmd.c_str(), "", outString, errString))
+                {
+                error("<cxxtestpart> problem: %s", errString.c_str());
+                return false;
+                }
+        }
+
+        return true;
+        }
+
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "out", destPathOpt))
+            return false;
+            
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
+            {
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "fileset")
+                {
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
+            }
+        return true;
+        }
+
+private:
+
+    String  commandOpt;
+    String  destPathOpt;
+    FileSet fileSet;
+
+};
+
+
+/**
+ * Generate the CxxTest root file
+ */
+class TaskCxxTestRoot: public Task
+{
+public:
+
+    TaskCxxTestRoot(MakeBase &par) : Task(par)
+         {
+         type    = TASK_CXXTEST_ROOT;
+         name    = "cxxtestroot";
+         }
+
+    virtual ~TaskCxxTestRoot()
+        {}
+
+    virtual bool execute()
+        {
+        if (!listFiles(parent, fileSet))
+            return false;
+        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
+        unsigned int newFiles = 0;
+                
+        String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
+        String cmd = parent.eval(commandOpt, "cxxtestgen.py");
+        cmd.append(" --root -o ");
+        cmd.append(fullDest);
+        String templateFile = parent.eval(templateFileOpt, "");
+        if (templateFile.size()>0) {
+            String fullTemplate = parent.resolve(templateFile);
+            cmd.append(" --template=");
+            cmd.append(fullTemplate);
+            if (isNewerThan(fullTemplate, fullDest)) newFiles++;
+        }
+
+        for (unsigned int i=0 ; i<fileSet.size() ; i++)
+            {
+            String fileName = fileSet[i];
+            if (getSuffix(fileName) != "h")
+                continue;
+            String sourcePath;
+            if (fileSetDir.size()>0)
+                {
+                sourcePath.append(fileSetDir);
+                sourcePath.append("/");
+                }
+            sourcePath.append(fileName);
+            String fullSource = parent.resolve(sourcePath);
+
+            cmd.append(" ");
+            cmd.append(fullSource);
+            if (isNewerThan(fullSource, fullDest)) newFiles++;
+            }
+        
+        if (newFiles>0) {
+            size_t const lastSlash = fullDest.find_last_of('/');
+            if (lastSlash != fullDest.npos) {
+                String directory(fullDest, 0, lastSlash);
+                if (!createDirectory(directory))
+                    return false;
+            }
+
+            String outString, errString;
+            if (!executeCommand(cmd.c_str(), "", outString, errString))
+                {
+                error("<cxxtestroot> problem: %s", errString.c_str());
+                return false;
+                }
+        }
+
+        return true;
+        }
+
+    virtual bool parse(Element *elem)
+        {
+        if (!parent.getAttribute(elem, "command", commandOpt))
+            return false;
+        if (!parent.getAttribute(elem, "template", templateFileOpt))
+            return false;
+        if (!parent.getAttribute(elem, "out", destPathOpt))
+            return false;
+            
+        std::vector<Element *> children = elem->getChildren();
+        for (unsigned int i=0 ; i<children.size() ; i++)
+            {
+            Element *child = children[i];
+            String tagName = child->getName();
+            if (tagName == "fileset")
+                {
+                if (!parseFileSet(child, parent, fileSet))
+                    return false;
+                }
+            }
+        return true;
+        }
+
+private:
+
+    String  commandOpt;
+    String  templateFileOpt;
+    String  destPathOpt;
+    FileSet fileSet;
+
+};
+
+
 /**
  *
  */
@@ -8481,6 +8686,10 @@ Task *Task::createTask(Element *elem, int lineNr)
         task = new TaskCC(parent);
     else if (tagName == "copy")
         task = new TaskCopy(parent);
+    else if (tagName == "cxxtestpart")
+        task = new TaskCxxTestPart(parent);
+    else if (tagName == "cxxtestroot")
+        task = new TaskCxxTestRoot(parent);
     else if (tagName == "delete")
         task = new TaskDelete(parent);
     else if (tagName == "echo")
diff --git a/src/display/bezier-utils-test.h b/src/display/bezier-utils-test.h
new file mode 100644 (file)
index 0000000..efed5ea
--- /dev/null
@@ -0,0 +1,347 @@
+#include <cxxtest/TestSuite.h>\r
+\r
+#include <glib.h>\r
+#include <libnr/nr-macros.h> /* NR_DF_TEST_CLOSE */\r
+#include <sstream>\r
+\r
+/* mental disclaims all responsibility for this evil idea for testing\r
+   static functions.  The main disadvantages are that we retain the\r
+   #define's and `using' directives of the included file. */\r
+#include "bezier-utils.cpp"\r
+\r
+using NR::Point;\r
+\r
+/* (Returns false if NaN encountered.) */\r
+static bool range_approx_equal(double const a[], double const b[], unsigned const len) {\r
+    for (unsigned i = 0; i < len; ++i) {\r
+        if (!( fabs( a[i] - b[i] ) < 1e-4 )) {\r
+            return false;\r
+        }\r
+    }\r
+    return true;\r
+}\r
+\r
+static inline bool point_approx_equal(NR::Point const &a, NR::Point const &b, double const eps)\r
+{\r
+    using NR::X; using NR::Y;\r
+    return ( NR_DF_TEST_CLOSE(a[X], b[X], eps) &&\r
+             NR_DF_TEST_CLOSE(a[Y], b[Y], eps) );\r
+}\r
+\r
+static inline double square(double const x) {\r
+    return x * x;\r
+}\r
+\r
+/** Determine whether the found control points are the same as previously found on some developer's\r
+    machine.  Doesn't call utest__fail, just writes a message to stdout for diagnostic purposes:\r
+    the most important test is that the root-mean-square of errors in the estimation are low rather\r
+    than that the control points found are the same.\r
+**/\r
+static void compare_ctlpts(Point const est_b[], Point const exp_est_b[])\r
+{\r
+    unsigned diff_mask = 0;\r
+    for (unsigned i = 0; i < 4; ++i) {\r
+        for (unsigned d = 0; d < 2; ++d) {\r
+            if ( fabs( est_b[i][d] - exp_est_b[i][d] ) > 1.1e-5 ) {\r
+                diff_mask |= 1 << ( i * 2 + d );\r
+            }\r
+        }\r
+    }\r
+    if ( diff_mask != 0 ) {\r
+        std::stringstream msg;\r
+        msg << "Got different control points from previously-coded (diffs=0x" << std::hex << diff_mask << "\n";\r
+        msg << " Previous:";\r
+        for (unsigned i = 0; i < 4; ++i) {\r
+            msg << " (" << exp_est_b[i][0] << ", " << exp_est_b[i][1] << ")"; // localizing ok\r
+        }\r
+        msg << "\n";\r
+        msg << " Found:   ";\r
+        for (unsigned i = 0; i < 4; ++i) {\r
+            msg << " (" << est_b[i][0] << ", " << est_b[i][1] << ")"; // localizing ok\r
+        }\r
+        msg << "\n";\r
+        TS_WARN(msg.str().c_str());\r
+    }\r
+}\r
+\r
+static void compare_rms(Point const est_b[], double const t[], Point const d[], unsigned const n,\r
+                        double const exp_rms_error)\r
+{\r
+    double sum_errsq = 0.0;\r
+    for (unsigned i = 0; i < n; ++i) {\r
+        Point const fit_pt = bezier_pt(3, est_b, t[i]);\r
+        Point const diff = fit_pt - d[i];\r
+        sum_errsq += dot(diff, diff);\r
+    }\r
+    double const rms_error = sqrt( sum_errsq / n );\r
+    TS_ASSERT_LESS_THAN_EQUALS( rms_error , exp_rms_error + 1.1e-6 );\r
+    if ( rms_error < exp_rms_error - 1.1e-6 ) {\r
+        /* The fitter code appears to have improved [or the floating point calculations differ\r
+           on this machine from the machine where exp_rms_error was calculated]. */\r
+        char msg[200];\r
+        sprintf(msg, "N.B. rms_error regression requirement can be decreased: have rms_error=%g.", rms_error); // localizing ok\r
+        TS_TRACE(msg);\r
+    }\r
+}\r
+\r
+class BezierUtilsTest : public CxxTest::TestSuite {\r
+public:\r
+    static Point const c[4];\r
+    static double const t[24];\r
+    static unsigned const n;\r
+    Point d[24];\r
+    static Point const src_b[4];\r
+    static Point const tHat1;\r
+    static Point const tHat2;\r
+\r
+    BezierUtilsTest()\r
+    {\r
+        /* Feed it some points that can be fit exactly with a single bezier segment, and see how\r
+        well it manages. */\r
+        for (unsigned i = 0; i < n; ++i) {\r
+            d[i] = bezier_pt(3, src_b, t[i]);\r
+        }\r
+    }\r
+    virtual ~BezierUtilsTest() {}\r
+\r
+    void testCopyWithoutNansOrAdjacentDuplicates()\r
+    {\r
+        NR::Point const src[] = {\r
+            Point(2., 3.),\r
+            Point(2., 3.),\r
+            Point(0., 0.),\r
+            Point(2., 3.),\r
+            Point(2., 3.),\r
+            Point(1., 9.),\r
+            Point(1., 9.)\r
+        };\r
+        Point const exp_dest[] = {\r
+            Point(2., 3.),\r
+            Point(0., 0.),\r
+            Point(2., 3.),\r
+            Point(1., 9.)\r
+        };\r
+        g_assert( G_N_ELEMENTS(src) == 7 );\r
+        Point dest[7];\r
+        struct tst {\r
+            unsigned src_ix0;\r
+            unsigned src_len;\r
+            unsigned exp_dest_ix0;\r
+            unsigned exp_dest_len;\r
+        } const test_data[] = {\r
+            /* src start ix, src len, exp_dest start ix, exp dest len */\r
+            {0, 0, 0, 0},\r
+            {2, 1, 1, 1},\r
+            {0, 1, 0, 1},\r
+            {0, 2, 0, 1},\r
+            {0, 3, 0, 2},\r
+            {1, 3, 0, 3},\r
+            {0, 5, 0, 3},\r
+            {0, 6, 0, 4},\r
+            {0, 7, 0, 4}\r
+        };\r
+        for (unsigned i = 0 ; i < G_N_ELEMENTS(test_data) ; ++i) {\r
+            tst const &t = test_data[i];\r
+            TS_ASSERT_EQUALS( t.exp_dest_len,\r
+                              copy_without_nans_or_adjacent_duplicates(src + t.src_ix0,\r
+                                                                       t.src_len,\r
+                                                                       dest) );\r
+            TS_ASSERT_SAME_DATA(dest,\r
+                                exp_dest + t.exp_dest_ix0,\r
+                                t.exp_dest_len);\r
+        }\r
+    }\r
+\r
+    void testBezierPt1()\r
+    {\r
+        Point const a[] = {Point(2.0, 4.0),\r
+                           Point(1.0, 8.0)};\r
+        TS_ASSERT_EQUALS( bezier_pt(1, a, 0.0) , a[0] );\r
+        TS_ASSERT_EQUALS( bezier_pt(1, a, 1.0) , a[1] );\r
+        TS_ASSERT_EQUALS( bezier_pt(1, a, 0.5) , Point(1.5, 6.0) );\r
+        double const t[] = {0.5, 0.25, 0.3, 0.6};\r
+        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {\r
+            double const ti = t[i], si = 1.0 - ti;\r
+            TS_ASSERT_EQUALS( bezier_pt(1, a, ti) , si * a[0] + ti * a[1] );\r
+        }\r
+    }\r
+\r
+    void testBezierPt2()\r
+    {\r
+        Point const b[] = {Point(1.0, 2.0),\r
+                           Point(8.0, 4.0),\r
+                           Point(3.0, 1.0)};\r
+        TS_ASSERT_EQUALS( bezier_pt(2, b, 0.0) , b[0] );\r
+        TS_ASSERT_EQUALS( bezier_pt(2, b, 1.0) , b[2] );\r
+        TS_ASSERT_EQUALS( bezier_pt(2, b, 0.5) , Point(5.0, 2.75) );\r
+        double const t[] = {0.5, 0.25, 0.3, 0.6};\r
+        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {\r
+            double const ti = t[i], si = 1.0 - ti;\r
+            Point const exp_pt( si*si * b[0] + 2*si*ti * b[1] + ti*ti * b[2] );\r
+            Point const pt(bezier_pt(2, b, ti));\r
+            TS_ASSERT(point_approx_equal(pt, exp_pt, 1e-11));\r
+        }\r
+    }\r
+\r
+    void testBezierPt3()\r
+    {\r
+        TS_ASSERT_EQUALS( bezier_pt(3, c, 0.0) , c[0] );\r
+        TS_ASSERT_EQUALS( bezier_pt(3, c, 1.0) , c[3] );\r
+        TS_ASSERT_EQUALS( bezier_pt(3, c, 0.5) , Point(4.0, 13.0/8.0) );\r
+        double const t[] = {0.5, 0.25, 0.3, 0.6};\r
+        for (unsigned i = 0; i < G_N_ELEMENTS(t); ++i) {\r
+            double const ti = t[i], si = 1.0 - ti;\r
+            TS_ASSERT( LInfty( bezier_pt(3, c, ti)\r
+                                  - ( si*si*si * c[0] +\r
+                                      3*si*si*ti * c[1] +\r
+                                      3*si*ti*ti * c[2] +\r
+                                      ti*ti*ti * c[3] ) )\r
+                          < 1e-4 );\r
+        }\r
+    }\r
+\r
+    void testComputeMaxErrorRatio()\r
+    {\r
+        struct Err_tst {\r
+            Point pt;\r
+            double u;\r
+            double err;\r
+        } const err_tst[] = {\r
+            {c[0], 0.0, 0.0},\r
+            {Point(4.0, 13.0/8.0), 0.5, 0.0},\r
+            {Point(4.0, 2.0), 0.5, 9.0/64.0},\r
+            {Point(3.0, 2.0), 0.5, 1.0 + 9.0/64.0},\r
+            {Point(6.0, 2.0), 0.5, 4.0 + 9.0/64.0},\r
+            {c[3], 1.0, 0.0},\r
+        };\r
+        Point d[G_N_ELEMENTS(err_tst)];\r
+        double u[G_N_ELEMENTS(err_tst)];\r
+        for (unsigned i = 0; i < G_N_ELEMENTS(err_tst); ++i) {\r
+            Err_tst const &t = err_tst[i];\r
+            d[i] = t.pt;\r
+            u[i] = t.u;\r
+        }\r
+        g_assert( G_N_ELEMENTS(u) == G_N_ELEMENTS(d) );\r
+        unsigned max_ix = ~0u;\r
+        double const err_ratio = compute_max_error_ratio(d, u, G_N_ELEMENTS(d), c, 1.0, &max_ix);\r
+        TS_ASSERT_LESS_THAN( fabs( sqrt(err_tst[4].err) - err_ratio ) , 1e-12 );\r
+        TS_ASSERT_EQUALS( max_ix , 4 );\r
+    }\r
+\r
+    void testChordLengthParameterize()\r
+    {\r
+        /* n == 2 */\r
+        {\r
+            Point const d[] = {Point(2.9415, -5.8149),\r
+                               Point(23.021, 4.9814)};\r
+            double u[G_N_ELEMENTS(d)];\r
+            double const exp_u[] = {0.0, 1.0};\r
+            g_assert( G_N_ELEMENTS(u) == G_N_ELEMENTS(exp_u) );\r
+            chord_length_parameterize(d, u, G_N_ELEMENTS(d));\r
+            TS_ASSERT_SAME_DATA(u, exp_u, G_N_ELEMENTS(exp_u));\r
+        }\r
+\r
+        /* Straight line. */\r
+        {\r
+            double const exp_u[] = {0.0, 0.1829, 0.2105, 0.2105, 0.619, 0.815, 0.999, 1.0};\r
+            unsigned const n = G_N_ELEMENTS(exp_u);\r
+            Point d[n];\r
+            double u[n];\r
+            Point const a(-23.985, 4.915), b(4.9127, 5.203);\r
+            for (unsigned i = 0; i < n; ++i) {\r
+                double bi = exp_u[i], ai = 1.0 - bi;\r
+                d[i] = ai * a  +  bi * b;\r
+            }\r
+            chord_length_parameterize(d, u, n);\r
+            TS_ASSERT(range_approx_equal(u, exp_u, n));\r
+        }\r
+    }\r
+\r
+    void testGenerateBezier()\r
+    {\r
+        Point est_b[4];\r
+        generate_bezier(est_b, d, t, n, tHat1, tHat2, 1.0);\r
+\r
+        compare_ctlpts(est_b, src_b);\r
+\r
+        /* We're being unfair here in using our t[] rather than best t[] for est_b: we\r
+           may over-estimate RMS of errors. */\r
+        compare_rms(est_b, t, d, n, 1e-8);\r
+    }\r
+\r
+    void testSpBezierFitCubicFull()\r
+    {\r
+        Point est_b[4];\r
+        int splitpoints[2];\r
+        gint const succ = sp_bezier_fit_cubic_full(est_b, splitpoints, d, n, tHat1, tHat2, square(1.2), 1);\r
+        TS_ASSERT_EQUALS( succ , 1 );\r
+\r
+        Point const exp_est_b[4] = {\r
+            Point(5.000000, -3.000000),\r
+            Point(7.5753, -0.4247),\r
+            Point(4.77533, 1.22467),\r
+            Point(3, 3)\r
+        };\r
+        compare_ctlpts(est_b, exp_est_b);\r
+\r
+        /* We're being unfair here in using our t[] rather than best t[] for est_b: we\r
+           may over-estimate RMS of errors. */\r
+        compare_rms(est_b, t, d, n, .307911);\r
+    }\r
+\r
+    void testSpBezierFitCubic()\r
+    {\r
+        Point est_b[4];\r
+        gint const succ = sp_bezier_fit_cubic(est_b, d, n, square(1.2));\r
+        TS_ASSERT_EQUALS( succ , 1 );\r
+\r
+        Point const exp_est_b[4] = {\r
+            Point(5.000000, -3.000000),\r
+            Point(7.57134, -0.423509),\r
+            Point(4.77929, 1.22426),\r
+            Point(3, 3)\r
+        };\r
+        compare_ctlpts(est_b, exp_est_b);\r
+\r
+#if 1 /* A change has been made to right_tangent.  I believe that usually this change\r
+         will result in better fitting, but it won't do as well for this example where\r
+         we happen to be feeding a t=0.999 point to the fitter. */\r
+        TS_WARN("TODO: Update this test case for revised right_tangent implementation.");\r
+        /* In particular, have a test case to show whether the new implementation\r
+           really is likely to be better on average. */\r
+#else\r
+        /* We're being unfair here in using our t[] rather than best t[] for est_b: we\r
+           may over-estimate RMS of errors. */\r
+        compare_rms(est_b, t, d, n, .307983);\r
+#endif\r
+    }\r
+};\r
+\r
+// This is not very neat, but since we know this header is only included by the generated CxxTest file it shouldn't give any problems\r
+Point const BezierUtilsTest::c[4] = {\r
+    Point(1.0, 2.0),\r
+    Point(8.0, 4.0),\r
+    Point(3.0, 1.0),\r
+    Point(-2.0, -4.0)};\r
+double const BezierUtilsTest::t[24] = {\r
+    0.0, .001, .03, .05, .09, .13, .18, .25, .29, .33,  .39, .44,\r
+    .51,  .57, .62, .69, .75, .81, .91, .93, .97, .98, .999, 1.0};\r
+unsigned const BezierUtilsTest::n = G_N_ELEMENTS(BezierUtilsTest::t);\r
+Point const BezierUtilsTest::src_b[4] = {\r
+    Point(5., -3.),\r
+    Point(8., 0.),\r
+    Point(4., 2.),\r
+    Point(3., 3.)};\r
+Point const BezierUtilsTest::tHat1(unit_vector( BezierUtilsTest::src_b[1] - BezierUtilsTest::src_b[0] ));\r
+Point const BezierUtilsTest::tHat2(unit_vector( BezierUtilsTest::src_b[2] - BezierUtilsTest::src_b[3] ));\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
diff --git a/src/helper/units-test.h b/src/helper/units-test.h
new file mode 100644 (file)
index 0000000..f704b98
--- /dev/null
@@ -0,0 +1,88 @@
+#include <cxxtest/TestSuite.h>\r
+\r
+#include <helper/units.h>\r
+#include <glibmm/i18n.h>\r
+#include <math.h>\r
+\r
+\r
+/* N.B. Wrongly returns false if both near 0.  (Not a problem for current users.) */\r
+static bool\r
+approx_equal(double const x, double const y)\r
+{\r
+    return fabs(x / y - 1) < 1e-15;\r
+}\r
+\r
+static double\r
+sp_units_get_points(double const x, SPUnit const &unit)\r
+{\r
+    SPUnit const &pt_unit = sp_unit_get_by_id(SP_UNIT_PT);\r
+    double const px = sp_units_get_pixels(x, unit);\r
+    return sp_pixels_get_units(px, pt_unit);\r
+}\r
+\r
+static double\r
+sp_points_get_units(double const pts, SPUnit const &unit)\r
+{\r
+    SPUnit const &pt_unit = sp_unit_get_by_id(SP_UNIT_PT);\r
+    double const px = sp_units_get_pixels(pts, pt_unit);\r
+    return sp_pixels_get_units(px, unit);\r
+}\r
+\r
+class UnitsTest : public CxxTest::TestSuite {\r
+public:\r
+\r
+    UnitsTest()\r
+    {\r
+    }\r
+    virtual ~UnitsTest() {}\r
+\r
+    void testConversions()\r
+    {\r
+        struct Case { double x; char const *abbr; double pts; } const tests[] = {\r
+            { 1.0, "pt", 1.0 },\r
+            { 5.0, "pt", 5.0 },\r
+            { 1.0, "in", 72.0 },\r
+            { 2.0, "in", 144.0 },\r
+            { 254., "mm", 720.0 },\r
+            { 254., "cm", 7200. },\r
+            { 254., "m", 720000. },\r
+            { 1.5, "mm", (15 * 72. / 254) }\r
+        };\r
+        for (unsigned i = 0; i < G_N_ELEMENTS(tests); ++i) {\r
+            Case const &c = tests[i];\r
+            SPUnit const &unit = *sp_unit_get_by_abbreviation(N_(c.abbr));\r
+\r
+            double const calc_pts = sp_units_get_points(c.x, unit);\r
+            TS_ASSERT(approx_equal(calc_pts, c.pts));\r
+\r
+            double const calc_x = sp_points_get_units(c.pts, unit);\r
+            TS_ASSERT(approx_equal(calc_x, c.x));\r
+\r
+            double tmp = c.x;\r
+            bool const converted_to_pts = sp_convert_distance(&tmp, &unit, SP_PS_UNIT);\r
+            TS_ASSERT(converted_to_pts);\r
+            TS_ASSERT(approx_equal(tmp, c.pts));\r
+\r
+            tmp = c.pts;\r
+            bool const converted_from_pts = sp_convert_distance(&tmp, SP_PS_UNIT, &unit);\r
+            TS_ASSERT(converted_from_pts);\r
+            TS_ASSERT(approx_equal(tmp, c.x));\r
+        }\r
+    }\r
+\r
+    void testUnitTable()\r
+    {\r
+        TS_ASSERT(sp_units_table_sane());\r
+    }\r
+};\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :encoding=utf-8:textwidth=99 :\r
index 39f7e616784d8d1b7dad48eb5e4c9a43d191a1fb..7f53728446500cf853c6c5b75a09d168df7ddf56 100644 (file)
@@ -1,5 +1,25 @@
 #include <cxxtest/TestSuite.h>
 
+/* Initial author: Peter Moulder.
+   Hereby released into the Public Domain. */
+
+#include <cstring>
+#include <functional>
+
+/* mental disclaims all responsibility for this evil idea for testing
+   static functions.  The main disadvantages are that we retain any
+   #define's and `using' directives of the included file. */
+#include "quote.cpp"
+
+struct streq_free2 {
+    bool operator()(char const *exp, char *got) const
+    {
+        bool const ret = (strcmp(exp, got) == 0);
+        g_free(got);
+        return ret;
+    }
+};
+
 class XmlQuoteTest : public CxxTest::TestSuite
 {
 public:
@@ -14,8 +34,45 @@ public:
     static XmlQuoteTest *createSuite() { return new XmlQuoteTest(); }
     static void destroySuite( XmlQuoteTest *suite ) { delete suite; }
 
-    void testFoo()
+    void testXmlQuotedStrlen()
+    {
+        struct {
+            char const *s;
+            size_t len;
+        } cases[] = {
+            {"", 0},
+            {"x", 1},
+            {"Foo", 3},
+            {"\"", 6},
+            {"&", 5},
+            {"<", 4},
+            {">", 4},
+            {"a\"b", 8},
+            {"a\"b<c>d;!@#$%^*(\\)?", 30}
+        };
+        for(size_t i=0; i<G_N_ELEMENTS(cases); i++) {
+            TS_ASSERT_EQUALS( xml_quoted_strlen(cases[i].s) , cases[i].len );
+        }
+    }
+
+    void testXmlQuoteStrdup()
     {
+        struct {
+            char const * s1;
+            char const * s2;
+        } cases[] = {
+            {"", ""},
+            {"x", "x"},
+            {"Foo", "Foo"},
+            {"\"", "&quot;"},
+            {"&", "&amp;"},
+            {"<", "&lt;"},
+            {">", "&gt;"},
+            {"a\"b<c>d;!@#$%^*(\\)?", "a&quot;b&lt;c&gt;d;!@#$%^*(\\)?"}
+        };
+        for(size_t i=0; i<G_N_ELEMENTS(cases); i++) {
+            TS_ASSERT_RELATION( streq_free2, cases[i].s2, xml_quote_strdup(cases[i].s1) );
+        }
     }
 };
 
index 10f7d52f13b03d11fd220dada9a86566d532cb8c..8fe1327aff29e11551f8f084c6e8f62ac03a7b83 100644 (file)
@@ -1,11 +1,30 @@
 #include <cxxtest/TestSuite.h>
 
+#include <cstdlib>
+#include <glib.h>
+
+#include "repr.h"
+#include "event-fns.h"
+
+static void * const null_ptr = 0;
+
 class XmlReprActionTest : public CxxTest::TestSuite
 {
+    Inkscape::XML::Document *document;
+    Inkscape::XML::Node *a, *b, *c, *root;
+
 public:
 
     XmlReprActionTest()
     {
+        Inkscape::GC::init();
+
+        document = sp_repr_document_new("test");
+        root = document->root();
+
+        a = document->createElement("a");
+        b = document->createElement("b");
+        c = document->createElement("c");
     }
     virtual ~XmlReprActionTest() {}
 
@@ -14,9 +33,62 @@ public:
     static XmlReprActionTest *createSuite() { return new XmlReprActionTest(); }
     static void destroySuite( XmlReprActionTest *suite ) { delete suite; }
 
-    void testFoo()
+    void testRollbackOfNodeAddition()
+    {
+        sp_repr_begin_transaction(document);
+        TS_ASSERT_EQUALS(sp_repr_parent(a) , null_ptr);
+
+        root->appendChild(a);
+        TS_ASSERT_EQUALS(sp_repr_parent(a) , root);
+
+        sp_repr_rollback(document);
+        TS_ASSERT_EQUALS(sp_repr_parent(a) , null_ptr);
+    }
+
+    void testRollbackOfNodeRemoval()
+    {
+        root->appendChild(a);
+
+        sp_repr_begin_transaction(document);
+        TS_ASSERT_EQUALS(sp_repr_parent(a) , root);
+
+        sp_repr_unparent(a);
+        TS_ASSERT_EQUALS(sp_repr_parent(a) , null_ptr);
+
+        sp_repr_rollback(document);
+        TS_ASSERT_EQUALS(sp_repr_parent(a) , root);
+
+        sp_repr_unparent(a);
+    }
+
+    void testRollbackOfNodeReordering()
     {
+        root->appendChild(a);
+        root->appendChild(b);
+        root->appendChild(c);
+
+        sp_repr_begin_transaction(document);
+        TS_ASSERT_EQUALS(sp_repr_next(a) , b);
+        TS_ASSERT_EQUALS(sp_repr_next(b) , c);
+        TS_ASSERT_EQUALS(sp_repr_next(c) , null_ptr);
+
+        root->changeOrder(b, c);
+        TS_ASSERT_EQUALS(sp_repr_next(a) , c);
+        TS_ASSERT_EQUALS(sp_repr_next(b) , null_ptr);
+        TS_ASSERT_EQUALS(sp_repr_next(c) , b);
+
+        sp_repr_rollback(document);
+        TS_ASSERT_EQUALS(sp_repr_next(a) , b);
+        TS_ASSERT_EQUALS(sp_repr_next(b) , c);
+        TS_ASSERT_EQUALS(sp_repr_next(c) , null_ptr);
+
+        sp_repr_unparent(a);
+        sp_repr_unparent(b);
+        sp_repr_unparent(c);
     }
+
+    /* lots more tests needed ... */
+
 };
 
 /*