<commit_before>/*
    Copyright (c) 2008 Kevin Krammer <kevin.krammer@gmx.at>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "kresourceassistant.h"

#include <kresources/configwidget.h>
#include <kresources/factory.h>
#include <kresources/resource.h>

#include <kdebug.h>
#include <klineedit.h>
#include <kpagewidget.h>

#include <QCheckBox>
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QStackedWidget>

class KResourceDescriptionLabel : public QWidget
{
  public:
    KResourceDescriptionLabel( const QString &type, const QString &desc, QWidget *parent)
      : QWidget( parent ), mType( type )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );

      QLabel *label = new QLabel( desc, this );
      label->setWordWrap( true );
      mainLayout->addWidget( label );

      mainLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) );
    }

  public:
    const QString mType;
};

class KResourceCreationWidget : public QWidget
{
  public:
    KResourceCreationWidget( const QString &familyName, KRES::Factory *factory, QWidget *parent )
      : QWidget( parent ), mFamilyName( familyName ),
        mFactory( factory ), mResource( 0 ), mPageWidget( 0 )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );
      mainLayout->setSpacing( KDialog::spacingHint() );
      mainLayout->setMargin( KDialog::marginHint() );

      mPageWidget = new KPageWidget( this );
      mPageWidget->setFaceType( KPageView::Tree );

      mainLayout->addWidget( mPageWidget );

      mTypes = mFactory->typeNames();
      int index = mTypes.indexOf( QLatin1String( "akonadi" ) );
      if ( index != -1 )
        mTypes.removeAt( index );

      foreach ( const QString &type, mTypes ) {
        QString description = mFactory->typeDescription( type );
        if ( description.isEmpty() )
          description = i18nc( "@info", "No description available" );

        QWidget *label = new KResourceDescriptionLabel( type, description, mPageWidget );
        mPageWidget->addPage( label, mFactory->typeName( type ) );
      }
    }

    void createResource()
    {
      KResourceDescriptionLabel *label =
        static_cast<KResourceDescriptionLabel*>( mPageWidget->currentPage()->widget() );

      if ( mResource != 0 && mResource->type() == label->mType )
        return;

      delete mResource;
      mResource = mFactory->resource( label->mType );

      mResource->setResourceName( mFamilyName );
    }

  public:
    QString mFamilyName;
    QStringList mTypes;

    KRES::Factory  *mFactory;
    KRES::Resource *mResource;

    KPageWidget *mPageWidget;
};

class KResourcePluginConfigWidget : public QGroupBox
{
  public:
    KResourcePluginConfigWidget( const QString &type, KRES::Factory *factory, QWidget *parent)
      : QGroupBox( parent ), mPluginWidget( 0 )
    {
      setTitle( i18nc( "@title:group", "%1 Plugin Settings", factory->typeName( type ) ) );

      QVBoxLayout *mainLayout = new QVBoxLayout( this );
      mPluginWidget = factory->configWidget( type, this );
      if ( mPluginWidget == 0 ) {
        kError() << "No plugin configuration widget for resource type" << type;
        QLabel *label = new QLabel( i18nc( "@info", "No plugin specific configuration available" ), this );
        label->setAlignment( Qt::AlignHCenter );
        mainLayout->addWidget( label );
      } else
        mainLayout->addWidget( mPluginWidget );

      mainLayout->addStretch();
    }

    public:
      KRES::ConfigWidget *mPluginWidget;
};

class KResourceConfigWidget : public QWidget
{
  public:
    KResourceConfigWidget( const QStringList &types, KRES::Factory *factory, QWidget *parent )
      : QWidget( parent ), mStackWidget( 0 )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );
      mainLayout->setSpacing( KDialog::spacingHint() );
      mainLayout->setMargin( KDialog::marginHint() );

      mStackWidget = new QStackedWidget( this );

      mainLayout->addWidget( mStackWidget );

      foreach ( const QString &type, types ) {
        KResourcePluginConfigWidget *configWidget =
          new KResourcePluginConfigWidget( type, factory, mStackWidget );
        mStackWidget->addWidget( configWidget );
        mStackedWidgets.insert( type, configWidget );

        connect( configWidget->mPluginWidget, SIGNAL( setReadOnly( bool ) ),
                 parent, SLOT( setReadOnly( bool ) ) );
      }
    }

    void loadSettings( KRES::Resource *resource )
    {
      KResourcePluginConfigWidget *widget = mStackedWidgets[ resource->type() ];
      Q_ASSERT( widget != 0 );

      if ( widget->mPluginWidget != 0 )
        widget->mPluginWidget->loadSettings( resource );
      mStackWidget->setCurrentWidget( widget );
    }

    void saveSettings( KRES::Resource *resource )
    {
      KResourcePluginConfigWidget *widget = mStackedWidgets[ resource->type() ];
      Q_ASSERT( widget != 0 );

      if ( widget->mPluginWidget != 0 )
        widget->mPluginWidget->saveSettings( resource );
    }

  public:
    QStackedWidget *mStackWidget;
    QMap<QString, KResourcePluginConfigWidget*> mStackedWidgets;
};

class KResourceFolderConfigWidget : public QWidget
{
  public:
    KResourceFolderConfigWidget( const QString &familyName, QWidget *parent )
      : QWidget( parent ), mName( 0 ), mReadOnly( 0 )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );

      QGroupBox *generalGroup = new QGroupBox( this );
      QGridLayout *generalLayout = new QGridLayout( generalGroup );
      generalLayout->setSpacing( KDialog::spacingHint() );

      generalGroup->setTitle( i18nc( "@title:group general resource settings",
                                     "%1 Folder Settings",
                                     familyName ) );

      QLabel *nameLabel = new QLabel( i18nc( "@label resource name", "Name:" ),
                                      generalGroup );
      generalLayout->addWidget( nameLabel, 0, 0 );

      mName = new KLineEdit( generalGroup );
      generalLayout->addWidget( mName, 0, 1 );

      mReadOnly = new QCheckBox( i18nc( "@option:check if resource is read-only",
                                        "Read-only" ),
                                 generalGroup );
      generalLayout->addWidget( mReadOnly, 1, 0, 1, 2 );

      mReadOnly->setChecked( false );

      mainLayout->addWidget( generalGroup );
      mainLayout->addStretch();

      connect( mName, SIGNAL( textChanged( const QString& ) ),
               parent, SLOT( slotNameChanged( const QString& ) ) );
    }

  public:
    KLineEdit *mName;
    QCheckBox *mReadOnly;
};

class KResourceAssistant::Private
{
  public:
    explicit Private( KResourceAssistant *parent )
      : mParent( parent ), mFactory( 0 ), mCreationWidget( 0 ),
        mConfigWidget( 0 ), mFolderWidget( 0 ),
        mLastPage( 0 )
    {
    }

  public:
    KResourceAssistant *mParent;

    KRES::Factory *mFactory;

    KResourceCreationWidget     *mCreationWidget;
    KResourceConfigWidget       *mConfigWidget;
    KResourceFolderConfigWidget *mFolderWidget;

    KPageWidgetItem *mLastPage;

  public:
    void setReadOnly( bool readOnly )
    {
      mFolderWidget->mReadOnly->setChecked( readOnly );
    }

    void slotNameChanged( const QString &text );
};

KResourceAssistant::KResourceAssistant( const QString& resourceFamily, QWidget *parent )
  : KAssistantDialog( parent ), d( new Private( this ) )
{
  // TODO they are most likely already defined somewhere
  QMap<QString, QString> familyNames;
  familyNames[ QLatin1String( "contact" ) ] =
    i18nc( "@title user visible resource type", "Address Book" );
  familyNames[ QLatin1String( "calendar" ) ] =
    i18nc( "@title user visible resource type", "Calendar" );

  const QString familyName = familyNames[ resourceFamily.toLower() ];

  setModal( true );
  setCaption( i18nc( "@title:window", "KDE Compatibility Assistant" ) );

  d->mFactory = KRES::Factory::self( resourceFamily.toLower() );

  d->mCreationWidget = new KResourceCreationWidget( familyName, d->mFactory, this );
  addPage( d->mCreationWidget, i18nc( "@title assistant dialog step",
                                      "Step 1: Select a KDE resource plugin") );

  d->mConfigWidget =
    new KResourceConfigWidget( d->mCreationWidget->mTypes, d->mFactory, this );
  addPage( d->mConfigWidget, i18nc( "@title assistant dialog step",
                                    "Step 2: Configure the selected KDE resource plugin") );

  d->mFolderWidget = new KResourceFolderConfigWidget( familyName, this );
  d->mLastPage = addPage( d->mFolderWidget, i18nc( "@title assistant dialog step",
                                                   "Step 3: Choose target folder properties" ) );
}

KResourceAssistant::~KResourceAssistant()
{
  delete d;
}

KRES::Resource *KResourceAssistant::resource()
{
  return d->mCreationWidget->mResource;
}

void KResourceAssistant::back()
{
  KPageWidgetItem *item = currentPage();
  if ( item->widget() == d->mConfigWidget ) {
    d->mConfigWidget->saveSettings( d->mCreationWidget->mResource );
  }

  KAssistantDialog::back();
}

void KResourceAssistant::next()
{
  KPageWidgetItem *item = currentPage();
  if ( item->widget() == d->mCreationWidget ) {
    d->mCreationWidget->createResource();

    d->mConfigWidget->loadSettings( d->mCreationWidget->mResource );
  } else if ( item->widget() == d->mConfigWidget ) {
    const QString resourceName = d->mCreationWidget->mResource->resourceName();
    d->mFolderWidget->mName->setText( resourceName );
  }

  KAssistantDialog::next();
}

void KResourceAssistant::Private::slotNameChanged( const QString &text )
{

  mParent->setValid( mLastPage, !text.isEmpty() );
  mCreationWidget->mResource->setResourceName( text );
}

#include "kresourceassistant.moc"
// kate: space-indent on; indent-width 2; replace-tabs on;
<commit_msg>Add help text to the general resource config page<commit_after>/*
    Copyright (c) 2008 Kevin Krammer <kevin.krammer@gmx.at>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "kresourceassistant.h"

#include <kresources/configwidget.h>
#include <kresources/factory.h>
#include <kresources/resource.h>

#include <kdebug.h>
#include <klineedit.h>
#include <kpagewidget.h>

#include <QCheckBox>
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QStackedWidget>

class KResourceDescriptionLabel : public QWidget
{
  public:
    KResourceDescriptionLabel( const QString &type, const QString &desc, QWidget *parent)
      : QWidget( parent ), mType( type )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );

      QLabel *label = new QLabel( desc, this );
      label->setWordWrap( true );
      mainLayout->addWidget( label );

      mainLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding ) );
    }

  public:
    const QString mType;
};

class KResourceCreationWidget : public QWidget
{
  public:
    KResourceCreationWidget( const QString &familyName, KRES::Factory *factory, QWidget *parent )
      : QWidget( parent ), mFamilyName( familyName ),
        mFactory( factory ), mResource( 0 ), mPageWidget( 0 )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );
      mainLayout->setSpacing( KDialog::spacingHint() );
      mainLayout->setMargin( KDialog::marginHint() );

      mPageWidget = new KPageWidget( this );
      mPageWidget->setFaceType( KPageView::Tree );

      mainLayout->addWidget( mPageWidget );

      mTypes = mFactory->typeNames();
      int index = mTypes.indexOf( QLatin1String( "akonadi" ) );
      if ( index != -1 )
        mTypes.removeAt( index );

      foreach ( const QString &type, mTypes ) {
        QString description = mFactory->typeDescription( type );
        if ( description.isEmpty() )
          description = i18nc( "@info", "No description available" );

        QWidget *label = new KResourceDescriptionLabel( type, description, mPageWidget );
        mPageWidget->addPage( label, mFactory->typeName( type ) );
      }
    }

    void createResource()
    {
      KResourceDescriptionLabel *label =
        static_cast<KResourceDescriptionLabel*>( mPageWidget->currentPage()->widget() );

      if ( mResource != 0 && mResource->type() == label->mType )
        return;

      delete mResource;
      mResource = mFactory->resource( label->mType );

      mResource->setResourceName( mFamilyName );
    }

  public:
    QString mFamilyName;
    QStringList mTypes;

    KRES::Factory  *mFactory;
    KRES::Resource *mResource;

    KPageWidget *mPageWidget;
};

class KResourcePluginConfigWidget : public QGroupBox
{
  public:
    KResourcePluginConfigWidget( const QString &type, KRES::Factory *factory, QWidget *parent)
      : QGroupBox( parent ), mPluginWidget( 0 )
    {
      setTitle( i18nc( "@title:group", "%1 Plugin Settings", factory->typeName( type ) ) );

      QVBoxLayout *mainLayout = new QVBoxLayout( this );
      mPluginWidget = factory->configWidget( type, this );
      if ( mPluginWidget == 0 ) {
        kError() << "No plugin configuration widget for resource type" << type;
        QLabel *label = new QLabel( i18nc( "@info", "No plugin specific configuration available" ), this );
        label->setAlignment( Qt::AlignHCenter );
        mainLayout->addWidget( label );
      } else
        mainLayout->addWidget( mPluginWidget );

      mainLayout->addStretch();
    }

    public:
      KRES::ConfigWidget *mPluginWidget;
};

class KResourceConfigWidget : public QWidget
{
  public:
    KResourceConfigWidget( const QStringList &types, KRES::Factory *factory, QWidget *parent )
      : QWidget( parent ), mStackWidget( 0 )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );
      mainLayout->setSpacing( KDialog::spacingHint() );
      mainLayout->setMargin( KDialog::marginHint() );

      mStackWidget = new QStackedWidget( this );

      mainLayout->addWidget( mStackWidget );

      foreach ( const QString &type, types ) {
        KResourcePluginConfigWidget *configWidget =
          new KResourcePluginConfigWidget( type, factory, mStackWidget );
        mStackWidget->addWidget( configWidget );
        mStackedWidgets.insert( type, configWidget );

        connect( configWidget->mPluginWidget, SIGNAL( setReadOnly( bool ) ),
                 parent, SLOT( setReadOnly( bool ) ) );
      }
    }

    void loadSettings( KRES::Resource *resource )
    {
      KResourcePluginConfigWidget *widget = mStackedWidgets[ resource->type() ];
      Q_ASSERT( widget != 0 );

      if ( widget->mPluginWidget != 0 )
        widget->mPluginWidget->loadSettings( resource );
      mStackWidget->setCurrentWidget( widget );
    }

    void saveSettings( KRES::Resource *resource )
    {
      KResourcePluginConfigWidget *widget = mStackedWidgets[ resource->type() ];
      Q_ASSERT( widget != 0 );

      if ( widget->mPluginWidget != 0 )
        widget->mPluginWidget->saveSettings( resource );
    }

  public:
    QStackedWidget *mStackWidget;
    QMap<QString, KResourcePluginConfigWidget*> mStackedWidgets;
};

class KResourceFolderConfigWidget : public QWidget
{
  public:
    KResourceFolderConfigWidget( const QString &familyName, QWidget *parent )
      : QWidget( parent ), mName( 0 ), mReadOnly( 0 )
    {
      QVBoxLayout *mainLayout = new QVBoxLayout( this );
      mainLayout->setSpacing( KDialog::spacingHint() );

      // TODO probably add Akonadi related options
      const QString helpText =
        i18nc( "@info",
               "The settings on this page allow you to customize how the "
               "data from the plugin will fit into your personal information "
               "setup." );

      QLabel *helpLabel = new QLabel( this );
      helpLabel->setWordWrap( true );
      helpLabel->setText( helpText );

      mainLayout->addWidget( helpLabel );

      QGroupBox *generalGroup = new QGroupBox( this );
      QGridLayout *generalLayout = new QGridLayout( generalGroup );
      generalLayout->setMargin( KDialog::marginHint() );
      generalLayout->setSpacing( KDialog::spacingHint() );

      generalGroup->setTitle( i18nc( "@title:group general resource settings",
                                     "%1 Folder Settings",
                                     familyName ) );

      QLabel *nameLabel = new QLabel( i18nc( "@label resource name", "Name:" ),
                                      generalGroup );
      generalLayout->addWidget( nameLabel, 0, 0 );

      mName = new KLineEdit( generalGroup );
      generalLayout->addWidget( mName, 0, 1 );

      mReadOnly = new QCheckBox( i18nc( "@option:check if resource is read-only",
                                        "Read-only" ),
                                 generalGroup );
      generalLayout->addWidget( mReadOnly, 1, 0, 1, 2 );

      mReadOnly->setChecked( false );

      mainLayout->addWidget( generalGroup );
      mainLayout->addStretch();

      connect( mName, SIGNAL( textChanged( const QString& ) ),
               parent, SLOT( slotNameChanged( const QString& ) ) );
    }

  public:
    KLineEdit *mName;
    QCheckBox *mReadOnly;
};

class KResourceAssistant::Private
{
  public:
    explicit Private( KResourceAssistant *parent )
      : mParent( parent ), mFactory( 0 ), mCreationWidget( 0 ),
        mConfigWidget( 0 ), mFolderWidget( 0 ),
        mLastPage( 0 )
    {
    }

  public:
    KResourceAssistant *mParent;

    KRES::Factory *mFactory;

    KResourceCreationWidget     *mCreationWidget;
    KResourceConfigWidget       *mConfigWidget;
    KResourceFolderConfigWidget *mFolderWidget;

    KPageWidgetItem *mLastPage;

  public:
    void setReadOnly( bool readOnly )
    {
      mFolderWidget->mReadOnly->setChecked( readOnly );
    }

    void slotNameChanged( const QString &text );
};

KResourceAssistant::KResourceAssistant( const QString& resourceFamily, QWidget *parent )
  : KAssistantDialog( parent ), d( new Private( this ) )
{
  // TODO they are most likely already defined somewhere
  QMap<QString, QString> familyNames;
  familyNames[ QLatin1String( "contact" ) ] =
    i18nc( "@title user visible resource type", "Address Book" );
  familyNames[ QLatin1String( "calendar" ) ] =
    i18nc( "@title user visible resource type", "Calendar" );

  const QString familyName = familyNames[ resourceFamily.toLower() ];

  setModal( true );
  setCaption( i18nc( "@title:window", "KDE Compatibility Assistant" ) );

  d->mFactory = KRES::Factory::self( resourceFamily.toLower() );

  d->mCreationWidget = new KResourceCreationWidget( familyName, d->mFactory, this );
  addPage( d->mCreationWidget, i18nc( "@title assistant dialog step",
                                      "Step 1: Select a KDE resource plugin") );

  d->mConfigWidget =
    new KResourceConfigWidget( d->mCreationWidget->mTypes, d->mFactory, this );
  addPage( d->mConfigWidget, i18nc( "@title assistant dialog step",
                                    "Step 2: Configure the selected KDE resource plugin") );

  d->mFolderWidget = new KResourceFolderConfigWidget( familyName, this );
  d->mLastPage = addPage( d->mFolderWidget, i18nc( "@title assistant dialog step",
                                                   "Step 3: Choose target folder properties" ) );
}

KResourceAssistant::~KResourceAssistant()
{
  delete d;
}

KRES::Resource *KResourceAssistant::resource()
{
  return d->mCreationWidget->mResource;
}

void KResourceAssistant::back()
{
  KPageWidgetItem *item = currentPage();
  if ( item->widget() == d->mConfigWidget ) {
    d->mConfigWidget->saveSettings( d->mCreationWidget->mResource );
  }

  KAssistantDialog::back();
}

void KResourceAssistant::next()
{
  KPageWidgetItem *item = currentPage();
  if ( item->widget() == d->mCreationWidget ) {
    d->mCreationWidget->createResource();

    d->mConfigWidget->loadSettings( d->mCreationWidget->mResource );
  } else if ( item->widget() == d->mConfigWidget ) {
    const QString resourceName = d->mCreationWidget->mResource->resourceName();
    d->mFolderWidget->mName->setText( resourceName );
  }

  KAssistantDialog::next();
}

void KResourceAssistant::Private::slotNameChanged( const QString &text )
{

  mParent->setValid( mLastPage, !text.isEmpty() );
  mCreationWidget->mResource->setResourceName( text );
}

#include "kresourceassistant.moc"
// kate: space-indent on; indent-width 2; replace-tabs on;
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <vcl/openglwin.hxx>
#include <vcl/opengl/OpenGLContext.hxx>
#include <vcl/event.hxx>

class OpenGLWindowImpl
{
public:
    OpenGLWindowImpl(Window* pWindow);
    OpenGLContext* getContext();
private:
    OpenGLContext maContext;
    boost::scoped_ptr<SystemChildWindow> mpChildWindow;
};

OpenGLWindowImpl::OpenGLWindowImpl(Window* pWindow):
    mpChildWindow(new SystemChildWindow(pWindow))
{
    mpChildWindow->Show();
    maContext.init(mpChildWindow.get());
    pWindow->SetMouseTransparent(false);
}

OpenGLContext* OpenGLWindowImpl::getContext()
{
    return &maContext;
}

OpenGLWindow::OpenGLWindow(Window* pParent):
    Window(pParent, 0),
    mpImpl(new OpenGLWindowImpl(this)),
    mpRenderer(NULL)
{
}

OpenGLWindow::~OpenGLWindow()
{
    if(mpRenderer)
        mpRenderer->contextDestroyed();
}

OpenGLContext* OpenGLWindow::getContext()
{
    return mpImpl->getContext();
}

void OpenGLWindow::Paint(const Rectangle&)
{
    if(mpRenderer)
        mpRenderer->update();
}

void OpenGLWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    maStartPoint = rMEvt.GetPosPixel();
}

void OpenGLWindow::MouseButtonUp( const MouseEvent& rMEvt )
{
    Point aPoint = rMEvt.GetPosPixel();
    if(aPoint == maStartPoint)
    {
        Color aColor = GetPixel(aPoint);
        SAL_WARN("vcl.opengl", aColor.GetColor());
        if(mpRenderer)
            mpRenderer->clickedAt(aPoint, rMEvt.GetButtons());
    }
    else
    {
        mpRenderer->mouseDragMove(maStartPoint, aPoint,
                                  rMEvt.GetButtons());
    }
}

void OpenGLWindow::Command( const CommandEvent& rCEvt )
{
    if(rCEvt.GetCommand() == COMMAND_WHEEL)
    {
        const CommandWheelData* pData = rCEvt.GetWheelData();
        if(pData->GetMode() == COMMAND_WHEEL_SCROLL)
        {
            long nDelta = pData->GetDelta();
            if(mpRenderer)
                mpRenderer->scroll(nDelta);
        }
    }
}

void OpenGLWindow::MouseMove( const MouseEvent& /*rMEvt*/ )
{
}

