<commit_before>/*
 * This file is part of Maliit Plugins
 *
 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
 *
 * Contact: Mohammad Anwari <Mohammad.Anwari@nokia.com>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list
 * of conditions and the following disclaimer.
 * 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.
 * Neither the name of Nokia Corporation 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "renderer.h"
#include "keyareaitem.h"
#include "keyitem.h"
#include "graphicsview.h"
#include "models/keyarea.h"

#ifdef MALIIT_KEYBOARD_HAVE_GL
#include <QGLWidget>
#endif

namespace MaliitKeyboard {

namespace {

QGraphicsView * createView(QWidget *widget,
                           AbstractBackgroundBuffer *buffer)
{
    GraphicsView *view = new GraphicsView(widget);
    view->setBackgroundBuffer(buffer);
    view->resize(widget->size());
    view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
    view->setOptimizationFlags(QGraphicsView::DontClipPainter | QGraphicsView::DontSavePainterState);
    QGraphicsScene *scene = new QGraphicsScene(view);
    view->setScene(scene);
    view->setFrameShape(QFrame::NoFrame);
    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

#ifdef MALIIT_KEYBOARD_HAVE_GL
    view->setViewport(new QGLWidget);
#endif

    return view;
}

QGraphicsItem * rootItem(QGraphicsView *view)
{
    if (not view || not view->scene()) {
        return 0;
    }

    QList<QGraphicsItem *> items = view->scene()->items(Qt::DescendingOrder);
    if (not items.isEmpty()) {
        return items.at(0);
    }

    return 0;
}

} // namespace

class RendererPrivate
{
public:
    typedef QVector<KeyAreaItem *> Pool;

    QWidget *window;
    QScopedPointer<QGraphicsView> view;
    AbstractBackgroundBuffer *buffer;
    QRegion region;
    QVector<KeyItem *> key_items;
    QHash<SharedLayout, Pool> pools;

    explicit RendererPrivate()
        : window(0)
        , view(0)
        , buffer(0)
        , region()
        , key_items()
        , pools()
    {}
};

Renderer::Renderer(QObject *parent)
    : QObject(parent)
    , d_ptr(new RendererPrivate)
{}

Renderer::~Renderer()
{}

void Renderer::setWindow(QWidget *window)
{
    Q_D(Renderer);
    d->window = window;

    d->view.reset(createView(d->window, d->buffer));
    d->view->show();

    QGraphicsRectItem *root = new QGraphicsRectItem;
    root->setRect(d->view->rect());
    root->show();
    d->view->scene()->addItem(root);
}

void Renderer::setBackgroundBuffer(AbstractBackgroundBuffer *buffer)
{
    Q_D(Renderer);
    d->buffer = buffer;
}

QRegion Renderer::region() const
{
    Q_D(const Renderer);
    return d->region;
}

QWidget * Renderer::viewport() const
{
    Q_D(const Renderer);
    return d->view->viewport();
}

void Renderer::show(const SharedLayout &layout)
{
    Q_D(Renderer);

    if (layout.isNull() || not d->window || d->view.isNull()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Invalid layout or no main window specified,"
                    << "don't know where to render to.";
        return;
    }

    // Allows for multiple key areas being shown at the same time:
    // TODO: Animate fade-in, from nearest window border.
    KeyAreaItem *item = 0;
    const RendererPrivate::Pool &pool(d->pools.value(layout));
    if (pool.isEmpty()) {
        item = new KeyAreaItem(rootItem(d->view.data()));
        RendererPrivate::Pool p(Layout::NumPanels, 0);
        p.replace(layout->activePanel(), item);
        d->pools.insert(layout, p);
    } else if (KeyAreaItem *found = pool.at(layout->activePanel())) {
        item = found;
    }

    // TODO: Construct region from polygon path so that split key areas dont
    // consume space in between them.
    d->region -= QRegion(item->boundingRect().toRect());
    item->setKeyArea(layout->activeKeyArea());

    d->region |= QRegion(item->boundingRect().toRect());
    item->show();
}

void Renderer::hide(const SharedLayout &layout)
{
    if (layout.isNull()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Cannot hide non-existant layout.";
        return;
    }

    Q_D(Renderer);

    // TODO: Animate fade-out, towards nearest window border.
    const RendererPrivate::Pool &pool(d->pools.value(layout));
    if (pool.isEmpty()) {
        return;
    } else if (KeyAreaItem *found = pool.at(Layout::CenterPanel)) {
        d->region -= QRegion(found->boundingRect().toRect());
        found->hide();
    }
}

void Renderer::hideAll()
{
    Q_D(Renderer);

    d->region = QRegion();
    foreach (RendererPrivate::Pool p, d->pools) {
        for (int index = 0; index < p.count(); ++index) {
            if (KeyAreaItem *found = p.at(index)) {
                found->hide();
            }
        }
    }
}

void Renderer::onLayoutChanged(const SharedLayout &layout)
{
    show(layout);
}

void Renderer::onKeysChanged(const SharedLayout &layout)
{
    if (layout.isNull()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Invalid layout.";
        return;
     }

    Q_D(Renderer);

    const RendererPrivate::Pool &pool(d->pools.value(layout));
    if (pool.isEmpty()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Unknown layout. Forgot to show it?";
        return;
    }

    if (KeyAreaItem *const ka_item = pool.at(layout->activePanel())) {
        const QVector<Key> &active_keys(layout->activeKeys());
        int index = 0;

        for (; index < active_keys.count(); ++index) {
            KeyItem *item = 0;

            if (index >= d->key_items.count()) {
                item = new KeyItem;
                d->key_items.append(item);
            } else {
                item = d->key_items.at(index);
            }

            item->setParentItem(ka_item);
            item->setKey(active_keys.at(index));
            item->show();
        }

        if (layout->magnifierKey().valid()) {
            KeyItem *magnifier = 0;

            if (index >= d->key_items.count()) {
                magnifier = new KeyItem;
                d->key_items.append(magnifier);
            } else {
                magnifier = d->key_items.at(index);
            }

            magnifier->setParentItem(ka_item);
            magnifier->setKey(layout->magnifierKey());
            magnifier->show();
            ++index;
        }

        // Hide remaining, currently unneeded key items:
        for (; index < d->key_items.count(); ++index) {
            d->key_items.at(index)->hide();
        }
    }
}

} // namespace MaliitKeyboard
<commit_msg>Refactor KeyItem handling in Renderer<commit_after>/*
 * This file is part of Maliit Plugins
 *
 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
 *
 * Contact: Mohammad Anwari <Mohammad.Anwari@nokia.com>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list
 * of conditions and the following disclaimer.
 * 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.
 * Neither the name of Nokia Corporation 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "renderer.h"
#include "keyareaitem.h"
#include "keyitem.h"
#include "graphicsview.h"
#include "models/keyarea.h"

#ifdef MALIIT_KEYBOARD_HAVE_GL
#include <QGLWidget>
#endif

namespace MaliitKeyboard {

namespace {

QGraphicsView * createView(QWidget *widget,
                           AbstractBackgroundBuffer *buffer)
{
    GraphicsView *view = new GraphicsView(widget);
    view->setBackgroundBuffer(buffer);
    view->resize(widget->size());
    view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
    view->setOptimizationFlags(QGraphicsView::DontClipPainter | QGraphicsView::DontSavePainterState);
    QGraphicsScene *scene = new QGraphicsScene(view);
    view->setScene(scene);
    view->setFrameShape(QFrame::NoFrame);
    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

#ifdef MALIIT_KEYBOARD_HAVE_GL
    view->setViewport(new QGLWidget);
#endif

    return view;
}

QGraphicsItem * rootItem(QGraphicsView *view)
{
    if (not view || not view->scene()) {
        return 0;
    }

    QList<QGraphicsItem *> items = view->scene()->items(Qt::DescendingOrder);
    if (not items.isEmpty()) {
        return items.at(0);
    }

    return 0;
}

void recycleKeyItem(QVector<KeyItem *> *key_items,
                    int index,
                    const Key &key,
                    QGraphicsItem *parent)
{
    KeyItem *item = 0;

    if (index >= key_items->count()) {
        item = new KeyItem;
        key_items->append(item);
    } else {
        item = key_items->at(index);
    }

    item->setParentItem(parent);
    item->setKey(key);
    item->show();
}

} // namespace

class RendererPrivate
{
public:
    typedef QVector<KeyAreaItem *> Pool;

    QWidget *window;
    QScopedPointer<QGraphicsView> view;
    AbstractBackgroundBuffer *buffer;
    QRegion region;
    QVector<KeyItem *> key_items;
    QHash<SharedLayout, Pool> pools;

    explicit RendererPrivate()
        : window(0)
        , view(0)
        , buffer(0)
        , region()
        , key_items()
        , pools()
    {}
};

Renderer::Renderer(QObject *parent)
    : QObject(parent)
    , d_ptr(new RendererPrivate)
{}

Renderer::~Renderer()
{}

void Renderer::setWindow(QWidget *window)
{
    Q_D(Renderer);
    d->window = window;

    d->view.reset(createView(d->window, d->buffer));
    d->view->show();

    QGraphicsRectItem *root = new QGraphicsRectItem;
    root->setRect(d->view->rect());
    root->show();
    d->view->scene()->addItem(root);
}

void Renderer::setBackgroundBuffer(AbstractBackgroundBuffer *buffer)
{
    Q_D(Renderer);
    d->buffer = buffer;
}

QRegion Renderer::region() const
{
    Q_D(const Renderer);
    return d->region;
}

QWidget * Renderer::viewport() const
{
    Q_D(const Renderer);
    return d->view->viewport();
}

void Renderer::show(const SharedLayout &layout)
{
    Q_D(Renderer);

    if (layout.isNull() || not d->window || d->view.isNull()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Invalid layout or no main window specified,"
                    << "don't know where to render to.";
        return;
    }

    // Allows for multiple key areas being shown at the same time:
    // TODO: Animate fade-in, from nearest window border.
    KeyAreaItem *item = 0;
    const RendererPrivate::Pool &pool(d->pools.value(layout));
    if (pool.isEmpty()) {
        item = new KeyAreaItem(rootItem(d->view.data()));
        RendererPrivate::Pool p(Layout::NumPanels, 0);
        p.replace(layout->activePanel(), item);
        d->pools.insert(layout, p);
    } else if (KeyAreaItem *found = pool.at(layout->activePanel())) {
        item = found;
    }

    // TODO: Construct region from polygon path so that split key areas dont
    // consume space in between them.
    d->region -= QRegion(item->boundingRect().toRect());
    item->setKeyArea(layout->activeKeyArea());

    d->region |= QRegion(item->boundingRect().toRect());
    item->show();
}

void Renderer::hide(const SharedLayout &layout)
{
    if (layout.isNull()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Cannot hide non-existant layout.";
        return;
    }

    Q_D(Renderer);

    // TODO: Animate fade-out, towards nearest window border.
    const RendererPrivate::Pool &pool(d->pools.value(layout));
    if (pool.isEmpty()) {
        return;
    } else if (KeyAreaItem *found = pool.at(Layout::CenterPanel)) {
        d->region -= QRegion(found->boundingRect().toRect());
        found->hide();
    }
}

void Renderer::hideAll()
{
    Q_D(Renderer);

    d->region = QRegion();
    foreach (RendererPrivate::Pool p, d->pools) {
        for (int index = 0; index < p.count(); ++index) {
            if (KeyAreaItem *found = p.at(index)) {
                found->hide();
            }
        }
    }
}

void Renderer::onLayoutChanged(const SharedLayout &layout)
{
    show(layout);
}

void Renderer::onKeysChanged(const SharedLayout &layout)
{
    if (layout.isNull()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Invalid layout.";
        return;
     }

    Q_D(Renderer);

    const RendererPrivate::Pool &pool(d->pools.value(layout));
    if (pool.isEmpty()) {
        qCritical() << __PRETTY_FUNCTION__
                    << "Unknown layout. Forgot to show it?";
        return;
    }

    if (KeyAreaItem *const ka_item = pool.at(layout->activePanel())) {
        const QVector<Key> &active_keys(layout->activeKeys());
        int index = 0;

        for (; index < active_keys.count(); ++index) {
            recycleKeyItem(&d->key_items, index, active_keys.at(index), ka_item);
        }

        if (layout->magnifierKey().valid()) {
            recycleKeyItem(&d->key_items, index, layout->magnifierKey(), ka_item);
            ++index;
        }

        // Hide remaining, currently unneeded key items:
        for (; index < d->key_items.count(); ++index) {
            d->key_items.at(index)->hide();
        }
    }
}

} // namespace MaliitKeyboard
<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include "red_black_tree.hpp"

namespace gt = testing;

namespace pk
{
namespace testing
{

struct red_black_tree_tester : public gt::Test
{
    static const int MAX_SIZE = 100;

    // tested class:
    red_black_tree<int, MAX_SIZE> rbt;
};

TEST_F(red_black_tree_tester, tests_emptyness_of_empty_tree)
{
    // when and then
    EXPECT_TRUE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_emptyness_after_insertion)
{
    // given
    const int elem = 11;

    // when
    rbt.insert(elem);

    // then
    EXPECT_FALSE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_emptyness_after_insertion_and_removal)
{
    // given
    const int elem = 11;
    rbt.insert(elem);

    // when
    rbt.remove(elem);

    // then
    EXPECT_TRUE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_emptyness_after_mixed_insertions_and_removals)
{
    // given
    const int elem1 = 11;
    const int elem2 = 22;
    const int elem3 = 33;

    // when
    rbt.insert(elem1);
    rbt.insert(elem2);
    rbt.remove(elem2);

    // then
    EXPECT_FALSE(rbt.empty());

    // when
    rbt.insert(elem3);

    // then
    EXPECT_FALSE(rbt.empty());

    // when
    rbt.remove(elem1);
    rbt.remove(elem3);

    // then
    EXPECT_TRUE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_size_of_empty_tree)
{
    // when and then
    EXPECT_EQ(0, rbt.size());
}

TEST_F(red_black_tree_tester, tests_size_after_insertion)
{
    // given
    const int elem = 11;

    // when
    rbt.insert(elem);

    // then
    EXPECT_EQ(1, rbt.size());
}

TEST_F(red_black_tree_tester, tests_size_after_insertion_and_removal)
{
    // given
    const int elem = 11;
    rbt.insert(elem);

    // when
    rbt.remove(elem);

    // then
    EXPECT_EQ(0, rbt.size());
}

TEST_F(red_black_tree_tester, tests_size_after_mixed_insertions_and_removals)
{
    // given
    const int elem1 = 11;
    const int elem2 = 22;
    const int elem3 = 33;

    // when
    rbt.insert(elem1);
    rbt.insert(elem2);
    rbt.remove(elem2);

    // then
    EXPECT_EQ(1, rbt.size());

    // when
    rbt.insert(elem3);

    // then
    EXPECT_EQ(2, rbt.size());

    // when
    rbt.remove(elem1);
    rbt.remove(elem3);

    // then
    EXPECT_EQ(0, rbt.size());
}

TEST_F(red_black_tree_tester, tests_existance_of_missing_element)
{
    // given
    const int elem = 11;

    // when and then
    EXPECT_FALSE(rbt.exists(elem));
}

TEST_F(red_black_tree_tester, tests_existance_of_inserted_element)
{
    // given
    const int elem = 11;

    // when
    rbt.insert(elem);

    // then
    EXPECT_TRUE(rbt.exists(elem));
}

TEST_F(red_black_tree_tester, tests_existance_of_many_inserted_elements)
{
    // given
    const int num_of_elements = 100;
    int elements[num_of_elements];
    for(int i = 0; i < num_of_elements; ++i)
        elements[i] = i; // sample values

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    for(int i = 0; i < num_of_elements; ++i)
        EXPECT_TRUE(rbt.exists(elements[i]));
}

TEST_F(red_black_tree_tester, tests_existance_of_removed_element)
{
    // given
    const int elem = 11;
    rbt.insert(elem);

    // when
    rbt.remove(elem);

    // then
    EXPECT_FALSE(rbt.exists(elem));
}

TEST_F(red_black_tree_tester, tests_existance_of_many_removed_elements)
{
    // given
    const int num_of_elements = 100;
    int elements[num_of_elements];
    for(int i = 0; i < num_of_elements; ++i)
        elements[i] = i; // sample values

    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.remove(elements[i]);

    // then
    for(int i = 0; i < num_of_elements; ++i)
        EXPECT_FALSE(rbt.exists(elements[i]));
}

TEST_F(red_black_tree_tester, tests_existance_of_mixed_inserted_and_removed_elements)
{
    // given
    const int elem1 = 33;
    const int elem2 = 22;
    const int elem3 = 11;

    // when
    rbt.insert(elem1);
    rbt.insert(elem2);
    rbt.remove(elem2);

    // then
    EXPECT_TRUE(rbt.exists(elem1));
    EXPECT_FALSE(rbt.exists(elem2));
    EXPECT_FALSE(rbt.exists(elem3));

    // when
    rbt.insert(elem3);

    // then
    EXPECT_TRUE(rbt.exists(elem1));
    EXPECT_FALSE(rbt.exists(elem2));
    EXPECT_TRUE(rbt.exists(elem3));

    // when
    rbt.remove(elem1);
    rbt.remove(elem3);

    // then
    EXPECT_FALSE(rbt.exists(elem1));
    EXPECT_FALSE(rbt.exists(elem2));
    EXPECT_FALSE(rbt.exists(elem3));
}

TEST_F(red_black_tree_tester, tests_minimum_element)
{
    // given
    const int num_of_elements = 7;
    const int elements[num_of_elements] = {55, 22, 66, -44, 0, -20, 33};

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    EXPECT_EQ(-44, rbt.min());
}

TEST_F(red_black_tree_tester, tests_maximum_element)
{
    // given
    const int num_of_elements = 7;
    const int elements[num_of_elements] = {55, 22, 66, -44, 0, -20, 33};

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    EXPECT_EQ(66, rbt.max());
}

TEST_F(red_black_tree_tester, tests_ranks_after_insertions)
{
    // given
    const int num_of_elements = 10;
    const int elements[num_of_elements] = {55, 22, 66, -44, 0, -22, 33, 11, 44, -33};

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    EXPECT_EQ(0, rbt.rank(-44));
    EXPECT_EQ(1, rbt.rank(-33));
    EXPECT_EQ(2, rbt.rank(-22));
    EXPECT_EQ(3, rbt.rank(0));
    EXPECT_EQ(4, rbt.rank(11));
    EXPECT_EQ(5, rbt.rank(22));
    EXPECT_EQ(6, rbt.rank(33));
    EXPECT_EQ(7, rbt.rank(44));
    EXPECT_EQ(8, rbt.rank(55));
    EXPECT_EQ(9, rbt.rank(66));
}

TEST_F(red_black_tree_tester, tests_ranks_after_removals)
{
    // given
    const int num_of_elements_to_insert = 10;
    const int num_of_elements_to_remove = 5;
    const int elements_to_insert[num_of_elements_to_insert] = {55, 22, 66, -44, 0, -22, 33, 11, 44, -33};
    const int elements_to_remove[num_of_elements_to_remove] = {55,     66,      0,      33,     44     };

    for(int i = 0; i < num_of_elements_to_insert; ++i)
        rbt.insert(elements_to_insert[i]);

    // when
    for(int i = 0; i < num_of_elements_to_remove; ++i)
        rbt.remove(elements_to_remove[i]);

    // then
    EXPECT_EQ(0, rbt.rank(-44));
    EXPECT_EQ(1, rbt.rank(-33));
    EXPECT_EQ(2, rbt.rank(-22));
    EXPECT_EQ(3, rbt.rank(11));
    EXPECT_EQ(4, rbt.rank(22));
}

} // namespace testing
} // namespace pk
<commit_msg>More complex unit tests for red-black tree<commit_after>#include <gtest/gtest.h>

#include "red_black_tree.hpp"

namespace gt = testing;

namespace pk
{
namespace testing
{

struct red_black_tree_tester : public gt::Test
{
    static const int MAX_SIZE = 100;

    // tested class:
    red_black_tree<int, MAX_SIZE> rbt;
};

TEST_F(red_black_tree_tester, tests_emptyness_of_empty_tree)
{
    // when and then
    EXPECT_TRUE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_emptyness_after_insertion)
{
    // given
    const int elem = 11;

    // when
    rbt.insert(elem);

    // then
    EXPECT_FALSE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_emptyness_after_insertion_and_removal)
{
    // given
    const int elem = 11;
    rbt.insert(elem);

    // when
    rbt.remove(elem);

    // then
    EXPECT_TRUE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_emptyness_after_mixed_insertions_and_removals)
{
    // given
    const int elem1 = 11;
    const int elem2 = 22;
    const int elem3 = 33;

    // when
    rbt.insert(elem1);
    rbt.insert(elem2);
    rbt.remove(elem2);

    // then
    EXPECT_FALSE(rbt.empty());

    // when
    rbt.insert(elem3);

    // then
    EXPECT_FALSE(rbt.empty());

    // when
    rbt.remove(elem1);
    rbt.remove(elem3);

    // then
    EXPECT_TRUE(rbt.empty());
}

TEST_F(red_black_tree_tester, tests_size_of_empty_tree)
{
    // when and then
    EXPECT_EQ(0, rbt.size());
}

TEST_F(red_black_tree_tester, tests_size_after_insertion)
{
    // given
    const int elem = 11;

    // when
    rbt.insert(elem);

    // then
    EXPECT_EQ(1, rbt.size());
}

TEST_F(red_black_tree_tester, tests_size_after_insertion_and_removal)
{
    // given
    const int elem = 11;
    rbt.insert(elem);

    // when
    rbt.remove(elem);

    // then
    EXPECT_EQ(0, rbt.size());
}

TEST_F(red_black_tree_tester, tests_size_after_mixed_insertions_and_removals)
{
    // given
    const int elem1 = 11;
    const int elem2 = 22;
    const int elem3 = 33;

    // when
    rbt.insert(elem1);
    rbt.insert(elem2);
    rbt.remove(elem2);

    // then
    EXPECT_EQ(1, rbt.size());

    // when
    rbt.insert(elem3);

    // then
    EXPECT_EQ(2, rbt.size());

    // when
    rbt.remove(elem1);
    rbt.remove(elem3);

    // then
    EXPECT_EQ(0, rbt.size());
}

TEST_F(red_black_tree_tester, tests_existance_of_missing_element)
{
    // given
    const int elem = 11;

    // when and then
    EXPECT_FALSE(rbt.exists(elem));
}

TEST_F(red_black_tree_tester, tests_existance_of_inserted_element)
{
    // given
    const int elem = 11;

    // when
    rbt.insert(elem);

    // then
    EXPECT_TRUE(rbt.exists(elem));
}

TEST_F(red_black_tree_tester, tests_existance_of_many_inserted_elements)
{
    // given
    const int num_of_elements = 100;
    int elements[num_of_elements];
    for(int i = 0; i < num_of_elements; ++i)
        elements[i] = i; // sample values

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    for(int i = 0; i < num_of_elements; ++i)
        EXPECT_TRUE(rbt.exists(elements[i]));
}

TEST_F(red_black_tree_tester, tests_existance_of_removed_element)
{
    // given
    const int elem = 11;
    rbt.insert(elem);

    // when
    rbt.remove(elem);

    // then
    EXPECT_FALSE(rbt.exists(elem));
}

TEST_F(red_black_tree_tester, tests_existance_of_many_removed_elements)
{
    // given
    const int num_of_elements = 100;
    int elements[num_of_elements];
    for(int i = 0; i < num_of_elements; ++i)
        elements[i] = i; // sample values

    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // when
    for(int i = num_of_elements - 1; i >= 0; --i)
        rbt.remove(elements[i]);

    // then
    for(int i = 0; i < num_of_elements; ++i)
        EXPECT_FALSE(rbt.exists(elements[i]));
}

TEST_F(red_black_tree_tester, tests_existance_of_mixed_inserted_and_removed_elements)
{
    // given
    const int elem1 = 33;
    const int elem2 = 22;
    const int elem3 = 11;

    // when
    rbt.insert(elem1);
    rbt.insert(elem2);
    rbt.remove(elem2);

    // then
    EXPECT_TRUE(rbt.exists(elem1));
    EXPECT_FALSE(rbt.exists(elem2));
    EXPECT_FALSE(rbt.exists(elem3));

    // when
    rbt.insert(elem3);

    // then
    EXPECT_TRUE(rbt.exists(elem1));
    EXPECT_FALSE(rbt.exists(elem2));
    EXPECT_TRUE(rbt.exists(elem3));

    // when
    rbt.remove(elem1);
    rbt.remove(elem3);

    // then
    EXPECT_FALSE(rbt.exists(elem1));
    EXPECT_FALSE(rbt.exists(elem2));
    EXPECT_FALSE(rbt.exists(elem3));
}

TEST_F(red_black_tree_tester, tests_minimum_element)
{
    // given
    const int num_of_elements = 7;
    const int elements[num_of_elements] = {55, 22, 66, -44, 0, -20, 33};

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    EXPECT_EQ(-44, rbt.min());
}

TEST_F(red_black_tree_tester, tests_maximum_element)
{
    // given
    const int num_of_elements = 7;
    const int elements[num_of_elements] = {55, 22, 66, -44, 0, -20, 33};

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    EXPECT_EQ(66, rbt.max());
}

TEST_F(red_black_tree_tester, tests_ranks_after_insertions)
{
    // given
    const int num_of_elements = 10;
    const int elements[num_of_elements] = {55, 22, 66, -44, 0, -22, 33, 11, 44, -33};

    // when
    for(int i = 0; i < num_of_elements; ++i)
        rbt.insert(elements[i]);

    // then
    EXPECT_EQ(0, rbt.rank(-44));
    EXPECT_EQ(1, rbt.rank(-33));
    EXPECT_EQ(2, rbt.rank(-22));
    EXPECT_EQ(3, rbt.rank(0));
    EXPECT_EQ(4, rbt.rank(11));
    EXPECT_EQ(5, rbt.rank(22));
    EXPECT_EQ(6, rbt.rank(33));
    EXPECT_EQ(7, rbt.rank(44));
    EXPECT_EQ(8, rbt.rank(55));
    EXPECT_EQ(9, rbt.rank(66));
}

TEST_F(red_black_tree_tester, tests_ranks_after_removals)
{
    // given
    const int num_of_elements_to_insert = 10;
    const int num_of_elements_to_remove = 5;
    const int elements_to_insert[num_of_elements_to_insert] = {55, 22, 66, -44, 0, -22, 33, 11, 44, -33};
    const int elements_to_remove[num_of_elements_to_remove] = {55,     66,      0,      33,     44     };

    for(int i = 0; i < num_of_elements_to_insert; ++i)
        rbt.insert(elements_to_insert[i]);

    // when
    for(int i = 0; i < num_of_elements_to_remove; ++i)
        rbt.remove(elements_to_remove[i]);

    // then
    EXPECT_EQ(0, rbt.rank(-44));
    EXPECT_EQ(1, rbt.rank(-33));
    EXPECT_EQ(2, rbt.rank(-22));
    EXPECT_EQ(3, rbt.rank(11));
    EXPECT_EQ(4, rbt.rank(22));
}

} // namespace testing
} // namespace pk
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 2018 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/

#include <osg/os_utils>

extern "C" {

int osg_system(const char* command)
{
#ifdef OSG_SYSTEM_SUPPORTED
    return system(command);
#else
    printf("osgSystem(%s) not supported.\n", command);
#endif
}

}

<commit_msg>Changed fallback message to be consistent with fucntion name<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 2018 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/

#include <osg/os_utils>

extern "C" {

int osg_system(const char* command)
{
#ifdef OSG_SYSTEM_SUPPORTED
    return system(command);
#else
    printf("osg_system(%s) not supported.\n", command);
#endif
}

}

<|endoftext|>


<commit_before>#include "../../SlothUnit/SlothUnit.h"
#include "__Tests__.generated.h"

using namespace SlothUnit;

int main()
{
	SlothTests::ExecuteAll();
	return 0;
}
<commit_msg>Cleanup cached files<commit_after><|endoftext|>


<commit_before>#include "Helper.h"
#include "cmath"
#include "time.h"
#include <QDir>
#include <QCoreApplication>
#include <QDateTime>

void Helper::randomInit()
{
	int seconds = time(NULL);
	int milliseconds = QTime::currentTime().msec();
	int process_id = QCoreApplication::applicationPid();
	srand(seconds + milliseconds + process_id + rand());
}

double Helper::randomNumber(double min, double max)
{
	double r = (double)rand() / (double)RAND_MAX;
	return min + r * (max - min);
}

QString Helper::randomString(int length, const QString& chars, bool init)
{
	//initialize random number generator
	if (init) randomInit();

	//create random string
	QString output;
	for (int i=0; i<length; ++i)
	{
        output.append(chars[rand() % chars.length()]);
	}
	return output;
}

QByteArray Helper::elapsedTime(QTime elapsed, bool only_seconds)
{
	//calculate minutes and seconds
	double s = elapsed.elapsed()/1000.0;
	double m = 0;
	double h = 0;
	if (!only_seconds)
	{
		m = floor(s/60.0);
		s -= 60.0 * m;
		h = floor(m/60.0);
		m -= 60.0 * h;
	}

	//create strings
	QByteArray sec = QByteArray::number(s, 'f', 3) + "s";
	QByteArray min = m==0.0 ? "" : QByteArray::number(m, 'f', 0) + "m ";
	QByteArray hours = h==0.0 ? "" : QByteArray::number(h, 'f', 0) + "h ";

	return hours + min + sec;
}

QStringList Helper::loadTextFile(QString file_name, bool trim_lines, QChar skip_header_char, bool skip_empty_lines)
{
	return loadTextFile(openFileForReading(file_name), trim_lines, skip_header_char, skip_empty_lines);
}

QStringList Helper::loadTextFile(QSharedPointer<QFile> file, bool trim_lines, QChar skip_header_char, bool skip_empty_lines)
{
	QStringList output;
	QTextStream stream(file.data());
	while (!stream.atEnd())
	{
		QString line = stream.readLine();

		//remove newline or trim
		if (trim_lines)
		{
			line = line.trimmed();
		}
		else
		{
			while (line.endsWith('\n') || line.endsWith('\r')) line.chop(1);
		}

		//skip empty lines
		if (skip_empty_lines && line.count()==0) continue;

		//skip header lines
		if (skip_header_char!=QChar::Null && line.count()!=0 && line[0]==skip_header_char) continue;

		output.append(line);
	}

	return output;
}

void Helper::storeTextFile(QSharedPointer<QFile> file, const QStringList& lines)
{
	QTextStream stream(file.data());
	foreach(QString line, lines)
	{
		while (line.endsWith('\n') || line.endsWith('\r')) line.chop(1);
		stream << line << "\n";
	}
}

void Helper::storeTextFile(QString file_name, const QStringList& lines)
{
	storeTextFile(openFileForWriting(file_name), lines);
}

QString Helper::fileText(QString filename)
{
	QFile file(filename);
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		THROW(FileAccessException, "Could not open file for reading: '" + filename + "'!");
	}

	QTextStream stream(&file); // we need a text stream to support UTF8 characters
	return stream.readAll();
}

void Helper::touchFile(QString filename)
{
	if (!QFile(filename).open(QFile::ReadWrite))
	{
		THROW(FileAccessException, "Could not open file for writing: '" + filename + "'!");
	}
}

QString Helper::tempFileName(QString extension, int length)
{
	QString name = Helper::randomString(length);
	if (extension!="")
	{
		if (!extension.startsWith(".")) name.append(".");
		name.append(extension);
	}

	return QDir::toNativeSeparators(QDir::tempPath() + "/" + name);
}

QStringList Helper::findFiles(const QString& directory, const QString& pattern, bool recursive)
{
	QStringList output;

	QDir dir(directory);
	if(!dir.exists()) THROW(FileAccessException, "Directory does not exist: " + directory);

	QFileInfoList file_infos = dir.entryInfoList(QStringList() << pattern, QDir::Files);
	foreach(const QFileInfo& entry, file_infos)
	{
		output.append(directory + "/" + entry.fileName());
	}

	if (recursive)
	{
		file_infos = dir.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot);
		foreach(const QFileInfo& entry, file_infos)
		{
			output << findFiles(directory + "/" + entry.fileName(), pattern, true);
		}
	}

	return output;
}

QStringList Helper::findFolders(const QString& directory, const QString& pattern, bool recursive)
{
	QStringList output;

	QDir dir(directory);
	if(!dir.exists()) THROW(FileAccessException, "Directory does not exist: " + directory);

	QFileInfoList file_infos = dir.entryInfoList(QStringList() << pattern, QDir::Dirs|QDir::NoDotAndDotDot);
	foreach(const QFileInfo& entry, file_infos)
	{
		output.append(directory + "/" + entry.fileName());
	}
	if (recursive)
	{
		file_infos = dir.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot);
		foreach(const QFileInfo& entry, file_infos)
		{
			output << findFolders(directory + "/" + entry.fileName(), pattern, true);
		}
	}

	return output;
}

int Helper::levenshtein(const QString& s1, const QString& s2)
{
	const int len1 = s1.size();
	const int len2 = s2.size();
	QVector<int> col(len2+1);
	QVector<int> prevCol(len2+1);

	for (int i=0; i<prevCol.size(); ++i)
	{
		prevCol[i] = i;
	}
	for (int i=0; i<len1; ++i)
	{
		col[0] = i+1;
		for (int j = 0; j < len2; j++)
		{
			col[j+1] = std::min( std::min(prevCol[1 + j] + 1, col[j] + 1), prevCol[j] + (s1[i]==s2[j] ? 0 : 1) );
		}
		col.swap(prevCol);
	}
	return prevCol[len2];
}

QString Helper::userName()
{
#ifdef _WIN32
	return qgetenv("USERNAME");
#else
	return qgetenv("USER");
#endif
}

QString Helper::dateTime(QString format)
{
	if (format=="")
	{
		return QDateTime::currentDateTime().toString(Qt::ISODate);
	}

	return QDateTime::currentDateTime().toString(format);
}

bool Helper::isWritable(QString filename)
{
	if (QFile::exists(filename))
	{
		QFileInfo file_info(filename);
		if (!file_info.isFile() || !file_info.isWritable())
		{
			return false;
		}
	}
	else
	{
		QString dir = QFileInfo(filename).absolutePath();
		QFileInfo dir_info(dir);
		if (!dir_info.isDir()  || !dir_info.isWritable())
		{
			return false;
		}
	}

	return true;
}

QString Helper::canonicalPath(QString filename)
{
	//Linux > Windows separator
	filename = filename.replace("/", "\\");

	//double > single separator (except for first character in case of UNC path)
	filename = filename.at(0) + filename.mid(1, filename.length()-1).replace("\\\\", "\\");

	//remove "." and folder before it
	QStringList parts = filename.split("\\");
	while (parts.contains("."))
	{
		int index = parts.indexOf(".");
		if (index==0) break;
		parts.removeAt(index);
	}

	//remove ".." and folder before it
	while (parts.contains(".."))
	{
		int index = parts.indexOf("..");
		if (index<=1) break;
		parts.removeAt(index);
		parts.removeAt(index-1);
	}

	return parts.join("\\");
}

QSharedPointer<QFile> Helper::openFileForReading(QString file_name, bool stdin_if_empty)
{
	QSharedPointer<QFile> file(new QFile(file_name));
	if (stdin_if_empty && file_name=="")
	{
		file->open(stdin, QFile::ReadOnly | QIODevice::Text);
	}
	else if (!file->open(QFile::ReadOnly | QIODevice::Text))
	{
		THROW(FileAccessException, "Could not open file for reading: '" + file_name + "'!");
	}

	return file;
}

QSharedPointer<QFile> Helper::openFileForWriting(QString file_name, bool stdout_if_file_empty, bool append)
{
	QSharedPointer<QFile> file(new QFile(file_name));
	if (stdout_if_file_empty && file_name=="")
	{
		file->open(stdout, QFile::WriteOnly | QIODevice::Text);
	}
	else if (!file->open(QFile::WriteOnly | QIODevice::Text |(append? QFile::Append : QFile::Truncate)))
	{
		THROW(FileAccessException, "Could not open file for writing: '" + file_name + "'!");
	}

	return file;
}
<commit_msg>Adjusted error message<commit_after>#include "Helper.h"
#include "cmath"
#include "time.h"
#include <QDir>
#include <QCoreApplication>
#include <QDateTime>

void Helper::randomInit()
{
	int seconds = time(NULL);
	int milliseconds = QTime::currentTime().msec();
	int process_id = QCoreApplication::applicationPid();
	srand(seconds + milliseconds + process_id + rand());
}

double Helper::randomNumber(double min, double max)
{
	double r = (double)rand() / (double)RAND_MAX;
	return min + r * (max - min);
}

QString Helper::randomString(int length, const QString& chars, bool init)
{
	//initialize random number generator
	if (init) randomInit();

	//create random string
	QString output;
	for (int i=0; i<length; ++i)
	{
        output.append(chars[rand() % chars.length()]);
	}
	return output;
}

QByteArray Helper::elapsedTime(QTime elapsed, bool only_seconds)
{
	//calculate minutes and seconds
	double s = elapsed.elapsed()/1000.0;
	double m = 0;
	double h = 0;
	if (!only_seconds)
	{
		m = floor(s/60.0);
		s -= 60.0 * m;
		h = floor(m/60.0);
		m -= 60.0 * h;
	}

	//create strings
	QByteArray sec = QByteArray::number(s, 'f', 3) + "s";
	QByteArray min = m==0.0 ? "" : QByteArray::number(m, 'f', 0) + "m ";
	QByteArray hours = h==0.0 ? "" : QByteArray::number(h, 'f', 0) + "h ";

	return hours + min + sec;
}

QStringList Helper::loadTextFile(QString file_name, bool trim_lines, QChar skip_header_char, bool skip_empty_lines)
{
	return loadTextFile(openFileForReading(file_name), trim_lines, skip_header_char, skip_empty_lines);
}

QStringList Helper::loadTextFile(QSharedPointer<QFile> file, bool trim_lines, QChar skip_header_char, bool skip_empty_lines)
{
	QStringList output;
	QTextStream stream(file.data());
	while (!stream.atEnd())
	{
		QString line = stream.readLine();

		//remove newline or trim
		if (trim_lines)
		{
			line = line.trimmed();
		}
		else
		{
			while (line.endsWith('\n') || line.endsWith('\r')) line.chop(1);
		}

		//skip empty lines
		if (skip_empty_lines && line.count()==0) continue;

		//skip header lines
		if (skip_header_char!=QChar::Null && line.count()!=0 && line[0]==skip_header_char) continue;

		output.append(line);
	}

	return output;
}

void Helper::storeTextFile(QSharedPointer<QFile> file, const QStringList& lines)
{
	QTextStream stream(file.data());
	foreach(QString line, lines)
	{
		while (line.endsWith('\n') || line.endsWith('\r')) line.chop(1);
		stream << line << "\n";
	}
}

void Helper::storeTextFile(QString file_name, const QStringList& lines)
{
	storeTextFile(openFileForWriting(file_name), lines);
}

QString Helper::fileText(QString filename)
{
	QFile file(filename);
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		THROW(FileAccessException, "Could not open text file for reading: '" + filename + "'!");
	}

	QTextStream stream(&file); // we need a text stream to support UTF8 characters
	return stream.readAll();
}

void Helper::touchFile(QString filename)
{
	if (!QFile(filename).open(QFile::ReadWrite))
	{
		THROW(FileAccessException, "Could not open file for writing: '" + filename + "'!");
	}
}

QString Helper::tempFileName(QString extension, int length)
{
	QString name = Helper::randomString(length);
	if (extension!="")
	{
		if (!extension.startsWith(".")) name.append(".");
		name.append(extension);
	}

	return QDir::toNativeSeparators(QDir::tempPath() + "/" + name);
}

QStringList Helper::findFiles(const QString& directory, const QString& pattern, bool recursive)
{
	QStringList output;

	QDir dir(directory);
	if(!dir.exists()) THROW(FileAccessException, "Directory does not exist: " + directory);

	QFileInfoList file_infos = dir.entryInfoList(QStringList() << pattern, QDir::Files);
	foreach(const QFileInfo& entry, file_infos)
	{
		output.append(directory + "/" + entry.fileName());
	}

	if (recursive)
	{
		file_infos = dir.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot);
		foreach(const QFileInfo& entry, file_infos)
		{
			output << findFiles(directory + "/" + entry.fileName(), pattern, true);
		}
	}

	return output;
}

QStringList Helper::findFolders(const QString& directory, const QString& pattern, bool recursive)
{
	QStringList output;

	QDir dir(directory);
	if(!dir.exists()) THROW(FileAccessException, "Directory does not exist: " + directory);

	QFileInfoList file_infos = dir.entryInfoList(QStringList() << pattern, QDir::Dirs|QDir::NoDotAndDotDot);
	foreach(const QFileInfo& entry, file_infos)
	{
		output.append(directory + "/" + entry.fileName());
	}
	if (recursive)
	{
		file_infos = dir.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot);
		foreach(const QFileInfo& entry, file_infos)
		{
			output << findFolders(directory + "/" + entry.fileName(), pattern, true);
		}
	}

	return output;
}

int Helper::levenshtein(const QString& s1, const QString& s2)
{
	const int len1 = s1.size();
	const int len2 = s2.size();
	QVector<int> col(len2+1);
	QVector<int> prevCol(len2+1);

	for (int i=0; i<prevCol.size(); ++i)
	{
		prevCol[i] = i;
	}
	for (int i=0; i<len1; ++i)
	{
		col[0] = i+1;
		for (int j = 0; j < len2; j++)
		{
			col[j+1] = std::min( std::min(prevCol[1 + j] + 1, col[j] + 1), prevCol[j] + (s1[i]==s2[j] ? 0 : 1) );
		}
		col.swap(prevCol);
	}
	return prevCol[len2];
}

QString Helper::userName()
{
#ifdef _WIN32
	return qgetenv("USERNAME");
#else
	return qgetenv("USER");
#endif
}

QString Helper::dateTime(QString format)
{
	if (format=="")
	{
		return QDateTime::currentDateTime().toString(Qt::ISODate);
	}

	return QDateTime::currentDateTime().toString(format);
}

bool Helper::isWritable(QString filename)
{
	if (QFile::exists(filename))
	{
		QFileInfo file_info(filename);
		if (!file_info.isFile() || !file_info.isWritable())
		{
			return false;
		}
	}
	else
	{
		QString dir = QFileInfo(filename).absolutePath();
		QFileInfo dir_info(dir);
		if (!dir_info.isDir()  || !dir_info.isWritable())
		{
			return false;
		}
	}

	return true;
}

QString Helper::canonicalPath(QString filename)
{
	//TODO: needs to be adjusted for the server version
	//Linux > Windows separator
	filename = filename.replace("/", "\\");

	//double > single separator (except for first character in case of UNC path)
	filename = filename.at(0) + filename.mid(1, filename.length()-1).replace("\\\\", "\\");

	//remove "." and folder before it
	QStringList parts = filename.split("\\");
	while (parts.contains("."))
	{
		int index = parts.indexOf(".");
		if (index==0) break;
		parts.removeAt(index);
	}

	//remove ".." and folder before it
	while (parts.contains(".."))
	{
		int index = parts.indexOf("..");
		if (index<=1) break;
		parts.removeAt(index);
		parts.removeAt(index-1);
	}

	return parts.join("\\");
}

QSharedPointer<QFile> Helper::openFileForReading(QString file_name, bool stdin_if_empty)
{
	QSharedPointer<QFile> file(new QFile(file_name));
	if (stdin_if_empty && file_name=="")
	{
		file->open(stdin, QFile::ReadOnly | QIODevice::Text);
	}
	else if (!file->open(QFile::ReadOnly | QIODevice::Text))
	{
		THROW(FileAccessException, "Could not open file for reading: '" + file_name + "'!");
	}

	return file;
}

QSharedPointer<QFile> Helper::openFileForWriting(QString file_name, bool stdout_if_file_empty, bool append)
{
	QSharedPointer<QFile> file(new QFile(file_name));
	if (stdout_if_file_empty && file_name=="")
	{
		file->open(stdout, QFile::WriteOnly | QIODevice::Text);
	}
	else if (!file->open(QFile::WriteOnly | QIODevice::Text |(append? QFile::Append : QFile::Truncate)))
	{
		THROW(FileAccessException, "Could not open file for writing: '" + file_name + "'!");
	}

	return file;
}
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* 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 the Free  *
* Software Foundation; either version 2 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 General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program. If not, see <http://www.gnu.org/licenses/>.              *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

/* Francois Faure, 2013 */
#include <SofaTest/Mapping_test.h>
#include <SofaSimulationGraph/DAGSimulation.h>
#include <sofa/defaulttype/VecTypes.h>
#include <sofa/defaulttype/RigidTypes.h>
#include <SofaRigid/RigidMapping.h>
#include <SofaBaseMechanics/MechanicalObject.h>

