<commit_before>#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "util.h"

#include <QString>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
#include <QLineEdit>
#include <QUrl>
#include <QTextDocument> // For Qt::escape
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QDesktopServices>
#include <QThread>

#include <boost/filesystem.hpp>

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "shlwapi.h"
#endif

namespace GUIUtil {

QString dateTimeStr(const QDateTime &date)
{
    return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
}

QString dateTimeStr(qint64 nTime)
{
    return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}

QFont bitcoinAddressFont()
{
    QFont font("Monospace");
    font.setStyleHint(QFont::TypeWriter);
    return font;
}

void setupAddressWidget(QLineEdit *widget, QWidget *parent)
{
    widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
    widget->setValidator(new BitcoinAddressValidator(parent));
    widget->setFont(bitcoinAddressFont());
}

void setupAmountWidget(QLineEdit *widget, QWidget *parent)
{
    QDoubleValidator *amountValidator = new QDoubleValidator(parent);
    amountValidator->setDecimals(8);
    amountValidator->setBottom(0.0);
    widget->setValidator(amountValidator);
    widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
}

bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
    if(uri.scheme() != QString("bitcoin"))
        return false;

    SendCoinsRecipient rv;
    rv.address = uri.path();
    rv.amount = 0;
    QList<QPair<QString, QString> > items = uri.queryItems();
    for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
    {
        bool fShouldReturnFalse = false;
        if (i->first.startsWith("req-"))
        {
            i->first.remove(0, 4);
            fShouldReturnFalse = true;
        }

        if (i->first == "label")
        {
            rv.label = i->second;
            fShouldReturnFalse = false;
        }
        else if (i->first == "amount")
        {
            if(!i->second.isEmpty())
            {
                if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
                {
                    return false;
                }
            }
            fShouldReturnFalse = false;
        }

        if (fShouldReturnFalse)
            return false;
    }
    if(out)
    {
        *out = rv;
    }
    return true;
}

bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
{
    // Convert bitcoin:// to bitcoin:
    //
    //    Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
    //    which will lowercase it (and thus invalidate the address).
    if(uri.startsWith("bitcoin://"))
    {
        uri.replace(0, 10, "bitcoin:");
    }
    QUrl uriInstance(uri);
    return parseBitcoinURI(uriInstance, out);
}

QString HtmlEscape(const QString& str, bool fMultiLine)
{
    QString escaped = Qt::escape(str);
    if(fMultiLine)
    {
        escaped = escaped.replace("\n", "
\n");
    }
    return escaped;
}

QString HtmlEscape(const std::string& str, bool fMultiLine)
{
    return HtmlEscape(QString::fromStdString(str), fMultiLine);
}

void copyEntryData(QAbstractItemView *view, int column, int role)
{
    if(!view || !view->selectionModel())
        return;
    QModelIndexList selection = view->selectionModel()->selectedRows(column);

    if(!selection.isEmpty())
    {
        // Copy first item
        QApplication::clipboard()->setText(selection.at(0).data(role).toString());
    }
}

QString getSaveFileName(QWidget *parent, const QString &caption,
                                 const QString &dir,
                                 const QString &filter,
                                 QString *selectedSuffixOut)
{
    QString selectedFilter;
    QString myDir;
    if(dir.isEmpty()) // Default to user documents location
    {
        myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
    }
    else
    {
        myDir = dir;
    }
    QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);

    /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
    QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
    QString selectedSuffix;
    if(filter_re.exactMatch(selectedFilter))
    {
        selectedSuffix = filter_re.cap(1);
    }

    /* Add suffix if needed */
    QFileInfo info(result);
    if(!result.isEmpty())
    {
        if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
        {
            /* No suffix specified, add selected suffix */
            if(!result.endsWith("."))
                result.append(".");
            result.append(selectedSuffix);
        }
    }

    /* Return selected suffix if asked to */
    if(selectedSuffixOut)
    {
        *selectedSuffixOut = selectedSuffix;
    }
    return result;
}

Qt::ConnectionType blockingGUIThreadConnection()
{
    if(QThread::currentThread() != QCoreApplication::instance()->thread())
    {
        return Qt::BlockingQueuedConnection;
    }
    else
    {
        return Qt::DirectConnection;
    }
}

bool checkPoint(const QPoint &p, const QWidget *w)
{
  QWidget *atW = qApp->widgetAt(w->mapToGlobal(p));
  if(!atW) return false;
  return atW->topLevelWidget() == w;
}

bool isObscured(QWidget *w)
{

  return !(checkPoint(QPoint(0, 0), w)
           && checkPoint(QPoint(w->width() - 1, 0), w)
           && checkPoint(QPoint(0, w->height() - 1), w)
           && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
           && checkPoint(QPoint(w->width()/2, w->height()/2), w));
}

void openDebugLogfile()
{
    boost::filesystem::path pathDebug = GetDataDir() / "debug.log";

#ifdef WIN32
    if (boost::filesystem::exists(pathDebug))
        /* Open debug.log with the associated application */
        ShellExecuteA((HWND)0, (LPCSTR)"open", (LPCSTR)pathDebug.string().c_str(), NULL, NULL, SW_SHOWNORMAL);
#endif
}

ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
    QObject(parent), size_threshold(size_threshold)
{

}

bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
{
    if(evt->type() == QEvent::ToolTipChange)
    {
        QWidget *widget = static_cast<QWidget*>(obj);
        QString tooltip = widget->toolTip();
        if(!Qt::mightBeRichText(tooltip) && tooltip.size() > size_threshold)
        {
            // Prefix <qt/> to make sure Qt detects this as rich text
            // Escape the current message as HTML and replace \n by

            tooltip = "<qt/>" + HtmlEscape(tooltip, true);
            widget->setToolTip(tooltip);
            return true;
        }
    }
    return QObject::eventFilter(obj, evt);
}

} // namespace GUIUtil

<commit_msg>Prevent tooltip filter from ever causing infinite loops<commit_after>#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "util.h"

#include <QString>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
#include <QLineEdit>
#include <QUrl>
#include <QTextDocument> // For Qt::escape
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QDesktopServices>
#include <QThread>

#include <boost/filesystem.hpp>

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "shlwapi.h"
#endif

namespace GUIUtil {

QString dateTimeStr(const QDateTime &date)
{
    return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
}

QString dateTimeStr(qint64 nTime)
{
    return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}

QFont bitcoinAddressFont()
{
    QFont font("Monospace");
    font.setStyleHint(QFont::TypeWriter);
    return font;
}

void setupAddressWidget(QLineEdit *widget, QWidget *parent)
{
    widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
    widget->setValidator(new BitcoinAddressValidator(parent));
    widget->setFont(bitcoinAddressFont());
}

void setupAmountWidget(QLineEdit *widget, QWidget *parent)
{
    QDoubleValidator *amountValidator = new QDoubleValidator(parent);
    amountValidator->setDecimals(8);
    amountValidator->setBottom(0.0);
    widget->setValidator(amountValidator);
    widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
}

bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
    if(uri.scheme() != QString("bitcoin"))
        return false;

    SendCoinsRecipient rv;
    rv.address = uri.path();
    rv.amount = 0;
    QList<QPair<QString, QString> > items = uri.queryItems();
    for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
    {
        bool fShouldReturnFalse = false;
        if (i->first.startsWith("req-"))
        {
            i->first.remove(0, 4);
            fShouldReturnFalse = true;
        }

        if (i->first == "label")
        {
            rv.label = i->second;
            fShouldReturnFalse = false;
        }
        else if (i->first == "amount")
        {
            if(!i->second.isEmpty())
            {
                if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
                {
                    return false;
                }
            }
            fShouldReturnFalse = false;
        }

        if (fShouldReturnFalse)
            return false;
    }
    if(out)
    {
        *out = rv;
    }
    return true;
}

bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
{
    // Convert bitcoin:// to bitcoin:
    //
    //    Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
    //    which will lowercase it (and thus invalidate the address).
    if(uri.startsWith("bitcoin://"))
    {
        uri.replace(0, 10, "bitcoin:");
    }
    QUrl uriInstance(uri);
    return parseBitcoinURI(uriInstance, out);
}

