<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore/qobject.h>
#include <qmlengine.h>
#include <private/qmlengine_p.h>
#include <QtScript/qscriptvalue.h>
#include <QtScript/qscriptvalueiterator.h>
#include <QtScript/qscriptcontext.h>
#include <QtScript/qscriptengine.h>
#include <QtScript/qscriptclasspropertyiterator.h>
#include <QtSql/qsqldatabase.h>
#include <QtSql/qsqlquery.h>
#include <QtSql/qsqlerror.h>
#include <QtSql/qsqlrecord.h>
#include <private/qmlrefcount_p.h>
#include <private/qmlengine_p.h>
#include <QtCore/qstack.h>
#include <QtCore/qcryptographichash.h>
#include "qmlsqldatabase_p.h"
#include <QtCore/qsettings.h>
#include <QtCore/qdir.h>
#include <QtCore/qdebug.h>

Q_DECLARE_METATYPE(QSqlDatabase)
Q_DECLARE_METATYPE(QSqlQuery)

class QmlSqlQueryScriptClass: public QScriptClass {
public:
    QmlSqlQueryScriptClass(QScriptEngine *engine) : QScriptClass(engine)
    {
        str_length = engine->toStringHandle(QLatin1String("length"));
        str_forwardOnly = engine->toStringHandle(QLatin1String("forwardOnly")); // not in HTML5 (an optimization)
    }

    QueryFlags queryProperty(const QScriptValue &object,
                             const QScriptString &name,
                             QueryFlags flags, uint *)
    {
        if (flags & HandlesReadAccess) {
            if (name == str_length) {
                return HandlesReadAccess;
            } else if (name == str_forwardOnly) {
                return flags;
            }
        }
        if (flags & HandlesWriteAccess)
            if (name == str_forwardOnly)
                return flags;
        return 0;
    }

    QScriptValue property(const QScriptValue &object,
                          const QScriptString &name, uint)
    {
        QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object.data());
        if (name == str_length) {
            int s = query.size();
            if (s<0) {
                // Inefficient.
                if (query.last()) {
                    return query.at()+1;
                } else {
                    return 0;
                }
            } else {
                return s;
            }
        } else if (name == str_forwardOnly) {
            return query.isForwardOnly();
        }
        return engine()->undefinedValue();
    }

    void setProperty(QScriptValue &object,
                      const QScriptString &name, uint, const QScriptValue & value)
    {
        if (name == str_forwardOnly) {
            QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object.data());
            query.setForwardOnly(value.toBool());
        }
    }

    QScriptValue::PropertyFlags propertyFlags(const QScriptValue &/*object*/, const QScriptString &name, uint /*id*/)
    {
        if (name == str_length) {
            return QScriptValue::Undeletable
                | QScriptValue::SkipInEnumeration;
        }
        return QScriptValue::Undeletable;
    }

private:
    QScriptString str_length;
    QScriptString str_forwardOnly;
};

// If the spec changes to allow iteration, check git history...
// class QmlSqlQueryScriptClassPropertyIterator : public QScriptClassPropertyIterator

enum SqlException {
    UNKNOWN_ERR,
    DATABASE_ERR,
    VERSION_ERR,
    TOO_LARGE_ERR,
    QUOTA_ERR,
    SYNTAX_ERR,
    CONSTRAINT_ERR,
    TIMEOUT_ERR
};

static const char* sqlerror[] = {
    "UNKNOWN_ERR",
    "DATABASE_ERR",
    "VERSION_ERR",
    "TOO_LARGE_ERR",
    "QUOTA_ERR",
    "SYNTAX_ERR",
    "CONSTRAINT_ERR",
    "TIMEOUT_ERR",
    0
};

#define THROW_SQL(error, desc) \
{ \
    QScriptValue errorValue = context->throwError(desc); \
    errorValue.setProperty(QLatin1String("code"), error); \
    return errorValue; \
}

static QString databaseFile(const QString& connectionName, QScriptEngine *engine)
{
    QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath
                + QDir::separator() + QLatin1String("Databases") + QDir::separator();
    basename += connectionName;
    return basename;
}

static QScriptValue qmlsqldatabase_item(QScriptContext *context, QScriptEngine *engine)
{
    QSqlQuery query = qscriptvalue_cast<QSqlQuery>(context->thisObject().data());
    int i = context->argument(0).toNumber();
    if (query.at() == i || query.seek(i)) { // Qt 4.6 doesn't optimize seek(at())
        QSqlRecord r = query.record();
        QScriptValue row = engine->newObject();
        for (int j=0; j<r.count(); ++j) {
            row.setProperty(r.fieldName(j), QScriptValue(engine,r.value(j).toString()));
        }
        return row;
    }
    return engine->undefinedValue();
}

static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEngine *engine)
{
    QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
    QString sql = context->argument(0).toString();
    QSqlQuery query(db);
    bool err = false;

    QScriptValue result;

    if (query.prepare(sql)) {
        if (context->argumentCount() > 1) {
            QScriptValue values = context->argument(1);
            if (values.isObject()) {
                for (QScriptValueIterator it(values); it.hasNext();) {
                    it.next();
                    query.bindValue(it.name(),it.value().toVariant());
                }
            } else {
                query.bindValue(0,values.toVariant());
            }
        }
        if (query.exec()) {
            result = engine->newObject();
            if (!QmlEnginePrivate::get(engine)->sqlQueryClass)
                QmlEnginePrivate::get(engine)->sqlQueryClass= new QmlSqlQueryScriptClass(engine);
            QScriptValue rows = engine->newObject(QmlEnginePrivate::get(engine)->sqlQueryClass);
            rows.setData(engine->newVariant(qVariantFromValue(query)));
            rows.setProperty(QLatin1String("item"), engine->newFunction(qmlsqldatabase_item,1), QScriptValue::SkipInEnumeration);
            result.setProperty(QLatin1String("rows"),rows);
            result.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected());
            result.setProperty(QLatin1String("insertId"),query.lastInsertId().toString());
        } else {
            err = true;
        }
    } else {
        err = true;
    }
    if (err)
        THROW_SQL(DATABASE_ERR,query.lastError().text());
    return result;
}

static QScriptValue qmlsqldatabase_executeSql_readonly(QScriptContext *context, QScriptEngine *engine)
{
    QString sql = context->argument(0).toString();
    if (sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
        return qmlsqldatabase_executeSql(context,engine);
    } else {
        THROW_SQL(SYNTAX_ERR,QmlEngine::tr("Read-only Transaction"))
    }
}

static QScriptValue qmlsqldatabase_change_version(QScriptContext *context, QScriptEngine *engine)
{
    if (context->argumentCount() < 2)
        return engine->undefinedValue();

    QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
    QString from_version = context->argument(0).toString();
    QString to_version = context->argument(1).toString();
    QScriptValue callback = context->argument(2);

    QScriptValue instance = engine->newObject();
    instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1));
    QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db));

    if (from_version!=context->thisObject().property(QLatin1String("version")).toString()) {
        THROW_SQL(2,QmlEngine::tr("Version mismatch"));
        return engine->undefinedValue();
    }

    bool ok = true;
    if (callback.isFunction()) {
        ok = false;
        db.transaction();
        callback.call(QScriptValue(), QScriptValueList() << tx);
        if (engine->hasUncaughtException()) {
            db.rollback();
        } else {
            if (!db.commit()) {
                db.rollback();
                THROW_SQL(0,QmlEngine::tr("SQL transaction failed"));
            } else {
                ok = true;
            }
        }
    }

    if (ok) {
        context->thisObject().setProperty(QLatin1String("version"), to_version, QScriptValue::ReadOnly);
        QSettings ini(databaseFile(db.connectionName(),engine)+QLatin1String(".ini"),QSettings::IniFormat);
        ini.setValue(QLatin1String("Version"), to_version);
    }

    return engine->undefinedValue();
}

static QScriptValue qmlsqldatabase_transaction_shared(QScriptContext *context, QScriptEngine *engine, bool readOnly)
{
    QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
    QScriptValue callback = context->argument(0);
    if (!callback.isFunction())
        THROW_SQL(UNKNOWN_ERR,QmlEngine::tr("transaction: missing callback"));

    QScriptValue instance = engine->newObject();
    instance.setProperty(QLatin1String("executeSql"),
        engine->newFunction(readOnly ? qmlsqldatabase_executeSql_readonly : qmlsqldatabase_executeSql,1));
    QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db));

    db.transaction();
    callback.call(QScriptValue(), QScriptValueList() << tx);
    if (engine->hasUncaughtException()) {
        db.rollback();
    } else {
        if (!db.commit())
            db.rollback();
    }
    return engine->undefinedValue();
}

static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine)
{
    return qmlsqldatabase_transaction_shared(context,engine,false);
}
static QScriptValue qmlsqldatabase_read_transaction(QScriptContext *context, QScriptEngine *engine)
{
    return qmlsqldatabase_transaction_shared(context,engine,true);
}