namespace sofa {
namespace {

// checks that a 1 second rotation at 1 rad.s^{-1} rotates by 1 rad.
template<class Rigid3Types>
struct QuaternionIntegrationTest : Sofa_test< typename Rigid3Types::Real > {

    using data_types = Rigid3Types;

    typename data_types::Coord coord;
    typename data_types::Deriv deriv;

    typename data_types::Real dt;

    QuaternionIntegrationTest()
        : dt(1) {

        deriv.getVOrientation()[1] = 1;

        // time integration
        coord += deriv * dt;
    }

    void test_quaternion_angle() const {
        using real = typename data_types::Real;
        defaulttype::Vec<3, real> axis; real angle;
        coord.getOrientation().quatToAxis(axis, angle);

        const real expected_angle = dt * deriv.getVOrientation().norm();

        // std::clog << expected_angle << " " << angle << std::endl;

        // child coordinates given directly in parent frame
        ASSERT_TRUE(this->isSmall(expected_angle - angle));
    }

};

// Define the list of types to instanciate. We do not necessarily need to test all combinations.
using testing::Types;
typedef Types<
    defaulttype::Rigid3dTypes,
    defaulttype::Rigid3fTypes
> DataTypes; // the types to instanciate.

// Test suite for all the instanciations
TYPED_TEST_CASE(QuaternionIntegrationTest, DataTypes);

// first test case
TYPED_TEST( QuaternionIntegrationTest, quaternion_angle) {
    this->test_quaternion_angle();
}

}
}
<commit_msg>[SofaRigid] randomized quaternion test<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* 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 the Free  *
* Software Foundation; either version 2 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 General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program. If not, see <http://www.gnu.org/licenses/>.              *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/