QString HtmlEscape(const QString& str, bool fMultiLine)
{
    QString escaped = Qt::escape(str);
    if(fMultiLine)
    {
        escaped = escaped.replace("\n", "
\n");
    }
    return escaped;
}

QString HtmlEscape(const std::string& str, bool fMultiLine)
{
    return HtmlEscape(QString::fromStdString(str), fMultiLine);
}

void copyEntryData(QAbstractItemView *view, int column, int role)
{
    if(!view || !view->selectionModel())
        return;
    QModelIndexList selection = view->selectionModel()->selectedRows(column);

    if(!selection.isEmpty())
    {
        // Copy first item
        QApplication::clipboard()->setText(selection.at(0).data(role).toString());
    }
}

QString getSaveFileName(QWidget *parent, const QString &caption,
                                 const QString &dir,
                                 const QString &filter,
                                 QString *selectedSuffixOut)
{
    QString selectedFilter;
    QString myDir;
    if(dir.isEmpty()) // Default to user documents location
    {
        myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
    }
    else
    {
        myDir = dir;
    }
    QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);

    /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
    QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
    QString selectedSuffix;
    if(filter_re.exactMatch(selectedFilter))
    {
        selectedSuffix = filter_re.cap(1);
    }

    /* Add suffix if needed */
    QFileInfo info(result);
    if(!result.isEmpty())
    {
        if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
        {
            /* No suffix specified, add selected suffix */
            if(!result.endsWith("."))
                result.append(".");
            result.append(selectedSuffix);
        }
    }

    /* Return selected suffix if asked to */
    if(selectedSuffixOut)
    {
        *selectedSuffixOut = selectedSuffix;
    }
    return result;
}

Qt::ConnectionType blockingGUIThreadConnection()
{
    if(QThread::currentThread() != QCoreApplication::instance()->thread())
    {
        return Qt::BlockingQueuedConnection;
    }
    else
    {
        return Qt::DirectConnection;
    }
}

bool checkPoint(const QPoint &p, const QWidget *w)
{
  QWidget *atW = qApp->widgetAt(w->mapToGlobal(p));
  if(!atW) return false;
  return atW->topLevelWidget() == w;
}

bool isObscured(QWidget *w)
{

  return !(checkPoint(QPoint(0, 0), w)
           && checkPoint(QPoint(w->width() - 1, 0), w)
           && checkPoint(QPoint(0, w->height() - 1), w)
           && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
           && checkPoint(QPoint(w->width()/2, w->height()/2), w));
}

void openDebugLogfile()
{
    boost::filesystem::path pathDebug = GetDataDir() / "debug.log";

#ifdef WIN32
    if (boost::filesystem::exists(pathDebug))
        /* Open debug.log with the associated application */
        ShellExecuteA((HWND)0, (LPCSTR)"open", (LPCSTR)pathDebug.string().c_str(), NULL, NULL, SW_SHOWNORMAL);
#endif
}

ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
    QObject(parent), size_threshold(size_threshold)
{

}

bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
{
    if(evt->type() == QEvent::ToolTipChange)
    {
        QWidget *widget = static_cast<QWidget*>(obj);
        QString tooltip = widget->toolTip();
        if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt/>") && !Qt::mightBeRichText(tooltip))
        {
            // Prefix <qt/> to make sure Qt detects this as rich text
            // Escape the current message as HTML and replace \n by

            tooltip = "<qt/>" + HtmlEscape(tooltip, true);
            widget->setToolTip(tooltip);
            return true;
        }
    }
    return QObject::eventFilter(obj, evt);
}

} // namespace GUIUtil

<|endoftext|>


<commit_before>#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "util.h"
#include "init.h"

#include <QString>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
#include <QLineEdit>
#include <QUrl>
#include <QTextDocument> // For Qt::escape
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QDesktopServices>
#include <QThread>

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "shlwapi.h"
#include "shlobj.h"
#include "shellapi.h"
#endif

namespace GUIUtil {

QString dateTimeStr(const QDateTime &date)
{
    return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
}

QString dateTimeStr(qint64 nTime)
{
    return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}

QFont bitcoinAddressFont()
{
    QFont font("Monospace");
    font.setStyleHint(QFont::TypeWriter);
    return font;
}

void setupAddressWidget(QLineEdit *widget, QWidget *parent)
{
    widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
    widget->setValidator(new BitcoinAddressValidator(parent));
    widget->setFont(bitcoinAddressFont());
}

void setupAmountWidget(QLineEdit *widget, QWidget *parent)
{
    QDoubleValidator *amountValidator = new QDoubleValidator(parent);
    amountValidator->setDecimals(8);
    amountValidator->setBottom(0.0);
    widget->setValidator(amountValidator);
    widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
}

bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
    if(uri.scheme() != QString("bitcoin"))
        return false;

    SendCoinsRecipient rv;
    rv.address = uri.path();
    rv.amount = 0;
    QList<QPair<QString, QString> > items = uri.queryItems();
    for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
    {
        bool fShouldReturnFalse = false;
        if (i->first.startsWith("req-"))
        {
            i->first.remove(0, 4);
            fShouldReturnFalse = true;
        }

        if (i->first == "label")
        {
            rv.label = i->second;
            fShouldReturnFalse = false;
        }
        else if (i->first == "amount")
        {
            if(!i->second.isEmpty())
            {
                if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
                {
                    return false;
                }
            }
            fShouldReturnFalse = false;
        }

        if (fShouldReturnFalse)
            return false;
    }
    if(out)
    {
        *out = rv;
    }
    return true;
}

bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
{
    // Convert bitcoin:// to bitcoin:
    //
    //    Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
    //    which will lower-case it (and thus invalidate the address).
    if(uri.startsWith("bitcoin://"))
    {
        uri.replace(0, 10, "bitcoin:");
    }
    QUrl uriInstance(uri);
    return parseBitcoinURI(uriInstance, out);
}

QString HtmlEscape(const QString& str, bool fMultiLine)
{
    QString escaped = Qt::escape(str);
    if(fMultiLine)
    {
        escaped = escaped.replace("\n", "
\n");
    }
    return escaped;
}

QString HtmlEscape(const std::string& str, bool fMultiLine)
{
    return HtmlEscape(QString::fromStdString(str), fMultiLine);
}

void copyEntryData(QAbstractItemView *view, int column, int role)
{
    if(!view || !view->selectionModel())
        return;
    QModelIndexList selection = view->selectionModel()->selectedRows(column);

    if(!selection.isEmpty())
    {
        // Copy first item
        QApplication::clipboard()->setText(selection.at(0).data(role).toString());
    }
}

QString getSaveFileName(QWidget *parent, const QString &caption,
                                 const QString &dir,
                                 const QString &filter,
                                 QString *selectedSuffixOut)
{
    QString selectedFilter;
    QString myDir;
    if(dir.isEmpty()) // Default to user documents location
    {
        myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
    }
    else
    {
        myDir = dir;
    }
    QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);

    /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
    QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
    QString selectedSuffix;
    if(filter_re.exactMatch(selectedFilter))
    {
        selectedSuffix = filter_re.cap(1);
    }

    /* Add suffix if needed */
    QFileInfo info(result);
    if(!result.isEmpty())
    {
        if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
        {
            /* No suffix specified, add selected suffix */
            if(!result.endsWith("."))
                result.append(".");
            result.append(selectedSuffix);
        }
    }

    /* Return selected suffix if asked to */
    if(selectedSuffixOut)
    {
        *selectedSuffixOut = selectedSuffix;
    }
    return result;
}

Qt::ConnectionType blockingGUIThreadConnection()
{
    if(QThread::currentThread() != QCoreApplication::instance()->thread())
    {
        return Qt::BlockingQueuedConnection;
    }
    else
    {
        return Qt::DirectConnection;
    }
}

bool checkPoint(const QPoint &p, const QWidget *w)
{
    QWidget *atW = qApp->widgetAt(w->mapToGlobal(p));
    if (!atW) return false;
    return atW->topLevelWidget() == w;
}

bool isObscured(QWidget *w)
{
    return !(checkPoint(QPoint(0, 0), w)
        && checkPoint(QPoint(w->width() - 1, 0), w)
        && checkPoint(QPoint(0, w->height() - 1), w)
        && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
        && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
}

void openDebugLogfile()
{
    boost::filesystem::path pathDebug = GetDataDir() / "debug.log";

    /* Open debug.log with the associated application */
    if (boost::filesystem::exists(pathDebug))
        QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string())));
}

ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
    QObject(parent), size_threshold(size_threshold)
{

}

bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
{
    if(evt->type() == QEvent::ToolTipChange)
    {
        QWidget *widget = static_cast<QWidget*>(obj);
        QString tooltip = widget->toolTip();
        if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt/>") && !Qt::mightBeRichText(tooltip))
        {
            // Prefix <qt/> to make sure Qt detects this as rich text
            // Escape the current message as HTML and replace \n by

            tooltip = "<qt/>" + HtmlEscape(tooltip, true);
            widget->setToolTip(tooltip);
            return true;
        }
    }
    return QObject::eventFilter(obj, evt);
}

#ifdef WIN32
boost::filesystem::path static StartupShortcutPath()
{
    return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
}

bool GetStartOnSystemStartup()
{
    // check for Bitcoin.lnk
    return boost::filesystem::exists(StartupShortcutPath());
}

bool SetStartOnSystemStartup(bool fAutoStart)
{
    // If the shortcut exists already, remove it for updating
    boost::filesystem::remove(StartupShortcutPath());

    if (fAutoStart)
    {
        CoInitialize(NULL);

        // Get a pointer to the IShellLink interface.
        IShellLink* psl = NULL;
        HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
                                CLSCTX_INPROC_SERVER, IID_IShellLink,
                                reinterpret_cast<void**>(&psl));

        if (SUCCEEDED(hres))
        {
            // Get the current executable path
            TCHAR pszExePath[MAX_PATH];
            GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));

            TCHAR pszArgs[5] = TEXT("-min");

            // Set the path to the shortcut target
            psl->SetPath(pszExePath);
            PathRemoveFileSpec(pszExePath);
            psl->SetWorkingDirectory(pszExePath);
            psl->SetShowCmd(SW_SHOWMINNOACTIVE);
            psl->SetArguments(pszArgs);

            // Query IShellLink for the IPersistFile interface for
            // saving the shortcut in persistent storage.
            IPersistFile* ppf = NULL;
            hres = psl->QueryInterface(IID_IPersistFile,
                                       reinterpret_cast<void**>(&ppf));
            if (SUCCEEDED(hres))
            {
                WCHAR pwsz[MAX_PATH];
                // Ensure that the string is ANSI.
                MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
                // Save the link by calling IPersistFile::Save.
                hres = ppf->Save(pwsz, TRUE);
                ppf->Release();
                psl->Release();
                CoUninitialize();
                return true;
            }
            psl->Release();
        }
        CoUninitialize();
        return false;
    }
    return true;
}

#elif defined(LINUX)

// Follow the Desktop Application Autostart Spec:
//  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html

boost::filesystem::path static GetAutostartDir()
{
    namespace fs = boost::filesystem;

    char* pszConfigHome = getenv("XDG_CONFIG_HOME");
    if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
    char* pszHome = getenv("HOME");
    if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
    return fs::path();
}

boost::filesystem::path static GetAutostartFilePath()
{
    return GetAutostartDir() / "bitcoin.desktop";
}

bool GetStartOnSystemStartup()
{
    boost::filesystem::ifstream optionFile(GetAutostartFilePath());
    if (!optionFile.good())
        return false;
    // Scan through file for "Hidden=true":
    std::string line;
    while (!optionFile.eof())
    {
        getline(optionFile, line);
        if (line.find("Hidden") != std::string::npos &&
            line.find("true") != std::string::npos)
            return false;
    }
    optionFile.close();

    return true;
}

bool SetStartOnSystemStartup(bool fAutoStart)
{
    if (!fAutoStart)
        boost::filesystem::remove(GetAutostartFilePath());
    else
    {
        char pszExePath[MAX_PATH+1];
        memset(pszExePath, 0, sizeof(pszExePath));
        if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
            return false;

        boost::filesystem::create_directories(GetAutostartDir());

        boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
        if (!optionFile.good())
            return false;
        // Write a bitcoin.desktop file to the autostart directory:
        optionFile << "[Desktop Entry]\n";
        optionFile << "Type=Application\n";
        optionFile << "Name=Bitcoin\n";
        optionFile << "Exec=" << pszExePath << " -min\n";
        optionFile << "Terminal=false\n";
        optionFile << "Hidden=false\n";
        optionFile.close();
    }
    return true;
}
#else

// TODO: OSX startup stuff; see:
// https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html

bool GetStartOnSystemStartup() { return false; }
bool SetStartOnSystemStartup(bool fAutoStart) { return false; }

#endif

HelpMessageBox::HelpMessageBox(QWidget *parent) :
    QMessageBox(parent)
{
    header = tr("Bitcoin-Qt") + " " + tr("version") + " " +
        QString::fromStdString(FormatFullVersion()) + "\n\n" +
        tr("Usage:") + "\n" +
        "  bitcoin-qt [" + tr("command-line options") + "]                     " + "\n";

    coreOptions = QString::fromStdString(HelpMessage());

    uiOptions = tr("UI options") + ":\n" +
        "  -lang=<lang>           " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
        "  -min                   " + tr("Start minimized") + "\n" +
        "  -splash                " + tr("Show splash screen on startup (default: 1)") + "\n";

    setWindowTitle(tr("Bitcoin-Qt"));
    setTextFormat(Qt::PlainText);
    // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider.
    setText(header + QString(QChar(0x2003)).repeated(50));
    setDetailedText(coreOptions + "\n" + uiOptions);
}

void HelpMessageBox::printToConsole()
{
    // On other operating systems, the expected action is to print the message to the console.
    QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions;
    fprintf(stdout, "%s", strUsage.toStdString().c_str());
}

void HelpMessageBox::showOrPrint()
{
#if defined(WIN32)
        // On Windows, show a message box, as there is no stderr/stdout in windowed applications
        exec();
#else
        // On other operating systems, print help text to console
        printToConsole();
#endif
}

} // namespace GUIUtil

<commit_msg>Bitcoin-Qt: extend parseBitcoinURI() pre-check<commit_after>#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "util.h"
#include "init.h"

#include <QString>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
#include <QLineEdit>
#include <QUrl>
#include <QTextDocument> // For Qt::escape
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QDesktopServices>
#include <QThread>

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "shlwapi.h"
#include "shlobj.h"
#include "shellapi.h"
#endif

namespace GUIUtil {

QString dateTimeStr(const QDateTime &date)
{
    return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
}

QString dateTimeStr(qint64 nTime)
{
    return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}

QFont bitcoinAddressFont()
{
    QFont font("Monospace");
    font.setStyleHint(QFont::TypeWriter);
    return font;
}

void setupAddressWidget(QLineEdit *widget, QWidget *parent)
{
    widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
    widget->setValidator(new BitcoinAddressValidator(parent));
    widget->setFont(bitcoinAddressFont());
}

void setupAmountWidget(QLineEdit *widget, QWidget *parent)
{
    QDoubleValidator *amountValidator = new QDoubleValidator(parent);
    amountValidator->setDecimals(8);
    amountValidator->setBottom(0.0);
    widget->setValidator(amountValidator);
    widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
}

bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
    // return if URI is not valid or is no bitcoin URI
    if(!uri.isValid() || uri.scheme() != QString("bitcoin"))
        return false;

    SendCoinsRecipient rv;
    rv.address = uri.path();
    rv.amount = 0;
    QList<QPair<QString, QString> > items = uri.queryItems();
    for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
    {
        bool fShouldReturnFalse = false;
        if (i->first.startsWith("req-"))
        {
            i->first.remove(0, 4);
            fShouldReturnFalse = true;
        }

        if (i->first == "label")
        {
            rv.label = i->second;
            fShouldReturnFalse = false;
        }
        else if (i->first == "amount")
        {
            if(!i->second.isEmpty())
            {
                if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
                {
                    return false;
                }
            }
            fShouldReturnFalse = false;
        }

        if (fShouldReturnFalse)
            return false;
    }
    if(out)
    {
        *out = rv;
    }
    return true;
}

bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
{
    // Convert bitcoin:// to bitcoin:
    //
    //    Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
    //    which will lower-case it (and thus invalidate the address).
    if(uri.startsWith("bitcoin://"))
    {
        uri.replace(0, 10, "bitcoin:");
    }
    QUrl uriInstance(uri);
    return parseBitcoinURI(uriInstance, out);
}