/*
    Currently documented in doc/src/declarastive/globalobject.qdoc
*/
static QScriptValue qmlsqldatabase_open_sync(QScriptContext *context, QScriptEngine *engine)
{
    QSqlDatabase database;

    QString dbname = context->argument(0).toString();
    QString dbversion = context->argument(1).toString();
    QString dbdescription = context->argument(2).toString();
    int dbestimatedsize = context->argument(3).toNumber();
    QScriptValue dbcreationCallback = context->argument(4);

    QCryptographicHash md5(QCryptographicHash::Md5);
    md5.addData(dbname.toUtf8());
    QString dbid(QLatin1String(md5.result().toHex()));

    QString basename = databaseFile(dbid,engine);
    bool created = false;
    QString version = dbversion;

    {
        QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);

        if (QSqlDatabase::connectionNames().contains(dbid)) {
            database = QSqlDatabase::database(dbid);
            version = ini.value(QLatin1String("Version")).toString();
        } else {
            created = !QFile::exists(basename+QLatin1String(".sqlite"));
            database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
            QDir().mkpath(basename);
            if (created) {
                ini.setValue(QLatin1String("Name"), dbname);
                if (dbcreationCallback.isFunction())
                    version = QString();
                ini.setValue(QLatin1String("Version"), version);
                ini.setValue(QLatin1String("Description"), dbdescription);
                ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize);
                ini.setValue(QLatin1String("Driver"), QLatin1String("QSQLITE"));
            } else {
                if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) {
                    // Incompatible
                    THROW_SQL(VERSION_ERR,QmlEngine::tr("SQL: database version mismatch"));
                }
                version = ini.value("Version").toString();
            }
            database.setDatabaseName(basename+QLatin1String(".sqlite"));
        }
        if (!database.isOpen())
            database.open();
    }

    QScriptValue instance = engine->newObject();
    instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,1));
    instance.setProperty(QLatin1String("readTransaction"), engine->newFunction(qmlsqldatabase_read_transaction,1));
    instance.setProperty(QLatin1String("version"), version, QScriptValue::ReadOnly);
    instance.setProperty(QLatin1String("changeVersion"), engine->newFunction(qmlsqldatabase_change_version,3));

    QScriptValue result = engine->newVariant(instance,qVariantFromValue(database));

    if (created && dbcreationCallback.isFunction()) {
        dbcreationCallback.call(QScriptValue(), QScriptValueList() << result);
    }

    return result;
}

void qt_add_qmlsqldatabase(QScriptEngine *engine)
{
    QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open_sync, 4);
    engine->globalObject().setProperty(QLatin1String("openDatabaseSync"), openDatabase);

    QScriptValue sqlExceptionPrototype = engine->newObject();
    for (int i=0; sqlerror[i]; ++i)
        sqlExceptionPrototype.setProperty(QLatin1String(sqlerror[i]),
            i,QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);

    engine->globalObject().setProperty(QLatin1String("SQLException"), sqlExceptionPrototype);
}

/*
HTML5 "spec" says "rs.rows[n]", but WebKit only impelments "rs.rows.item(n)". We do both (and property iterator).
We add a "forwardOnly" property that stops Qt caching results (code promises to only go forward
through the data.
*/

<commit_msg>Check version for cached DB too.<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtCore/qobject.h>
#include <qmlengine.h>
#include <private/qmlengine_p.h>
#include <QtScript/qscriptvalue.h>
#include <QtScript/qscriptvalueiterator.h>
#include <QtScript/qscriptcontext.h>
#include <QtScript/qscriptengine.h>
#include <QtScript/qscriptclasspropertyiterator.h>
#include <QtSql/qsqldatabase.h>
#include <QtSql/qsqlquery.h>
#include <QtSql/qsqlerror.h>
#include <QtSql/qsqlrecord.h>
#include <private/qmlrefcount_p.h>
#include <private/qmlengine_p.h>
#include <QtCore/qstack.h>
#include <QtCore/qcryptographichash.h>
#include "qmlsqldatabase_p.h"
#include <QtCore/qsettings.h>
#include <QtCore/qdir.h>
#include <QtCore/qdebug.h>

Q_DECLARE_METATYPE(QSqlDatabase)
Q_DECLARE_METATYPE(QSqlQuery)

class QmlSqlQueryScriptClass: public QScriptClass {
public:
    QmlSqlQueryScriptClass(QScriptEngine *engine) : QScriptClass(engine)
    {
        str_length = engine->toStringHandle(QLatin1String("length"));
        str_forwardOnly = engine->toStringHandle(QLatin1String("forwardOnly")); // not in HTML5 (an optimization)
    }

    QueryFlags queryProperty(const QScriptValue &object,
                             const QScriptString &name,
                             QueryFlags flags, uint *)
    {
        if (flags & HandlesReadAccess) {
            if (name == str_length) {
                return HandlesReadAccess;
            } else if (name == str_forwardOnly) {
                return flags;
            }
        }
        if (flags & HandlesWriteAccess)
            if (name == str_forwardOnly)
                return flags;
        return 0;
    }

    QScriptValue property(const QScriptValue &object,
                          const QScriptString &name, uint)
    {
        QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object.data());
        if (name == str_length) {
            int s = query.size();
            if (s<0) {
                // Inefficient.
                if (query.last()) {
                    return query.at()+1;
                } else {
                    return 0;
                }
            } else {
                return s;
            }
        } else if (name == str_forwardOnly) {
            return query.isForwardOnly();
        }
        return engine()->undefinedValue();
    }

    void setProperty(QScriptValue &object,
                      const QScriptString &name, uint, const QScriptValue & value)
    {
        if (name == str_forwardOnly) {
            QSqlQuery query = qscriptvalue_cast<QSqlQuery>(object.data());
            query.setForwardOnly(value.toBool());
        }
    }

    QScriptValue::PropertyFlags propertyFlags(const QScriptValue &/*object*/, const QScriptString &name, uint /*id*/)
    {
        if (name == str_length) {
            return QScriptValue::Undeletable
                | QScriptValue::SkipInEnumeration;
        }
        return QScriptValue::Undeletable;
    }

private:
    QScriptString str_length;
    QScriptString str_forwardOnly;
};

// If the spec changes to allow iteration, check git history...
// class QmlSqlQueryScriptClassPropertyIterator : public QScriptClassPropertyIterator

enum SqlException {
    UNKNOWN_ERR,
    DATABASE_ERR,
    VERSION_ERR,
    TOO_LARGE_ERR,
    QUOTA_ERR,
    SYNTAX_ERR,
    CONSTRAINT_ERR,
    TIMEOUT_ERR
};

static const char* sqlerror[] = {
    "UNKNOWN_ERR",
    "DATABASE_ERR",
    "VERSION_ERR",
    "TOO_LARGE_ERR",
    "QUOTA_ERR",
    "SYNTAX_ERR",
    "CONSTRAINT_ERR",
    "TIMEOUT_ERR",
    0
};

#define THROW_SQL(error, desc) \
{ \
    QScriptValue errorValue = context->throwError(desc); \
    errorValue.setProperty(QLatin1String("code"), error); \
    return errorValue; \
}

static QString databaseFile(const QString& connectionName, QScriptEngine *engine)
{
    QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath
                + QDir::separator() + QLatin1String("Databases") + QDir::separator();
    basename += connectionName;
    return basename;
}

static QScriptValue qmlsqldatabase_item(QScriptContext *context, QScriptEngine *engine)
{
    QSqlQuery query = qscriptvalue_cast<QSqlQuery>(context->thisObject().data());
    int i = context->argument(0).toNumber();
    if (query.at() == i || query.seek(i)) { // Qt 4.6 doesn't optimize seek(at())
        QSqlRecord r = query.record();
        QScriptValue row = engine->newObject();
        for (int j=0; j<r.count(); ++j) {
            row.setProperty(r.fieldName(j), QScriptValue(engine,r.value(j).toString()));
        }
        return row;
    }
    return engine->undefinedValue();
}

static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEngine *engine)
{
    QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
    QString sql = context->argument(0).toString();
    QSqlQuery query(db);
    bool err = false;

    QScriptValue result;

    if (query.prepare(sql)) {
        if (context->argumentCount() > 1) {
            QScriptValue values = context->argument(1);
            if (values.isObject()) {
                for (QScriptValueIterator it(values); it.hasNext();) {
                    it.next();
                    query.bindValue(it.name(),it.value().toVariant());
                }
            } else {
                query.bindValue(0,values.toVariant());
            }
        }
        if (query.exec()) {
            result = engine->newObject();
            if (!QmlEnginePrivate::get(engine)->sqlQueryClass)
                QmlEnginePrivate::get(engine)->sqlQueryClass= new QmlSqlQueryScriptClass(engine);
            QScriptValue rows = engine->newObject(QmlEnginePrivate::get(engine)->sqlQueryClass);
            rows.setData(engine->newVariant(qVariantFromValue(query)));
            rows.setProperty(QLatin1String("item"), engine->newFunction(qmlsqldatabase_item,1), QScriptValue::SkipInEnumeration);
            result.setProperty(QLatin1String("rows"),rows);
            result.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected());
            result.setProperty(QLatin1String("insertId"),query.lastInsertId().toString());
        } else {
            err = true;
        }
    } else {
        err = true;
    }
    if (err)
        THROW_SQL(DATABASE_ERR,query.lastError().text());
    return result;
}

static QScriptValue qmlsqldatabase_executeSql_readonly(QScriptContext *context, QScriptEngine *engine)
{
    QString sql = context->argument(0).toString();
    if (sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
        return qmlsqldatabase_executeSql(context,engine);
    } else {
        THROW_SQL(SYNTAX_ERR,QmlEngine::tr("Read-only Transaction"))
    }
}