#include <SofaTest/Mapping_test.h>
#include <SofaSimulationGraph/DAGSimulation.h>
#include <sofa/defaulttype/VecTypes.h>
#include <sofa/defaulttype/RigidTypes.h>
#include <SofaRigid/RigidMapping.h>
#include <SofaBaseMechanics/MechanicalObject.h>

namespace sofa {
namespace {

// checks that a 1 second rotation at 1 rad.s^{-1} rotates by 1 rad.
template<class Rigid3Types>
struct QuaternionIntegrationTest : Sofa_test< typename Rigid3Types::Real > {

    using data_types = Rigid3Types;

    typename data_types::Coord coord;
    typename data_types::Deriv deriv;

    typename data_types::Real dt;

    QuaternionIntegrationTest()
        : dt(1) {

        deriv = data_types::randomDeriv(0, M_PI / 2);

        // time integration
        coord += deriv * dt;
    }

    void test_quaternion_angle() const {
        using real = typename data_types::Real;
        defaulttype::Vec<3, real> axis; real angle;
        coord.getOrientation().quatToAxis(axis, angle);

        const real expected_angle = dt * deriv.getVOrientation().norm();

        // std::clog << expected_angle << " " << angle << std::endl;

        // child coordinates given directly in parent frame
        ASSERT_TRUE(this->isSmall(expected_angle - angle, 10));
    }

};

// Define the list of types to instanciate. We do not necessarily need to test all combinations.
using testing::Types;
typedef Types<
    defaulttype::Rigid3dTypes,
    defaulttype::Rigid3fTypes
> DataTypes; // the types to instanciate.

// Test suite for all the instanciations
TYPED_TEST_CASE(QuaternionIntegrationTest, DataTypes);

// first test case
TYPED_TEST( QuaternionIntegrationTest, quaternion_angle) {
    this->test_quaternion_angle();
}

}
}
<|endoftext|>