QString HtmlEscape(const QString& str, bool fMultiLine)
{
    QString escaped = Qt::escape(str);
    if(fMultiLine)
    {
        escaped = escaped.replace("\n", "
\n");
    }
    return escaped;
}

QString HtmlEscape(const std::string& str, bool fMultiLine)
{
    return HtmlEscape(QString::fromStdString(str), fMultiLine);
}

void copyEntryData(QAbstractItemView *view, int column, int role)
{
    if(!view || !view->selectionModel())
        return;
    QModelIndexList selection = view->selectionModel()->selectedRows(column);

    if(!selection.isEmpty())
    {
        // Copy first item
        QApplication::clipboard()->setText(selection.at(0).data(role).toString());
    }
}

QString getSaveFileName(QWidget *parent, const QString &caption,
                                 const QString &dir,
                                 const QString &filter,
                                 QString *selectedSuffixOut)
{
    QString selectedFilter;
    QString myDir;
    if(dir.isEmpty()) // Default to user documents location
    {
        myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
    }
    else
    {
        myDir = dir;
    }
    QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);

    /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
    QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
    QString selectedSuffix;
    if(filter_re.exactMatch(selectedFilter))
    {
        selectedSuffix = filter_re.cap(1);
    }

    /* Add suffix if needed */
    QFileInfo info(result);
    if(!result.isEmpty())
    {
        if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
        {
            /* No suffix specified, add selected suffix */
            if(!result.endsWith("."))
                result.append(".");
            result.append(selectedSuffix);
        }
    }

    /* Return selected suffix if asked to */
    if(selectedSuffixOut)
    {
        *selectedSuffixOut = selectedSuffix;
    }
    return result;
}

Qt::ConnectionType blockingGUIThreadConnection()
{
    if(QThread::currentThread() != QCoreApplication::instance()->thread())
    {
        return Qt::BlockingQueuedConnection;
    }
    else
    {
        return Qt::DirectConnection;
    }
}

bool checkPoint(const QPoint &p, const QWidget *w)
{
    QWidget *atW = qApp->widgetAt(w->mapToGlobal(p));
    if (!atW) return false;
    return atW->topLevelWidget() == w;
}

bool isObscured(QWidget *w)
{
    return !(checkPoint(QPoint(0, 0), w)
        && checkPoint(QPoint(w->width() - 1, 0), w)
        && checkPoint(QPoint(0, w->height() - 1), w)
        && checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
        && checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
}

void openDebugLogfile()
{
    boost::filesystem::path pathDebug = GetDataDir() / "debug.log";

    /* Open debug.log with the associated application */
    if (boost::filesystem::exists(pathDebug))
        QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string())));
}

ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
    QObject(parent), size_threshold(size_threshold)
{

}

bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
{
    if(evt->type() == QEvent::ToolTipChange)
    {
        QWidget *widget = static_cast<QWidget*>(obj);
        QString tooltip = widget->toolTip();
        if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt/>") && !Qt::mightBeRichText(tooltip))
        {
            // Prefix <qt/> to make sure Qt detects this as rich text
            // Escape the current message as HTML and replace \n by

            tooltip = "<qt/>" + HtmlEscape(tooltip, true);
            widget->setToolTip(tooltip);
            return true;
        }
    }
    return QObject::eventFilter(obj, evt);
}

#ifdef WIN32
boost::filesystem::path static StartupShortcutPath()
{
    return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk";
}

bool GetStartOnSystemStartup()
{
    // check for Bitcoin.lnk
    return boost::filesystem::exists(StartupShortcutPath());
}

bool SetStartOnSystemStartup(bool fAutoStart)
{
    // If the shortcut exists already, remove it for updating
    boost::filesystem::remove(StartupShortcutPath());

    if (fAutoStart)
    {
        CoInitialize(NULL);

        // Get a pointer to the IShellLink interface.
        IShellLink* psl = NULL;
        HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
                                CLSCTX_INPROC_SERVER, IID_IShellLink,
                                reinterpret_cast<void**>(&psl));

        if (SUCCEEDED(hres))
        {
            // Get the current executable path
            TCHAR pszExePath[MAX_PATH];
            GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));

            TCHAR pszArgs[5] = TEXT("-min");

            // Set the path to the shortcut target
            psl->SetPath(pszExePath);
            PathRemoveFileSpec(pszExePath);
            psl->SetWorkingDirectory(pszExePath);
            psl->SetShowCmd(SW_SHOWMINNOACTIVE);
            psl->SetArguments(pszArgs);

            // Query IShellLink for the IPersistFile interface for
            // saving the shortcut in persistent storage.
            IPersistFile* ppf = NULL;
            hres = psl->QueryInterface(IID_IPersistFile,
                                       reinterpret_cast<void**>(&ppf));
            if (SUCCEEDED(hres))
            {
                WCHAR pwsz[MAX_PATH];
                // Ensure that the string is ANSI.
                MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
                // Save the link by calling IPersistFile::Save.
                hres = ppf->Save(pwsz, TRUE);
                ppf->Release();
                psl->Release();
                CoUninitialize();
                return true;
            }
            psl->Release();
        }
        CoUninitialize();
        return false;
    }
    return true;
}

#elif defined(LINUX)

// Follow the Desktop Application Autostart Spec:
//  http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html

boost::filesystem::path static GetAutostartDir()
{
    namespace fs = boost::filesystem;

    char* pszConfigHome = getenv("XDG_CONFIG_HOME");
    if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
    char* pszHome = getenv("HOME");
    if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
    return fs::path();
}

boost::filesystem::path static GetAutostartFilePath()
{
    return GetAutostartDir() / "bitcoin.desktop";
}

bool GetStartOnSystemStartup()
{
    boost::filesystem::ifstream optionFile(GetAutostartFilePath());
    if (!optionFile.good())
        return false;
    // Scan through file for "Hidden=true":
    std::string line;
    while (!optionFile.eof())
    {
        getline(optionFile, line);
        if (line.find("Hidden") != std::string::npos &&
            line.find("true") != std::string::npos)
            return false;
    }
    optionFile.close();

    return true;
}

bool SetStartOnSystemStartup(bool fAutoStart)
{
    if (!fAutoStart)
        boost::filesystem::remove(GetAutostartFilePath());
    else
    {
        char pszExePath[MAX_PATH+1];
        memset(pszExePath, 0, sizeof(pszExePath));
        if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
            return false;

        boost::filesystem::create_directories(GetAutostartDir());

        boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
        if (!optionFile.good())
            return false;
        // Write a bitcoin.desktop file to the autostart directory:
        optionFile << "[Desktop Entry]\n";
        optionFile << "Type=Application\n";
        optionFile << "Name=Bitcoin\n";
        optionFile << "Exec=" << pszExePath << " -min\n";
        optionFile << "Terminal=false\n";
        optionFile << "Hidden=false\n";
        optionFile.close();
    }
    return true;
}
#else

// TODO: OSX startup stuff; see:
// https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html

bool GetStartOnSystemStartup() { return false; }
bool SetStartOnSystemStartup(bool fAutoStart) { return false; }

#endif

HelpMessageBox::HelpMessageBox(QWidget *parent) :
    QMessageBox(parent)
{
    header = tr("Bitcoin-Qt") + " " + tr("version") + " " +
        QString::fromStdString(FormatFullVersion()) + "\n\n" +
        tr("Usage:") + "\n" +
        "  bitcoin-qt [" + tr("command-line options") + "]                     " + "\n";

    coreOptions = QString::fromStdString(HelpMessage());

    uiOptions = tr("UI options") + ":\n" +
        "  -lang=<lang>           " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
        "  -min                   " + tr("Start minimized") + "\n" +
        "  -splash                " + tr("Show splash screen on startup (default: 1)") + "\n";

    setWindowTitle(tr("Bitcoin-Qt"));
    setTextFormat(Qt::PlainText);
    // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider.
    setText(header + QString(QChar(0x2003)).repeated(50));
    setDetailedText(coreOptions + "\n" + uiOptions);
}

void HelpMessageBox::printToConsole()
{
    // On other operating systems, the expected action is to print the message to the console.
    QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions;
    fprintf(stdout, "%s", strUsage.toStdString().c_str());
}

void HelpMessageBox::showOrPrint()
{
#if defined(WIN32)
        // On Windows, show a message box, as there is no stderr/stdout in windowed applications
        exec();
#else
        // On other operating systems, print help text to console
        printToConsole();
#endif
}

} // namespace GUIUtil

<|endoftext|>