void OpenGLWindow::setRenderer(IRenderer* pRenderer)
{
    mpRenderer = pRenderer;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>fix crash if there is no OpenGLRenderer set<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <vcl/openglwin.hxx>
#include <vcl/opengl/OpenGLContext.hxx>
#include <vcl/event.hxx>

class OpenGLWindowImpl
{
public:
    OpenGLWindowImpl(Window* pWindow);
    OpenGLContext* getContext();
private:
    OpenGLContext maContext;
    boost::scoped_ptr<SystemChildWindow> mpChildWindow;
};

OpenGLWindowImpl::OpenGLWindowImpl(Window* pWindow):
    mpChildWindow(new SystemChildWindow(pWindow))
{
    mpChildWindow->Show();
    maContext.init(mpChildWindow.get());
    pWindow->SetMouseTransparent(false);
}

OpenGLContext* OpenGLWindowImpl::getContext()
{
    return &maContext;
}

OpenGLWindow::OpenGLWindow(Window* pParent):
    Window(pParent, 0),
    mpImpl(new OpenGLWindowImpl(this)),
    mpRenderer(NULL)
{
}

OpenGLWindow::~OpenGLWindow()
{
    if(mpRenderer)
        mpRenderer->contextDestroyed();
}

OpenGLContext* OpenGLWindow::getContext()
{
    return mpImpl->getContext();
}

void OpenGLWindow::Paint(const Rectangle&)
{
    if(mpRenderer)
        mpRenderer->update();
}

void OpenGLWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    maStartPoint = rMEvt.GetPosPixel();
}