static QScriptValue qmlsqldatabase_change_version(QScriptContext *context, QScriptEngine *engine)
{
    if (context->argumentCount() < 2)
        return engine->undefinedValue();

    QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
    QString from_version = context->argument(0).toString();
    QString to_version = context->argument(1).toString();
    QScriptValue callback = context->argument(2);

    QScriptValue instance = engine->newObject();
    instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1));
    QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db));

    if (from_version!=context->thisObject().property(QLatin1String("version")).toString()) {
        THROW_SQL(2,QmlEngine::tr("Version mismatch"));
        return engine->undefinedValue();
    }

    bool ok = true;
    if (callback.isFunction()) {
        ok = false;
        db.transaction();
        callback.call(QScriptValue(), QScriptValueList() << tx);
        if (engine->hasUncaughtException()) {
            db.rollback();
        } else {
            if (!db.commit()) {
                db.rollback();
                THROW_SQL(0,QmlEngine::tr("SQL transaction failed"));
            } else {
                ok = true;
            }
        }
    }

    if (ok) {
        context->thisObject().setProperty(QLatin1String("version"), to_version, QScriptValue::ReadOnly);
        QSettings ini(databaseFile(db.connectionName(),engine)+QLatin1String(".ini"),QSettings::IniFormat);
        ini.setValue(QLatin1String("Version"), to_version);
    }

    return engine->undefinedValue();
}

static QScriptValue qmlsqldatabase_transaction_shared(QScriptContext *context, QScriptEngine *engine, bool readOnly)
{
    QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject());
    QScriptValue callback = context->argument(0);
    if (!callback.isFunction())
        THROW_SQL(UNKNOWN_ERR,QmlEngine::tr("transaction: missing callback"));

    QScriptValue instance = engine->newObject();
    instance.setProperty(QLatin1String("executeSql"),
        engine->newFunction(readOnly ? qmlsqldatabase_executeSql_readonly : qmlsqldatabase_executeSql,1));
    QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db));

    db.transaction();
    callback.call(QScriptValue(), QScriptValueList() << tx);
    if (engine->hasUncaughtException()) {
        db.rollback();
    } else {
        if (!db.commit())
            db.rollback();
    }
    return engine->undefinedValue();
}

static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine)
{
    return qmlsqldatabase_transaction_shared(context,engine,false);
}
static QScriptValue qmlsqldatabase_read_transaction(QScriptContext *context, QScriptEngine *engine)
{
    return qmlsqldatabase_transaction_shared(context,engine,true);
}

/*
    Currently documented in doc/src/declarastive/globalobject.qdoc
*/
static QScriptValue qmlsqldatabase_open_sync(QScriptContext *context, QScriptEngine *engine)
{
    QSqlDatabase database;

    QString dbname = context->argument(0).toString();
    QString dbversion = context->argument(1).toString();
    QString dbdescription = context->argument(2).toString();
    int dbestimatedsize = context->argument(3).toNumber();
    QScriptValue dbcreationCallback = context->argument(4);

    QCryptographicHash md5(QCryptographicHash::Md5);
    md5.addData(dbname.toUtf8());
    QString dbid(QLatin1String(md5.result().toHex()));

    QString basename = databaseFile(dbid,engine);
    bool created = false;
    QString version = dbversion;

    {
        QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);

        if (QSqlDatabase::connectionNames().contains(dbid)) {
            database = QSqlDatabase::database(dbid);
            version = ini.value(QLatin1String("Version")).toString();
            if (version != dbversion && !dbversion.isEmpty() && !version.isEmpty())
                THROW_SQL(VERSION_ERR,QmlEngine::tr("SQL: database version mismatch"));
        } else {
            created = !QFile::exists(basename+QLatin1String(".sqlite"));
            database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
            QDir().mkpath(basename);
            if (created) {
                ini.setValue(QLatin1String("Name"), dbname);
                if (dbcreationCallback.isFunction())
                    version = QString();
                ini.setValue(QLatin1String("Version"), version);
                ini.setValue(QLatin1String("Description"), dbdescription);
                ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize);
                ini.setValue(QLatin1String("Driver"), QLatin1String("QSQLITE"));
            } else {
                if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) {
                    // Incompatible
                    THROW_SQL(VERSION_ERR,QmlEngine::tr("SQL: database version mismatch"));
                }
                version = ini.value("Version").toString();
            }
            database.setDatabaseName(basename+QLatin1String(".sqlite"));
        }
        if (!database.isOpen())
            database.open();
    }

    QScriptValue instance = engine->newObject();
    instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,1));
    instance.setProperty(QLatin1String("readTransaction"), engine->newFunction(qmlsqldatabase_read_transaction,1));
    instance.setProperty(QLatin1String("version"), version, QScriptValue::ReadOnly);
    instance.setProperty(QLatin1String("changeVersion"), engine->newFunction(qmlsqldatabase_change_version,3));

    QScriptValue result = engine->newVariant(instance,qVariantFromValue(database));

    if (created && dbcreationCallback.isFunction()) {
        dbcreationCallback.call(QScriptValue(), QScriptValueList() << result);
    }

    return result;
}

void qt_add_qmlsqldatabase(QScriptEngine *engine)
{
    QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open_sync, 4);
    engine->globalObject().setProperty(QLatin1String("openDatabaseSync"), openDatabase);

    QScriptValue sqlExceptionPrototype = engine->newObject();
    for (int i=0; sqlerror[i]; ++i)
        sqlExceptionPrototype.setProperty(QLatin1String(sqlerror[i]),
            i,QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration);

    engine->globalObject().setProperty(QLatin1String("SQLException"), sqlExceptionPrototype);
}

/*
HTML5 "spec" says "rs.rows[n]", but WebKit only impelments "rs.rows.item(n)". We do both (and property iterator).
We add a "forwardOnly" property that stops Qt caching results (code promises to only go forward
through the data.
*/

<|endoftext|>