<commit_before>#include <errno.h>

#include <Ogre.h>
#include <OgreArchiveFactory.h>

#ifdef NO_PLUGINS
#  include "OgreOctreePlugin.h"
#  include "OgreGLPlugin.h"
#  include "OgreParticleFXPlugin.h"
#  ifdef WIN32
#    include "OgreD3D9Plugin.h"
#  endif
#endif

#ifdef WIN32
#  include "win32/MouseDirectInput8.h"
#  include "win32/KeyboardDirectInput8.h"
#  include "win32/KeyboardWinAPI.h"
#else
#  include "linux/KeyboardX11.h"
#  include "linux/MouseX11.h"
#endif

#include "Grit.h"
#include "BackgroundMeshLoader.h"
#include "CentralisedLog.h"

CentralisedLog clog;

Grit *grit = NULL;

#include "console_colour.h"

int already_fatal = 0;

void app_fatal()
{
        if (!already_fatal) {
                already_fatal = 1;
                delete grit;
        }

        abort();
}

int main(int argc, const char **argv)
{

        try {

                Ogre::LogManager *lmgr = OGRE_NEW Ogre::LogManager();
                Ogre::Log *ogre_log = OGRE_NEW Ogre::Log("",false,true);
                ogre_log->addListener(&clog);
                lmgr->setDefaultLog(ogre_log);
                lmgr->setLogDetail(Ogre::LL_NORMAL);

                #ifdef NO_PLUGINS
                        Ogre::Root* ogre = OGRE_NEW Ogre::Root("","","");

                        Ogre::GLPlugin *gl = OGRE_NEW Ogre::GLPlugin();
                        ogre->installPlugin(gl);

                        Ogre::OctreePlugin *octree =
                                OGRE_NEW Ogre::OctreePlugin();
                        ogre->installPlugin(octree);

                        Ogre::ParticleFXPlugin *pfx =
                                OGRE_NEW Ogre::ParticleFXPlugin();
                        ogre->installPlugin(pfx);

                        #ifdef WIN32
                        Ogre::D3D9Plugin *d3d9 = OGRE_NEW Ogre::D3D9Plugin();
                        ogre->installPlugin(d3d9);
                        #endif
                #else
                        Ogre::Root* ogre = OGRE_NEW Ogre::Root("plugins.cfg","","");
                #endif

                BackgroundMeshLoader *bml = new BackgroundMeshLoader();

                Ogre::ConfigFile cfg;
		        try {
        	        cfg.loadDirect("grit.cfg");
		        } catch (Ogre::Exception) { }

                #ifdef WIN32
                bool use_d3d9 = cfg.getSetting("d3d9") == "yes";
                bool use_dinput = cfg.getSetting("dinput") == "yes";
                #else
                bool use_d3d9 = false;
                #endif

                bool use_vsync = cfg.getSetting("vsync") == "yes";
                bool use_fullscreen = cfg.getSetting("fullscreen") == "yes";

                if (use_d3d9) {
                        Ogre::RenderSystem *rs =
                                ogre->getRenderSystemByName("Direct3D9 Rendering Subsystem");
                        rs->setConfigOption("Allow NVPerfHUD","Yes");
                        rs->setConfigOption("Anti aliasing","None");
                        rs->setConfigOption("Floating-point mode","Consistent");
                        rs->setConfigOption("Full Screen",use_fullscreen?"Yes":"No");
                        rs->setConfigOption("VSync",use_vsync?"Yes":"No");
                        rs->setConfigOption("Video Mode","800 x 600 @ 32-bit colour");
                        rs->setConfigOption("sRGB Gamma Conversion","No");
                        ogre->setRenderSystem(rs);
                } else {
                        Ogre::RenderSystem *rs =
                                ogre->getRenderSystemByName("OpenGL Rendering Subsystem");
                        rs->setConfigOption("FSAA","0");
                        rs->setConfigOption("Full Screen",use_fullscreen?"Yes":"No");
                        rs->setConfigOption("RTT Preferred Mode","FBO");
                        rs->setConfigOption("VSync",use_vsync?"Yes":"No");
                        rs->setConfigOption("Video Mode","800 x 600");
                        rs->setConfigOption("sRGB Gamma Conversion","No");
                        ogre->setRenderSystem(rs);
                }

                Ogre::RenderWindow *win = ogre->initialise(true,"Grit Game Window");

                size_t winid;
                win->getCustomAttribute("WINDOW", &winid);

                #ifdef WIN32
                Mouse *mouse = new MouseDirectInput8(winid);
                Keyboard *keyb = use_dinput ? (Keyboard *)new KeyboardDirectInput8(winid)
                                            : (Keyboard *)new KeyboardWinAPI(winid);
                HMODULE mod = GetModuleHandle(NULL);
                HICON icon = LoadIcon(mod,MAKEINTRESOURCE(111));
                SendMessage((HWND)winid, (UINT)WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) icon);
                SendMessage((HWND)winid, (UINT)WM_SETICON, (WPARAM) ICON_SMALL, (LPARAM) icon);
                #else
                Mouse *mouse = new MouseX11(winid);
                Keyboard *keyb = new KeyboardX11(winid);
                #endif

                new Grit(ogre,mouse,keyb,grit); // writes itself back to grit

                // now call out to lua

                grit->doMain(argc,argv);

                // lua returns - we quit

                bml->shutdown();

                delete grit;

                // ogre was deleted inside the grit destructor above

                delete bml;

                #ifdef NO_PLUGINS
                        OGRE_DELETE gl;
                        OGRE_DELETE octree;
                        OGRE_DELETE pfx;
                        #ifdef WIN32
                                OGRE_DELETE d3d9;
                        #endif
                #endif

        } catch( Ogre::Exception& e ) {
                std::cerr << "An exception has occured: "
                          << e.getFullDescription() << std::endl;
        }
        return EXIT_SUCCESS;
}