void OpenGLWindow::MouseButtonUp( const MouseEvent& rMEvt )
{
    if(!mpRenderer)
        return;

    Point aPoint = rMEvt.GetPosPixel();
    if(aPoint == maStartPoint)
    {
        Color aColor = GetPixel(aPoint);
        SAL_WARN("vcl.opengl", aColor.GetColor());
        mpRenderer->clickedAt(aPoint, rMEvt.GetButtons());
    }
    else
    {
        mpRenderer->mouseDragMove(maStartPoint, aPoint,
                                  rMEvt.GetButtons());
    }
}

void OpenGLWindow::Command( const CommandEvent& rCEvt )
{
    if(!mpRenderer)
        return;

    if(rCEvt.GetCommand() == COMMAND_WHEEL)
    {
        const CommandWheelData* pData = rCEvt.GetWheelData();
        if(pData->GetMode() == COMMAND_WHEEL_SCROLL)
        {
            long nDelta = pData->GetDelta();
            mpRenderer->scroll(nDelta);
        }
    }
}

void OpenGLWindow::MouseMove( const MouseEvent& /*rMEvt*/ )
{
}

void OpenGLWindow::setRenderer(IRenderer* pRenderer)
{
    mpRenderer = pRenderer;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#include "contest.h"
#include "db.h"
#include "number.h"
#include "track.h"
#include "cgi.h"
#include "session.h"
#include "user.h"
#include "log.h"

Contest::Contest(int id){
    _id = 0;
    if(id <= 0) return;
    DB::Result r = DB::query("SELECT name, state FROM contests WHERE id = " + number(id));
    if(!r.empty()){
        _id = id;
        _name = r[0][0];
        _state = r[0][1] == "submissions" ? Contest::Submissions : r[0][1] == "voting" ? Contest::Voting : Contest::Closed;
    }
}

std::string Contest::getDescription() const{
    DB::Result r = DB::query(
        "SELECT description FROM contests "
        "WHERE id = " + number(_id)
    );
    return r.empty()?"":r[0][0];
}

void Contest::addTrack(int tid){
    if(state() != Submissions) return;
    DB::query(
        "INSERT INTO contest_submissions (track_id, contest_id) "
        "VALUES (" + number(tid) + ", " + number(_id) + ")"
    );
}

void Contest::vote(int tid){
    if(state() != Voting) return;
    std::string host = cgi.getEnvironment().getRemoteAddr();
    User u = Session::user();
    // Check if the user didn't already vote
    if(u) log("u!");
    else log(host);
    DB::Result r = DB::query(
        "SELECT 1 FROM votes "
        "WHERE contest_id = " + number(_id) +
        " AND track_id = " + number(tid) +
        " AND " +
         (u.id()!=0?"user_id = "+number(u.id()):
            "host = '"+host+"' AND user_id ISNULL")
        );
    if(!r.empty()) return;
    // Check if the track is linked to the contest
    r = DB::query(
        "SELECT 1 FROM contest_submissions WHERE contest_id = " + number(_id) +
        " AND track_id = " + number(tid)
    );
    if(r.empty()) return;
    // Update
    DB::query(
        "INSERT INTO votes (host, track_id, contest_id, user_id) "
        "VALUES ($1, "+number(tid)+", "+number(_id)+", "+(u?number(u.id()):"null")+")"
        , host);
    DB::query(
        "UPDATE contest_submissions "
        "SET votes = votes + 1"
        "WHERE track_id = " + number(tid) +
        "AND contest_id = " + number(_id)
        );
}

void Contest::unvote(int tid){
    if(state() != Voting) return;
    User u = Session::user();
    DB::Result r = DB::query(
        "DELETE FROM votes "
        "WHERE contest_id = " + number(_id) +
        " AND track_id = " + number(tid) +
     (u?" AND user_id = " + number(u.id()):
        " AND host = '"+cgi.getEnvironment().getRemoteAddr()+"'"
        " AND user_id ISNULL") +
        " RETURNING 1");
    if(!r.empty())
        DB::query(
            "UPDATE contest_submissions "
            "SET votes = votes - 1"
            "WHERE track_id = " + number(tid) +
            "AND contest_id = " + number(_id)
            );
}

std::map<int, int> Contest::getVotes() const{
    DB::Result r = DB::query(
        "SELECT track_id, votes FROM contest_submissions "
        "WHERE contest_id = " + number(_id)
        );
    std::map<int, int> tracks;
    for(DB::Result::iterator i = r.begin(); i != r.end(); i++)
        tracks[number((*i)[0])] = number((*i)[1]);
    return tracks;
}

std::string Contest::url(int id){
    return "/contest/" + number(id);
}

std::vector<int> Contest::usersVotes() const{
    User u = Session::user();
    DB::Result r = DB::query(
        "SELECT track_id FROM votes "
        "WHERE contest_id = " + number(_id) +
     (u?" AND user_id = " + number(u.id()):
        " AND host = '" + cgi.getEnvironment().getRemoteAddr() + "'"
        " AND user_id ISNULL"));
    std::vector<int> tracks;
    for(DB::Result::iterator i = r.begin(); i != r.end(); i++)
        tracks.push_back(number((*i)[0]));
    return tracks;
}

std::vector<Track> Contest::submissions(){
    return Track::select("contest_submissions",
        "tracks.id = contest_submissions.track_id AND contest_submissions.contest_id = " + number(_id),
        "tracks.date ASC");
}
<commit_msg>stop logging inane things<commit_after>#include "contest.h"
#include "db.h"
#include "number.h"
#include "track.h"
#include "cgi.h"
#include "session.h"
#include "user.h"

Contest::Contest(int id){
    _id = 0;
    if(id <= 0) return;
    DB::Result r = DB::query("SELECT name, state FROM contests WHERE id = " + number(id));
    if(!r.empty()){
        _id = id;
        _name = r[0][0];
        _state = r[0][1] == "submissions" ? Contest::Submissions : r[0][1] == "voting" ? Contest::Voting : Contest::Closed;
    }
}

std::string Contest::getDescription() const{
    DB::Result r = DB::query(
        "SELECT description FROM contests "
        "WHERE id = " + number(_id)
    );
    return r.empty()?"":r[0][0];
}

void Contest::addTrack(int tid){
    if(state() != Submissions) return;
    DB::query(
        "INSERT INTO contest_submissions (track_id, contest_id) "
        "VALUES (" + number(tid) + ", " + number(_id) + ")"
    );
}

void Contest::vote(int tid){
    if(state() != Voting) return;
    std::string host = cgi.getEnvironment().getRemoteAddr();
    User u = Session::user();
    // Check if the user didn't already vote
    DB::Result r = DB::query(
        "SELECT 1 FROM votes "
        "WHERE contest_id = " + number(_id) +
        " AND track_id = " + number(tid) +
        " AND " +
         (u.id()!=0?"user_id = "+number(u.id()):
            "host = '"+host+"' AND user_id ISNULL")
        );
    if(!r.empty()) return;
    // Check if the track is linked to the contest
    r = DB::query(
        "SELECT 1 FROM contest_submissions WHERE contest_id = " + number(_id) +
        " AND track_id = " + number(tid)
    );
    if(r.empty()) return;
    // Update
    DB::query(
        "INSERT INTO votes (host, track_id, contest_id, user_id) "
        "VALUES ($1, "+number(tid)+", "+number(_id)+", "+(u?number(u.id()):"null")+")"
        , host);
    DB::query(
        "UPDATE contest_submissions "
        "SET votes = votes + 1"
        "WHERE track_id = " + number(tid) +
        "AND contest_id = " + number(_id)
        );
}

void Contest::unvote(int tid){
    if(state() != Voting) return;
    User u = Session::user();
    DB::Result r = DB::query(
        "DELETE FROM votes "
        "WHERE contest_id = " + number(_id) +
        " AND track_id = " + number(tid) +
     (u?" AND user_id = " + number(u.id()):
        " AND host = '"+cgi.getEnvironment().getRemoteAddr()+"'"
        " AND user_id ISNULL") +
        " RETURNING 1");
    if(!r.empty())
        DB::query(
            "UPDATE contest_submissions "
            "SET votes = votes - 1"
            "WHERE track_id = " + number(tid) +
            "AND contest_id = " + number(_id)
            );
}

std::map<int, int> Contest::getVotes() const{
    DB::Result r = DB::query(
        "SELECT track_id, votes FROM contest_submissions "
        "WHERE contest_id = " + number(_id)
        );
    std::map<int, int> tracks;
    for(DB::Result::iterator i = r.begin(); i != r.end(); i++)
        tracks[number((*i)[0])] = number((*i)[1]);
    return tracks;
}

std::string Contest::url(int id){
    return "/contest/" + number(id);
}

std::vector<int> Contest::usersVotes() const{
    User u = Session::user();
    DB::Result r = DB::query(
        "SELECT track_id FROM votes "
        "WHERE contest_id = " + number(_id) +
     (u?" AND user_id = " + number(u.id()):
        " AND host = '" + cgi.getEnvironment().getRemoteAddr() + "'"
        " AND user_id ISNULL"));
    std::vector<int> tracks;
    for(DB::Result::iterator i = r.begin(); i != r.end(); i++)
        tracks.push_back(number((*i)[0]));
    return tracks;
}

std::vector<Track> Contest::submissions(){
    return Track::select("contest_submissions",
        "tracks.id = contest_submissions.track_id AND contest_submissions.contest_id = " + number(_id),
        "tracks.date ASC");
}
<|endoftext|>


<commit_before>/*
 * nt loader
 *
 * Copyright 2006-2008 Mike McCormack
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <poll.h>
#include <signal.h>
#include <execinfo.h>
#include <getopt.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"

#include "debug.h"
#include "mem.h"
#include "object.h"
#include "objdir.h"
#include "ntcall.h"
#include "section.h"
#include "timer.h"
#include "unicode.h"
#include "fiber.h"
#include "file.h"
#include "event.h"
#include "symlink.h"
#include "alloc_bitmap.h"

process_list_t processes;
thread_t *current;
object_t *ntdll_section;
int option_debug = 0;
ULONG KiIntSystemCall = 0;
bool forced_quit;

class default_sleeper_t : public sleeper_t
{
public:
	virtual bool check_events( bool wait );
	virtual ~default_sleeper_t() {}
};

int sleeper_t::get_int_timeout( LARGE_INTEGER& timeout )
{
	timeout.QuadPart = (timeout.QuadPart+9999)/10000;
	int t = INT_MAX;
	if (timeout.QuadPart < t)
		t = timeout.QuadPart;
	return t;
}

bool default_sleeper_t::check_events( bool wait )
{
	LARGE_INTEGER timeout;

	// check for expired timers
	bool timers_left = timeout_t::check_timers(timeout);

	// Check for a deadlock and quit.
	//  This happens if we're the only active thread,
	//  there's no more timers, and we're asked to wait.
	if (!timers_left && wait && fiber_t::last_fiber())
		return true;
	if (!wait)
		return false;

	int t = get_int_timeout( timeout );
	int r = poll( 0, 0, t );
	if (r >= 0)
		return false;
	if (errno != EINTR)
		die("poll failed %d\n", errno);
	return false;
}

default_sleeper_t default_sleeper;
sleeper_t* sleeper = &default_sleeper;

int schedule(void)
{
	/* while there's still a thread running */
	while (processes.head())
	{
		// check if any thing interesting has happened
		sleeper->check_events( false );

		// other fibers are active... schedule run them
		if (!fiber_t::last_fiber())
		{
			fiber_t::yield();
			continue;
		}

		// there's still processes but no active threads ... sleep
		if (sleeper->check_events( true ))
			break;
	}

	return 0;
}