<commit_before>/*ckwg +5
 * Copyright 2012-2013 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "read_video_gravl_process.h"

#include <processes/helpers/image/format.h>
#include <processes/helpers/image/read.h>

#include <vistk/pipeline/config.h>
#include <vistk/pipeline/datum.h>
#include <vistk/pipeline/process_exception.h>

#include <gravl/core/api/data_block.h>
#include <gravl/core/api/frame_ptr.h>
#include <gravl/core/api/image.h>
#include <gravl/core/api/image_data.h>
#include <gravl/core/api/resource_ptr.h>

#include <gravl/raf/raf.h>

#include <vil/vil_image_view.h>

#include <boost/bind.hpp>

#include <string>

/**
 * \file read_video_gravl_process.cxx
 *
 * \brief Implementation of the read video gravl process.
 */

namespace vistk
{

class read_video_gravl_process::priv
{
  public:
    priv(std::string const& input_uri);
    ~priv();

    std::string const uri;

    gravl::resource_ptr resource;
    gravl::data_block* video;

    boost::function<datum_t (gravl::data_block*)> convert_func;

    static config::key_t const config_pixtype;
    static config::key_t const config_pixfmt;
    static config::key_t const config_uri;
    static config::key_t const config_verify;
    static config::value_t const default_pixtype;
    static config::value_t const default_pixfmt;
    static config::value_t const default_verify;
    static port_t const port_output;
};

config::key_t const read_video_gravl_process::priv::config_pixtype = config::key_t("pixtype");
config::key_t const read_video_gravl_process::priv::config_pixfmt = config::key_t("pixfmt");
config::key_t const read_video_gravl_process::priv::config_uri = config::key_t("input");
config::value_t const read_video_gravl_process::priv::default_pixtype = config::value_t(pixtypes::pixtype_byte());
config::value_t const read_video_gravl_process::priv::default_pixfmt = config::value_t(pixfmts::pixfmt_rgb());
process::port_t const read_video_gravl_process::priv::port_output = port_t("image");

read_video_gravl_process
::read_video_gravl_process(config_t const& config)
  : process(config)
{
  declare_configuration_key(
    priv::config_pixtype,
    priv::default_pixtype,
    config::description_t("The pixel type of the input images."));
  declare_configuration_key(
    priv::config_pixfmt,
    priv::default_pixfmt,
    config::description_t("The pixel format of the input images."));
  declare_configuration_key(
    priv::config_uri,
    config::value_t(),
    config::description_t("The URI of the resource."));

  pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
  pixfmt_t const pixfmt = config_value<pixfmt_t>(priv::config_pixfmt);

  port_type_t const port_type_output = port_type_for_pixtype(pixtype, pixfmt);

  port_flags_t required;

  required.insert(flag_required);

  declare_output_port(
    priv::port_output,
    port_type_output,
    required,
    port_description_t("The images that are read in."));
}

read_video_gravl_process
::~read_video_gravl_process()
{
}

template <typename PixType>
static vistk::datum_t convert_image(
  gravl::data_block* video, vistk::pixtype_t const& pixtype,
  vil_pixel_format pixfmt);

void
read_video_gravl_process
::_configure()
{
  // Configure the process.
  {
    std::string const uri = config_value<std::string>(priv::config_uri);

    d.reset(new priv(uri));
  }

  if (d->uri.empty())
  {
    static std::string const reason = "The URI given was empty";
    config::value_t const value = config::value_t(d->uri);

    throw invalid_configuration_value_exception(name(), priv::config_uri, value, reason);
  }

  d->resource = gravl::raf::get_resource(d->uri.c_str());
  if (!d->resource)
  {
    std::string const reason = "Failed to open the resource: " + d->uri;

    throw invalid_configuration_exception(name(), reason);
  }

  d->video = d->resource.get_data<gravl::data_block>();
  if (!d->video)
  {
    static std::string const reason = "Failed to obtain data_block from resource";

    throw invalid_configuration_exception(name(), reason);
  }

  // Get the function to convert the gravl::image to a vil_image_view
  pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
  if (pixtype == pixtypes::pixtype_byte()) // uint8_t
  {
    d->convert_func = boost::bind(&convert_image<uint8_t>, _1, pixtype, VIL_PIXEL_FORMAT_BYTE);
  }
  /// \todo Add support for uint16, uint32 and uint64 images when supported by
  ///       vistk::pixtypes.
//   else if (pixtype == pixtypes::pixtype_short()) // uint16_t - TODO
//   {
//     d->convert_func = boost::bind(&convert_image<uint16_t>, _1, pixtype, VIL_PIXEL_FORMAT_INT_16);
//   }
//   else if (pixtype == pixtypes::pixtype_int()) // uint32_t - TODO
//   {
//     d->convert_func = boost::bind(&convert_image<uint32_t>, _1, pixtype, VIL_PIXEL_FORMAT_INT_32);
//   }
//   else if (pixtype == pixtypes::pixtype_long()) // uint64_t - TODO
//   {
//     d->convert_func = boost::bind(&convert_image<uint64_t>, _1, pixtype, VIL_PIXEL_FORMAT_INT_64);
//   }
  else if (pixtype == pixtypes::pixtype_float()) // float
  {
    d->convert_func = boost::bind(&convert_image<float>, _1, pixtype, VIL_PIXEL_FORMAT_FLOAT);
  }
  else if (pixtype == pixtypes::pixtype_double()) // double
  {
    d->convert_func = boost::bind(&convert_image<double>, _1, pixtype, VIL_PIXEL_FORMAT_DOUBLE);
  }
  else
  {
    std::string const reason = "The pixtype \'" + pixtype + "\' "
                                "is not supported";
    throw invalid_configuration_exception(name(), reason);
  }

  process::_configure();
}

void
read_video_gravl_process
::_init()
{
  d->video->rewind();

  process::_init();
}

void
read_video_gravl_process
::_step()
{
  datum_t dat;

  if (d->video->at_end())
  {
    mark_process_as_complete();
    dat = datum::complete_datum();
  }
  else if (!d->video)
  {
    static datum::error_t const err_string = datum::error_t("Error with input file stream");

    dat = datum::error_datum(err_string);
  }
  else
  {
    dat = d->convert_func(d->video);
    d->video->advance();
  }

  push_datum_to_port(priv::port_output, dat);

  process::_step();
}

read_video_gravl_process::priv
::priv(std::string const& input_uri)
  : uri(input_uri)
{
}

read_video_gravl_process::priv
::~priv()
{
}

static size_t compute_block_size(
  gravl::image::dimension dim, gravl::image::stride s);
template <typename T>
static T* compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);
template <typename T>
static T* compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);

template <typename PixType>
datum_t
convert_image(
  gravl::data_block* video, pixtype_t const& pixtype,
  vil_pixel_format pixfmt)
{
  gravl::frame_ptr const frame = video->current_frame();
  gravl::image_data const* const image_data =
    frame.get_const_data<gravl::image_data>();
  gravl::image const image =
    (image_data ? image_data->pixels() : gravl::image());

  if (!image)
  {
    return datum::empty_datum();
  }
  else if (image.format() != gravl::image::format_of<PixType>())
  {
    std::stringstream reason;
    reason << "Unable to convert the image: the image pixel type "
           << image.format() << " does not match the port output type "
           << '\'' << pixtype << '\'';
    return datum::error_datum(reason.str());
  }
  else
  {
    gravl::image::dimension const dim = image.dimensions();
    gravl::image::stride const ps = image.strides();
    PixType const* const top_left = image.data<PixType>();
    size_t const size = compute_block_size(dim, ps);

    // vil_image_view is implicitly mutable, even though the ctors would
    // appear to suggest otherwise... so we must copy the pixels
    vil_memory_chunk* const mem = new vil_memory_chunk(size, pixfmt);
    PixType* const buffer = reinterpret_cast<PixType*>(mem->data());
    memcpy(buffer, compute_block_start(top_left, dim, ps), size);

    vil_image_view<PixType> const vil(vil_memory_chunk_sptr(mem),
                                      compute_top_left(buffer, dim, ps),
                                      dim.width, dim.height, dim.planes,
                                      ps.width, ps.height, ps.planes);
    return datum::new_datum(vil);
  }
}

static size_t sabs(ptrdiff_t a);

size_t
compute_block_size(gravl::image::dimension dim, gravl::image::stride s)
{
  return ((dim.width  - 1) * sabs(s.width)) +
         ((dim.height - 1) * sabs(s.height)) +
         ((dim.planes - 1) * sabs(s.planes)) + 1;
}

static ptrdiff_t compute_offset(
  gravl::image::dimension dim, gravl::image::stride s);