// vim: shiftwidth=8:tabstop=8:expandtab
<commit_msg>Added FPE trapping stuff (didn't work out in the end though) fix whitespace<commit_after>#include <fenv.h>
#include <errno.h>

#include <Ogre.h>
#include <OgreArchiveFactory.h>

#ifdef NO_PLUGINS
#  include "OgreOctreePlugin.h"
#  include "OgreGLPlugin.h"
#  include "OgreParticleFXPlugin.h"
#  ifdef WIN32
#    include "OgreD3D9Plugin.h"
#  endif
#endif

#ifdef WIN32
#  include "win32/MouseDirectInput8.h"
#  include "win32/KeyboardDirectInput8.h"
#  include "win32/KeyboardWinAPI.h"
#else
#  include "linux/KeyboardX11.h"
#  include "linux/MouseX11.h"
#endif

#include "Grit.h"
#include "BackgroundMeshLoader.h"
#include "CentralisedLog.h"

CentralisedLog clog;

Grit *grit = NULL;

#include "console_colour.h"

int already_fatal = 0;

void app_fatal()
{
        if (!already_fatal) {
                already_fatal = 1;
                delete grit;
        }

        abort();
}

int main(int argc, const char **argv)
{

        try {

                Ogre::LogManager *lmgr = OGRE_NEW Ogre::LogManager();
                Ogre::Log *ogre_log = OGRE_NEW Ogre::Log("",false,true);
                ogre_log->addListener(&clog);
                lmgr->setDefaultLog(ogre_log);
                lmgr->setLogDetail(Ogre::LL_NORMAL);

                #ifdef FE_NOMASK_ENV
                //feenableexcept(FE_INVALID);
                //feenableexcept(FE_DIVBYZERO | FE_INVALID);
                #endif

                #ifdef NO_PLUGINS
                        Ogre::Root* ogre = OGRE_NEW Ogre::Root("","","");

                        Ogre::GLPlugin *gl = OGRE_NEW Ogre::GLPlugin();
                        ogre->installPlugin(gl);

                        Ogre::OctreePlugin *octree =
                                OGRE_NEW Ogre::OctreePlugin();
                        ogre->installPlugin(octree);

                        Ogre::ParticleFXPlugin *pfx =
                                OGRE_NEW Ogre::ParticleFXPlugin();
                        ogre->installPlugin(pfx);

                        #ifdef WIN32
                        Ogre::D3D9Plugin *d3d9 = OGRE_NEW Ogre::D3D9Plugin();
                        ogre->installPlugin(d3d9);
                        #endif
                #else
                        Ogre::Root* ogre = OGRE_NEW Ogre::Root("plugins.cfg","","");
                #endif

                BackgroundMeshLoader *bml = new BackgroundMeshLoader();

                Ogre::ConfigFile cfg;
                try {
                        cfg.loadDirect("grit.cfg");
                } catch (Ogre::Exception) { }

                #ifdef WIN32
                bool use_d3d9 = cfg.getSetting("d3d9") == "yes";
                bool use_dinput = cfg.getSetting("dinput") == "yes";
                #else
                bool use_d3d9 = false;
                #endif

                bool use_vsync = cfg.getSetting("vsync") == "yes";
                bool use_fullscreen = cfg.getSetting("fullscreen") == "yes";

                if (use_d3d9) {
                        Ogre::RenderSystem *rs =
                                ogre->getRenderSystemByName("Direct3D9 Rendering Subsystem");
                        rs->setConfigOption("Allow NVPerfHUD","Yes");
                        rs->setConfigOption("Anti aliasing","None");
                        rs->setConfigOption("Floating-point mode","Consistent");
                        rs->setConfigOption("Full Screen",use_fullscreen?"Yes":"No");
                        rs->setConfigOption("VSync",use_vsync?"Yes":"No");
                        rs->setConfigOption("Video Mode","800 x 600 @ 32-bit colour");
                        rs->setConfigOption("sRGB Gamma Conversion","No");
                        ogre->setRenderSystem(rs);
                } else {
                        Ogre::RenderSystem *rs =
                                ogre->getRenderSystemByName("OpenGL Rendering Subsystem");
                        rs->setConfigOption("FSAA","0");
                        rs->setConfigOption("Full Screen",use_fullscreen?"Yes":"No");
                        rs->setConfigOption("RTT Preferred Mode","FBO");
                        rs->setConfigOption("VSync",use_vsync?"Yes":"No");
                        rs->setConfigOption("Video Mode","800 x 600");
                        rs->setConfigOption("sRGB Gamma Conversion","No");
                        ogre->setRenderSystem(rs);
                }

                Ogre::RenderWindow *win = ogre->initialise(true,"Grit Game Window");

                size_t winid;
                win->getCustomAttribute("WINDOW", &winid);

                #ifdef WIN32
                Mouse *mouse = new MouseDirectInput8(winid);
                Keyboard *keyb = use_dinput ? (Keyboard *)new KeyboardDirectInput8(winid)
                                            : (Keyboard *)new KeyboardWinAPI(winid);
                HMODULE mod = GetModuleHandle(NULL);
                HICON icon = LoadIcon(mod,MAKEINTRESOURCE(111));
                SendMessage((HWND)winid, (UINT)WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) icon);
                SendMessage((HWND)winid, (UINT)WM_SETICON, (WPARAM) ICON_SMALL, (LPARAM) icon);
                #else
                Mouse *mouse = new MouseX11(winid);
                Keyboard *keyb = new KeyboardX11(winid);
                #endif

                new Grit(ogre,mouse,keyb,grit); // writes itself back to grit

                // now call out to lua

                grit->doMain(argc,argv);

                // lua returns - we quit

                bml->shutdown();

                delete grit;

                // ogre was deleted inside the grit destructor above

                delete bml;

                #ifdef NO_PLUGINS
                        OGRE_DELETE gl;
                        OGRE_DELETE octree;
                        OGRE_DELETE pfx;
                        #ifdef WIN32
                                OGRE_DELETE d3d9;
                        #endif
                #endif

        } catch( Ogre::Exception& e ) {
                std::cerr << "An exception has occured: "
                          << e.getFullDescription() << std::endl;
        }
        return EXIT_SUCCESS;
}