NTSTATUS create_initial_process( thread_t **t, UNICODE_STRING& us )
{
	BYTE *pstack;
	const unsigned int stack_size = 0x100 * PAGE_SIZE;
	process_t *p = NULL;
	CONTEXT ctx;
	INITIAL_TEB init_teb;
	CLIENT_ID id;
	object_t *section = NULL;
	file_t *file = 0;
	int r;

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		return r;

	/* load the executable and ntdll */
	r = create_section( &section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	release( file );
	if (r < STATUS_SUCCESS)
		return r;

	/* create the initial process */
	r = create_process( &p, section );
	release( section );
	section = NULL;

	if (r < STATUS_SUCCESS)
		return r;

	PPEB ppeb = (PPEB) p->peb_section->get_kernel_address();
	p->create_exe_ppb( &ppeb->ProcessParameters, us );

	/* map the stack */
	pstack = NULL;
	r = p->vm->allocate_virtual_memory( &pstack, 0, stack_size, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE );
	if (r < STATUS_SUCCESS)
		return r;

	/* teb initialization data */
	memset( &init_teb, 0, sizeof init_teb );
	init_teb.StackReserved = pstack;
	init_teb.StackCommit = (BYTE*)init_teb.StackReserved + stack_size;
	init_teb.StackCommitMax = (BYTE*)init_teb.StackCommit - PAGE_SIZE;

	/* initialize the first thread's context */
	p->vm->init_context( ctx );
	ctx.Eip = (DWORD) get_entry_point( p );
	ctx.Esp = (DWORD) pstack + stack_size - 8;

	dprintf("entry point = %08lx\n", ctx.Eip);

	/* when starting nt processes, make the PEB the first arg of NtProcessStartup */
	r = p->vm->copy_to_user( (BYTE*) ctx.Esp + 4, &p->PebBaseAddress, sizeof p->PebBaseAddress );

	if (r == STATUS_SUCCESS)
		r = create_thread( t, p, &id, &ctx, &init_teb, FALSE );

	release( p );

	return r;
}

NTSTATUS init_ntdll( void )
{
	WCHAR ntdll[] = {
		'\\','?','?','\\','c',':','\\','w','i','n','n','t','\\',
		's','y','s','t','e','m','3','2','\\','n','t','d','l','l','.','d','l','l',0 };
	unicode_string_t us;
	file_t *file = 0;
	NTSTATUS r;

	us.set( ntdll );

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		die("failed to open ntdll\n");

	r = create_section( &ntdll_section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	if (r < STATUS_SUCCESS)
		die("failed to create ntdll section\n");

	KiIntSystemCall = get_proc_address( ntdll_section, "KiIntSystemCall" );
	dprintf("KiIntSystemCall = %08lx\n", KiIntSystemCall);
	init_syscalls(KiIntSystemCall != 0);

	release( file );

	return r;
}

void free_ntdll( void )
{
	release( ntdll_section );
	ntdll_section = NULL;
}

void do_cleanup( void )
{
	int num_threads = 0, num_processes = 0;

	for ( process_iter_t pi(processes); pi; pi.next() )
	{
		process_t *p = pi;
		if (p->is_signalled())
			continue;
		num_processes++;
		fprintf(stderr, "process %04lx\n", p->id);
		for ( sibling_iter_t ti(p->threads); ti; ti.next() )
		{
			thread_t *t = ti;
			if (t->is_signalled())
				continue;
			fprintf(stderr, "\tthread %04lx\n", t->trace_id());
			num_threads++;
		}
	}
	if (num_threads || num_processes)
		fprintf(stderr, "%d threads %d processes left\n", num_threads, num_processes);
}

static void segv_handler(int)
{
	const int max_frames = 20;
	void *bt[max_frames];
	char **names;
	int n=0, size;
	ULONG id = 0;

	if (current)
		id = current->trace_id();

	size = backtrace(bt, max_frames);
	names = backtrace_symbols(bt, size);

	fprintf(stderr, "%04lx: caught kernel SEGV (%d frames):\n", id, size);
	for (n=0; n<size; n++)
	{
		fprintf(stderr, "%d: %s\n", n, names[n]);
	}
	exit(1);
}

bool init_skas();
bool init_tt( const char *loader_path );

void usage( void )
{
	const char usage[] =
		"Usage: %s [options] [native.exe]\n"
		"Options:\n"
		"  -d,--debug    break into debugger on exceptions\n"
		"  -h,--help     print this message\n"
		"  -q,--quiet    quiet, suppress debug messages\n"
		"  -t,--trace    trace syscall entry and exit\n"
		"  -v,--version  print version\n\n"
		"  smss.exe is started by default\n\n";
	printf( usage, PACKAGE_NAME );
	exit(0);
}

void version( void )
{
	const char version[] = "%s\n"
		"Copyright (C) 2008-2009 Mike McCormack\n"
		"Licence LGPL\n"
		"This is free software: you are free to change and redistribute it.\n"
		"There is NO WARRANTY, to the extent permitted by law.\n\n";
	printf( version, PACKAGE_STRING );
	exit(0);
}

void parse_options(int argc, char **argv)
{
	while (1)
	{
		int option_index;
		static struct option long_options[] = {
			{"debug", 0, &option_debug, 1 },
			{"help", 0, 0, 'h' },
			{"trace", 0, &option_trace, 1 },
			{"version", 0, 0, 'v' },
			{NULL, 0, 0, 0 },
		};

		int ch = getopt_long(argc, argv, "dhtqv?", long_options, &option_index );
		if (ch == -1)
			break;

		switch (ch)
		{
		case 'd':
			option_debug = 1;
			break;
		case '?':
		case 'h':
			usage();
			break;
		case 't':
			option_trace = 1;
			break;
		case 'v':
			version();
		}
	}
}

int main(int argc, char **argv)
{
	unicode_string_t us;
	thread_t *initial_thread = NULL;
	const char *exename;

	parse_options( argc, argv );

	if (optind == argc)
	{
		// default to starting smss.exe
		exename = "\\??\\c:\\winnt\\system32\\smss.exe";
	}
	else
	{
		exename = argv[optind];
	}

	// the skas3 patch is deprecated...
	if (0) init_skas();

	// pass our path so thread tracing can find the client stub
	init_tt( argv[0] );
	if (!pcreate_address_space)
		die("no way to manage address spaces found\n");

	// enable backtraces
	signal(SIGSEGV, segv_handler);

	// quick sanity test
	allocation_bitmap_t::test();

	// initialize boottime
	SYSTEM_TIME_OF_DAY_INFORMATION dummy;
	get_system_time_of_day( dummy );

	init_registry();
	fiber_t::fibers_init();
	init_root();
	create_directory_object( (PWSTR) L"\\" );
	create_directory_object( (PWSTR) L"\\??" );
	unicode_string_t link_name, link_target;
	link_name.set( L"\\DosDevices" );
	link_target.copy( L"\\??" );
	create_symlink( link_name, link_target );
	create_directory_object( (PWSTR) L"\\Device" );
	create_directory_object( (PWSTR) L"\\Device\\MailSlot" );
	create_directory_object( (PWSTR) L"\\Security" );
	//create_directory_object( (PWSTR) L"\\DosDevices" );
	create_directory_object( (PWSTR) L"\\BaseNamedObjects" );
	create_sync_event( (PWSTR) L"\\Security\\LSA_AUTHENTICATION_INITIALIZED" );
	create_sync_event( (PWSTR) L"\\SeLsaInitEvent" );
	init_random();
	init_pipe_device();
	// XP
	create_directory_object( (PWSTR) L"\\KernelObjects" );
	create_sync_event( (PWSTR) L"\\KernelObjects\\CritSecOutOfMemoryEvent" );
	init_drives();
	init_ntdll();
	create_kthread();

	us.copy( exename );

	int r = create_initial_process( &initial_thread, us );
	if (r < STATUS_SUCCESS)
		die("create_initial_process() failed (%08x)\n", r);

	// run the main loop
	schedule();

	ntgdi_fini();
	r = initial_thread->process->ExitStatus;
	//fprintf(stderr, "process exited (%08x)\n", r);
	release( initial_thread );

	shutdown_kthread();
	do_cleanup();

	free_root();
	fiber_t::fibers_finish();
	free_registry();
	free_ntdll();

	return r;
}
<commit_msg>Generate a backtrace on SIGTRAP (catches gcc malloc errors)<commit_after>/*
 * nt loader
 *
 * Copyright 2006-2008 Mike McCormack
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <poll.h>
#include <signal.h>
#include <execinfo.h>
#include <getopt.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"

#include "debug.h"
#include "mem.h"
#include "object.h"
#include "objdir.h"
#include "ntcall.h"
#include "section.h"
#include "timer.h"
#include "unicode.h"
#include "fiber.h"
#include "file.h"
#include "event.h"
#include "symlink.h"
#include "alloc_bitmap.h"

process_list_t processes;
thread_t *current;
object_t *ntdll_section;
int option_debug = 0;
ULONG KiIntSystemCall = 0;
bool forced_quit;

class default_sleeper_t : public sleeper_t
{
public:
	virtual bool check_events( bool wait );
	virtual ~default_sleeper_t() {}
};

int sleeper_t::get_int_timeout( LARGE_INTEGER& timeout )
{
	timeout.QuadPart = (timeout.QuadPart+9999)/10000;
	int t = INT_MAX;
	if (timeout.QuadPart < t)
		t = timeout.QuadPart;
	return t;
}

bool default_sleeper_t::check_events( bool wait )
{
	LARGE_INTEGER timeout;

	// check for expired timers
	bool timers_left = timeout_t::check_timers(timeout);

	// Check for a deadlock and quit.
	//  This happens if we're the only active thread,
	//  there's no more timers, and we're asked to wait.
	if (!timers_left && wait && fiber_t::last_fiber())
		return true;
	if (!wait)
		return false;

	int t = get_int_timeout( timeout );
	int r = poll( 0, 0, t );
	if (r >= 0)
		return false;
	if (errno != EINTR)
		die("poll failed %d\n", errno);
	return false;
}

default_sleeper_t default_sleeper;
sleeper_t* sleeper = &default_sleeper;

int schedule(void)
{
	/* while there's still a thread running */
	while (processes.head())
	{
		// check if any thing interesting has happened
		sleeper->check_events( false );

		// other fibers are active... schedule run them
		if (!fiber_t::last_fiber())
		{
			fiber_t::yield();
			continue;
		}

		// there's still processes but no active threads ... sleep
		if (sleeper->check_events( true ))
			break;
	}

	return 0;
}

NTSTATUS create_initial_process( thread_t **t, UNICODE_STRING& us )
{
	BYTE *pstack;
	const unsigned int stack_size = 0x100 * PAGE_SIZE;
	process_t *p = NULL;
	CONTEXT ctx;
	INITIAL_TEB init_teb;
	CLIENT_ID id;
	object_t *section = NULL;
	file_t *file = 0;
	int r;

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		return r;

	/* load the executable and ntdll */
	r = create_section( &section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	release( file );
	if (r < STATUS_SUCCESS)
		return r;

	/* create the initial process */
	r = create_process( &p, section );
	release( section );
	section = NULL;

	if (r < STATUS_SUCCESS)
		return r;

	PPEB ppeb = (PPEB) p->peb_section->get_kernel_address();
	p->create_exe_ppb( &ppeb->ProcessParameters, us );

	/* map the stack */
	pstack = NULL;
	r = p->vm->allocate_virtual_memory( &pstack, 0, stack_size, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE );
	if (r < STATUS_SUCCESS)
		return r;

	/* teb initialization data */
	memset( &init_teb, 0, sizeof init_teb );
	init_teb.StackReserved = pstack;
	init_teb.StackCommit = (BYTE*)init_teb.StackReserved + stack_size;
	init_teb.StackCommitMax = (BYTE*)init_teb.StackCommit - PAGE_SIZE;

	/* initialize the first thread's context */
	p->vm->init_context( ctx );
	ctx.Eip = (DWORD) get_entry_point( p );
	ctx.Esp = (DWORD) pstack + stack_size - 8;

	dprintf("entry point = %08lx\n", ctx.Eip);

	/* when starting nt processes, make the PEB the first arg of NtProcessStartup */
	r = p->vm->copy_to_user( (BYTE*) ctx.Esp + 4, &p->PebBaseAddress, sizeof p->PebBaseAddress );

	if (r == STATUS_SUCCESS)
		r = create_thread( t, p, &id, &ctx, &init_teb, FALSE );

	release( p );

	return r;
}

NTSTATUS init_ntdll( void )
{
	WCHAR ntdll[] = {
		'\\','?','?','\\','c',':','\\','w','i','n','n','t','\\',
		's','y','s','t','e','m','3','2','\\','n','t','d','l','l','.','d','l','l',0 };
	unicode_string_t us;
	file_t *file = 0;
	NTSTATUS r;

	us.set( ntdll );

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		die("failed to open ntdll\n");

	r = create_section( &ntdll_section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	if (r < STATUS_SUCCESS)
		die("failed to create ntdll section\n");

	KiIntSystemCall = get_proc_address( ntdll_section, "KiIntSystemCall" );
	dprintf("KiIntSystemCall = %08lx\n", KiIntSystemCall);
	init_syscalls(KiIntSystemCall != 0);

	release( file );

	return r;
}

void free_ntdll( void )
{
	release( ntdll_section );
	ntdll_section = NULL;
}

void do_cleanup( void )
{
	int num_threads = 0, num_processes = 0;

	for ( process_iter_t pi(processes); pi; pi.next() )
	{
		process_t *p = pi;
		if (p->is_signalled())
			continue;
		num_processes++;
		fprintf(stderr, "process %04lx\n", p->id);
		for ( sibling_iter_t ti(p->threads); ti; ti.next() )
		{
			thread_t *t = ti;
			if (t->is_signalled())
				continue;
			fprintf(stderr, "\tthread %04lx\n", t->trace_id());
			num_threads++;
		}
	}
	if (num_threads || num_processes)
		fprintf(stderr, "%d threads %d processes left\n", num_threads, num_processes);
}

static void backtrace_and_quit()
{
	const int max_frames = 20;
	void *bt[max_frames];
	char **names;
	int n=0, size;
	ULONG id = 0;

	if (current)
		id = current->trace_id();

	size = backtrace(bt, max_frames);
	names = backtrace_symbols(bt, size);

	fprintf(stderr, "%04lx: caught kernel SEGV (%d frames):\n", id, size);
	for (n=0; n<size; n++)
	{
		fprintf(stderr, "%d: %s\n", n, names[n]);
	}
	exit(1);
}

static void segv_handler(int)
{
	backtrace_and_quit();
}

static void abort_handler(int)
{
	backtrace_and_quit();
}

bool init_skas();
bool init_tt( const char *loader_path );

void usage( void )
{
	const char usage[] =
		"Usage: %s [options] [native.exe]\n"
		"Options:\n"
		"  -d,--debug    break into debugger on exceptions\n"
		"  -h,--help     print this message\n"
		"  -q,--quiet    quiet, suppress debug messages\n"
		"  -t,--trace    trace syscall entry and exit\n"
		"  -v,--version  print version\n\n"
		"  smss.exe is started by default\n\n";
	printf( usage, PACKAGE_NAME );
	exit(0);
}

void version( void )
{
	const char version[] = "%s\n"
		"Copyright (C) 2008-2009 Mike McCormack\n"
		"Licence LGPL\n"
		"This is free software: you are free to change and redistribute it.\n"
		"There is NO WARRANTY, to the extent permitted by law.\n\n";
	printf( version, PACKAGE_STRING );
	exit(0);
}

void parse_options(int argc, char **argv)
{
	while (1)
	{
		int option_index;
		static struct option long_options[] = {
			{"debug", 0, &option_debug, 1 },
			{"help", 0, 0, 'h' },
			{"trace", 0, &option_trace, 1 },
			{"version", 0, 0, 'v' },
			{NULL, 0, 0, 0 },
		};

		int ch = getopt_long(argc, argv, "dhtqv?", long_options, &option_index );
		if (ch == -1)
			break;

		switch (ch)
		{
		case 'd':
			option_debug = 1;
			break;
		case '?':
		case 'h':
			usage();
			break;
		case 't':
			option_trace = 1;
			break;
		case 'v':
			version();
		}
	}
}

int main(int argc, char **argv)
{
	unicode_string_t us;
	thread_t *initial_thread = NULL;
	const char *exename;

	parse_options( argc, argv );

	if (optind == argc)
	{
		// default to starting smss.exe
		exename = "\\??\\c:\\winnt\\system32\\smss.exe";
	}
	else
	{
		exename = argv[optind];
	}

	// the skas3 patch is deprecated...
	if (0) init_skas();

	// pass our path so thread tracing can find the client stub
	init_tt( argv[0] );
	if (!pcreate_address_space)
		die("no way to manage address spaces found\n");

	// enable backtraces
	signal(SIGSEGV, segv_handler);
	signal(SIGABRT, abort_handler);

	// quick sanity test
	allocation_bitmap_t::test();

	// initialize boottime
	SYSTEM_TIME_OF_DAY_INFORMATION dummy;
	get_system_time_of_day( dummy );

	init_registry();
	fiber_t::fibers_init();
	init_root();
	create_directory_object( (PWSTR) L"\\" );
	create_directory_object( (PWSTR) L"\\??" );
	unicode_string_t link_name, link_target;
	link_name.set( L"\\DosDevices" );
	link_target.copy( L"\\??" );
	create_symlink( link_name, link_target );
	create_directory_object( (PWSTR) L"\\Device" );
	create_directory_object( (PWSTR) L"\\Device\\MailSlot" );
	create_directory_object( (PWSTR) L"\\Security" );
	//create_directory_object( (PWSTR) L"\\DosDevices" );
	create_directory_object( (PWSTR) L"\\BaseNamedObjects" );
	create_sync_event( (PWSTR) L"\\Security\\LSA_AUTHENTICATION_INITIALIZED" );
	create_sync_event( (PWSTR) L"\\SeLsaInitEvent" );
	init_random();
	init_pipe_device();
	// XP
	create_directory_object( (PWSTR) L"\\KernelObjects" );
	create_sync_event( (PWSTR) L"\\KernelObjects\\CritSecOutOfMemoryEvent" );
	init_drives();
	init_ntdll();
	create_kthread();

	us.copy( exename );

	int r = create_initial_process( &initial_thread, us );
	if (r < STATUS_SUCCESS)
		die("create_initial_process() failed (%08x)\n", r);

	// run the main loop
	schedule();

	ntgdi_fini();
	r = initial_thread->process->ExitStatus;
	//fprintf(stderr, "process exited (%08x)\n", r);
	release( initial_thread );

	shutdown_kthread();
	do_cleanup();

	free_root();
	fiber_t::fibers_finish();
	free_registry();
	free_ntdll();

	return r;
}
<|endoftext|>


<commit_before>/*******************************************************************************
 * tests/core/test_hash_table.cpp
 *
 * Part of Project c7a.
 *
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#include <c7a/core/reduce_pre_table.hpp>
#include <c7a/core/reduce_post_table.hpp>
#include <tests/c7a_tests.hpp>
#include <c7a/api/context.hpp>

#include <functional>
#include <cstdio>
#include <utility>
#include <vector>
#include <string>

#include "gtest/gtest.h"

TEST(PreTable, CreateEmptyTable) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    assert(table.Size() == 0);
}

TEST(PostTable, CreateEmptyTable) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    assert(table.Size() == 0);
}

TEST(PreTable, AddIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    assert(table.Size() == 3);

    table.Insert(2);

    assert(table.Size() == 3);
}

TEST(PostTable, AddIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    table.Print();

    assert(table.Size() == 3);

    table.Insert(2);

    table.Print();

    assert(table.Size() == 3);
}

TEST(PreTable, PopIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    table.SetMaxSize(3);

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);
    table.Insert(4);

    assert(table.Size() == 0);

    table.Insert(1);

    assert(table.Size() == 1);
}

TEST(PreTable, FlushIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    assert(table.Size() == 3);

    table.Flush();

    assert(table.Size() == 0);

    table.Insert(1);

    assert(table.Size() == 1);
}

TEST(PostTable, FlushIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    assert(table.Size() == 3);

    table.Flush();

    assert(table.Size() == 0);

    table.Insert(1);

    assert(table.Size() == 1);
}

TEST(PostTable, MultipleEmitters) {
    std::vector<int> vec1;

    auto emit1 = [&vec1](int in) {
                     vec1.push_back(in);
                 };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    std::vector<decltype(emit1)> emitters;
    emitters.push_back(emit1);
    emitters.push_back(emit1);
    emitters.push_back(emit1);

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit1)>
    table(key_ex, red_fn, emitters);

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    assert(table.Size() == 3);

    table.Flush();

    assert(table.Size() == 0);

    table.Insert(1);

    assert(table.Size() == 1);

    assert(vec1.size() == 9);
}

TEST(PreTable, ComplexType) {
    using StringPair = std::pair<std::string, double>;

    auto emit = [](StringPair in) {
                    std::cout << in.second << std::endl;
                };

    auto key_ex = [](StringPair in) {
                      return in.first;
                  };

    auto red_fn = [](StringPair in1, StringPair in2) {
                      return std::make_pair(in1.first, in1.second + in2.second);
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, 2, 2, 10, 3, key_ex, red_fn, { emit });

    table.Insert(std::make_pair("hallo", 1));
    table.Insert(std::make_pair("hello", 2));
    table.Insert(std::make_pair("bonjour", 3));

    assert(table.Size() == 3);

    table.Insert(std::make_pair("hello", 5));

    assert(table.Size() == 3);

    table.Insert(std::make_pair("baguette", 42));

    assert(table.Size() == 0);
}

TEST(PreTable, Resize) {
    using StringPair = std::pair<std::string, double>;

    auto emit = [](StringPair in) {
                    std::cout << in.second << std::endl;
                };

    auto key_ex = [](StringPair in) { return in.first; };

    auto red_fn = [](StringPair in1, StringPair in2) {
                      return std::make_pair(in1.first, in1.second + in2.second);
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, 10, 2, 1, 10, key_ex, red_fn, { emit });

    assert(table.NumBuckets() == 10);

    table.Insert(std::make_pair("hallo", 1));
    table.Insert(std::make_pair("hello", 2));

    assert(table.NumBuckets() == 10);

    table.Insert(std::make_pair("bonjour", 3));

    assert(table.NumBuckets() == 20);
}

TEST(PostTable, ComplexType) {
    using StringPair = std::pair<std::string, double>;

    auto emit = [](StringPair in) {
                    std::cout << in.second << std::endl;
                };

    auto key_ex = [](StringPair in) {
                      return in.first;
                  };

    auto red_fn = [](StringPair in1, StringPair in2) {
                      return std::make_pair(in1.first, in1.second + in2.second);
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    table.Insert(std::make_pair("hallo", 1));
    table.Insert(std::make_pair("hello", 2));
    table.Insert(std::make_pair("bonjour", 3));

    assert(table.Size() == 3);

    table.Insert(std::make_pair("hello", 5));

    assert(table.Size() == 3);

    table.Insert(std::make_pair("baguette", 42));

    assert(table.Size() == 4);
}

TEST(PreTable, MultipleWorkers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(2, key_ex, red_fn, { emit });

    assert(table.Size() == 0);
    table.SetMaxSize(5);

    for (int i = 0; i < 6; i++) {
        table.Insert(i * 35001);
    }

    assert(table.Size() <= 3);
    assert(table.Size() > 0);
}

// TODO(ms): add one test with a for loop inserting 10000 items. -> trigger

/******************************************************************************/
<commit_msg>CA-71 #comment fix assertions in hash table tests<commit_after>/*******************************************************************************
 * tests/core/test_hash_table.cpp
 *
 * Part of Project c7a.
 *
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#include <c7a/core/reduce_pre_table.hpp>
#include <c7a/core/reduce_post_table.hpp>
#include <tests/c7a_tests.hpp>
#include <c7a/api/context.hpp>

#include <functional>
#include <cstdio>
#include <utility>
#include <vector>
#include <string>

#include "gtest/gtest.h"

TEST(PreTable, CreateEmptyTable) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    ASSERT_EQ(0, table.Size());
}

TEST(PostTable, CreateEmptyTable) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    ASSERT_EQ(0, table.Size());
}

TEST(PreTable, AddIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    ASSERT_EQ(3, table.Size());

    table.Insert(2);

    ASSERT_EQ(3, table.Size());
}

TEST(PostTable, AddIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    table.Print();

    ASSERT_EQ(3, table.Size());

    table.Insert(2);

    table.Print();

    ASSERT_EQ(3, table.Size());
}

TEST(PreTable, PopIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    table.SetMaxSize(3);

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);
    table.Insert(4);

    ASSERT_EQ(0, table.Size());

    table.Insert(1);

    ASSERT_EQ(1, table.Size());
}