template <typename T>
T*
compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left - compute_offset(dim, s);
}

template <typename T>
T*
compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left + compute_offset(dim, s);
}

size_t
sabs(ptrdiff_t a)
{
  return static_cast<size_t>((a < 0 ? -a : a));
}

ptrdiff_t
compute_offset(gravl::image::dimension dim, gravl::image::stride s)
{
  ptrdiff_t result = 0;
  if (s.width < 0)
  {
    result -= s.width * (dim.width - 1);
  }
  if (s.height < 0)
  {
    result -= s.height * (dim.height - 1);
  }
  if (s.planes < 0)
  {
    result -= s.planes * (dim.planes - 1);
  }
  return result;
}

}
<commit_msg>Add note to improve error reporting<commit_after>/*ckwg +5
 * Copyright 2012-2013 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "read_video_gravl_process.h"

#include <processes/helpers/image/format.h>
#include <processes/helpers/image/read.h>

#include <vistk/pipeline/config.h>
#include <vistk/pipeline/datum.h>
#include <vistk/pipeline/process_exception.h>

#include <gravl/core/api/data_block.h>
#include <gravl/core/api/frame_ptr.h>
#include <gravl/core/api/image.h>
#include <gravl/core/api/image_data.h>
#include <gravl/core/api/resource_ptr.h>

#include <gravl/raf/raf.h>

#include <vil/vil_image_view.h>

#include <boost/bind.hpp>

#include <string>

/**
 * \file read_video_gravl_process.cxx
 *
 * \brief Implementation of the read video gravl process.
 */

namespace vistk
{

class read_video_gravl_process::priv
{
  public:
    priv(std::string const& input_uri);
    ~priv();

    std::string const uri;

    gravl::resource_ptr resource;
    gravl::data_block* video;

    boost::function<datum_t (gravl::data_block*)> convert_func;

    static config::key_t const config_pixtype;
    static config::key_t const config_pixfmt;
    static config::key_t const config_uri;
    static config::key_t const config_verify;
    static config::value_t const default_pixtype;
    static config::value_t const default_pixfmt;
    static config::value_t const default_verify;
    static port_t const port_output;
};

config::key_t const read_video_gravl_process::priv::config_pixtype = config::key_t("pixtype");
config::key_t const read_video_gravl_process::priv::config_pixfmt = config::key_t("pixfmt");
config::key_t const read_video_gravl_process::priv::config_uri = config::key_t("input");
config::value_t const read_video_gravl_process::priv::default_pixtype = config::value_t(pixtypes::pixtype_byte());
config::value_t const read_video_gravl_process::priv::default_pixfmt = config::value_t(pixfmts::pixfmt_rgb());
process::port_t const read_video_gravl_process::priv::port_output = port_t("image");

read_video_gravl_process
::read_video_gravl_process(config_t const& config)
  : process(config)
{
  declare_configuration_key(
    priv::config_pixtype,
    priv::default_pixtype,
    config::description_t("The pixel type of the input images."));
  declare_configuration_key(
    priv::config_pixfmt,
    priv::default_pixfmt,
    config::description_t("The pixel format of the input images."));
  declare_configuration_key(
    priv::config_uri,
    config::value_t(),
    config::description_t("The URI of the resource."));

  pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
  pixfmt_t const pixfmt = config_value<pixfmt_t>(priv::config_pixfmt);

  port_type_t const port_type_output = port_type_for_pixtype(pixtype, pixfmt);

  port_flags_t required;

  required.insert(flag_required);

  declare_output_port(
    priv::port_output,
    port_type_output,
    required,
    port_description_t("The images that are read in."));
}

read_video_gravl_process
::~read_video_gravl_process()
{
}

template <typename PixType>
static vistk::datum_t convert_image(
  gravl::data_block* video, vistk::pixtype_t const& pixtype,
  vil_pixel_format pixfmt);

void
read_video_gravl_process
::_configure()
{
  // Configure the process.
  {
    std::string const uri = config_value<std::string>(priv::config_uri);

    d.reset(new priv(uri));
  }

  if (d->uri.empty())
  {
    static std::string const reason = "The URI given was empty";
    config::value_t const value = config::value_t(d->uri);

    throw invalid_configuration_value_exception(name(), priv::config_uri, value, reason);
  }

  d->resource = gravl::raf::get_resource(d->uri.c_str());
  if (!d->resource)
  {
    /// \todo Use an error handler to try to extract more useful information
    ///       about why we were not able to obtain a resource.
    std::string const reason = "Failed to open the resource: " + d->uri;

    throw invalid_configuration_exception(name(), reason);
  }

  d->video = d->resource.get_data<gravl::data_block>();
  if (!d->video)
  {
    static std::string const reason = "Failed to obtain data_block from resource";

    throw invalid_configuration_exception(name(), reason);
  }

  // Get the function to convert the gravl::image to a vil_image_view
  pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
  if (pixtype == pixtypes::pixtype_byte()) // uint8_t
  {
    d->convert_func = boost::bind(&convert_image<uint8_t>, _1, pixtype, VIL_PIXEL_FORMAT_BYTE);
  }
  /// \todo Add support for uint16, uint32 and uint64 images when supported by
  ///       vistk::pixtypes.
//   else if (pixtype == pixtypes::pixtype_short()) // uint16_t - TODO
//   {
//     d->convert_func = boost::bind(&convert_image<uint16_t>, _1, pixtype, VIL_PIXEL_FORMAT_INT_16);
//   }
//   else if (pixtype == pixtypes::pixtype_int()) // uint32_t - TODO
//   {
//     d->convert_func = boost::bind(&convert_image<uint32_t>, _1, pixtype, VIL_PIXEL_FORMAT_INT_32);
//   }
//   else if (pixtype == pixtypes::pixtype_long()) // uint64_t - TODO
//   {
//     d->convert_func = boost::bind(&convert_image<uint64_t>, _1, pixtype, VIL_PIXEL_FORMAT_INT_64);
//   }
  else if (pixtype == pixtypes::pixtype_float()) // float
  {
    d->convert_func = boost::bind(&convert_image<float>, _1, pixtype, VIL_PIXEL_FORMAT_FLOAT);
  }
  else if (pixtype == pixtypes::pixtype_double()) // double
  {
    d->convert_func = boost::bind(&convert_image<double>, _1, pixtype, VIL_PIXEL_FORMAT_DOUBLE);
  }
  else
  {
    std::string const reason = "The pixtype \'" + pixtype + "\' "
                                "is not supported";
    throw invalid_configuration_exception(name(), reason);
  }

  process::_configure();
}

void
read_video_gravl_process
::_init()
{
  d->video->rewind();

  process::_init();
}

void
read_video_gravl_process
::_step()
{
  datum_t dat;

  if (d->video->at_end())
  {
    mark_process_as_complete();
    dat = datum::complete_datum();
  }
  else
  {
    dat = d->convert_func(d->video);
    d->video->advance();
  }

  push_datum_to_port(priv::port_output, dat);

  process::_step();
}

read_video_gravl_process::priv
::priv(std::string const& input_uri)
  : uri(input_uri)
{
}

read_video_gravl_process::priv
::~priv()
{
}

static size_t compute_block_size(
  gravl::image::dimension dim, gravl::image::stride s);
template <typename T>
static T* compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);
template <typename T>
static T* compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);

template <typename PixType>
datum_t
convert_image(
  gravl::data_block* video, pixtype_t const& pixtype,
  vil_pixel_format pixfmt)
{
  gravl::frame_ptr const frame = video->current_frame();
  gravl::image_data const* const image_data =
    frame.get_const_data<gravl::image_data>();
  gravl::image const image =
    (image_data ? image_data->pixels() : gravl::image());

  if (!image)
  {
    return datum::empty_datum();
  }
  else if (image.format() != gravl::image::format_of<PixType>())
  {
    std::stringstream reason;
    reason << "Unable to convert the image: the image pixel type "
           << image.format() << " does not match the port output type "
           << '\'' << pixtype << '\'';
    return datum::error_datum(reason.str());
  }
  else
  {
    gravl::image::dimension const dim = image.dimensions();
    gravl::image::stride const ps = image.strides();
    PixType const* const top_left = image.data<PixType>();
    size_t const size = compute_block_size(dim, ps);

    // vil_image_view is implicitly mutable, even though the ctors would
    // appear to suggest otherwise... so we must copy the pixels
    vil_memory_chunk* const mem = new vil_memory_chunk(size, pixfmt);
    PixType* const buffer = reinterpret_cast<PixType*>(mem->data());
    memcpy(buffer, compute_block_start(top_left, dim, ps), size);

    vil_image_view<PixType> const vil(vil_memory_chunk_sptr(mem),
                                      compute_top_left(buffer, dim, ps),
                                      dim.width, dim.height, dim.planes,
                                      ps.width, ps.height, ps.planes);
    return datum::new_datum(vil);
  }
}