// vim: shiftwidth=8:tabstop=8:expandtab
<|endoftext|>


<commit_before>#include <map>
#include <memory>
#include <string>
#include <vector>

#include "common.h"

#include <Shlwapi.h>  // NOLINT

// Size of the buffer to store result of ReadDirectoryChangesW.
static const unsigned int kDirectoryWatcherBufferSize = 4096;

// Object template to create representation of WatcherHandle.
Persistent<ObjectTemplate> g_object_template;

// Mutex for the HandleWrapper map.
uv_mutex_t g_handle_wrap_map_mutex;

// The global IOCP to be quested.
static HANDLE g_iocp;

struct HandleWrapper {
  HandleWrapper(WatcherHandle handle, const char* path)
      : dir_handle(handle),
        path(path) {
    map_[dir_handle] = this;
    memset(&overlapped, 0, sizeof(overlapped));
  }

  ~HandleWrapper() {
    map_.erase(dir_handle);
  }

  WatcherHandle dir_handle;
  OVERLAPPED overlapped;
  std::string path;
  char buffer[kDirectoryWatcherBufferSize];

  static HandleWrapper* Get(WatcherHandle key) { return map_[key]; }

  static std::map<WatcherHandle, HandleWrapper*> map_;
};

std::map<WatcherHandle, HandleWrapper*> HandleWrapper::map_;

struct WatcherEvent {
  EVENT_TYPE type;
  WatcherHandle handle;
  std::string new_path;
  std::string old_path;
};

Handle<Value> WatcherHandleToV8Value(WatcherHandle handle) {
  Handle<Value> value = g_object_template->NewInstance();
  value->ToObject()->SetPointerInInternalField(0, handle);
  return value;
}

WatcherHandle V8ValueToWatcherHandle(Handle<Value> value) {
  return reinterpret_cast<WatcherHandle>(value->ToObject()->
      GetPointerFromInternalField(0));
}

bool IsV8ValueWatcherHandle(Handle<Value> value) {
  return value->IsObject() && value->ToObject()->InternalFieldCount() == 1;
}

void PlatformInit() {
  uv_mutex_init(&g_handle_wrap_map_mutex);

  g_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);

  g_object_template = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
  g_object_template->SetInternalFieldCount(1);

  WakeupNewThread();
}

void PlatformThread() {
  DWORD bytes;
  ULONG_PTR key;
  OVERLAPPED* overlapped;

  while (true) {
    GetQueuedCompletionStatus(g_iocp, &bytes, &key, &overlapped, 0);
    if (overlapped && overlapped->InternalHigh > 0) {
      std::string old_path;
      std::vector<WatcherEvent> events;

      uv_mutex_lock(&g_handle_wrap_map_mutex);
      HandleWrapper* handle = reinterpret_cast<HandleWrapper*>(key);
      if (!handle) {
        uv_mutex_unlock(&g_handle_wrap_map_mutex);
        return;
      }

      DWORD offset = 0;
      do {
        FILE_NOTIFY_INFORMATION* file_info =
            reinterpret_cast<FILE_NOTIFY_INFORMATION*>(handle->buffer + offset);

        // Emit events for children.
        EVENT_TYPE event = EVENT_NONE;
        switch (file_info->Action) {
          case FILE_ACTION_ADDED:
            event = EVENT_CHILD_CREATE;
            break;
          case FILE_ACTION_REMOVED:
            event = EVENT_CHILD_DELETE;
            break;
          case FILE_ACTION_RENAMED_OLD_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_RENAMED_NEW_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_MODIFIED:
            event = EVENT_CHILD_CHANGE;
            break;
        }

        if (event != EVENT_NONE) {
          char filename[MAX_PATH] = { 0 };
          WideCharToMultiByte(CP_UTF8,
                              0,
                              file_info->FileName,
                              file_info->FileNameLength,
                              filename,
                              MAX_PATH,
                              NULL,
                              NULL);

          // Convert filename to normalized path.
          std::string cat_path = handle->path + '\\' + filename;
          char path[MAX_PATH] = { 0 };
          PathCanonicalize(path, cat_path.c_str());

          if (file_info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            // Do not send rename event until the NEW_NAME event, but still keep
            // a record of old name.
            old_path = path;
          } else if (file_info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            WatcherEvent e = { event, handle->dir_handle, path, old_path };
            events.push_back(e);
            old_path.clear();
          } else {
            WatcherEvent e = { event, handle->dir_handle, path };
            events.push_back(e);
          }
        }

        offset = file_info->NextEntryOffset;
      } while (offset);
      uv_mutex_unlock(&g_handle_wrap_map_mutex);

      for (int i = 0; i < events.size(); ++i)
        PostEventAndWait(events[i].type,
                         events[i].handle,
                         events[i].new_path.c_str(),
                         events[i].old_path.c_str());
    }
  }
}

WatcherHandle PlatformWatch(const char* path) {
  wchar_t wpath[MAX_PATH];
  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);

  // Requires a directory, file watching is emulated in js.
  DWORD attr = GetFileAttributesW(wpath);
  if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
    fprintf(stderr, "%s is not a directory\n", path);
    return INVALID_HANDLE_VALUE;
  }

  WatcherHandle handle = CreateFileW(wpath,
                                     FILE_LIST_DIRECTORY,
                                     FILE_SHARE_READ | FILE_SHARE_DELETE |
                                       FILE_SHARE_WRITE,
                                     NULL,
                                     OPEN_EXISTING,
                                     FILE_FLAG_BACKUP_SEMANTICS |
                                       FILE_FLAG_OVERLAPPED,
                                     NULL);
  if (!PlatformIsHandleValid(handle)) {
    fprintf(stderr, "Unable to call CreateFileW for %s\n", path);
    return INVALID_HANDLE_VALUE;
  }

  uv_mutex_lock(&g_handle_wrap_map_mutex);
  std::unique_ptr<HandleWrapper> handle_wrapper(
      new HandleWrapper(handle, path));
  uv_mutex_unlock(&g_handle_wrap_map_mutex);

  if (CreateIoCompletionPort(handle_wrapper->dir_handle,
                             g_iocp,
                             reinterpret_cast<ULONG_PTR>(handle_wrapper.get()),
                             0) == NULL) {
    fprintf(stderr, "CreateIoCompletionPort failed\n");
    return INVALID_HANDLE_VALUE;
  }

  if (!ReadDirectoryChangesW(handle_wrapper->dir_handle,
                             handle_wrapper->buffer,
                             kDirectoryWatcherBufferSize,
                             FALSE,
                             FILE_NOTIFY_CHANGE_FILE_NAME      |
                               FILE_NOTIFY_CHANGE_DIR_NAME     |
                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                               FILE_NOTIFY_CHANGE_SIZE         |
                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                               FILE_NOTIFY_CHANGE_CREATION     |
                               FILE_NOTIFY_CHANGE_SECURITY,
                             NULL,
                             &handle_wrapper->overlapped,
                             NULL)) {
    fprintf(stderr, "ReadDirectoryChangesW failed\n");
    return INVALID_HANDLE_VALUE;
  }

  // The pointer is leaked if no error happened.
  return handle_wrapper.release()->dir_handle;
}

void PlatformUnwatch(WatcherHandle handle) {
  if (PlatformIsHandleValid(handle)) {
    uv_mutex_lock(&g_handle_wrap_map_mutex);
    CloseHandle(handle);
    delete HandleWrapper::Get(handle);
    uv_mutex_unlock(&g_handle_wrap_map_mutex);
  }
}

bool PlatformIsHandleValid(WatcherHandle handle) {
  return handle != INVALID_HANDLE_VALUE;
}
<commit_msg>win: Fix a buffer overflow.<commit_after>#include <map>
#include <memory>
#include <string>
#include <vector>

#include "common.h"

#include <Shlwapi.h>  // NOLINT

// Size of the buffer to store result of ReadDirectoryChangesW.
static const unsigned int kDirectoryWatcherBufferSize = 4096;