TEST(PreTable, FlushIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    ASSERT_EQ(3, table.Size());

    table.Flush();

    ASSERT_EQ(0, table.Size());

    table.Insert(1);

    ASSERT_EQ(1, table.Size());
}

TEST(PostTable, FlushIntegers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    ASSERT_EQ(3, table.Size());

    table.Flush();

    ASSERT_EQ(0, table.Size());

    table.Insert(1);

    ASSERT_EQ(1, table.Size());
}

TEST(PostTable, MultipleEmitters) {
    std::vector<int> vec1;

    auto emit1 = [&vec1](int in) {
                     vec1.push_back(in);
                 };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    std::vector<decltype(emit1)> emitters;
    emitters.push_back(emit1);
    emitters.push_back(emit1);
    emitters.push_back(emit1);

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit1)>
    table(key_ex, red_fn, emitters);

    table.Insert(1);
    table.Insert(2);
    table.Insert(3);

    ASSERT_EQ(3, table.Size());

    table.Flush();

    ASSERT_EQ(0, table.Size());

    table.Insert(1);

    ASSERT_EQ(1, table.Size());

    ASSERT_EQ(9, vec1.size());
}

TEST(PreTable, ComplexType) {
    using StringPair = std::pair<std::string, double>;

    auto emit = [](StringPair in) {
                    std::cout << in.second << std::endl;
                };

    auto key_ex = [](StringPair in) {
                      return in.first;
                  };

    auto red_fn = [](StringPair in1, StringPair in2) {
                      return std::make_pair(in1.first, in1.second + in2.second);
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, 2, 2, 10, 3, key_ex, red_fn, { emit });

    table.Insert(std::make_pair("hallo", 1));
    table.Insert(std::make_pair("hello", 2));
    table.Insert(std::make_pair("bonjour", 3));

    ASSERT_EQ(3, table.Size());

    table.Insert(std::make_pair("hello", 5));

    ASSERT_EQ(3, table.Size());

    table.Insert(std::make_pair("baguette", 42));

    ASSERT_EQ(0, table.Size());
}