static size_t sabs(ptrdiff_t a);

size_t
compute_block_size(gravl::image::dimension dim, gravl::image::stride s)
{
  return ((dim.width  - 1) * sabs(s.width)) +
         ((dim.height - 1) * sabs(s.height)) +
         ((dim.planes - 1) * sabs(s.planes)) + 1;
}

static ptrdiff_t compute_offset(
  gravl::image::dimension dim, gravl::image::stride s);

template <typename T>
T*
compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left - compute_offset(dim, s);
}

template <typename T>
T*
compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left + compute_offset(dim, s);
}

size_t
sabs(ptrdiff_t a)
{
  return static_cast<size_t>((a < 0 ? -a : a));
}

ptrdiff_t
compute_offset(gravl::image::dimension dim, gravl::image::stride s)
{
  ptrdiff_t result = 0;
  if (s.width < 0)
  {
    result -= s.width * (dim.width - 1);
  }
  if (s.height < 0)
  {
    result -= s.height * (dim.height - 1);
  }
  if (s.planes < 0)
  {
    result -= s.planes * (dim.planes - 1);
  }
  return result;
}

}
<|endoftext|>


<commit_before>/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#include <eosio/test_control_plugin/test_control_plugin.hpp>
#include <fc/optional.hpp>
#include <atomic>

namespace fc { class variant; }

namespace eosio {

static appbase::abstract_plugin& _test_control_plugin = app().register_plugin<test_control_plugin>();

class test_control_plugin_impl {
public:
   test_control_plugin_impl(chain::controller& c) : _chain(c) {}
   void connect();
   void disconnect();
   void kill_on_lib(account_name prod, uint32_t where_in_seq);
   void kill_on_head(account_name prod, uint32_t where_in_seq);

private:
   void accepted_block(const chain::block_state_ptr& bsp);
   void applied_irreversible_block(const chain::block_state_ptr& bsp);
   void retrieve_next_block_state(const chain::block_state_ptr& bsp);
   void process_next_block_state(const chain::block_header_state& bhs);

   fc::optional<boost::signals2::scoped_connection> _accepted_block_connection;
   fc::optional<boost::signals2::scoped_connection> _irreversible_block_connection;
   chain::controller&  _chain;
   account_name        _producer;
   int32_t             _where_in_sequence;
   int32_t             _producer_sequence;
   bool                _clean_producer_sequence;
   std::atomic_bool    _track_lib;
   std::atomic_bool    _track_head;
};

void test_control_plugin_impl::connect() {
   _irreversible_block_connection.emplace(
         _chain.irreversible_block.connect( [&]( const chain::block_state_ptr& bs ) {
            applied_irreversible_block( bs );
         } ));
   _accepted_block_connection =
         _chain.accepted_block.connect( [&]( const chain::block_state_ptr& bs ) {
            accepted_block( bs );
         } );
}

void test_control_plugin_impl::disconnect() {
   _accepted_block_connection.reset();
   _irreversible_block_connection.reset();
}

void test_control_plugin_impl::applied_irreversible_block(const chain::block_state_ptr& bsp) {
   if (_track_lib)
      retrieve_next_block_state(bsp);
}

void test_control_plugin_impl::accepted_block(const chain::block_state_ptr& bsp) {
   if (_track_head)
      retrieve_next_block_state(bsp);
}

void test_control_plugin_impl::retrieve_next_block_state(const chain::block_state_ptr& bsp) {
   const auto hbn = bsp->block_num;
   auto new_block_header = bsp->header;
   new_block_header.timestamp = new_block_header.timestamp.next();
   new_block_header.previous = bsp->id;
   auto new_bs = bsp->generate_next(new_block_header.timestamp);
   process_next_block_state(new_bs);
}

void test_control_plugin_impl::process_next_block_state(const chain::block_header_state& bhs) {
   const auto block_time = _chain.head_block_time() + fc::microseconds(chain::config::block_interval_us);
   const auto& producer_name = bhs.get_scheduled_producer(block_time).producer_name;
   // start counting sequences for this producer (once we
   if (producer_name == _producer && _clean_producer_sequence) {
      _producer_sequence += 1;

      if (_producer_sequence >= _where_in_sequence) {
         app().quit();
      }
   } else if (producer_name != _producer) {
      _producer_sequence = -1;
      // can now guarantee we are at the start of the producer
      _clean_producer_sequence = true;
   }
}

void test_control_plugin_impl::kill_on_lib(account_name prod, uint32_t where_in_seq) {
   _track_head = false;
   _producer = prod;
   _where_in_sequence = static_cast<uint32_t>(where_in_seq);
   _producer_sequence = -1;
   _clean_producer_sequence = false;
   _track_lib = true;
}

void test_control_plugin_impl::kill_on_head(account_name prod, uint32_t where_in_seq) {
   _track_lib = false;
   _producer = prod;
   _where_in_sequence = static_cast<uint32_t>(where_in_seq);
   _producer_sequence = -1;
   _clean_producer_sequence = false;
   _track_head = true;
}

test_control_plugin::test_control_plugin()
: my(new test_control_plugin_impl(app().get_plugin<chain_plugin>().chain()))
{
}

void test_control_plugin::set_program_options(options_description& cli, options_description& cfg) {
}

void test_control_plugin::plugin_initialize(const variables_map& options) {
}

void test_control_plugin::plugin_startup() {
   my->connect();
}

void test_control_plugin::plugin_shutdown() {
   my->disconnect();
}

namespace test_control_apis {
read_write::kill_node_on_producer_results read_write::kill_node_on_producer(const read_write::kill_node_on_producer_params& params) const {

   if (params.based_on_lib) {
      my->kill_on_lib(params.producer, params.where_in_sequence);
   } else {
      my->kill_on_head(params.producer, params.where_in_sequence);
   }
   return read_write::kill_node_on_producer_results{};
}

} // namespace test_control_apis

} // namespace eosio
<commit_msg>Moved call to chain_plugin till startup phase. GH #5653<commit_after>/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#include <eosio/test_control_plugin/test_control_plugin.hpp>
#include <fc/optional.hpp>
#include <atomic>

namespace fc { class variant; }

namespace eosio {

static appbase::abstract_plugin& _test_control_plugin = app().register_plugin<test_control_plugin>();

class test_control_plugin_impl {
public:
   test_control_plugin_impl(chain::controller& c) : _chain(c) {}
   void connect();
   void disconnect();
   void kill_on_lib(account_name prod, uint32_t where_in_seq);
   void kill_on_head(account_name prod, uint32_t where_in_seq);

private:
   void accepted_block(const chain::block_state_ptr& bsp);
   void applied_irreversible_block(const chain::block_state_ptr& bsp);
   void retrieve_next_block_state(const chain::block_state_ptr& bsp);
   void process_next_block_state(const chain::block_header_state& bhs);