// Object template to create representation of WatcherHandle.
Persistent<ObjectTemplate> g_object_template;

// Mutex for the HandleWrapper map.
uv_mutex_t g_handle_wrap_map_mutex;

// The global IOCP to be quested.
static HANDLE g_iocp;

struct HandleWrapper {
  HandleWrapper(WatcherHandle handle, const char* path)
      : dir_handle(handle),
        path(path) {
    map_[dir_handle] = this;
    memset(&overlapped, 0, sizeof(overlapped));
  }

  ~HandleWrapper() {
    map_.erase(dir_handle);
  }

  WatcherHandle dir_handle;
  OVERLAPPED overlapped;
  std::string path;
  char buffer[kDirectoryWatcherBufferSize];

  static HandleWrapper* Get(WatcherHandle key) { return map_[key]; }

  static std::map<WatcherHandle, HandleWrapper*> map_;
};

std::map<WatcherHandle, HandleWrapper*> HandleWrapper::map_;

struct WatcherEvent {
  EVENT_TYPE type;
  WatcherHandle handle;
  std::string new_path;
  std::string old_path;
};

Handle<Value> WatcherHandleToV8Value(WatcherHandle handle) {
  Handle<Value> value = g_object_template->NewInstance();
  value->ToObject()->SetPointerInInternalField(0, handle);
  return value;
}

WatcherHandle V8ValueToWatcherHandle(Handle<Value> value) {
  return reinterpret_cast<WatcherHandle>(value->ToObject()->
      GetPointerFromInternalField(0));
}

bool IsV8ValueWatcherHandle(Handle<Value> value) {
  return value->IsObject() && value->ToObject()->InternalFieldCount() == 1;
}

void PlatformInit() {
  uv_mutex_init(&g_handle_wrap_map_mutex);

  g_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);

  g_object_template = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
  g_object_template->SetInternalFieldCount(1);

  WakeupNewThread();
}

void PlatformThread() {
  DWORD bytes;
  ULONG_PTR key;
  OVERLAPPED* overlapped;

  while (true) {
    GetQueuedCompletionStatus(g_iocp, &bytes, &key, &overlapped, 0);
    if (overlapped && overlapped->InternalHigh > 0) {
      std::string old_path;
      std::vector<WatcherEvent> events;

      uv_mutex_lock(&g_handle_wrap_map_mutex);
      HandleWrapper* handle = reinterpret_cast<HandleWrapper*>(key);
      if (!handle) {
        uv_mutex_unlock(&g_handle_wrap_map_mutex);
        return;
      }

      DWORD offset = 0;
      do {
        FILE_NOTIFY_INFORMATION* file_info =
            reinterpret_cast<FILE_NOTIFY_INFORMATION*>(handle->buffer + offset);

        // Emit events for children.
        EVENT_TYPE event = EVENT_NONE;
        switch (file_info->Action) {
          case FILE_ACTION_ADDED:
            event = EVENT_CHILD_CREATE;
            break;
          case FILE_ACTION_REMOVED:
            event = EVENT_CHILD_DELETE;
            break;
          case FILE_ACTION_RENAMED_OLD_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_RENAMED_NEW_NAME:
            event = EVENT_CHILD_RENAME;
            break;
          case FILE_ACTION_MODIFIED:
            event = EVENT_CHILD_CHANGE;
            break;
        }

        if (event != EVENT_NONE) {
          // The FileNameLength is in "bytes", but the WideCharToMultiByte
          // requires the length to be in "characters"!
          int file_name_length_in_characters =
              file_info->FileNameLength / sizeof(wchar_t);

          char filename[MAX_PATH + 1] = { 0 };
          int size = WideCharToMultiByte(CP_UTF8,
                                         0,
                                         file_info->FileName,
                                         file_name_length_in_characters,
                                         filename,
                                         MAX_PATH,
                                         NULL,
                                         NULL);

          // Convert filename to normalized path.
          std::string cat_path = handle->path + '\\' + filename;
          char path[MAX_PATH + 1] = { 0 };
          PathCanonicalize(path, cat_path.c_str());

          if (file_info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            // Do not send rename event until the NEW_NAME event, but still keep
            // a record of old name.
            old_path = path;
          } else if (file_info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            WatcherEvent e = { event, handle->dir_handle, path, old_path };
            events.push_back(e);
            old_path.clear();
          } else {
            WatcherEvent e = { event, handle->dir_handle, path };
            events.push_back(e);
          }
        }

        offset = file_info->NextEntryOffset;
      } while (offset);
      uv_mutex_unlock(&g_handle_wrap_map_mutex);

      for (int i = 0; i < events.size(); ++i)
        PostEventAndWait(events[i].type,
                         events[i].handle,
                         events[i].new_path.c_str(),
                         events[i].old_path.c_str());
    }
  }
}

WatcherHandle PlatformWatch(const char* path) {
  wchar_t wpath[MAX_PATH + 1];
  MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH);

  // Requires a directory, file watching is emulated in js.
  DWORD attr = GetFileAttributesW(wpath);
  if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
    fprintf(stderr, "%s is not a directory\n", path);
    return INVALID_HANDLE_VALUE;
  }

  WatcherHandle handle = CreateFileW(wpath,
                                     FILE_LIST_DIRECTORY,
                                     FILE_SHARE_READ | FILE_SHARE_DELETE |
                                       FILE_SHARE_WRITE,
                                     NULL,
                                     OPEN_EXISTING,
                                     FILE_FLAG_BACKUP_SEMANTICS |
                                       FILE_FLAG_OVERLAPPED,
                                     NULL);
  if (!PlatformIsHandleValid(handle)) {
    fprintf(stderr, "Unable to call CreateFileW for %s\n", path);
    return INVALID_HANDLE_VALUE;
  }

  uv_mutex_lock(&g_handle_wrap_map_mutex);
  std::unique_ptr<HandleWrapper> handle_wrapper(
      new HandleWrapper(handle, path));
  uv_mutex_unlock(&g_handle_wrap_map_mutex);

  if (CreateIoCompletionPort(handle_wrapper->dir_handle,
                             g_iocp,
                             reinterpret_cast<ULONG_PTR>(handle_wrapper.get()),
                             0) == NULL) {
    fprintf(stderr, "CreateIoCompletionPort failed\n");
    return INVALID_HANDLE_VALUE;
  }

  if (!ReadDirectoryChangesW(handle_wrapper->dir_handle,
                             handle_wrapper->buffer,
                             kDirectoryWatcherBufferSize,
                             FALSE,
                             FILE_NOTIFY_CHANGE_FILE_NAME      |
                               FILE_NOTIFY_CHANGE_DIR_NAME     |
                               FILE_NOTIFY_CHANGE_ATTRIBUTES   |
                               FILE_NOTIFY_CHANGE_SIZE         |
                               FILE_NOTIFY_CHANGE_LAST_WRITE   |
                               FILE_NOTIFY_CHANGE_LAST_ACCESS  |
                               FILE_NOTIFY_CHANGE_CREATION     |
                               FILE_NOTIFY_CHANGE_SECURITY,
                             NULL,
                             &handle_wrapper->overlapped,
                             NULL)) {
    fprintf(stderr, "ReadDirectoryChangesW failed\n");
    return INVALID_HANDLE_VALUE;
  }

  // The pointer is leaked if no error happened.
  return handle_wrapper.release()->dir_handle;
}

void PlatformUnwatch(WatcherHandle handle) {
  if (PlatformIsHandleValid(handle)) {
    uv_mutex_lock(&g_handle_wrap_map_mutex);
    CloseHandle(handle);
    delete HandleWrapper::Get(handle);
    uv_mutex_unlock(&g_handle_wrap_map_mutex);
  }
}

bool PlatformIsHandleValid(WatcherHandle handle) {
  return handle != INVALID_HANDLE_VALUE;
}
<|endoftext|>


<commit_before>// Copyright 2008, Google Inc. All rights reserved.
//
// 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 Google Inc. 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 AUTHOR ``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 AUTHOR 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.

// This file contains the unit tests for the CreateBalloonText function.

#include "kml/engine/feature_balloon.h"
#include "boost/scoped_ptr.hpp"
#include "kml/base/file.h"
#include "kml/base/unit_test.h"
#include "kml/engine/kml_file.h"

#ifndef DATADIR
#error *** DATADIR must be defined! ***
#endif

using kmlbase::File;
using kmldom::DocumentPtr;
using kmldom::FeaturePtr;
using kmldom::PlacemarkPtr;

namespace kmlengine {

class FeatureBalloonTest : public CPPUNIT_NS::TestFixture {
  CPPUNIT_TEST_SUITE(FeatureBalloonTest);
  CPPUNIT_TEST(TestBasicCreateBalloonText);
  CPPUNIT_TEST(TestComplexCreateBalloonText);
  CPPUNIT_TEST(TestAllBalloonsFile);
  CPPUNIT_TEST_SUITE_END();

 protected:
  void TestBasicCreateBalloonText();
  void TestComplexCreateBalloonText();
  void TestAllBalloonsFile();