TEST(PreTable, Resize) {
    using StringPair = std::pair<std::string, double>;

    auto emit = [](StringPair in) {
                    std::cout << in.second << std::endl;
                };

    auto key_ex = [](StringPair in) { return in.first; };

    auto red_fn = [](StringPair in1, StringPair in2) {
                      return std::make_pair(in1.first, in1.second + in2.second);
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(1, 10, 2, 1, 10, key_ex, red_fn, { emit });

    ASSERT_EQ(10, table.NumBuckets());

    table.Insert(std::make_pair("hallo", 1));
    table.Insert(std::make_pair("hello", 2));

    ASSERT_EQ(10, table.NumBuckets());

    table.Insert(std::make_pair("bonjour", 3));

    ASSERT_EQ(20, table.NumBuckets());
}

TEST(PostTable, ComplexType) {
    using StringPair = std::pair<std::string, double>;

    auto emit = [](StringPair in) {
                    std::cout << in.second << std::endl;
                };

    auto key_ex = [](StringPair in) {
                      return in.first;
                  };

    auto red_fn = [](StringPair in1, StringPair in2) {
                      return std::make_pair(in1.first, in1.second + in2.second);
                  };

    c7a::core::ReducePostTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(key_ex, red_fn, { emit });

    table.Insert(std::make_pair("hallo", 1));
    table.Insert(std::make_pair("hello", 2));
    table.Insert(std::make_pair("bonjour", 3));

    ASSERT_EQ(3, table.Size());

    table.Insert(std::make_pair("hello", 5));

    ASSERT_EQ(3, table.Size());

    table.Insert(std::make_pair("baguette", 42));

    ASSERT_EQ(4, table.Size());
}

TEST(PreTable, MultipleWorkers) {
    auto emit = [](int in) {
                    std::cout << in << std::endl;
                };

    auto key_ex = [](int in) {
                      return in;
                  };

    auto red_fn = [](int in1, int in2) {
                      return in1 + in2;
                  };

    c7a::core::ReducePreTable<decltype(key_ex), decltype(red_fn), decltype(emit)>
    table(2, key_ex, red_fn, { emit });

    ASSERT_EQ(0, table.Size());
    table.SetMaxSize(5);

    for (int i = 0; i < 6; i++) {
        table.Insert(i * 35001);
    }

    ASSERT_LE(table.Size(), 3);
    ASSERT_GT(table.Size(), 0);
}

// TODO(ms): add one test with a for loop inserting 10000 items. -> trigger

/******************************************************************************/
<|endoftext|>


<commit_before>/**
 * @file PreviewController.cpp
 *
 * @author Xu, Yuan
 * Preview controller for the Inverted pendulum
 */

#include <fstream>
#include <map>
#include <sstream>
#include <iostream>

#include "PreviewController.h"
#include <PlatformInterface/Platform.h>
#include <Tools/DataConversion.h>

PreviewController::PreviewController()
:parameters(NULL),
theHeight(0)
{
}

void PreviewController::setHeight(double height)
{
  unsigned int newHeight = (unsigned int)Math::round(height);
  ASSERT(newHeight>=200);
  ASSERT(newHeight<=300);

  if ( theHeight != newHeight )
  {
    theHeight = newHeight;
    if ( loadedParameters.find(theHeight) == loadedParameters.end() )
    {
      string path =naoth::Platform::getInstance().theConfigDirectory + "scheme/"
            + naoth::Platform::getInstance().theScheme + "/previewControl/";
      path += DataConversion::toStr(theHeight)+".prm";
      cout<<"PreviewController load "<<path<<endl;

      ifstream ifs(path.c_str());
      ASSERT(ifs.good());
      ifs >> loadedParameters[theHeight];
    }
    parameters = &(loadedParameters[theHeight]);
  }
}

void PreviewController::update(const std::list<double>& ref, Vector3<double>&x, double& err) const
{
  double u = -parameters->Ki * err - parameters->K*x;
  std::list<double>::const_iterator refi = ref.begin();
  std::list<double>::const_iterator fi = parameters->f.begin();
  for (; refi != ref.end() && fi != parameters->f.end(); ++fi, ++refi)
  {
    u -= (*fi) * (*refi);
  }

  x = parameters->A * x + parameters->b*u;
  double zmp = parameters->c*x;
  err += (zmp - ref.front());
}

bool PreviewController::ready() const
{
  if (parameters == NULL)
    return false;

  size_t size = previewSteps() - 1;
  return refZMPx.size() >= size && refZMPy.size() >= size;
}

void PreviewController::control(Vector3<double>& com, Vector2<double>& dcom, Vector2<double>& ddcom)
{
  ASSERT(ready());
  double h = refZMPz.front();
  setHeight(h);
  update(refZMPx, theX, theErr.x);
  update(refZMPy, theY, theErr.y);
  refZMPx.pop_front();
  refZMPy.pop_front();
  refZMPz.pop_front();

  com.x = theX[0];
  dcom.x = theX[1];
  ddcom.x = theX[2];
  com.y = theY[0];
  dcom.y = theY[1];
  ddcom.y = theY[2];
  com.z = h;
}

void PreviewController::clear()
{
  refZMPx.clear();
  refZMPy.clear();
  refZMPz.clear();
  theErr = Vector2<double>(0, 0);
}

void PreviewController::init(const Vector3d& com, const Vector2<double>& dcom, const Vector2<double>& ddcom)
{
  setHeight(com.z);

  theX[0] = com.x;
  theX[1] = dcom.x;
  theX[2] = ddcom.x;

  theY[0] = com.y;
  theY[1] = dcom.y;
  theY[2] = ddcom.y;
}

void PreviewController::push(const Vector3d& zmp)
{
  refZMPx.push_back(zmp.x);
  refZMPy.push_back(zmp.y);
  refZMPz.push_back(zmp.z);
}

Vector3d PreviewController::front() const
{
  return Vector3d(refZMPx.front(), refZMPy.front(), refZMPz.front());
}

Vector3d PreviewController::back() const
{
  return Vector3d(refZMPx.back(), refZMPy.back(), refZMPz.back());
}

std::istream & operator >>(std::istream& ist, PreviewController::Parameters& p)
{
  ist >> p.A >> p.b >> p.c >> p.Ki >> p.K;

  double v;
  while (!ist.eof())
  {
    ist >> v;
    p.f.push_back(v);
  }
  return ist;
}
<commit_msg>stand is not necessary before walking<commit_after>/**
 * @file PreviewController.cpp
 *
 * @author Xu, Yuan
 * Preview controller for the Inverted pendulum
 */

#include <fstream>
#include <map>
#include <sstream>
#include <iostream>

#include "PreviewController.h"
#include <PlatformInterface/Platform.h>
#include <Tools/DataConversion.h>

PreviewController::PreviewController()
:parameters(NULL),
theHeight(0)
{
}

void PreviewController::setHeight(double height)
{
  unsigned int newHeight = (unsigned int)Math::round(height);
  newHeight = Math::clamp(newHeight, 200u, 300u);

  if ( theHeight != newHeight )
  {
    theHeight = newHeight;
    if ( loadedParameters.find(theHeight) == loadedParameters.end() )
    {
      string path =naoth::Platform::getInstance().theConfigDirectory + "scheme/"
            + naoth::Platform::getInstance().theScheme + "/previewControl/";
      path += DataConversion::toStr(theHeight)+".prm";
      cout<<"PreviewController load "<<path<<endl;

      ifstream ifs(path.c_str());
      ASSERT(ifs.good());
      ifs >> loadedParameters[theHeight];
    }
    parameters = &(loadedParameters[theHeight]);
  }
}

void PreviewController::update(const std::list<double>& ref, Vector3<double>&x, double& err) const
{
  double u = -parameters->Ki * err - parameters->K*x;
  std::list<double>::const_iterator refi = ref.begin();
  std::list<double>::const_iterator fi = parameters->f.begin();
  for (; refi != ref.end() && fi != parameters->f.end(); ++fi, ++refi)
  {
    u -= (*fi) * (*refi);
  }

  x = parameters->A * x + parameters->b*u;
  double zmp = parameters->c*x;
  err += (zmp - ref.front());
}

bool PreviewController::ready() const
{
  if (parameters == NULL)
    return false;

  size_t size = previewSteps() - 1;
  return refZMPx.size() >= size && refZMPy.size() >= size;
}

void PreviewController::control(Vector3<double>& com, Vector2<double>& dcom, Vector2<double>& ddcom)
{
  ASSERT(ready());
  double h = refZMPz.front();
  setHeight(h);
  update(refZMPx, theX, theErr.x);
  update(refZMPy, theY, theErr.y);
  refZMPx.pop_front();
  refZMPy.pop_front();
  refZMPz.pop_front();

  com.x = theX[0];
  dcom.x = theX[1];
  ddcom.x = theX[2];
  com.y = theY[0];
  dcom.y = theY[1];
  ddcom.y = theY[2];
  com.z = h;
}

void PreviewController::clear()
{
  refZMPx.clear();
  refZMPy.clear();
  refZMPz.clear();
  theErr = Vector2<double>(0, 0);
}

void PreviewController::init(const Vector3d& com, const Vector2<double>& dcom, const Vector2<double>& ddcom)
{
  setHeight(com.z);

  theX[0] = com.x;
  theX[1] = dcom.x;
  theX[2] = ddcom.x;

  theY[0] = com.y;
  theY[1] = dcom.y;
  theY[2] = ddcom.y;
}

void PreviewController::push(const Vector3d& zmp)
{
  refZMPx.push_back(zmp.x);
  refZMPy.push_back(zmp.y);
  refZMPz.push_back(zmp.z);
}

Vector3d PreviewController::front() const
{
  return Vector3d(refZMPx.front(), refZMPy.front(), refZMPz.front());
}

Vector3d PreviewController::back() const
{
  return Vector3d(refZMPx.back(), refZMPy.back(), refZMPz.back());
}

std::istream & operator >>(std::istream& ist, PreviewController::Parameters& p)
{
  ist >> p.A >> p.b >> p.c >> p.Ki >> p.K;

  double v;
  while (!ist.eof())
  {
    ist >> v;
    p.f.push_back(v);
  }
  return ist;
}
<|endoftext|>


<commit_before>/* Copyright (c) 2020-2021 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 "gtest/gtest.h"
#include "gmock/gmock.h"
#include "blockdevice/internal/SFDP.h"

using ::testing::_;
using ::testing::MockFunction;
using ::testing::Return;

/**
 * The Sector Map Parameter Table of Cypress S25FS512S:
 * https://www.cypress.com/file/216376/download Table 71.
 */
static const mbed::bd_addr_t sector_map_start_addr = 0xD81000;

/**
 * Based on Cypress S25FS512S, modified to have one descriptor,
 * three regions for test purpose.
 */
static const uint8_t sector_map_single_descriptor[] {
    0xFF, 0x01, 0x02, 0xFF,     // header, highest region = 0x02
    0xF1, 0x7F, 0x00, 0x00,     // region 0
    0xF4, 0x7F, 0x03, 0x00,     // region 1
    0xF4, 0xFF, 0xFB, 0x03      // region 2
};

/**
 * Based on Cypress S25FS512S, modified to have one descriptor,
 * twelve regions for test purpose.
 */
static const uint8_t sector_map_single_descriptor_twelve_regions[] {
    0xFF, 0x01, 0x0B, 0xFF,     // header, highest region = 0x0B
    0xF1, 0x7F, 0x00, 0x00,     // region 0
    0xF4, 0x7F, 0x03, 0x00,     // region 1
    0xF4, 0xFF, 0xFB, 0x03,     // region 2
    0xF1, 0x7F, 0x00, 0x00,     // region 3
    0xF4, 0x7F, 0x03, 0x00,     // region 4
    0xF4, 0xFF, 0xFB, 0x03,     // region 5
    0xF1, 0x7F, 0x00, 0x00,     // region 6
    0xF4, 0x7F, 0x03, 0x00,     // region 7
    0xF4, 0xFF, 0xFB, 0x03,     // region 8
    0xF1, 0x7F, 0x00, 0x00,     // region 9
    0xF4, 0x7F, 0x03, 0x00,     // region 10
    0xF4, 0xFF, 0xFB, 0x03,     // region 11
};

class TestSFDP : public testing::Test {

public:
    mbed::Callback<int(mbed::bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void*, bd_size_t)> sfdp_reader_callback;

protected:
    TestSFDP() : sfdp_reader_callback(this, &TestSFDP::sfdp_reader) {};

    int sfdp_reader(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size, uint8_t instr, uint8_t cycles, void *buff, bd_size_t buff_size)
    {
        int mock_return = sfdp_reader_mock.Call(addr, addr_size, instr, cycles, buff, buff_size);
        if (mock_return != 0) {
            return mock_return;
        }

        memcpy(buff, sector_descriptors, sector_descriptors_size);
        return 0;
    };

    void set_sector_map_param_table(mbed::sfdp_smptbl_info &smptbl, const uint8_t *table, const size_t table_size)
    {
        smptbl.size = table_size;
        smptbl.addr = sector_map_start_addr;

        sector_descriptors = table;
        sector_descriptors_size = table_size;
    }

    MockFunction<int(mbed::bd_addr_t, uint8_t, uint8_t, uint8_t, void*, bd_size_t)> sfdp_reader_mock;
    const uint8_t *sector_descriptors;
    bd_size_t sector_descriptors_size;
};

/**
 * Utilities for conversions to bytes.
 */
namespace{
    auto operator "" _B(unsigned long long int size) {
        return size;
    }

    auto operator "" _KB(unsigned long long int size) {
        return size * 1024;
    }

    auto operator "" _MB(unsigned long long int size) {
        return size * 1024 * 1024;
    }
}

/**
 * Test if sfdp_iterate_next_largest_erase_type() returns the most
 * optimal erase type, whose erase size is as large as possible
 * while being compatible with both alignment and size requirements.
 */
TEST_F(TestSFDP, TestEraseTypeAlgorithm)
{
    // Erase 104KB starting at 92KB
    int address = 92 * 1024;
    int size = 104 * 1024;
    int region = 1;
    int type;

    // The mock flash supports 4KB, 32KB and 64KB erase types
    struct mbed::sfdp_smptbl_info smptbl;
    smptbl.erase_type_size_arr[0] = 4 * 1024;
    smptbl.erase_type_size_arr[1] = 32 * 1024;
    smptbl.erase_type_size_arr[2] = 64 * 1024;

    // The mock flash has three regions, with address ranges:
    // * 0 to 64KB - 1B
    // * 64KB to 256KB - 1B
    // * 256KB to 1024KB - 1B
    smptbl.region_high_boundary[0] = 64 * 1024 - 1;
    smptbl.region_high_boundary[1] = 256 * 1024 - 1;
    smptbl.region_high_boundary[2] = 1024 * 1024 - 1;

    // Bitfields indicating which regions support which erase types
    smptbl.region_erase_types_bitfld[0] = 0b0001;   // 4KB only
    smptbl.region_erase_types_bitfld[1] = 0b0111;   // 64KB, 32KB, 4KB
    smptbl.region_erase_types_bitfld[2] = 0b0110;   // 64KB, 32KB

    // Expected outcome:
    // * The starting position 92KB is 4KB-aligned
    // * The next position 96KB (92KB + 4KB) is 32KB-aligned
    // * The next position 128KB (96KB + 32KB) is 64KB-aligned
    // * At the final position 192KB (128KB + 64KB), we only
    //   have 4KB (104KB - 4KB - 32KB - 64KB) remaining to erase
    int expected_erase_KBs[] = {4, 32, 64, 4};

    for (int i = 0; i < sizeof(expected_erase_KBs) / sizeof(int); i++) {
        type = sfdp_iterate_next_largest_erase_type(
                    smptbl.region_erase_types_bitfld[region],
                    size,
                    address,
                    region,
                    smptbl);
        int erase_size = smptbl.erase_type_size_arr[type];
        EXPECT_EQ(erase_size, expected_erase_KBs[i] * 1024);
        address += erase_size;
        size -= erase_size;
    }

    EXPECT_EQ(size, 0); // All erased

    // Test unaligned erase
    // Region 2 only allows at least 32KB-aligned erases
    address = (512 + 16) * 1024;
    size = 64 * 1024;
    region = 2;
    type = sfdp_iterate_next_largest_erase_type(
                smptbl.region_erase_types_bitfld[region],
                size,
                address,
                region,
                smptbl);
    EXPECT_EQ(type, -1); // Invalid erase
}

/**
 * Test that sfdp_parse_sector_map_table() treats a whole flash
 * as one region if no sector map is available.
 */
TEST_F(TestSFDP, TestNoSectorMap)
{
    const bd_size_t device_size = 512_KB;

    mbed::sfdp_hdr_info header_info;
    header_info.smptbl.size = 0; // No Sector Map Table
    header_info.bptbl.device_size_bytes = device_size;

    // No need to read anything
    EXPECT_CALL(sfdp_reader_mock, Call(_, _, _, _, _, _)).Times(0);

    EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));

    EXPECT_EQ(1, header_info.smptbl.region_cnt);
    EXPECT_EQ(device_size, header_info.smptbl.region_size[0]);
    EXPECT_EQ(device_size - 1, header_info.smptbl.region_high_boundary[0]);
}