<commit_before>/*
 Tests of custom Google Test assertions.

 Copyright (c) 2012-2014, Victor Zverovich
 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.

 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 "gtest-extra.h"

#include <algorithm>
#include <stdexcept>
#include <gtest/gtest-spi.h>

namespace {

std::string FormatSystemErrorMessage(int error_code, fmt::StringRef message) {
  fmt::Writer out;
  fmt::internal::FormatSystemErrorMessage(out, error_code, message);
  return str(out);
}

#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
  EXPECT_THROW_MSG(statement, fmt::SystemError, \
      FormatSystemErrorMessage(error_code, message))

// Tests that assertion macros evaluate their arguments exactly once.
class SingleEvaluationTest : public ::testing::Test {
 protected:
  SingleEvaluationTest() {
    a_ = 0;
  }

  static int a_;
};

int SingleEvaluationTest::a_;

void ThrowNothing() {}

void ThrowException() {
  throw std::runtime_error("test");
}

// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) {
  // successful EXPECT_THROW_MSG
  EXPECT_THROW_MSG({  // NOLINT
    a_++;
    ThrowException();
  }, std::exception, "test");
  EXPECT_EQ(1, a_);

  // failed EXPECT_THROW_MSG, throws different type
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({  // NOLINT
    a_++;
    ThrowException();
  }, std::logic_error, "test"), "throws a different type");
  EXPECT_EQ(2, a_);

  // failed EXPECT_THROW_MSG, throws an exception with different message
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({  // NOLINT
    a_++;
    ThrowException();
  }, std::exception, "other"), "throws an exception with a different message");
  EXPECT_EQ(3, a_);

  // failed EXPECT_THROW_MSG, throws nothing
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(a_++, std::exception, "test"), "throws nothing");
  EXPECT_EQ(4, a_);
}

// Tests that the compiler will not complain about unreachable code in the
// EXPECT_THROW_MSG macro.
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
  int n = 0;
  using std::runtime_error;
  EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, runtime_error, ""), "");
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(throw 1, runtime_error, ""), "");
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
      throw runtime_error("a"), runtime_error, "b"), "");
}

TEST(AssertionSyntaxTest, ExceptionAssertionsBehavesLikeSingleStatement) {
  if (::testing::internal::AlwaysFalse())
    EXPECT_THROW_MSG(ThrowNothing(), std::exception, "");

  if (::testing::internal::AlwaysTrue())
    EXPECT_THROW_MSG(ThrowException(), std::exception, "test");
  else
    ;  // NOLINT
}

// Tests EXPECT_THROW_MSG.
TEST(ExpectTest, EXPECT_THROW_MSG) {
  EXPECT_THROW_MSG(ThrowException(), std::exception, "test");
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowException(), std::logic_error, "test"),
      "Expected: ThrowException() throws an exception of "
      "type std::logic_error.\n  Actual: it throws a different type.");
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowNothing(), std::exception, "test"),
      "Expected: ThrowNothing() throws an exception of type std::exception.\n"
      "  Actual: it throws nothing.");
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowException(), std::exception, "other"),
      "ThrowException() throws an exception with a different message.\n"
      "Expected: other\n"
      "  Actual: test");
}

TEST(StreamingAssertionsTest, ThrowMsg) {
  EXPECT_THROW_MSG(ThrowException(), std::exception, "test")
      << "unexpected failure";
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowException(), std::exception, "other")
      << "expected failure", "expected failure");
}

#if FMT_USE_FILE_DESCRIPTORS

TEST(ErrorCodeTest, Ctor) {
  EXPECT_EQ(0, ErrorCode().get());
  EXPECT_EQ(42, ErrorCode(42).get());
}

TEST(FileTest, DefaultCtor) {
  File f;
  EXPECT_EQ(-1, f.get());
}

// Checks if the file is open by reading one character from it.
bool IsOpen(int fd) {
  char buffer;
  return read(fd, &buffer, 1) == 1;
}

bool IsClosed(int fd) {
  char buffer;
  std::streamsize result = read(fd, &buffer, 1);
  return result == -1 && errno == EBADF;
}

TEST(FileTest, OpenFileInCtor) {
  int fd = 0;
  {
    File f(".travis.yml", File::RDONLY);
    fd = f.get();
    ASSERT_TRUE(IsOpen(fd));
  }
  ASSERT_TRUE(IsClosed(fd));
}

TEST(FileTest, OpenFileError) {
  EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY),
      ENOENT, "cannot open file nonexistent");
}

TEST(FileTest, MoveCtor) {
  File f(".travis.yml", File::RDONLY);
  int fd = f.get();
  EXPECT_NE(-1, fd);
  File f2(std::move(f));
  EXPECT_EQ(fd, f2.get());
  EXPECT_EQ(-1, f.get());
}

TEST(FileTest, MoveAssignment) {
  File f(".travis.yml", File::RDONLY);
  int fd = f.get();
  EXPECT_NE(-1, fd);
  File f2;
  f2 = std::move(f);
  EXPECT_EQ(fd, f2.get());
  EXPECT_EQ(-1, f.get());
}

TEST(FileTest, MoveAssignmentClosesFile) {
  File f(".travis.yml", File::RDONLY);
  File f2("CMakeLists.txt", File::RDONLY);
  int old_fd = f2.get();
  f2 = std::move(f);
  EXPECT_TRUE(IsClosed(old_fd));
}

File OpenFile(int &fd) {
  File f(".travis.yml", File::RDONLY);
  fd = f.get();
  return std::move(f);
}

TEST(FileTest, MoveFromTemporaryInCtor) {
  int fd = 0xdeadbeef;
  File f(OpenFile(fd));
  EXPECT_EQ(fd, f.get());
}

TEST(FileTest, MoveFromTemporaryInAssignment) {
  int fd = 0xdeadbeef;
  File f;
  f = OpenFile(fd);
  EXPECT_EQ(fd, f.get());
}

TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
  int fd = 0xdeadbeef;
  File f(".travis.yml", File::RDONLY);
  int old_fd = f.get();
  f = OpenFile(fd);
  EXPECT_TRUE(IsClosed(old_fd));
}

TEST(FileTest, CloseFileInDtor) {
  int fd = 0;
  {
    File f(".travis.yml", File::RDONLY);
    fd = f.get();
  }
  FILE *f = fdopen(fd, "r");
  int error_code = errno;
  if (f)
    fclose(f);
  EXPECT_TRUE(f == 0);
  EXPECT_EQ(EBADF, error_code);
}

TEST(FileTest, CloseError) {
  File *fd = new File(".travis.yml", File::RDONLY);
  EXPECT_STDERR(close(fd->get()); delete fd,
    FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
}

std::string ReadLine(File &f) {
  enum { BUFFER_SIZE = 100 };
  char buffer[BUFFER_SIZE];
  std::streamsize result = f.read(buffer, BUFFER_SIZE);
  buffer[std::min<std::streamsize>(BUFFER_SIZE - 1, result)] = '\0';
  if (char *end = strchr(buffer, '\n'))
    *end = '\0';
  return buffer;
}

TEST(FileTest, Read) {
  File f(".travis.yml", File::RDONLY);
  EXPECT_EQ("language: cpp", ReadLine(f));
}

TEST(FileTest, ReadError) {
  File f;
  char buf;
  EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file");
}

TEST(FileTest, Dup) {
  File f(".travis.yml", File::RDONLY);
  File dup = File::dup(f.get());
  EXPECT_NE(f.get(), dup.get());
  EXPECT_EQ("language: cpp", ReadLine(dup));
}

TEST(FileTest, DupError) {
  EXPECT_SYSTEM_ERROR(File::dup(-1),
      EBADF, "cannot duplicate file descriptor -1");
}

TEST(FileTest, Dup2) {
  File f(".travis.yml", File::RDONLY);
  File dup("CMakeLists.txt", File::RDONLY);
  f.dup2(dup.get());
  EXPECT_NE(f.get(), dup.get());
  EXPECT_EQ("language: cpp", ReadLine(dup));
}

TEST(FileTest, Dup2Error) {
  File f(".travis.yml", File::RDONLY);
  EXPECT_SYSTEM_ERROR(f.dup2(-1), EBADF,
      fmt::Format("cannot duplicate file descriptor {} to -1") << f.get());

}

TEST(FileTest, Dup2NoExcept) {
  File f(".travis.yml", File::RDONLY);
  File dup("CMakeLists.txt", File::RDONLY);
  ErrorCode ec;
  f.dup2(dup.get(), ec);
  EXPECT_EQ(0, ec.get());
  EXPECT_NE(f.get(), dup.get());
  EXPECT_EQ("language: cpp", ReadLine(dup));
}

TEST(FileTest, Dup2NoExceptError) {
  File f(".travis.yml", File::RDONLY);
  ErrorCode ec;
  f.dup2(-1, ec);
  EXPECT_EQ(EBADF, ec.get());
}

TEST(FileTest, Pipe) {
  File read_end, write_end;
  File::pipe(read_end, write_end);
  EXPECT_NE(-1, read_end.get());
  EXPECT_NE(-1, write_end.get());
  // TODO: try writing to write_fd and reading from read_fd
}

// TODO: test pipe

// TODO: test File::read

// TODO: compile both with C++11 & C++98 mode

#endif

// TODO: test OutputRedirector

}  // namespace
<commit_msg>More tests.<commit_after>/*
 Tests of custom Google Test assertions.

 Copyright (c) 2012-2014, Victor Zverovich
 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.

 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 "gtest-extra.h"

#include <cstring>
#include <algorithm>
#include <stdexcept>
#include <gtest/gtest-spi.h>

namespace {

std::string FormatSystemErrorMessage(int error_code, fmt::StringRef message) {
  fmt::Writer out;
  fmt::internal::FormatSystemErrorMessage(out, error_code, message);
  return str(out);
}

#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
  EXPECT_THROW_MSG(statement, fmt::SystemError, \
      FormatSystemErrorMessage(error_code, message))

// Tests that assertion macros evaluate their arguments exactly once.
class SingleEvaluationTest : public ::testing::Test {
 protected:
  SingleEvaluationTest() {
    a_ = 0;
  }

  static int a_;
};

int SingleEvaluationTest::a_;

void ThrowNothing() {}

void ThrowException() {
  throw std::runtime_error("test");
}

// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) {
  // successful EXPECT_THROW_MSG
  EXPECT_THROW_MSG({  // NOLINT
    a_++;
    ThrowException();
  }, std::exception, "test");
  EXPECT_EQ(1, a_);

  // failed EXPECT_THROW_MSG, throws different type
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({  // NOLINT
    a_++;
    ThrowException();
  }, std::logic_error, "test"), "throws a different type");
  EXPECT_EQ(2, a_);

  // failed EXPECT_THROW_MSG, throws an exception with different message
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG({  // NOLINT
    a_++;
    ThrowException();
  }, std::exception, "other"), "throws an exception with a different message");
  EXPECT_EQ(3, a_);

  // failed EXPECT_THROW_MSG, throws nothing
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(a_++, std::exception, "test"), "throws nothing");
  EXPECT_EQ(4, a_);
}

// Tests that the compiler will not complain about unreachable code in the
// EXPECT_THROW_MSG macro.
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
  int n = 0;
  using std::runtime_error;
  EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, runtime_error, ""), "");
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(throw 1, runtime_error, ""), "");
  EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(
      throw runtime_error("a"), runtime_error, "b"), "");
}

TEST(AssertionSyntaxTest, ExceptionAssertionsBehavesLikeSingleStatement) {
  if (::testing::internal::AlwaysFalse())
    EXPECT_THROW_MSG(ThrowNothing(), std::exception, "");

  if (::testing::internal::AlwaysTrue())
    EXPECT_THROW_MSG(ThrowException(), std::exception, "test");
  else
    ;  // NOLINT
}

// Tests EXPECT_THROW_MSG.
TEST(ExpectTest, EXPECT_THROW_MSG) {
  EXPECT_THROW_MSG(ThrowException(), std::exception, "test");
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowException(), std::logic_error, "test"),
      "Expected: ThrowException() throws an exception of "
      "type std::logic_error.\n  Actual: it throws a different type.");
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowNothing(), std::exception, "test"),
      "Expected: ThrowNothing() throws an exception of type std::exception.\n"
      "  Actual: it throws nothing.");
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowException(), std::exception, "other"),
      "ThrowException() throws an exception with a different message.\n"
      "Expected: other\n"
      "  Actual: test");
}

TEST(StreamingAssertionsTest, ThrowMsg) {
  EXPECT_THROW_MSG(ThrowException(), std::exception, "test")
      << "unexpected failure";
  EXPECT_NONFATAL_FAILURE(
      EXPECT_THROW_MSG(ThrowException(), std::exception, "other")
      << "expected failure", "expected failure");
}

#if FMT_USE_FILE_DESCRIPTORS

TEST(ErrorCodeTest, Ctor) {
  EXPECT_EQ(0, ErrorCode().get());
  EXPECT_EQ(42, ErrorCode(42).get());
}

TEST(FileTest, DefaultCtor) {
  File f;
  EXPECT_EQ(-1, f.get());
}

// Checks if the file is open by reading one character from it.
// Note that IsOpen is not equivalent to !IsClosed.
bool IsOpen(int fd) {
  char buffer;
  return read(fd, &buffer, 1) == 1;
}

// Checks if the file is closed.
// Note that IsClosed is not equivalent to !IsOpen.
bool IsClosed(int fd) {
  char buffer;
  std::streamsize result = read(fd, &buffer, 1);
  return result == -1 && errno == EBADF;
}

TEST(FileTest, OpenFileInCtor) {
  int fd = 0;
  {
    File f(".travis.yml", File::RDONLY);
    fd = f.get();
    ASSERT_TRUE(IsOpen(fd));
  }
  ASSERT_TRUE(IsClosed(fd));
}

TEST(FileTest, OpenFileError) {
  EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY),
      ENOENT, "cannot open file nonexistent");
}

TEST(FileTest, MoveCtor) {
  File f(".travis.yml", File::RDONLY);
  int fd = f.get();
  EXPECT_NE(-1, fd);
  File f2(std::move(f));
  EXPECT_EQ(fd, f2.get());
  EXPECT_EQ(-1, f.get());
}

TEST(FileTest, MoveAssignment) {
  File f(".travis.yml", File::RDONLY);
  int fd = f.get();
  EXPECT_NE(-1, fd);
  File f2;
  f2 = std::move(f);
  EXPECT_EQ(fd, f2.get());
  EXPECT_EQ(-1, f.get());
}

TEST(FileTest, MoveAssignmentClosesFile) {
  File f(".travis.yml", File::RDONLY);
  File f2("CMakeLists.txt", File::RDONLY);
  int old_fd = f2.get();
  f2 = std::move(f);
  EXPECT_TRUE(IsClosed(old_fd));
}

File OpenFile(int &fd) {
  File f(".travis.yml", File::RDONLY);
  fd = f.get();
  return std::move(f);
}

TEST(FileTest, MoveFromTemporaryInCtor) {
  int fd = 0xdeadbeef;
  File f(OpenFile(fd));
  EXPECT_EQ(fd, f.get());
}

TEST(FileTest, MoveFromTemporaryInAssignment) {
  int fd = 0xdeadbeef;
  File f;
  f = OpenFile(fd);
  EXPECT_EQ(fd, f.get());
}

TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
  int fd = 0xdeadbeef;
  File f(".travis.yml", File::RDONLY);
  int old_fd = f.get();
  f = OpenFile(fd);
  EXPECT_TRUE(IsClosed(old_fd));
}

TEST(FileTest, CloseFileInDtor) {
  int fd = 0;
  {
    File f(".travis.yml", File::RDONLY);
    fd = f.get();
  }
  FILE *f = fdopen(fd, "r");
  int error_code = errno;
  if (f)
    fclose(f);
  EXPECT_TRUE(f == 0);
  EXPECT_EQ(EBADF, error_code);
}

TEST(FileTest, CloseError) {
  File *fd = new File(".travis.yml", File::RDONLY);
  EXPECT_STDERR(close(fd->get()); delete fd,
    FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
}

// Attempts to read count characters from the file.
std::string Read(File &f, std::size_t count) {
  std::string buffer(count, '\0');
  std::streamsize offset = 0, n = 0;
  do {
    n = f.read(&buffer[offset], count - offset);
    offset += n;
  } while (offset < count && n != 0);
  buffer.resize(offset);
  return buffer;
}

#define EXPECT_READ(file, expected_content) \
  EXPECT_EQ(expected_content, Read(f, std::strlen(expected_content)))

TEST(FileTest, Read) {
  File f(".travis.yml", File::RDONLY);
  EXPECT_READ(f, "language: cpp");
}

TEST(FileTest, ReadError) {
  File f;
  char buf;
  EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file");
}

TEST(FileTest, Write) {
  // TODO: use pipes
  const char EXPECTED[] = "test";
  {
    File f("out", File::WRONLY);
    enum { SIZE = sizeof(EXPECTED) - 1 };
    std::streamsize offset = 0, count = 0;
    do {
      count = f.write(EXPECTED + offset, SIZE - offset);
      offset += count;
    } while (offset < SIZE && count != 0);
  }
  File f("out", File::RDONLY);
  EXPECT_READ(f, EXPECTED);
}

TEST(FileTest, Dup) {
  File f(".travis.yml", File::RDONLY);
  File dup = File::dup(f.get());
  EXPECT_NE(f.get(), dup.get());
  const char EXPECTED[] = "language: cpp";
  EXPECT_EQ(EXPECTED, Read(dup, sizeof(EXPECTED) - 1));
}

TEST(FileTest, DupError) {
  EXPECT_SYSTEM_ERROR(File::dup(-1),
      EBADF, "cannot duplicate file descriptor -1");
}

TEST(FileTest, Dup2) {
  File f(".travis.yml", File::RDONLY);
  File dup("CMakeLists.txt", File::RDONLY);
  f.dup2(dup.get());
  EXPECT_NE(f.get(), dup.get());
  EXPECT_READ(dup, "language: cpp");
}

TEST(FileTest, Dup2Error) {
  File f(".travis.yml", File::RDONLY);
  EXPECT_SYSTEM_ERROR(f.dup2(-1), EBADF,
      fmt::Format("cannot duplicate file descriptor {} to -1") << f.get());
}

TEST(FileTest, Dup2NoExcept) {
  File f(".travis.yml", File::RDONLY);
  File dup("CMakeLists.txt", File::RDONLY);
  ErrorCode ec;
  f.dup2(dup.get(), ec);
  EXPECT_EQ(0, ec.get());
  EXPECT_NE(f.get(), dup.get());
  EXPECT_READ(dup, "language: cpp");
}

TEST(FileTest, Dup2NoExceptError) {
  File f(".travis.yml", File::RDONLY);
  ErrorCode ec;
  f.dup2(-1, ec);
  EXPECT_EQ(EBADF, ec.get());
}

TEST(FileTest, Pipe) {
  File read_end, write_end;
  File::pipe(read_end, write_end);
  EXPECT_NE(-1, read_end.get());
  EXPECT_NE(-1, write_end.get());
  // TODO: try writing to write_fd and reading from read_fd
}

// TODO: test pipe

// TODO: test File::read

// TODO: compile both with C++11 & C++98 mode

#endif

// TODO: test OutputRedirector

}  // namespace
<|endoftext|>


<commit_before>/**************************************************************************

 The MIT License (MIT)

 Copyright (c) 2015 Dmitry Sovetov

 https://github.com/dmsovetov

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 **************************************************************************/