 private:
  KmlFilePtr kml_file_;
};

CPPUNIT_TEST_SUITE_REGISTRATION(FeatureBalloonTest);

// A struct of the KML content and the expected balloon text.
const static struct {
  const char* kml;
  const char* balloontext;
} kKml[] = {
  {
    // A Feature with neither name nor description has no balloon text.
    // TODO: geDirections is there by default?
    "<Placemark/>",
    NULL
  },
  {
    "<Placemark><name>foo</name></Placemark>",
    "
foo

"
  },
  {
    "<Placemark><name>bar</name><description>goo</description></Placemark>",
    "
bar

goo"
  },
};
void FeatureBalloonTest::TestBasicCreateBalloonText() {
  for (size_t i = 0; i < sizeof(kKml)/sizeof(kKml[0]); ++i) {
    // Parse the file into a KmlFile object.
    kml_file_ = KmlFile::CreateFromParse(kKml[i].kml, NULL);
    // Extract the Placemark.
    const PlacemarkPtr placemark = kmldom::AsPlacemark(kml_file_->root());
    // Verify that that balloon text has been composited as expected.
    const std::string kResult(CreateBalloonText(kml_file_, placemark));
    if (kKml[i].balloontext == NULL) {
      CPPUNIT_ASSERT(kResult.empty());
    } else {
      CPPUNIT_ASSERT_EQUAL(kResult,
                           static_cast<std::string>(kKml[i].balloontext));
    }
  }
}

void FeatureBalloonTest::TestComplexCreateBalloonText() {
  // A hunk of KML that requires the ballon to be composited from a
  // BalloonStyle templated on Data and Schema/SchemaData.
  const std::string kKitchenSinkKml(
      "<Document>"
      "<Schema name=\"TrailHeadType\" id=\"TrailHeadTypeId\">"
      "<SimpleField type=\"string\" name=\"TrailHeadName\">"
      "<displayName><![CDATA[Trail Head Name]]></displayName>"
      "</SimpleField>"
      "<SimpleField type=\"double\" name=\"TrailLength\">"
      "<displayName><![CDATA[The length in miles]]></displayName>"
      "</SimpleField>"
      "<SimpleField type=\"int\" name=\"ElevationGain\">"
      "<displayName><![CDATA[change in altitude]]></displayName>"
      "</SimpleField>"
      "</Schema>"
      "<Placemark id=\"foo\" targetId=\"bar\">"
      "<name>__NAME__</name>"
      "<address>__ADDRESS__</address>"
      "<Snippet>__SNIPPET__</Snippet>"
      "<description>__DESCRIPTION__</description>"
      ""
      "<ExtendedData>"
      "<Data name=\"holeNumber\">"
      "<value>1</value>"
      "</Data>"
      "<Data name=\"holeYardage\">"
      "<value>234</value>"
      "</Data>"
      "<SchemaData schemaUrl=\"#TrailHeadTypeId\">"
      "<SimpleData name=\"TrailHeadName\">Mount Everest</SimpleData>"
      "<SimpleData name=\"TrailLength\">347.45</SimpleData>"
      "<SimpleData name=\"ElevationGain\">10000</SimpleData>"
      "</SchemaData>"
      "</ExtendedData>"
      "<Point>"
      "<coordinates>-123.115786, 49.283323</coordinates>"
      "</Point>"
      "</Placemark>"
      "</Document>");

  // The resultant balloon text from the above KML.
  const std::string kKitchenSinkBalloonText(
      "__NAME__

"
      "__DESCRIPTION__

"
      "The Trail Head Name is"
      "Mount Everest
"
      "The trail is 347.45 miles
"
      "The climb is 10000 meters

"
      "$[geDirections]");  // TODO: geDirections.

  // Parse the file into a KmlFile object.
  kml_file_ = KmlFile::CreateFromParse(kKitchenSinkKml, NULL);

  // Extract the Placemark.
  const DocumentPtr doc = kmldom::AsDocument(kml_file_->root());
  const PlacemarkPtr placemark =
      kmldom::AsPlacemark(doc->get_feature_array_at(0));

  // Verify that that balloon text has been composited as expected.
  CPPUNIT_ASSERT_EQUAL(kKitchenSinkBalloonText,
                       CreateBalloonText(kml_file_, placemark));
}

// A struct of the Feature ID from which we'll pull the KML content, and the
// expected balloon text.
const static struct {
  const char* featureid;
  const char* balloontext;
} kFeatures[] = {
  {
  "empty-placemark",
  NULL
  },
  {
  "empty-name",
  "

"
  },
  {
  "empty-description",
  NULL
  },
  {
  "empty-name-description",
  "

"
  },
  {
  "name",
  "
named

"
  },
  {
  "name-description",
  "
named

described"
  },
  {
  "name-description-entity",
  "
named

name is named
id is name-description-entity"
  },
  {
  "simple-balloon-style",
  "name is named
id is simple-balloon-style"
  },
  {
  "extendeddata-data",
  "to be or not to be that is the question"
  },
  {
  "extendeddata-schemadata",
  "She was called Balloon Girl and she had just turned 23"
  }
};

void FeatureBalloonTest::TestAllBalloonsFile() {
  const std::string kBalloonKml = std::string(DATADIR) +
                                  "/balloon/all-balloons.kml";
  std::string data;
  std::string errors;
  CPPUNIT_ASSERT(kmlbase::File::ReadFileToString(kBalloonKml, &data));
  kml_file_ = KmlFile::CreateFromParse(data, &errors);
  CPPUNIT_ASSERT(!data.empty());
  CPPUNIT_ASSERT(errors.empty());
  CPPUNIT_ASSERT(kml_file_);
  for (size_t i = 0; i < sizeof(kFeatures)/sizeof(kFeatures[0]); ++i) {
    // Extract the Placemark.
    const FeaturePtr feature=
        kmldom::AsFeature(kml_file_->GetObjectById(kFeatures[i].featureid));
    // Verify that that balloon text has been composited as expected.
    const std::string kActual(CreateBalloonText(kml_file_, feature));
    if (kFeatures[i].balloontext == NULL) {
      CPPUNIT_ASSERT(kActual.empty());
      CPPUNIT_ASSERT_EQUAL(std::string(""), kActual);
    } else {
      CPPUNIT_ASSERT_EQUAL(static_cast<std::string>(kFeatures[i].balloontext),
                           kActual);
    }
  }
}

}  // end namespace kmlengine

TEST_MAIN
<commit_msg>use xhtml-valid markup<commit_after>// Copyright 2008, Google Inc. All rights reserved.
//
// 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 Google Inc. 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 AUTHOR ``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 AUTHOR 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.

// This file contains the unit tests for the CreateBalloonText function.

#include "kml/engine/feature_balloon.h"
#include "boost/scoped_ptr.hpp"
#include "kml/base/file.h"
#include "kml/base/unit_test.h"
#include "kml/engine/kml_file.h"

#ifndef DATADIR
#error *** DATADIR must be defined! ***
#endif

using kmlbase::File;
using kmldom::DocumentPtr;
using kmldom::FeaturePtr;
using kmldom::PlacemarkPtr;

namespace kmlengine {

class FeatureBalloonTest : public CPPUNIT_NS::TestFixture {
  CPPUNIT_TEST_SUITE(FeatureBalloonTest);
  CPPUNIT_TEST(TestBasicCreateBalloonText);
  CPPUNIT_TEST(TestComplexCreateBalloonText);
  CPPUNIT_TEST(TestAllBalloonsFile);
  CPPUNIT_TEST_SUITE_END();

 protected:
  void TestBasicCreateBalloonText();
  void TestComplexCreateBalloonText();
  void TestAllBalloonsFile();

 private:
  KmlFilePtr kml_file_;
};

CPPUNIT_TEST_SUITE_REGISTRATION(FeatureBalloonTest);

// A struct of the KML content and the expected balloon text.
const static struct {
  const char* kml;
  const char* balloontext;
} kKml[] = {
  {
    // A Feature with neither name nor description has no balloon text.
    // TODO: geDirections is there by default?
    "<Placemark/>",
    NULL
  },
  {
    "<Placemark><name>foo</name></Placemark>",
    "
foo

"
  },
  {
    "<Placemark><name>bar</name><description>goo</description></Placemark>",
    "
bar

goo"
  },
};
void FeatureBalloonTest::TestBasicCreateBalloonText() {
  for (size_t i = 0; i < sizeof(kKml)/sizeof(kKml[0]); ++i) {
    // Parse the file into a KmlFile object.
    kml_file_ = KmlFile::CreateFromParse(kKml[i].kml, NULL);
    // Extract the Placemark.
    const PlacemarkPtr placemark = kmldom::AsPlacemark(kml_file_->root());
    // Verify that that balloon text has been composited as expected.
    const std::string kResult(CreateBalloonText(kml_file_, placemark));
    if (kKml[i].balloontext == NULL) {
      CPPUNIT_ASSERT(kResult.empty());
    } else {
      CPPUNIT_ASSERT_EQUAL(kResult,
                           static_cast<std::string>(kKml[i].balloontext));
    }
  }
}

void FeatureBalloonTest::TestComplexCreateBalloonText() {
  // A hunk of KML that requires the ballon to be composited from a
  // BalloonStyle templated on Data and Schema/SchemaData.
  const std::string kKitchenSinkKml(
      "<Document>"
      "<Schema name=\"TrailHeadType\" id=\"TrailHeadTypeId\">"
      "<SimpleField type=\"string\" name=\"TrailHeadName\">"
      "<displayName><![CDATA[Trail Head Name]]></displayName>"
      "</SimpleField>"
      "<SimpleField type=\"double\" name=\"TrailLength\">"
      "<displayName><![CDATA[The length in miles]]></displayName>"
      "</SimpleField>"
      "<SimpleField type=\"int\" name=\"ElevationGain\">"
      "<displayName><![CDATA[change in altitude]]></displayName>"
      "</SimpleField>"
      "</Schema>"
      "<Placemark id=\"foo\" targetId=\"bar\">"
      "<name>__NAME__</name>"
      "<address>__ADDRESS__</address>"
      "<Snippet>__SNIPPET__</Snippet>"
      "<description>__DESCRIPTION__</description>"
      ""
      "<ExtendedData>"
      "<Data name=\"holeNumber\">"
      "<value>1</value>"
      "</Data>"
      "<Data name=\"holeYardage\">"
      "<value>234</value>"
      "</Data>"
      "<SchemaData schemaUrl=\"#TrailHeadTypeId\">"
      "<SimpleData name=\"TrailHeadName\">Mount Everest</SimpleData>"
      "<SimpleData name=\"TrailLength\">347.45</SimpleData>"
      "<SimpleData name=\"ElevationGain\">10000</SimpleData>"
      "</SchemaData>"
      "</ExtendedData>"
      "<Point>"
      "<coordinates>-123.115786, 49.283323</coordinates>"
      "</Point>"
      "</Placemark>"
      "</Document>");

  // The resultant balloon text from the above KML.
  const std::string kKitchenSinkBalloonText(
      "__NAME__

"
      "__DESCRIPTION__

"
      "The Trail Head Name is"
      "Mount Everest
"
      "The trail is 347.45 miles
"
      "The climb is 10000 meters

"
      "$[geDirections]");  // TODO: geDirections.

  // Parse the file into a KmlFile object.
  kml_file_ = KmlFile::CreateFromParse(kKitchenSinkKml, NULL);

  // Extract the Placemark.
  const DocumentPtr doc = kmldom::AsDocument(kml_file_->root());
  const PlacemarkPtr placemark =
      kmldom::AsPlacemark(doc->get_feature_array_at(0));

  // Verify that that balloon text has been composited as expected.
  CPPUNIT_ASSERT_EQUAL(kKitchenSinkBalloonText,
                       CreateBalloonText(kml_file_, placemark));
}

// A struct of the Feature ID from which we'll pull the KML content, and the
// expected balloon text.
const static struct {
  const char* featureid;
  const char* balloontext;
} kFeatures[] = {
  {
  "empty-placemark",
  NULL
  },
  {
  "empty-name",
  "

"
  },
  {
  "empty-description",
  NULL
  },
  {
  "empty-name-description",
  "

"
  },
  {
  "name",
  "
named

"
  },
  {
  "name-description",
  "
named

described"
  },
  {
  "name-description-entity",
  // TODO: the one br is kind of odd...
  "
named

name is named
id is name-description-entity"
  },
  {
  "simple-balloon-style",
  // TODO: same case as above
  "name is named
id is simple-balloon-style"
  },
  {
  "extendeddata-data",
  "to be or not to be that is the question"
  },
  {
  "extendeddata-schemadata",
  "She was called Balloon Girl and she had just turned 23"
  }
};

void FeatureBalloonTest::TestAllBalloonsFile() {
  const std::string kBalloonKml = std::string(DATADIR) +
                                  "/balloon/all-balloons.kml";
  std::string data;
  std::string errors;
  CPPUNIT_ASSERT(kmlbase::File::ReadFileToString(kBalloonKml, &data));
  kml_file_ = KmlFile::CreateFromParse(data, &errors);
  CPPUNIT_ASSERT(!data.empty());
  CPPUNIT_ASSERT(errors.empty());
  CPPUNIT_ASSERT(kml_file_);
  for (size_t i = 0; i < sizeof(kFeatures)/sizeof(kFeatures[0]); ++i) {
    // Extract the Placemark.
    const FeaturePtr feature=
        kmldom::AsFeature(kml_file_->GetObjectById(kFeatures[i].featureid));
    // Verify that that balloon text has been composited as expected.
    const std::string kActual(CreateBalloonText(kml_file_, feature));
    if (kFeatures[i].balloontext == NULL) {
      CPPUNIT_ASSERT(kActual.empty());
      CPPUNIT_ASSERT_EQUAL(std::string(""), kActual);
    } else {
      CPPUNIT_ASSERT_EQUAL(static_cast<std::string>(kFeatures[i].balloontext),
                           kActual);
    }
  }
}

}  // end namespace kmlengine

TEST_MAIN
<|endoftext|>