   fc::optional<boost::signals2::scoped_connection> _accepted_block_connection;
   fc::optional<boost::signals2::scoped_connection> _irreversible_block_connection;
   chain::controller&  _chain;
   account_name        _producer;
   int32_t             _where_in_sequence;
   int32_t             _producer_sequence;
   bool                _clean_producer_sequence;
   std::atomic_bool    _track_lib;
   std::atomic_bool    _track_head;
};

void test_control_plugin_impl::connect() {
   _irreversible_block_connection.emplace(
         _chain.irreversible_block.connect( [&]( const chain::block_state_ptr& bs ) {
            applied_irreversible_block( bs );
         } ));
   _accepted_block_connection =
         _chain.accepted_block.connect( [&]( const chain::block_state_ptr& bs ) {
            accepted_block( bs );
         } );
}

void test_control_plugin_impl::disconnect() {
   _accepted_block_connection.reset();
   _irreversible_block_connection.reset();
}

void test_control_plugin_impl::applied_irreversible_block(const chain::block_state_ptr& bsp) {
   if (_track_lib)
      retrieve_next_block_state(bsp);
}

void test_control_plugin_impl::accepted_block(const chain::block_state_ptr& bsp) {
   if (_track_head)
      retrieve_next_block_state(bsp);
}

void test_control_plugin_impl::retrieve_next_block_state(const chain::block_state_ptr& bsp) {
   const auto hbn = bsp->block_num;
   auto new_block_header = bsp->header;
   new_block_header.timestamp = new_block_header.timestamp.next();
   new_block_header.previous = bsp->id;
   auto new_bs = bsp->generate_next(new_block_header.timestamp);
   process_next_block_state(new_bs);
}

void test_control_plugin_impl::process_next_block_state(const chain::block_header_state& bhs) {
   const auto block_time = _chain.head_block_time() + fc::microseconds(chain::config::block_interval_us);
   const auto& producer_name = bhs.get_scheduled_producer(block_time).producer_name;
   // start counting sequences for this producer (once we
   if (producer_name == _producer && _clean_producer_sequence) {
      _producer_sequence += 1;

      if (_producer_sequence >= _where_in_sequence) {
         app().quit();
      }
   } else if (producer_name != _producer) {
      _producer_sequence = -1;
      // can now guarantee we are at the start of the producer
      _clean_producer_sequence = true;
   }
}

void test_control_plugin_impl::kill_on_lib(account_name prod, uint32_t where_in_seq) {
   _track_head = false;
   _producer = prod;
   _where_in_sequence = static_cast<uint32_t>(where_in_seq);
   _producer_sequence = -1;
   _clean_producer_sequence = false;
   _track_lib = true;
}

void test_control_plugin_impl::kill_on_head(account_name prod, uint32_t where_in_seq) {
   _track_lib = false;
   _producer = prod;
   _where_in_sequence = static_cast<uint32_t>(where_in_seq);
   _producer_sequence = -1;
   _clean_producer_sequence = false;
   _track_head = true;
}

test_control_plugin::test_control_plugin()
{
}

void test_control_plugin::set_program_options(options_description& cli, options_description& cfg) {
}

void test_control_plugin::plugin_initialize(const variables_map& options) {
}

void test_control_plugin::plugin_startup() {
   my.reset(new test_control_plugin_impl(app().get_plugin<chain_plugin>().chain()));
   my->connect();
}

void test_control_plugin::plugin_shutdown() {
   my->disconnect();
}

namespace test_control_apis {
read_write::kill_node_on_producer_results read_write::kill_node_on_producer(const read_write::kill_node_on_producer_params& params) const {

   if (params.based_on_lib) {
      my->kill_on_lib(params.producer, params.where_in_sequence);
   } else {
      my->kill_on_head(params.producer, params.where_in_sequence);
   }
   return read_write::kill_node_on_producer_results{};
}

} // namespace test_control_apis

} // namespace eosio
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/profiling/memory/socket_listener.h"

#include "perfetto/base/scoped_file.h"
#include "src/base/test/test_task_runner.h"
#include "src/ipc/test/test_socket.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace perfetto {
namespace profiling {
namespace {

using ::testing::_;
using ::testing::InvokeWithoutArgs;

constexpr char kSocketName[] = TEST_SOCK_NAME("socket_listener_unittest");

class SocketListenerTest : public ::testing::Test {
 protected:
  void SetUp() override { DESTROY_TEST_SOCK(kSocketName); }
  void TearDown() override { DESTROY_TEST_SOCK(kSocketName); }
};

class MockEventListener : public base::UnixSocket::EventListener {
 public:
  MOCK_METHOD2(OnConnect, void(base::UnixSocket*, bool));
};

TEST_F(SocketListenerTest, ReceiveRecord) {
  base::TestTaskRunner task_runner;
  auto callback_called = task_runner.CreateCheckpoint("callback.called");
  auto connected = task_runner.CreateCheckpoint("connected");
  auto callback_fn = [&callback_called](UnwindingRecord r) {
    ASSERT_EQ(r.size, 1u);
    ASSERT_EQ(r.data[0], '1');
    ASSERT_FALSE(r.metadata.expired());
    callback_called();
  };

  BookkeepingThread bookkeeping_thread;
  SocketListener listener(std::move(callback_fn), &bookkeeping_thread);
  ProcessSetSpec spec{};
  spec.pids.emplace(getpid());
  auto handle = listener.process_matcher().AwaitProcessSetSpec(std::move(spec));
  MockEventListener client_listener;
  EXPECT_CALL(client_listener, OnConnect(_, _))
      .WillOnce(InvokeWithoutArgs(connected));

  std::unique_ptr<base::UnixSocket> recv_socket =
      base::UnixSocket::Listen(kSocketName, &listener, &task_runner);

  std::unique_ptr<base::UnixSocket> client_socket =
      base::UnixSocket::Connect(kSocketName, &client_listener, &task_runner);

  task_runner.RunUntilCheckpoint("connected");
  uint64_t size = 1;
  base::ScopedFile fds[2] = {
      base::ScopedFile(base::OpenFile("/dev/null", O_RDONLY)),
      base::ScopedFile(base::OpenFile("/dev/null", O_RDONLY))};
  int raw_fds[2] = {*fds[0], *fds[1]};
  ASSERT_TRUE(client_socket->Send(&size, sizeof(size), raw_fds,
                                  base::ArraySize(raw_fds),
                                  base::UnixSocket::BlockingMode::kBlocking));
  ASSERT_TRUE(client_socket->Send("1", 1, -1,
                                  base::UnixSocket::BlockingMode::kBlocking));

  task_runner.RunUntilCheckpoint("callback.called");
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto
<commit_msg>Fix test failure on is_debug. am: 152255fcc7 am: 1989accbaa<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "src/profiling/memory/socket_listener.h"

#include "perfetto/base/scoped_file.h"
#include "src/base/test/test_task_runner.h"
#include "src/ipc/test/test_socket.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace perfetto {
namespace profiling {
namespace {

using ::testing::_;
using ::testing::InvokeWithoutArgs;

constexpr char kSocketName[] = TEST_SOCK_NAME("socket_listener_unittest");

class SocketListenerTest : public ::testing::Test {
 protected:
  void SetUp() override { DESTROY_TEST_SOCK(kSocketName); }
  void TearDown() override { DESTROY_TEST_SOCK(kSocketName); }
};

class MockEventListener : public base::UnixSocket::EventListener {
 public:
  MOCK_METHOD2(OnConnect, void(base::UnixSocket*, bool));
};

TEST_F(SocketListenerTest, ReceiveRecord) {
  base::TestTaskRunner task_runner;
  auto callback_called = task_runner.CreateCheckpoint("callback.called");
  auto connected = task_runner.CreateCheckpoint("connected");
  auto callback_fn = [&callback_called](UnwindingRecord r) {
    ASSERT_EQ(r.size, 1u);
    ASSERT_EQ(r.data[0], '1');
    ASSERT_FALSE(r.metadata.expired());
    callback_called();
  };

  BookkeepingThread bookkeeping_thread;
  SocketListener listener(std::move(callback_fn), &bookkeeping_thread);
  ProcessSetSpec spec{};
  spec.pids.emplace(getpid());
  spec.client_configuration.interval = 1;
  auto handle = listener.process_matcher().AwaitProcessSetSpec(std::move(spec));
  MockEventListener client_listener;
  EXPECT_CALL(client_listener, OnConnect(_, _))
      .WillOnce(InvokeWithoutArgs(connected));

  std::unique_ptr<base::UnixSocket> recv_socket =
      base::UnixSocket::Listen(kSocketName, &listener, &task_runner);

  std::unique_ptr<base::UnixSocket> client_socket =
      base::UnixSocket::Connect(kSocketName, &client_listener, &task_runner);

  task_runner.RunUntilCheckpoint("connected");
  uint64_t size = 1;
  base::ScopedFile fds[2] = {
      base::ScopedFile(base::OpenFile("/dev/null", O_RDONLY)),
      base::ScopedFile(base::OpenFile("/dev/null", O_RDONLY))};
  int raw_fds[2] = {*fds[0], *fds[1]};
  ASSERT_TRUE(client_socket->Send(&size, sizeof(size), raw_fds,
                                  base::ArraySize(raw_fds),
                                  base::UnixSocket::BlockingMode::kBlocking));
  ASSERT_TRUE(client_socket->Send("1", 1, -1,
                                  base::UnixSocket::BlockingMode::kBlocking));

  task_runner.RunUntilCheckpoint("callback.called");
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto
<|endoftext|>