#include "Rvm.h"
#include "RenderingContext.h"

DC_BEGIN_DREEMCHEST

namespace Scene {

// ** Rvm::s_samplersUniformNames
CString Rvm::s_samplersUniformNames[] = { "u_tex0" , "u_tex1", "u_tex2", "u_tex3", "u_tex4", "u_tex5", "u_tex6", "u_tex7" };

// ** Rvm::s_colorsUniformNames
CString Rvm::s_colorsUniformNames[]   = { "u_clr0" , "u_clr1", "u_clr2", "u_clr3", "u_clr4", "u_clr5", "u_clr6", "u_clr7" };

// ** Rvm::s_transformUniformName
CString Rvm::s_transformUniformName = "u_transform";

// ** Rvm::s_vpUniformName
CString Rvm::s_vpUniformName = "u_vp";

// ** Rvm::SkipRasterization
const Rvm::RasterizationOptions Rvm::SkipRasterization = { false };

// ** Rvm::OpaqueRasterization
const Rvm::RasterizationOptions Rvm::OpaqueRasterization = { true, Renderer::BlendDisabled, Renderer::BlendDisabled, Renderer::CompareDisabled, 0.0f, true, Renderer::LessEqual };

// ** Rvm::CutoutRasterization
const Rvm::RasterizationOptions Rvm::CutoutRasterization = { true, Renderer::BlendDisabled, Renderer::BlendDisabled, Renderer::Greater, 0.5f, true, Renderer::LessEqual };

// ** Rvm::TranslucentRasterization
const Rvm::RasterizationOptions Rvm::TranslucentRasterization = { true, Renderer::BlendSrcAlpha, Renderer::BlendInvSrcAlpha, Renderer::CompareDisabled, 0.0f, false, Renderer::LessEqual };

// ** Rvm::AdditiveRasterization
const Rvm::RasterizationOptions Rvm::AdditiveRasterization = { true, Renderer::BlendOne, Renderer::BlendOne, Renderer::CompareDisabled, 0.0f, false, Renderer::LessEqual };

// ** Rvm::Rvm
Rvm::Rvm( Renderer::HalPtr hal, u32 maxCommands ) : m_hal( hal ), m_allocator( maxCommands )
{
	m_defaultCullFace		= Renderer::TriangleFaceBack;
	m_defaultPolygonMode	= Renderer::PolygonFill;

	resetCounters();
}

// ** Rvm::setRenderingContext
void Rvm::setRenderingContext( RenderingContextWPtr value )
{
	m_renderingContext = value;
}

// ** Rvm::increase
void Rvm::increase( Counter counter, u32 amount )
{
	m_counters[counter] += amount;
}

// ** Rvm::counter
s32 Rvm::counter( Counter counter ) const
{
	return m_counters[counter];
}

// ** Rvm::resetCounters
void Rvm::resetCounters( void )
{
	memset( m_counters, 0, sizeof( m_counters ) );
	increase( RopAllocatedMemory, sizeof( Command ) * m_allocator.size() );
}

// ** Rvm::hal
Renderer::HalPtr Rvm::hal( void ) const
{
	return m_hal;
}

// ** Rvm::willRender
bool Rvm::willRender( RenderingMode mode ) const
{
	return m_rasterization[mode];
}

// ** Rvm::setDefaultShader
void Rvm::setDefaultShader( const Renderer::ShaderPtr& value )
{
	m_defaultShader = value;
}

// ** Rvm::setRasterization
void Rvm::setRasterization( RenderingMode mode, const RasterizationOptions& value )
{
	m_rasterization[mode] = value;
}

// ** Rvm::setRasterization
void Rvm::setRasterization( u32 mask, const RasterizationOptions& value )
{
	for( u32 mode = 0; mode < TotalRenderModes; mode++ ) {
		if( mask & BIT( mode ) ) {
			m_rasterization[mode] = value;
		}
	}
}

// ** Rvm::setDefaultPolygonMode
void Rvm::setDefaultPolygonMode( Renderer::PolygonMode value )
{
	m_defaultPolygonMode = value;
}

// ** Rvm::setDefaultCullFace
void Rvm::setDefaultCullFace( Renderer::TriangleFace value )
{
	m_defaultCullFace = value;
}

// ** Rvm::setViewProjection
void Rvm::setViewProjection( const Matrix4& value )
{
	m_viewProjection = value;
}

// ** Rvm::setShader
void Rvm::setShader( u32 id, const Renderer::ShaderPtr& shader )
{
	if( id >= ( u32 )m_shaders.size() ) {
		m_shaders.resize( id + 1 );
	}

	m_shaders[id] = shader;
}

// ** Rvm::viewProjection
const Matrix4& Rvm::viewProjection( void ) const
{
	return m_viewProjection;
}

// ** Rvm::findShaderById
Renderer::Shader* Rvm::findShaderById( u32 idx ) const
{
	return idx < ( u32 )m_shaders.size() ? m_shaders[idx].get() : m_defaultShader.get();
}

// ** Rvm::flush
void Rvm::flush( void )
{
	// Sort the command buffer
	m_commands.sort( sortByShaderTextureMesh );

	// Active texture
	Renderer::Texture* activeTextures[Material::TotalMaterialLayers] = { 0 };

	// Active vertex buffer
	Renderer::VertexBuffer* activeVertexBuffer = NULL;

	//! Active shader
	Renderer::Shader* activeShader			= ( Renderer::Shader* )-1;
	u8				  activeRenderingMode	= -1;

	// Set culling
	m_hal->setCulling( m_defaultCullFace );

	// Set polygon mode
	m_hal->setPolygonMode( m_defaultPolygonMode );

	// Perform all rendering operations
	for( EmittedCommands::const_iterator i = m_commands.begin(), end = m_commands.end(); i != end; ++i ) {
		const Command* cmd = *i;

		// Set the shader
		if( activeShader != cmd->shader ) {
			activeShader = cmd->shader ? cmd->shader : m_defaultShader.get();
			setShader( activeShader );
			increase( ShaderSwitches );
		}

		DC_ABORT_IF( activeShader == NULL, "no active shader" );

		// Set rendering mode
		if( activeRenderingMode != cmd->mode ) {
			setRenderingMode( cmd->mode );
			activeRenderingMode = cmd->mode;
		}

		// Set the instance uniforms
		if( activeShader ) {
			setShaderUniforms( activeShader, cmd );
		}

		// Set the texture
		for( u32 j = 0; j < Material::TotalMaterialLayers; j++ ) {
            Renderer::Texture* texture = cmd->textures[j];

			if( texture == activeTextures[j] ) {
                continue;
            }

			m_hal->setTexture( j, texture );
			activeTextures[j] = texture;
			increase( TextureSwitches );
		}

		// Set the vertex buffer
		const RenderingContext::Renderable& renderable = m_renderingContext->renderable( cmd->mesh );

		if( renderable.vertexBuffer != activeVertexBuffer ) {
			m_hal->setVertexBuffer( renderable.vertexBuffer );
			activeVertexBuffer = renderable.vertexBuffer.get();
			increase( VertexBufferSwitches );
		}

		// Render the mesh
		m_hal->renderIndexed( renderable.primitiveType, renderable.indexBuffer, 0, renderable.indexBuffer->size() );
		increase( DrawIndexed );
		increase( Triangles, renderable.indexBuffer->size() / 3 );
		increase( Vertices, renderable.indexBuffer->size() );
	}

	// Clear the command list
	clear();
}

// ** Rvm::clear
void Rvm::clear( void )
{
	// Clear emitted commands
	m_commands.clear();
	m_allocator.reset();
}

// ** Rvm::reset
void Rvm::reset( void )
{
	// Reset rasterization options
	setRasterization( RenderOpaque, OpaqueRasterization );
	setRasterization( RenderAdditive, AdditiveRasterization );
	setRasterization( RenderTranslucent, TranslucentRasterization );
	setRasterization( RenderCutout, CutoutRasterization );

	// Reset render states
	setDefaultCullFace( Renderer::TriangleFaceBack );
	setDefaultPolygonMode( Renderer::PolygonFill );

	// Set the default polygon mode
	m_hal->setPolygonMode( Renderer::PolygonFill );

	// Set the default shader
	m_hal->setShader( NULL );

	// Set the default vertex buffer
	m_hal->setVertexBuffer( NULL );

	// Set default textures
	for( s32 i = 0; i < 8; i++ ) {
		m_hal->setTexture( i, NULL );
	}

	// Enable the depth test back
	m_hal->setDepthTest( true, Renderer::LessEqual );
}

// ** Rvm::emit
Rvm::Command* Rvm::emit( void )
{
	Command* rop = m_allocator.allocate();
	DC_ABORT_IF( rop == NULL, "failed to allocate render operation" )

	memset( rop, 0, sizeof( Command ) );
	m_commands.push_back( rop );

	return rop;
}

// ** Rvm::setRenderingMode
void Rvm::setRenderingMode( u8 value )
{
	DC_ABORT_IF( value >= TotalRenderModes, "invalid rendering mode" )

	const RasterizationOptions& options = m_rasterization[value];
	DC_BREAK_IF( !options, "invalid rasterization options" )

	m_hal->setBlendFactors( options.m_blend[0], options.m_blend[1] );
	m_hal->setAlphaTest( options.m_alphaTest, options.m_alphaRef );
	m_hal->setDepthTest( options.m_depthMask, options.m_depthTest );
}

// ** Rvm::setShader
void Rvm::setShader( Renderer::Shader* shader )
{
	m_hal->setShader( shader );

	u32 location = 0;

	if( location = shader->findUniformLocation( s_vpUniformName ) ) {
		shader->setMatrix( location, m_viewProjection );
	}

	// Bind texture samplers
	for( u32 i = 0; i < 8; i++ ) {
		if( location = shader->findUniformLocation( s_samplersUniformNames[i] ) ) {
			shader->setInt( location, i );
		}
	}

	// Set the constant color value
	if( location = shader->findUniformLocation( "u_color" ) ) {
		shader->setVec4( location, m_registers[ConstantColor] );
	}

	// Set the light position
	if( location = shader->findUniformLocation( "u_lightPosition" ) ) {
		shader->setVec4( location, m_registers[LightPosition] );
	}

	// Set the light color
	if( location = shader->findUniformLocation( "u_lightColor" ) ) {
		shader->setVec4( location, m_registers[LightColor] );
	}
}

// ** Rvm::setRegister
void Rvm::setRegister( Register id, const Vec4& value )
{
	m_registers[id] = value;
}

// ** Rvm::setShaderUniforms
void Rvm::setShaderUniforms( Renderer::Shader* shader, const Command* cmd )
{
	u32 location = 0;

	// Set colors
	if( cmd->colors ) {
		for( u32 i = 0; i < 8; i++ ) {
			if( !cmd->colors[i] ) {
				continue;
			}

			if( location = shader->findUniformLocation( s_colorsUniformNames[i] ) ) {
				shader->setVec4( location, Vec4( cmd->colors[i]->r, cmd->colors[i]->g, cmd->colors[i]->b, cmd->colors[i]->a ) );
			}
		}
	}

	// Set the transformation matrix
	if( location = shader->findUniformLocation( s_transformUniformName ) ) {
		shader->setMatrix( location,  cmd->transform );
	}
}

// ** Rvm::sortByShaderTextureMesh
bool Rvm::sortByShaderTextureMesh( const Command* a, const Command* b )
{
	if( a->mode != b->mode ) return a->mode < b->mode;
	if( a->distance != b->distance ) return a->distance > b->distance;
	if( a->shader != b->shader ) return a->shader < b->shader;
	if( a->mesh != b->mesh ) return a->mesh > b->mesh;
	//if( a->vertexBuffer != b->vertexBuffer ) return a->vertexBuffer > b->vertexBuffer;

	for( u32 i = 0; i < Material::TotalMaterialLayers; i++ ) {
		if( a->textures[i] != b->textures[i] ) return a->textures[i] > b->textures[i];
	}

	return false;
}

} // namespace Scene