/**
 * When a Sector Map Parameter Table has a single descriptor (i.e. non-configurable flash layout).
 */
TEST_F(TestSFDP, TestSingleSectorConfig)
{
    mbed::sfdp_hdr_info header_info;
    set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));

    EXPECT_CALL(
        sfdp_reader_mock,
        Call(
            sector_map_start_addr,
            mbed::SFDP_READ_CMD_ADDR_TYPE,
            mbed::SFDP_READ_CMD_INST,
            mbed::SFDP_READ_CMD_DUMMY_CYCLES,
            _,
            sizeof(sector_map_single_descriptor)
        )
    ).Times(1).WillOnce(Return(0));

    EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));

    // Three regions
    EXPECT_EQ(3, header_info.smptbl.region_cnt);

    // Region 0: erase type 1 (32KB erase)
    EXPECT_EQ(32_KB, header_info.smptbl.region_size[0]);
    EXPECT_EQ(32_KB - 1_B, header_info.smptbl.region_high_boundary[0]);
    EXPECT_EQ(1 << (1 - 1), header_info.smptbl.region_erase_types_bitfld[0]);

    // Region 1: erase type 3 (256KB erase which includes 32KB from Region 0)
    EXPECT_EQ(224_KB, header_info.smptbl.region_size[1]);
    EXPECT_EQ(256_KB - 1_B, header_info.smptbl.region_high_boundary[1]);
    EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[1]);

    // Region 2: erase type 3 (256KB erase)
    EXPECT_EQ(64_MB - 32_KB - 224_KB, header_info.smptbl.region_size[2]);
    EXPECT_EQ(64_MB - 1_B, header_info.smptbl.region_high_boundary[2]);
    EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[2]);
}

/**
 * When an SFDP reader fails to read data requested by sfdp_parse_sector_map_table().
 */
TEST_F(TestSFDP, TestSFDPReadFailure)
{
    mbed::sfdp_hdr_info header_info;
    set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));

    EXPECT_CALL(
        sfdp_reader_mock,
        Call(
            sector_map_start_addr,
            mbed::SFDP_READ_CMD_ADDR_TYPE,
            mbed::SFDP_READ_CMD_INST,
            mbed::SFDP_READ_CMD_DUMMY_CYCLES,
            _,
            sizeof(sector_map_single_descriptor)
        )
    ).Times(1).WillOnce(Return(-1)); // Emulate read failure

    EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
}

