From 89a5b9dbe8aa56452dfa1a9241c194eeb9c06a28 Mon Sep 17 00:00:00 2001
From: Benjamin C Meyer <ben@meyerhome.net>
Date: Mon, 24 Feb 2014 19:31:06 -0500
Subject: [PATCH 12/23] Create autotest for BookmarkNode class

BookmarkNode was causing Arora to crash on exit
because of the way it was deleting its children.
---
 autotests/autotests.pro                     |   1 +
 autotests/bookmarknode/.gitignore           |   1 +
 autotests/bookmarknode/bookmarknode.pro     |  14 +
 autotests/bookmarknode/tst_bookmarknode.cpp | 272 ++++++++++++++++++++
 src/bookmarks/bookmarknode.cpp              |  13 +-
 src/bookmarks/bookmarknode.h                |   4 +-
 6 files changed, 300 insertions(+), 5 deletions(-)
 create mode 100644 autotests/bookmarknode/.gitignore
 create mode 100644 autotests/bookmarknode/bookmarknode.pro
 create mode 100644 autotests/bookmarknode/tst_bookmarknode.cpp

diff --git autotests/autotests.pro autotests/autotests.pro
index 5e31e94..fdd3290 100644
--- autotests/autotests.pro
+++ autotests/autotests.pro
@@ -3,6 +3,7 @@ SUBDIRS  = \
     adblock \
     addbookmarkdialog \
     autosaver \
+    bookmarknode \
     cookiejar \
     historyfiltermodel \
     historymanager \