DC_END_DREEMCHEST<commit_msg>Fixed: compilation error with deprecated code<commit_after>/**************************************************************************

 The MIT License (MIT)

 Copyright (c) 2015 Dmitry Sovetov

 https://github.com/dmsovetov

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 **************************************************************************/

#include "Rvm.h"
#include "RenderingContext.h"

DC_BEGIN_DREEMCHEST

namespace Scene {

// ** Rvm::s_samplersUniformNames
CString Rvm::s_samplersUniformNames[] = { "u_tex0" , "u_tex1", "u_tex2", "u_tex3", "u_tex4", "u_tex5", "u_tex6", "u_tex7" };

// ** Rvm::s_colorsUniformNames
CString Rvm::s_colorsUniformNames[]   = { "u_clr0" , "u_clr1", "u_clr2", "u_clr3", "u_clr4", "u_clr5", "u_clr6", "u_clr7" };

// ** Rvm::s_transformUniformName
CString Rvm::s_transformUniformName = "u_transform";

// ** Rvm::s_vpUniformName
CString Rvm::s_vpUniformName = "u_vp";

// ** Rvm::SkipRasterization
const Rvm::RasterizationOptions Rvm::SkipRasterization = { false };

// ** Rvm::OpaqueRasterization
const Rvm::RasterizationOptions Rvm::OpaqueRasterization = { true, Renderer::BlendDisabled, Renderer::BlendDisabled, Renderer::CompareDisabled, 0.0f, true, Renderer::LessEqual };

// ** Rvm::CutoutRasterization
const Rvm::RasterizationOptions Rvm::CutoutRasterization = { true, Renderer::BlendDisabled, Renderer::BlendDisabled, Renderer::Greater, 0.5f, true, Renderer::LessEqual };

// ** Rvm::TranslucentRasterization
const Rvm::RasterizationOptions Rvm::TranslucentRasterization = { true, Renderer::BlendSrcAlpha, Renderer::BlendInvSrcAlpha, Renderer::CompareDisabled, 0.0f, false, Renderer::LessEqual };

// ** Rvm::AdditiveRasterization
const Rvm::RasterizationOptions Rvm::AdditiveRasterization = { true, Renderer::BlendOne, Renderer::BlendOne, Renderer::CompareDisabled, 0.0f, false, Renderer::LessEqual };

// ** Rvm::Rvm
Rvm::Rvm( Renderer::HalPtr hal, u32 maxCommands ) : m_hal( hal ), m_allocator( maxCommands )
{
	m_defaultCullFace		= Renderer::TriangleFaceBack;
	m_defaultPolygonMode	= Renderer::PolygonFill;

	resetCounters();
}

// ** Rvm::setRenderingContext
void Rvm::setRenderingContext( RenderingContextWPtr value )
{
	m_renderingContext = value;
}

// ** Rvm::increase
void Rvm::increase( Counter counter, u32 amount )
{
	m_counters[counter] += amount;
}

// ** Rvm::counter
s32 Rvm::counter( Counter counter ) const
{
	return m_counters[counter];
}

// ** Rvm::resetCounters
void Rvm::resetCounters( void )
{
	memset( m_counters, 0, sizeof( m_counters ) );
	increase( RopAllocatedMemory, sizeof( Command ) * m_allocator.size() );
}

// ** Rvm::hal
Renderer::HalPtr Rvm::hal( void ) const
{
	return m_hal;
}

// ** Rvm::willRender
bool Rvm::willRender( RenderingMode mode ) const
{
	return m_rasterization[mode];
}

// ** Rvm::setDefaultShader
void Rvm::setDefaultShader( const Renderer::ShaderPtr& value )
{
	m_defaultShader = value;
}

// ** Rvm::setRasterization
void Rvm::setRasterization( RenderingMode mode, const RasterizationOptions& value )
{
	m_rasterization[mode] = value;
}

// ** Rvm::setRasterization
void Rvm::setRasterization( u32 mask, const RasterizationOptions& value )
{
	for( u32 mode = 0; mode < TotalRenderModes; mode++ ) {
		if( mask & BIT( mode ) ) {
			m_rasterization[mode] = value;
		}
	}
}

// ** Rvm::setDefaultPolygonMode
void Rvm::setDefaultPolygonMode( Renderer::PolygonMode value )
{
	m_defaultPolygonMode = value;
}

// ** Rvm::setDefaultCullFace
void Rvm::setDefaultCullFace( Renderer::TriangleFace value )
{
	m_defaultCullFace = value;
}

// ** Rvm::setViewProjection
void Rvm::setViewProjection( const Matrix4& value )
{
	m_viewProjection = value;
}

// ** Rvm::setShader
void Rvm::setShader( u32 id, const Renderer::ShaderPtr& shader )
{
	if( id >= ( u32 )m_shaders.size() ) {
		m_shaders.resize( id + 1 );
	}

	m_shaders[id] = shader;
}

// ** Rvm::viewProjection
const Matrix4& Rvm::viewProjection( void ) const
{
	return m_viewProjection;
}

// ** Rvm::findShaderById
Renderer::Shader* Rvm::findShaderById( u32 idx ) const
{
	return idx < ( u32 )m_shaders.size() ? m_shaders[idx].get() : m_defaultShader.get();
}

// ** Rvm::flush
void Rvm::flush( void )
{
	// Sort the command buffer
	m_commands.sort( sortByShaderTextureMesh );

	// Active texture
	Renderer::Texture* activeTextures[Material::TotalMaterialLayers] = { 0 };

	// Active vertex buffer
	Renderer::VertexBuffer* activeVertexBuffer = NULL;

	//! Active shader
	Renderer::Shader* activeShader			= ( Renderer::Shader* )-1;
	u8				  activeRenderingMode	= -1;

	// Set culling
	m_hal->setCulling( m_defaultCullFace );

	// Set polygon mode
	m_hal->setPolygonMode( m_defaultPolygonMode );

	// Perform all rendering operations
	for( EmittedCommands::const_iterator i = m_commands.begin(), end = m_commands.end(); i != end; ++i ) {
		const Command* cmd = *i;

		// Set the shader
		if( activeShader != cmd->shader ) {
			activeShader = cmd->shader ? cmd->shader : m_defaultShader.get();
			setShader( activeShader );
			increase( ShaderSwitches );
		}

		DC_ABORT_IF( activeShader == NULL, "no active shader" );

		// Set rendering mode
		if( activeRenderingMode != cmd->mode ) {
			setRenderingMode( cmd->mode );
			activeRenderingMode = cmd->mode;
		}

		// Set the instance uniforms
		if( activeShader ) {
			setShaderUniforms( activeShader, cmd );
		}

		// Set the texture
		for( u32 j = 0; j < Material::TotalMaterialLayers; j++ ) {
            Renderer::Texture* texture = cmd->textures[j];

			if( texture == activeTextures[j] ) {
                continue;
            }

			m_hal->setTexture( j, texture );
			activeTextures[j] = texture;
			increase( TextureSwitches );
		}

		// Set the vertex buffer
		//const RenderingContext::Renderable& renderable = m_renderingContext->requestRenderable( cmd->mesh, 0 );

		//if( renderable.vertexBuffer != activeVertexBuffer ) {
		//	m_hal->setVertexBuffer( renderable.vertexBuffer );
		//	activeVertexBuffer = renderable.vertexBuffer.get();
		//	increase( VertexBufferSwitches );
		//}

		// Render the mesh
		//m_hal->renderIndexed( renderable.primitiveType, renderable.indexBuffer, 0, renderable.indexBuffer->size() );
		//increase( DrawIndexed );
		//increase( Triangles, renderable.indexBuffer->size() / 3 );
		//increase( Vertices, renderable.indexBuffer->size() );

        DC_NOT_IMPLEMENTED;
	}

	// Clear the command list
	clear();
}

// ** Rvm::clear
void Rvm::clear( void )
{
	// Clear emitted commands
	m_commands.clear();
	m_allocator.reset();
}

// ** Rvm::reset
void Rvm::reset( void )
{
	// Reset rasterization options
	setRasterization( RenderOpaque, OpaqueRasterization );
	setRasterization( RenderAdditive, AdditiveRasterization );
	setRasterization( RenderTranslucent, TranslucentRasterization );
	setRasterization( RenderCutout, CutoutRasterization );

	// Reset render states
	setDefaultCullFace( Renderer::TriangleFaceBack );
	setDefaultPolygonMode( Renderer::PolygonFill );

	// Set the default polygon mode
	m_hal->setPolygonMode( Renderer::PolygonFill );

	// Set the default shader
	m_hal->setShader( NULL );

	// Set the default vertex buffer
	m_hal->setVertexBuffer( NULL );

	// Set default textures
	for( s32 i = 0; i < 8; i++ ) {
		m_hal->setTexture( i, NULL );
	}

	// Enable the depth test back
	m_hal->setDepthTest( true, Renderer::LessEqual );
}

// ** Rvm::emit
Rvm::Command* Rvm::emit( void )
{
	Command* rop = m_allocator.allocate();
	DC_ABORT_IF( rop == NULL, "failed to allocate render operation" )

	memset( rop, 0, sizeof( Command ) );
	m_commands.push_back( rop );

	return rop;
}

// ** Rvm::setRenderingMode
void Rvm::setRenderingMode( u8 value )
{
	DC_ABORT_IF( value >= TotalRenderModes, "invalid rendering mode" )

	const RasterizationOptions& options = m_rasterization[value];
	DC_BREAK_IF( !options, "invalid rasterization options" )

	m_hal->setBlendFactors( options.m_blend[0], options.m_blend[1] );
	m_hal->setAlphaTest( options.m_alphaTest, options.m_alphaRef );
	m_hal->setDepthTest( options.m_depthMask, options.m_depthTest );
}

// ** Rvm::setShader
void Rvm::setShader( Renderer::Shader* shader )
{
	m_hal->setShader( shader );

	u32 location = 0;

	if( location = shader->findUniformLocation( s_vpUniformName ) ) {
		shader->setMatrix( location, m_viewProjection );
	}

	// Bind texture samplers
	for( u32 i = 0; i < 8; i++ ) {
		if( location = shader->findUniformLocation( s_samplersUniformNames[i] ) ) {
			shader->setInt( location, i );
		}
	}

	// Set the constant color value
	if( location = shader->findUniformLocation( "u_color" ) ) {
		shader->setVec4( location, m_registers[ConstantColor] );
	}

	// Set the light position
	if( location = shader->findUniformLocation( "u_lightPosition" ) ) {
		shader->setVec4( location, m_registers[LightPosition] );
	}

	// Set the light color
	if( location = shader->findUniformLocation( "u_lightColor" ) ) {
		shader->setVec4( location, m_registers[LightColor] );
	}
}

// ** Rvm::setRegister
void Rvm::setRegister( Register id, const Vec4& value )
{
	m_registers[id] = value;
}

// ** Rvm::setShaderUniforms
void Rvm::setShaderUniforms( Renderer::Shader* shader, const Command* cmd )
{
	u32 location = 0;

	// Set colors
	if( cmd->colors ) {
		for( u32 i = 0; i < 8; i++ ) {
			if( !cmd->colors[i] ) {
				continue;
			}

			if( location = shader->findUniformLocation( s_colorsUniformNames[i] ) ) {
				shader->setVec4( location, Vec4( cmd->colors[i]->r, cmd->colors[i]->g, cmd->colors[i]->b, cmd->colors[i]->a ) );
			}
		}
	}

	// Set the transformation matrix
	if( location = shader->findUniformLocation( s_transformUniformName ) ) {
		shader->setMatrix( location,  cmd->transform );
	}
}

// ** Rvm::sortByShaderTextureMesh
bool Rvm::sortByShaderTextureMesh( const Command* a, const Command* b )
{
	if( a->mode != b->mode ) return a->mode < b->mode;
	if( a->distance != b->distance ) return a->distance > b->distance;
	if( a->shader != b->shader ) return a->shader < b->shader;
	if( a->mesh != b->mesh ) return a->mesh > b->mesh;
	//if( a->vertexBuffer != b->vertexBuffer ) return a->vertexBuffer > b->vertexBuffer;

	for( u32 i = 0; i < Material::TotalMaterialLayers; i++ ) {
		if( a->textures[i] != b->textures[i] ) return a->textures[i] > b->textures[i];
	}

	return false;
}

} // namespace Scene