/**
 * When a flash layout has more regions than Mbed OS supports (10).
 * Note: This is unlikely to happens in practice.
 */
TEST_F(TestSFDP, TestMoreRegionsThanSupported)
{
    mbed::sfdp_hdr_info header_info;
    set_sector_map_param_table(
        header_info.smptbl,
        sector_map_single_descriptor_twelve_regions,
        sizeof(sector_map_single_descriptor_twelve_regions)
    );

    EXPECT_CALL(
        sfdp_reader_mock,
        Call(
            sector_map_start_addr,
            mbed::SFDP_READ_CMD_ADDR_TYPE,
            mbed::SFDP_READ_CMD_INST,
            mbed::SFDP_READ_CMD_DUMMY_CYCLES,
            _,
            sizeof(sector_map_single_descriptor_twelve_regions)
        )
    ).Times(1).WillOnce(Return(0));

    EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
}
<commit_msg>SFDP: unit tests: Move unshared test data into case<commit_after>/* Copyright (c) 2020-2021 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 "gtest/gtest.h"
#include "gmock/gmock.h"
#include "blockdevice/internal/SFDP.h"

using ::testing::_;
using ::testing::MockFunction;
using ::testing::Return;

/**
 * The Sector Map Parameter Table of Cypress S25FS512S:
 * https://www.cypress.com/file/216376/download Table 71.
 */
static const mbed::bd_addr_t sector_map_start_addr = 0xD81000;

/**
 * Based on Cypress S25FS512S, modified to have one descriptor,
 * three regions for test purpose.
 */
static const uint8_t sector_map_single_descriptor[] {
    0xFF, 0x01, 0x02, 0xFF,     // header, highest region = 0x02
    0xF1, 0x7F, 0x00, 0x00,     // region 0
    0xF4, 0x7F, 0x03, 0x00,     // region 1
    0xF4, 0xFF, 0xFB, 0x03      // region 2
};

class TestSFDP : public testing::Test {

public:
    mbed::Callback<int(mbed::bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void*, bd_size_t)> sfdp_reader_callback;

protected:
    TestSFDP() : sfdp_reader_callback(this, &TestSFDP::sfdp_reader) {};

    int sfdp_reader(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size, uint8_t instr, uint8_t cycles, void *buff, bd_size_t buff_size)
    {
        int mock_return = sfdp_reader_mock.Call(addr, addr_size, instr, cycles, buff, buff_size);
        if (mock_return != 0) {
            return mock_return;
        }

        memcpy(buff, sector_descriptors, sector_descriptors_size);
        return 0;
    };

    void set_sector_map_param_table(mbed::sfdp_smptbl_info &smptbl, const uint8_t *table, const size_t table_size)
    {
        smptbl.size = table_size;
        smptbl.addr = sector_map_start_addr;

        sector_descriptors = table;
        sector_descriptors_size = table_size;
    }

    MockFunction<int(mbed::bd_addr_t, uint8_t, uint8_t, uint8_t, void*, bd_size_t)> sfdp_reader_mock;
    const uint8_t *sector_descriptors;
    bd_size_t sector_descriptors_size;
};

/**
 * Utilities for conversions to bytes.
 */
namespace{
    auto operator "" _B(unsigned long long int size) {
        return size;
    }

    auto operator "" _KB(unsigned long long int size) {
        return size * 1024;
    }

    auto operator "" _MB(unsigned long long int size) {
        return size * 1024 * 1024;
    }
}

/**
 * Test if sfdp_iterate_next_largest_erase_type() returns the most
 * optimal erase type, whose erase size is as large as possible
 * while being compatible with both alignment and size requirements.
 */
TEST_F(TestSFDP, TestEraseTypeAlgorithm)
{
    // Erase 104KB starting at 92KB
    int address = 92 * 1024;
    int size = 104 * 1024;
    int region = 1;
    int type;

    // The mock flash supports 4KB, 32KB and 64KB erase types
    struct mbed::sfdp_smptbl_info smptbl;
    smptbl.erase_type_size_arr[0] = 4 * 1024;
    smptbl.erase_type_size_arr[1] = 32 * 1024;
    smptbl.erase_type_size_arr[2] = 64 * 1024;

    // The mock flash has three regions, with address ranges:
    // * 0 to 64KB - 1B
    // * 64KB to 256KB - 1B
    // * 256KB to 1024KB - 1B
    smptbl.region_high_boundary[0] = 64 * 1024 - 1;
    smptbl.region_high_boundary[1] = 256 * 1024 - 1;
    smptbl.region_high_boundary[2] = 1024 * 1024 - 1;

    // Bitfields indicating which regions support which erase types
    smptbl.region_erase_types_bitfld[0] = 0b0001;   // 4KB only
    smptbl.region_erase_types_bitfld[1] = 0b0111;   // 64KB, 32KB, 4KB
    smptbl.region_erase_types_bitfld[2] = 0b0110;   // 64KB, 32KB

    // Expected outcome:
    // * The starting position 92KB is 4KB-aligned
    // * The next position 96KB (92KB + 4KB) is 32KB-aligned
    // * The next position 128KB (96KB + 32KB) is 64KB-aligned
    // * At the final position 192KB (128KB + 64KB), we only
    //   have 4KB (104KB - 4KB - 32KB - 64KB) remaining to erase
    int expected_erase_KBs[] = {4, 32, 64, 4};

    for (int i = 0; i < sizeof(expected_erase_KBs) / sizeof(int); i++) {
        type = sfdp_iterate_next_largest_erase_type(
                    smptbl.region_erase_types_bitfld[region],
                    size,
                    address,
                    region,
                    smptbl);
        int erase_size = smptbl.erase_type_size_arr[type];
        EXPECT_EQ(erase_size, expected_erase_KBs[i] * 1024);
        address += erase_size;
        size -= erase_size;
    }

    EXPECT_EQ(size, 0); // All erased

    // Test unaligned erase
    // Region 2 only allows at least 32KB-aligned erases
    address = (512 + 16) * 1024;
    size = 64 * 1024;
    region = 2;
    type = sfdp_iterate_next_largest_erase_type(
                smptbl.region_erase_types_bitfld[region],
                size,
                address,
                region,
                smptbl);
    EXPECT_EQ(type, -1); // Invalid erase
}

/**
 * Test that sfdp_parse_sector_map_table() treats a whole flash
 * as one region if no sector map is available.
 */
TEST_F(TestSFDP, TestNoSectorMap)
{
    const bd_size_t device_size = 512_KB;

    mbed::sfdp_hdr_info header_info;
    header_info.smptbl.size = 0; // No Sector Map Table
    header_info.bptbl.device_size_bytes = device_size;

    // No need to read anything
    EXPECT_CALL(sfdp_reader_mock, Call(_, _, _, _, _, _)).Times(0);

    EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));

    EXPECT_EQ(1, header_info.smptbl.region_cnt);
    EXPECT_EQ(device_size, header_info.smptbl.region_size[0]);
    EXPECT_EQ(device_size - 1, header_info.smptbl.region_high_boundary[0]);
}

/**
 * When a Sector Map Parameter Table has a single descriptor (i.e. non-configurable flash layout).
 */
TEST_F(TestSFDP, TestSingleSectorConfig)
{
    mbed::sfdp_hdr_info header_info;
    set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));

    EXPECT_CALL(
        sfdp_reader_mock,
        Call(
            sector_map_start_addr,
            mbed::SFDP_READ_CMD_ADDR_TYPE,
            mbed::SFDP_READ_CMD_INST,
            mbed::SFDP_READ_CMD_DUMMY_CYCLES,
            _,
            sizeof(sector_map_single_descriptor)
        )
    ).Times(1).WillOnce(Return(0));

    EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));

    // Three regions
    EXPECT_EQ(3, header_info.smptbl.region_cnt);

    // Region 0: erase type 1 (32KB erase)
    EXPECT_EQ(32_KB, header_info.smptbl.region_size[0]);
    EXPECT_EQ(32_KB - 1_B, header_info.smptbl.region_high_boundary[0]);
    EXPECT_EQ(1 << (1 - 1), header_info.smptbl.region_erase_types_bitfld[0]);

    // Region 1: erase type 3 (256KB erase which includes 32KB from Region 0)
    EXPECT_EQ(224_KB, header_info.smptbl.region_size[1]);
    EXPECT_EQ(256_KB - 1_B, header_info.smptbl.region_high_boundary[1]);
    EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[1]);

    // Region 2: erase type 3 (256KB erase)
    EXPECT_EQ(64_MB - 32_KB - 224_KB, header_info.smptbl.region_size[2]);
    EXPECT_EQ(64_MB - 1_B, header_info.smptbl.region_high_boundary[2]);
    EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[2]);
}

/**
 * When an SFDP reader fails to read data requested by sfdp_parse_sector_map_table().
 */
TEST_F(TestSFDP, TestSFDPReadFailure)
{
    mbed::sfdp_hdr_info header_info;
    set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));

    EXPECT_CALL(
        sfdp_reader_mock,
        Call(
            sector_map_start_addr,
            mbed::SFDP_READ_CMD_ADDR_TYPE,
            mbed::SFDP_READ_CMD_INST,
            mbed::SFDP_READ_CMD_DUMMY_CYCLES,
            _,
            sizeof(sector_map_single_descriptor)
        )
    ).Times(1).WillOnce(Return(-1)); // Emulate read failure

    EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
}

/**
 * When a flash layout has more regions than Mbed OS supports (10).
 * Note: This is unlikely to happens in practice.
 */
TEST_F(TestSFDP, TestMoreRegionsThanSupported)
{
    /**
     * Based on Cypress S25FS512S, modified to have one descriptor,
     * twelve regions for test purpose.
     */
    const uint8_t sector_map_single_descriptor_twelve_regions[] {
        0xFF, 0x01, 0x0B, 0xFF,     // header, highest region = 0x0B
        0xF1, 0x7F, 0x00, 0x00,     // region 0
        0xF4, 0x7F, 0x03, 0x00,     // region 1
        0xF4, 0xFF, 0xFB, 0x03,     // region 2
        0xF1, 0x7F, 0x00, 0x00,     // region 3
        0xF4, 0x7F, 0x03, 0x00,     // region 4
        0xF4, 0xFF, 0xFB, 0x03,     // region 5
        0xF1, 0x7F, 0x00, 0x00,     // region 6
        0xF4, 0x7F, 0x03, 0x00,     // region 7
        0xF4, 0xFF, 0xFB, 0x03,     // region 8
        0xF1, 0x7F, 0x00, 0x00,     // region 9
        0xF4, 0x7F, 0x03, 0x00,     // region 10
        0xF4, 0xFF, 0xFB, 0x03,     // region 11
    };

    mbed::sfdp_hdr_info header_info;
    set_sector_map_param_table(
        header_info.smptbl,
        sector_map_single_descriptor_twelve_regions,
        sizeof(sector_map_single_descriptor_twelve_regions)
    );

    EXPECT_CALL(
        sfdp_reader_mock,
        Call(
            sector_map_start_addr,
            mbed::SFDP_READ_CMD_ADDR_TYPE,
            mbed::SFDP_READ_CMD_INST,
            mbed::SFDP_READ_CMD_DUMMY_CYCLES,
            _,
            sizeof(sector_map_single_descriptor_twelve_regions)
        )
    ).Times(1).WillOnce(Return(0));

    EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
}
<|endoftext|>


<commit_before><commit_msg>Allow app/popup Windows to be programmatically sent a CURRENT_TAB navigation instead of having it change to a tabbed browser.  Original patch by David Garcia (see http://codereview.chromium.org/505013 ), r=me, tweaked.<commit_after><|endoftext|>