diff --git autotests/bookmarknode/.gitignore autotests/bookmarknode/.gitignore
new file mode 100644
index 0000000..e2f5b4a
--- /dev/null
+++ autotests/bookmarknode/.gitignore
@@ -0,0 +1 @@
+bookmarknode
diff --git autotests/bookmarknode/bookmarknode.pro autotests/bookmarknode/bookmarknode.pro
new file mode 100644
index 0000000..b796c01
--- /dev/null
+++ autotests/bookmarknode/bookmarknode.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += . ../
+
+include(../autotests.pri)
+
+QT = core
+
+# Input
+SOURCES = bookmarknode.cpp tst_bookmarknode.cpp
+HEADERS = bookmarknode.h
+FORMS =
+RESOURCE =
diff --git autotests/bookmarknode/tst_bookmarknode.cpp autotests/bookmarknode/tst_bookmarknode.cpp
new file mode 100644
index 0000000..a8307e7
--- /dev/null
+++ autotests/bookmarknode/tst_bookmarknode.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2014 Benjamin C. Meyer <ben@meyerhome.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Benjamin Meyer nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <QtTest/QtTest>
+#include <bookmarknode.h>
+
+class tst_BookmarkNode : public QObject
+{
+    Q_OBJECT
+
+public slots:
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+    void cleanup();
+
+private slots:
+    void bookmarknode_data();
+    void bookmarknode();
+
+    void add_duplicate();
+    void add_to_bookmark();
+    void add_move();
+    void add_data();
+    void add();
+    void equal();
+    void remove();
+    void deleteNode();
+    void deleteChildren();
+    void type_data();
+    void type();
+};
+
+// This will be called before the first test function is executed.
+// It is only called once.
+void tst_BookmarkNode::initTestCase()
+{
+}
+
+// This will be called after the last test function is executed.
+// It is only called once.
+void tst_BookmarkNode::cleanupTestCase()
+{
+}
+
+// This will be called before each test function is executed.
+void tst_BookmarkNode::init()
+{
+}
+
+// This will be called after every test function.
+void tst_BookmarkNode::cleanup()
+{
+}
+
+void tst_BookmarkNode::bookmarknode_data()
+{
+}
+
+void tst_BookmarkNode::bookmarknode()
+{
+    BookmarkNode node;
+    QCOMPARE(node.children(), QList<BookmarkNode*>());
+    QCOMPARE(node.parent(), (BookmarkNode*)0);
+    QCOMPARE(node.type(), BookmarkNode::Root);
+    node.add((BookmarkNode*)0, -1);
+    node.remove((BookmarkNode*)0);
+}
+
+void tst_BookmarkNode::add_to_bookmark()
+{
+    BookmarkNode node(BookmarkNode::Bookmark);
+    BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+    node.add(child);
+    // qWarning()?
+    QCOMPARE(node.children().count(), 0);
+    QCOMPARE(child->parent(), (BookmarkNode*)0);
+    delete child;
+}
+
+void tst_BookmarkNode::add_duplicate()
+{
+    BookmarkNode node;
+    BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+    node.add(child);
+    node.add(child);
+    QCOMPARE(node.children().count(), 1);
+}
+
+void tst_BookmarkNode::add_move()
+{
+    BookmarkNode node1;
+    BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+    node1.add(child);
+
+    BookmarkNode node2;
+    node2.add(child);
+
+    QCOMPARE(child->parent(), &node2);
+    QVERIFY(node2.children().contains(child));
+    QVERIFY(!node1.children().contains(child));
+}
+
+void tst_BookmarkNode::add_data()
+{
+    QTest::addColumn<int>("startcount");
+    QTest::addColumn<int>("insertoffset");
+    QTest::addColumn<int>("resultingoffset");
+    QTest::newRow("0,-1,0") << 0 << -1 << 0;
+    QTest::newRow("0,0,0")  << 0 << 0  << 0;
+    QTest::newRow("0,1,0")  << 0 << 1  << 0;
+    QTest::newRow("1,-1,1") << 1 << -1 << 1;
+    QTest::newRow("1,0,0")  << 1 << 0  << 0;
+    QTest::newRow("1,1,0")  << 1 << 1  << 1;
+}
+
+// public void add(BookmarkNode *child, int offset = -1)
+void tst_BookmarkNode::add()
+{
+    QFETCH(int, startcount);
+    QFETCH(int, insertoffset);
+    QFETCH(int, resultingoffset);
+
+    BookmarkNode node;
+
+    for (int i = 0; i < startcount; ++i) {
+        BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+        node.add(child, -1);
+    }
+
+    BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+    node.add(child, insertoffset);
+
+    QCOMPARE(child->parent(), &node);
+    QList<BookmarkNode*> children = node.children();
+    QVERIFY(children.contains(child));
+    QCOMPARE(children.at(resultingoffset), child);
+}
+
+Q_DECLARE_METATYPE(BookmarkNode)
+// public bool equal(BookmarkNode const &other)
+void tst_BookmarkNode::equal()
+{
+    BookmarkNode null1;
+    BookmarkNode null2;
+    QCOMPARE(null1, null2);
+
+    BookmarkNode b1(BookmarkNode::Bookmark);
+    BookmarkNode b2(BookmarkNode::Bookmark);
+    QCOMPARE(b1, b2);
+
+    b1.title = "title";
+    QVERIFY(!(b1 == b2));
+    b2.title = "title";
+    QCOMPARE(b1, b2);
+
+    b1.url = "url";
+    QVERIFY(!(b1 == b2));
+    b2.url = "url";
+    QCOMPARE(b1, b2);
+
+    b1.desc = "description";
+    QVERIFY(!(b1 == b2));
+    b2.desc = "description";
+    QCOMPARE(b1, b2);
+
+    BookmarkNode f1(BookmarkNode::Folder);
+    BookmarkNode f2(BookmarkNode::Folder);
+    QCOMPARE(f1, f2);
+
+    f1.expanded = true;
+    QVERIFY(!(f1 == f2));
+    f2.expanded = true;
+    QCOMPARE(f1, f2);
+
+    BookmarkNode *child1 = new BookmarkNode(BookmarkNode::Bookmark);
+    child1->title = "title";
+    f1.add(child1);
+    QVERIFY(!(f1 == f2));
+
+    BookmarkNode *child2 = new BookmarkNode(BookmarkNode::Bookmark);
+    child2->title = "title";
+    f2.add(child2);
+    QCOMPARE(f1, f2);
+}
+
+// public void remove(BookmarkNode *child)
+void tst_BookmarkNode::remove()
+{
+    BookmarkNode node;
+    BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+    node.add(child);
+    node.remove(child);
+    QCOMPARE(node.children().count(), 0);
+    QCOMPARE(child->parent(), (BookmarkNode*)0);
+    delete child;
+}
+
+void tst_BookmarkNode::deleteNode()
+{
+    BookmarkNode node;
+    BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+    node.add(child);
+
+    delete child;
+
+    QCOMPARE(node.children().count(), 0);
+    QCOMPARE(child->parent(), (BookmarkNode*)0);
+}
+
+void tst_BookmarkNode::deleteChildren()
+{
+    BookmarkNode *node = new BookmarkNode();
+    for (int i = 0; i < 10; ++i) {
+        BookmarkNode *child = new BookmarkNode(BookmarkNode::Bookmark);
+        node->add(child, -1);
+    }
+    delete node;
+    // This shouldn't segfualt :)
+}
+
+Q_DECLARE_METATYPE(BookmarkNode::Type)
+void tst_BookmarkNode::type_data()
+{
+    QTest::addColumn<BookmarkNode::Type>("type");
+    QTest::newRow("root") << BookmarkNode::Root;
+    QTest::newRow("folder") << BookmarkNode::Folder;
+    QTest::newRow("sep") << BookmarkNode::Separator;
+    QTest::newRow("bookmark") << BookmarkNode::Bookmark;
+}
+
+// public BookmarkNode::Type type() const
+void tst_BookmarkNode::type()
+{
+    QFETCH(BookmarkNode::Type, type);
+
+    BookmarkNode node(type);
+
+    QCOMPARE(node.type(), type);
+    if (node.type() == BookmarkNode::Root)
+        QVERIFY(!node.parent());
+    if (node.type() == BookmarkNode::Bookmark)
+        QVERIFY(node.children().isEmpty());
+}
+
+QTEST_MAIN(tst_BookmarkNode)
+#include "tst_bookmarknode.moc"
diff --git src/bookmarks/bookmarknode.cpp src/bookmarks/bookmarknode.cpp
index ce6d742..a05e239 100644
--- src/bookmarks/bookmarknode.cpp
+++ src/bookmarks/bookmarknode.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008-2009 Benjamin C. Meyer <ben@meyerhome.net>
+ * Copyright 2008-2014 Benjamin C. Meyer <ben@meyerhome.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -75,12 +75,13 @@ BookmarkNode::~BookmarkNode()
 {
     if (m_parent)
         m_parent->remove(this);
-    qDeleteAll(m_children);
+    for (int i = m_children.count() -1; i >= 0; --i)
+        delete m_children[i];
     m_parent = 0;
     m_type = BookmarkNode::Root;
 }
 