DC_END_DREEMCHEST<|endoftext|>


<commit_before>//
// Created by permal on 2018-10-26.
//

#include "publish.h"
#include <smooth/core/task_priorities.h>
#include <smooth/core/json/Value.h>
#include <fstream>
#include <cassert>
#include <smooth/core/logging/log.h>
#include <chrono>
#include <smooth/core/ipc/SubscribingTaskEventQueue.h>
#include <smooth/core/ipc/Publisher.h>
#include <smooth/core/Application.h>

using namespace smooth;

using namespace smooth::core;
using namespace smooth::core::json;
using namespace smooth::core::ipc;
using namespace smooth::core::logging;
using namespace std::chrono;

namespace publish
{

    PublisherTask::PublisherTask()
            : smooth::core::Task("Publisher", 8192, smooth::core::APPLICATION_BASE_PRIO, milliseconds(1))
    {
    }

    void PublisherTask::init()
    {
    }

    void PublisherTask::tick()
    {
        smooth::core::ipc::Publisher<ItemToPublish>::publish(ItemToPublish(std::chrono::steady_clock::now()));
    }

    App::App()
            : Application(APPLICATION_BASE_PRIO, seconds(1)),
              sub("sub", 10, *this, *this)
    {
    }

    void App::init()
    {
        POSIXApplication::init();
        p.start();
    }

    void App::tick()
    {
    }

    void App::event(const ItemToPublish& item)
    {
        // Although this task has tick of one second, this response method will be called approximately
        // every millisecond since that is the pace at which the publisher publishes items.

        auto duration = duration_cast<microseconds>(std::chrono::steady_clock::now() - item.get_start());
        Log::info("event", Format("Time in queue: {1} us", Int64(duration.count())));
    }

}<commit_msg>Updated comment.<commit_after>//
// Created by permal on 2018-10-26.
//

#include "publish.h"
#include <smooth/core/task_priorities.h>
#include <smooth/core/json/Value.h>
#include <fstream>
#include <cassert>
#include <smooth/core/logging/log.h>
#include <chrono>
#include <smooth/core/ipc/SubscribingTaskEventQueue.h>
#include <smooth/core/ipc/Publisher.h>
#include <smooth/core/Application.h>

using namespace smooth;

using namespace smooth::core;
using namespace smooth::core::json;
using namespace smooth::core::ipc;
using namespace smooth::core::logging;
using namespace std::chrono;

namespace publish
{

    PublisherTask::PublisherTask()
            : smooth::core::Task("Publisher", 8192, smooth::core::APPLICATION_BASE_PRIO, milliseconds(1))
    {
    }

    void PublisherTask::init()
    {
    }

    void PublisherTask::tick()
    {
        smooth::core::ipc::Publisher<ItemToPublish>::publish(ItemToPublish(std::chrono::steady_clock::now()));
    }

    App::App()
            : Application(APPLICATION_BASE_PRIO, seconds(1)),
              sub("sub", 10, *this, *this)
    {
    }

    void App::init()
    {
        POSIXApplication::init();
        p.start();
    }

    void App::tick()
    {
    }

    void App::event(const ItemToPublish& item)
    {
        // Although this task has tick of one second, this response method will be called approximately
        // every millisecond since that is the pace at which the publisher publishes items.
        // With CONFIG_FREERTOS_HZ at 100, average time is ~150us compared to ~29500us with CONFIG_FREERTOS_HZ
        // at 1000 so there is a noticeable difference in overhead with a higher tick rate.

        auto duration = duration_cast<microseconds>(std::chrono::steady_clock::now() - item.get_start());
        Log::info("event", Format("Time in queue: {1} us", Int64(duration.count())));
    }

}<|endoftext|>


<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013-2014, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * 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.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <iostream>
#include <vector>
#include <algorithm>    // std::generate
#include <ctime>        // std::time
#include <cstdlib>      // std::rand, std::srand
#include <cmath> 	// pow

#include "nupic/algorithms/SpatialPooler.hpp"
#include "nupic/algorithms/Cells4.hpp"

using namespace std;
using namespace nupic;
using namespace nupic::algorithms::spatial_pooler;
using namespace nupic::algorithms::Cells4;

// function generator:
int RandomNumber01 () { return (rand()%2); } // returns random (binary) numbers from {0,1}

int main(int argc, const char * argv[])
{
const int DIM = 2048; // number of columns in SP, TP
const int DIM_INPUT = 10000;
const int TP_CELLS_PER_COL = 10; // cells per column in TP
const int EPOCHS = pow(10, 4); // number of iterations (calls to SP/TP compute() )

  vector<UInt> inputDim = {DIM_INPUT};
  vector<UInt> colDim = {DIM};

  // generate random input
  vector<UInt> input(DIM_INPUT);
  vector<UInt> outSP(DIM); // active array, output of SP/TP
  const int _CELLS = DIM * TP_CELLS_PER_COL;
  vector<UInt> outTP(_CELLS);
  Real rIn[DIM] = {}; // input for TP (must be Reals)
  Real rOut[_CELLS] = {};

  // initialize SP, TP
  SpatialPooler sp(inputDim, colDim);
  Cells4 tp(DIM, TP_CELLS_PER_COL, 12, 8, 15, 5, .5, .8, 1.0, .1, .1, 0.0, false, 42, true, false);

  //run
  for (int e=0; e< EPOCHS; e++) {
    generate(input.begin(), input.end(), RandomNumber01);
    fill(outSP.begin(), outSP.end(), 0);
    sp.compute(input.data(), true, outSP.data());

    for (int i=0; i< DIM; i++) {
      rIn[i] = (Real)(outSP[i]);
    }

    tp.compute(rIn, rOut, true, true);

    for (int i=0; i< _CELLS; i++) {
      outTP[i] = (UInt)rOut[i];
    }

    // print
    if (e == EPOCHS-1) {
      cout << "Epoch = " << e << endl;
      cout << "SP=" << outSP << endl;
      cout << "TP=" << outTP << endl;
    }
  }
  return 0;
}
<commit_msg>review<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013-2015, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * 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.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <iostream>
#include <vector>
#include <algorithm>    // std::generate
#include <ctime>        // std::time
#include <cstdlib>      // std::rand, std::srand
#include <cmath> 	// pow

#include "nupic/algorithms/SpatialPooler.hpp"
#include "nupic/algorithms/Cells4.hpp"

using namespace std;
using namespace nupic;
using namespace nupic::algorithms::spatial_pooler;
using namespace nupic::algorithms::Cells4;

// function generator:
int RandomNumber01 () { return (rand()%2); } // returns random (binary) numbers from {0,1}

int main(int argc, const char * argv[])
{
const int DIM = 2048; // number of columns in SP, TP
const int DIM_INPUT = 10000;
const int TP_CELLS_PER_COL = 10; // cells per column in TP
const int EPOCHS = pow(10, 4); // number of iterations (calls to SP/TP compute() )

  vector<UInt> inputDim = {DIM_INPUT};
  vector<UInt> colDim = {DIM};

  // generate random input
  vector<UInt> input(DIM_INPUT);
  vector<UInt> outSP(DIM); // active array, output of SP/TP
  const int _CELLS = DIM * TP_CELLS_PER_COL;
  vector<UInt> outTP(_CELLS);
  Real rIn[DIM] = {}; // input for TP (must be Reals)
  Real rOut[_CELLS] = {};

  // initialize SP, TP
  SpatialPooler sp(inputDim, colDim);
  Cells4 tp(DIM, TP_CELLS_PER_COL, 12, 8, 15, 5, .5, .8, 1.0, .1, .1, 0.0, false, 42, true, false);

  //run
  for (int e=0; e< EPOCHS; e++) {
    generate(input.begin(), input.end(), RandomNumber01);
    fill(outSP.begin(), outSP.end(), 0);
    sp.compute(input.data(), true, outSP.data());

    for (int i=0; i< DIM; i++) {
      rIn[i] = (Real)(outSP[i]);
    }

    tp.compute(rIn, rOut, true, true);

    for (int i=0; i< _CELLS; i++) {
      outTP[i] = (UInt)rOut[i];
    }

    // print
    if (e == EPOCHS-1) {
      cout << "Epoch = " << e << endl;
      cout << "SP=" << outSP << endl;
      cout << "TP=" << outTP << endl;
    }
  }
  return 0;
}
<|endoftext|>