-bool BookmarkNode::operator==(const BookmarkNode &other)
+bool BookmarkNode::operator==(const BookmarkNode &other) const
 {
     if (url != other.url
         || title != other.title
@@ -118,6 +119,10 @@ BookmarkNode *BookmarkNode::parent() const
 
 void BookmarkNode::add(BookmarkNode *child, int offset)
 {
+    if (!child)
+        return;
+    if (m_type == BookmarkNode::Bookmark)
+        return;
     Q_ASSERT(child->m_type != Root);
     if (child->m_parent)
         child->m_parent->remove(child);
@@ -129,6 +134,8 @@ void BookmarkNode::add(BookmarkNode *child, int offset)
 
 void BookmarkNode::remove(BookmarkNode *child)
 {
+    if (!child)
+        return;
     child->m_parent = 0;
     m_children.removeAll(child);
 }
diff --git src/bookmarks/bookmarknode.h src/bookmarks/bookmarknode.h
index b817c28..69c05d2 100644
--- src/bookmarks/bookmarknode.h
+++ src/bookmarks/bookmarknode.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008-2009 Benjamin C. Meyer <ben@meyerhome.net>
+ * Copyright 2008-2014 Benjamin C. Meyer <ben@meyerhome.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -78,7 +78,7 @@ public:
 
     BookmarkNode(Type type = Root, BookmarkNode *parent = 0);
     ~BookmarkNode();
-    bool operator==(const BookmarkNode &other);
+    bool operator==(const BookmarkNode &other) const;
 
     Type type() const;
     void setType(Type type);
-- 
2.44.0

