<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2012 Scientific Computing and Imaging Institute,
   University of Utah.

   License for the specific language governing rights and limitations under
   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Dataflow/Network/ModuleStateInterface.h>
#include <Interface/Modules/Base/ModuleDialogGeneric.h>
#include <Core/Logging/Log.h>
#include <boost/foreach.hpp>

using namespace SCIRun::Gui;
using namespace SCIRun::Dataflow::Networks;
using namespace SCIRun::Core::Algorithms;

ModuleDialogGeneric::ModuleDialogGeneric(SCIRun::Dataflow::Networks::ModuleStateHandle state, QWidget* parent) : QDialog(parent),
  state_(state),
  pulling_(false)
{
  setModal(false);

  if (state_)
  {
    //TODO: replace with pull_newVersion
    LOG_DEBUG("ModuleDialogGeneric connecting to state" << std::endl);
    stateConnection_ = state_->connect_state_changed([this]() { pull(); });
  }

  createExecuteAction();
}

ModuleDialogGeneric::~ModuleDialogGeneric()
{
}

void ModuleDialogGeneric::fixSize()
{
  if (minimumWidth() > 0 && minimumHeight() > 0)
  {
    setFixedSize(minimumWidth(), minimumHeight());
  }
}

void ModuleDialogGeneric::createExecuteAction()
{
  executeAction_ = new QAction(this);
  executeAction_->setText("Execute");
  executeAction_->setShortcut(QKeySequence("Ctrl+Shift+E"));
  executeAction_->setIcon(QApplication::style()->standardIcon(QStyle::SP_MediaPlay));
  connect(executeAction_, SIGNAL(triggered()), this, SIGNAL(executeActionTriggered()));
}

void ModuleDialogGeneric::contextMenuEvent(QContextMenuEvent* e)
{
  QMenu menu(this);
  menu.addAction(executeAction_);
  menu.exec(e->globalPos());

  QDialog::contextMenuEvent(e);
}

void ModuleDialogGeneric::addWidgetSlotManager(WidgetSlotManagerPtr ptr)
{
  slotManagers_.push_back(ptr);
}

void ModuleDialogGeneric::pull_newVersionToReplaceOld()
{
  Pulling p(this);
  BOOST_FOREACH(WidgetSlotManagerPtr wsm, slotManagers_)
    wsm->pull();
}

class ComboBoxSlotManager : public WidgetSlotManager
{
public:
  typedef boost::function<std::string(const QString&)> FromQStringConverter;
  typedef boost::function<QString(const std::string&)> ToQStringConverter;
  ComboBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QComboBox* comboBox,
    FromQStringConverter fromLabelConverter = boost::bind(&QString::toStdString, _1),
    ToQStringConverter toLabelConverter = &QString::fromStdString) :
  WidgetSlotManager(state, dialog), stateKey_(stateKey), comboBox_(comboBox), fromLabelConverter_(fromLabelConverter), toLabelConverter_(toLabelConverter)
  {
    connect(comboBox, SIGNAL(activated(const QString&)), this, SLOT(push()));
  }
  ComboBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QComboBox* comboBox,
    const GuiStringTranslationMap& stringMap) :
  WidgetSlotManager(state, dialog), stateKey_(stateKey), comboBox_(comboBox), stringMap_(stringMap)
  {
    fromLabelConverter_ = [this](const QString& qstr) { return stringMap_.left.at(qstr.toStdString()); };
    toLabelConverter_ = [this](const std::string& str) { return QString::fromStdString(stringMap_.right.at(str)); };
    connect(comboBox, SIGNAL(activated(const QString&)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto value = state_->getValue(stateKey_).toString();
    auto qstring = toLabelConverter_(value);
    if (qstring != comboBox_->currentText())
    {
      LOG_DEBUG("In new version of pull code for combobox: " << value);
      comboBox_->setCurrentIndex(comboBox_->findText(qstring));
    }
  }
  virtual void pushImpl() override
  {
    auto label = fromLabelConverter_(comboBox_->currentText());
    if (label != state_->getValue(stateKey_).toString())
    {
      LOG_DEBUG("In new version of push code for combobox: " << label);
      state_->setValue(stateKey_, label);
    }
  }
private:
  AlgorithmParameterName stateKey_;
  QComboBox* comboBox_;
  FromQStringConverter fromLabelConverter_;
  ToQStringConverter toLabelConverter_;
  GuiStringTranslationMap stringMap_;
};

void ModuleDialogGeneric::addComboBoxManager(QComboBox* comboBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<ComboBoxSlotManager>(state_, *this, stateKey, comboBox));
}

void ModuleDialogGeneric::addComboBoxManager(QComboBox* comboBox, const AlgorithmParameterName& stateKey, const GuiStringTranslationMap& stringMap)
{
  addWidgetSlotManager(boost::make_shared<ComboBoxSlotManager>(state_, *this, stateKey, comboBox, stringMap));
}

// ASSUMEs true state = comboBox index 1, false state = comboBox index 0.
class TwoChoiceBooleanComboBoxSlotManager : public WidgetSlotManager
{
public:
  TwoChoiceBooleanComboBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QComboBox* comboBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), comboBox_(comboBox)
  {
    connect(comboBox, SIGNAL(activated(int)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto value = state_->getValue(stateKey_).toBool();
    auto index = value ? 1 : 0;
    if (index != comboBox_->currentIndex())
    {
      LOG_DEBUG("In new version of pull code for combobox, boolean mode: " << index);
      comboBox_->setCurrentIndex(index);
    }
  }
  virtual void pushImpl() override
  {
    auto index = comboBox_->currentIndex();
    if (index != (state_->getValue(stateKey_).toBool() ? 1 : 0))
    {
      LOG_DEBUG("In new version of push code for combobox, boolean mode: " << index);
      state_->setValue(stateKey_, index == 1);
    }
  }
private:
  AlgorithmParameterName stateKey_;
  QComboBox* comboBox_;
};

void ModuleDialogGeneric::addTwoChoiceBooleanComboBoxManager(QComboBox* comboBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<TwoChoiceBooleanComboBoxSlotManager>(state_, *this, stateKey, comboBox));
}

class TextEditSlotManager : public WidgetSlotManager
{
public:
  TextEditSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QTextEdit* textEdit) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), textEdit_(textEdit)
  {
    connect(textEdit, SIGNAL(textChanged()), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = QString::fromStdString(state_->getValue(stateKey_).toString());
    if (newValue != textEdit_->toPlainText())
    {
      textEdit_->setPlainText(newValue);
      LOG_DEBUG("In new version of pull code for TextEdit: " << newValue.toStdString());
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for TextEdit: " << textEdit_->toPlainText().toStdString());
    state_->setValue(stateKey_, textEdit_->toPlainText().toStdString());
  }
private:
  AlgorithmParameterName stateKey_;
  QTextEdit* textEdit_;
};

void ModuleDialogGeneric::addTextEditManager(QTextEdit* textEdit, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<TextEditSlotManager>(state_, *this, stateKey, textEdit));
}

class LineEditSlotManager : public WidgetSlotManager
{
public:
  LineEditSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QLineEdit* lineEdit) :
      WidgetSlotManager(state, dialog), stateKey_(stateKey), lineEdit_(lineEdit)
  {
    connect(lineEdit_, SIGNAL(textChanged(const QString&)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = QString::fromStdString(state_->getValue(stateKey_).toString());
    if (newValue != lineEdit_->text())
    {
      lineEdit_->setText(newValue);
      LOG_DEBUG("In new version of pull code for LineEdit: " << newValue.toStdString());
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for LineEdit: " << lineEdit_->text().toStdString());
    state_->setValue(stateKey_, lineEdit_->text().toStdString());
  }
private:
  AlgorithmParameterName stateKey_;
  QLineEdit* lineEdit_;
};

void ModuleDialogGeneric::addLineEditManager(QLineEdit* lineEdit, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<LineEditSlotManager>(state_, *this, stateKey, lineEdit));
}

class DoubleLineEditSlotManager : public WidgetSlotManager
{
public:
  DoubleLineEditSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QLineEdit* lineEdit) :
      WidgetSlotManager(state, dialog), stateKey_(stateKey), lineEdit_(lineEdit)
      {
        connect(lineEdit_, SIGNAL(textChanged(const QString&)), this, SLOT(push()));
      }
      virtual void pull() override
      {
        auto newValue = QString::number(state_->getValue(stateKey_).toDouble());
        if (newValue != lineEdit_->text())
        {
          lineEdit_->setText(newValue);
          LOG_DEBUG("In new version of pull code for DoubleLineEdit: " << newValue.toStdString());
        }
      }
      virtual void pushImpl() override
      {
        LOG_DEBUG("In new version of push code for LineEdit: " << lineEdit_->text().toStdString());
        state_->setValue(stateKey_, boost::lexical_cast<double>(lineEdit_->text().toStdString()));
      }
private:
  AlgorithmParameterName stateKey_;
  QLineEdit* lineEdit_;
};

void ModuleDialogGeneric::addDoubleLineEditManager(QLineEdit* lineEdit, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<DoubleLineEditSlotManager>(state_, *this, stateKey, lineEdit));
}

class SpinBoxSlotManager : public WidgetSlotManager
{
public:
  SpinBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QSpinBox* spinBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), spinBox_(spinBox)
  {
    connect(spinBox_, SIGNAL(valueChanged(int)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = state_->getValue(stateKey_).toInt();
    if (newValue != spinBox_->value())
    {
      spinBox_->setValue(newValue);
      LOG_DEBUG("In new version of pull code for SpinBox: " << newValue);
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for SpinBox: " << spinBox_->value());
    state_->setValue(stateKey_, spinBox_->value());
  }
private:
  AlgorithmParameterName stateKey_;
  QSpinBox* spinBox_;
};

void ModuleDialogGeneric::addSpinBoxManager(QSpinBox* spinBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<SpinBoxSlotManager>(state_, *this, stateKey, spinBox));
}

class DoubleSpinBoxSlotManager : public WidgetSlotManager
{
public:
  DoubleSpinBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QDoubleSpinBox* spinBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), spinBox_(spinBox)
  {
    connect(spinBox_, SIGNAL(valueChanged(double)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = state_->getValue(stateKey_).toDouble();
    if (newValue != spinBox_->value())
    {
      spinBox_->setValue(newValue);
      LOG_DEBUG("In new version of pull code for DoubleSpinBox: " << newValue);
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for DoubleSpinBox: " << spinBox_->value());
    state_->setValue(stateKey_, spinBox_->value());
  }
private:
  AlgorithmParameterName stateKey_;
  QDoubleSpinBox* spinBox_;
};

void ModuleDialogGeneric::addDoubleSpinBoxManager(QDoubleSpinBox* spinBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<DoubleSpinBoxSlotManager>(state_, *this, stateKey, spinBox));
}

class CheckBoxSlotManager : public WidgetSlotManager
{
public:
  CheckBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QCheckBox* checkBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), checkBox_(checkBox)
  {
    connect(checkBox_, SIGNAL(stateChanged(int)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    bool newValue = state_->getValue(stateKey_).toBool();
    if (newValue != checkBox_->isChecked())
    {
      LOG_DEBUG("In new version of pull code for CheckBox: " << newValue);
      checkBox_->setChecked(newValue);
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for CheckBox: " << checkBox_->isChecked());
    state_->setValue(stateKey_, checkBox_->isChecked());
  }
private:
  AlgorithmParameterName stateKey_;
  QCheckBox* checkBox_;
};

void ModuleDialogGeneric::addCheckBoxManager(QCheckBox* checkBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<CheckBoxSlotManager>(state_, *this, stateKey, checkBox));
}

class CheckableButtonSlotManager : public WidgetSlotManager
{
public:
  CheckableButtonSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QAbstractButton* checkable) :
      WidgetSlotManager(state, dialog), stateKey_(stateKey), checkable_(checkable)
      {
        connect(checkable_, SIGNAL(clicked()), this, SLOT(push()));
      }
      virtual void pull() override
      {
        bool newValue = state_->getValue(stateKey_).toBool();
        if (newValue != checkable_->isChecked())
        {
          LOG_DEBUG("In new version of pull code for checkable QAbstractButton: " << newValue);
          checkable_->setChecked(newValue);
        }
      }
      virtual void pushImpl() override
      {
        LOG_DEBUG("In new version of push code for checkable QAbstractButton: " << checkable_->isChecked());
        state_->setValue(stateKey_, checkable_->isChecked());
      }
private:
  AlgorithmParameterName stateKey_;
  QAbstractButton* checkable_;
};

void ModuleDialogGeneric::addCheckableButtonManager(QAbstractButton* checkable, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<CheckableButtonSlotManager>(state_, *this, stateKey, checkable));
}
<commit_msg>Shortcut no work on Mac<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2012 Scientific Computing and Imaging Institute,
   University of Utah.

   License for the specific language governing rights and limitations under
   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Dataflow/Network/ModuleStateInterface.h>
#include <Interface/Modules/Base/ModuleDialogGeneric.h>
#include <Core/Logging/Log.h>
#include <boost/foreach.hpp>

using namespace SCIRun::Gui;
using namespace SCIRun::Dataflow::Networks;
using namespace SCIRun::Core::Algorithms;

ModuleDialogGeneric::ModuleDialogGeneric(SCIRun::Dataflow::Networks::ModuleStateHandle state, QWidget* parent) : QDialog(parent),
  state_(state),
  pulling_(false)
{
  setModal(false);

  if (state_)
  {
    //TODO: replace with pull_newVersion
    LOG_DEBUG("ModuleDialogGeneric connecting to state" << std::endl);
    stateConnection_ = state_->connect_state_changed([this]() { pull(); });
  }

  createExecuteAction();
}

ModuleDialogGeneric::~ModuleDialogGeneric()
{
}

void ModuleDialogGeneric::fixSize()
{
  if (minimumWidth() > 0 && minimumHeight() > 0)
  {
    setFixedSize(minimumWidth(), minimumHeight());
  }
}

void ModuleDialogGeneric::createExecuteAction()
{
  executeAction_ = new QAction(this);
  executeAction_->setText("Execute");
  //TODO: doesn't work on Mac
  //executeAction_->setShortcut(QKeySequence("Ctrl+1"));
  executeAction_->setIcon(QApplication::style()->standardIcon(QStyle::SP_MediaPlay));
  connect(executeAction_, SIGNAL(triggered()), this, SIGNAL(executeActionTriggered()));
}

void ModuleDialogGeneric::contextMenuEvent(QContextMenuEvent* e)
{
  QMenu menu(this);
  menu.addAction(executeAction_);
  menu.exec(e->globalPos());

  QDialog::contextMenuEvent(e);
}

void ModuleDialogGeneric::addWidgetSlotManager(WidgetSlotManagerPtr ptr)
{
  slotManagers_.push_back(ptr);
}

void ModuleDialogGeneric::pull_newVersionToReplaceOld()
{
  Pulling p(this);
  BOOST_FOREACH(WidgetSlotManagerPtr wsm, slotManagers_)
    wsm->pull();
}

class ComboBoxSlotManager : public WidgetSlotManager
{
public:
  typedef boost::function<std::string(const QString&)> FromQStringConverter;
  typedef boost::function<QString(const std::string&)> ToQStringConverter;
  ComboBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QComboBox* comboBox,
    FromQStringConverter fromLabelConverter = boost::bind(&QString::toStdString, _1),
    ToQStringConverter toLabelConverter = &QString::fromStdString) :
  WidgetSlotManager(state, dialog), stateKey_(stateKey), comboBox_(comboBox), fromLabelConverter_(fromLabelConverter), toLabelConverter_(toLabelConverter)
  {
    connect(comboBox, SIGNAL(activated(const QString&)), this, SLOT(push()));
  }
  ComboBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QComboBox* comboBox,
    const GuiStringTranslationMap& stringMap) :
  WidgetSlotManager(state, dialog), stateKey_(stateKey), comboBox_(comboBox), stringMap_(stringMap)
  {
    fromLabelConverter_ = [this](const QString& qstr) { return stringMap_.left.at(qstr.toStdString()); };
    toLabelConverter_ = [this](const std::string& str) { return QString::fromStdString(stringMap_.right.at(str)); };
    connect(comboBox, SIGNAL(activated(const QString&)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto value = state_->getValue(stateKey_).toString();
    auto qstring = toLabelConverter_(value);
    if (qstring != comboBox_->currentText())
    {
      LOG_DEBUG("In new version of pull code for combobox: " << value);
      comboBox_->setCurrentIndex(comboBox_->findText(qstring));
    }
  }
  virtual void pushImpl() override
  {
    auto label = fromLabelConverter_(comboBox_->currentText());
    if (label != state_->getValue(stateKey_).toString())
    {
      LOG_DEBUG("In new version of push code for combobox: " << label);
      state_->setValue(stateKey_, label);
    }
  }
private:
  AlgorithmParameterName stateKey_;
  QComboBox* comboBox_;
  FromQStringConverter fromLabelConverter_;
  ToQStringConverter toLabelConverter_;
  GuiStringTranslationMap stringMap_;
};

void ModuleDialogGeneric::addComboBoxManager(QComboBox* comboBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<ComboBoxSlotManager>(state_, *this, stateKey, comboBox));
}

void ModuleDialogGeneric::addComboBoxManager(QComboBox* comboBox, const AlgorithmParameterName& stateKey, const GuiStringTranslationMap& stringMap)
{
  addWidgetSlotManager(boost::make_shared<ComboBoxSlotManager>(state_, *this, stateKey, comboBox, stringMap));
}

// ASSUMEs true state = comboBox index 1, false state = comboBox index 0.
class TwoChoiceBooleanComboBoxSlotManager : public WidgetSlotManager
{
public:
  TwoChoiceBooleanComboBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QComboBox* comboBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), comboBox_(comboBox)
  {
    connect(comboBox, SIGNAL(activated(int)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto value = state_->getValue(stateKey_).toBool();
    auto index = value ? 1 : 0;
    if (index != comboBox_->currentIndex())
    {
      LOG_DEBUG("In new version of pull code for combobox, boolean mode: " << index);
      comboBox_->setCurrentIndex(index);
    }
  }
  virtual void pushImpl() override
  {
    auto index = comboBox_->currentIndex();
    if (index != (state_->getValue(stateKey_).toBool() ? 1 : 0))
    {
      LOG_DEBUG("In new version of push code for combobox, boolean mode: " << index);
      state_->setValue(stateKey_, index == 1);
    }
  }
private:
  AlgorithmParameterName stateKey_;
  QComboBox* comboBox_;
};

void ModuleDialogGeneric::addTwoChoiceBooleanComboBoxManager(QComboBox* comboBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<TwoChoiceBooleanComboBoxSlotManager>(state_, *this, stateKey, comboBox));
}

class TextEditSlotManager : public WidgetSlotManager
{
public:
  TextEditSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QTextEdit* textEdit) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), textEdit_(textEdit)
  {
    connect(textEdit, SIGNAL(textChanged()), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = QString::fromStdString(state_->getValue(stateKey_).toString());
    if (newValue != textEdit_->toPlainText())
    {
      textEdit_->setPlainText(newValue);
      LOG_DEBUG("In new version of pull code for TextEdit: " << newValue.toStdString());
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for TextEdit: " << textEdit_->toPlainText().toStdString());
    state_->setValue(stateKey_, textEdit_->toPlainText().toStdString());
  }
private:
  AlgorithmParameterName stateKey_;
  QTextEdit* textEdit_;
};

void ModuleDialogGeneric::addTextEditManager(QTextEdit* textEdit, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<TextEditSlotManager>(state_, *this, stateKey, textEdit));
}

class LineEditSlotManager : public WidgetSlotManager
{
public:
  LineEditSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QLineEdit* lineEdit) :
      WidgetSlotManager(state, dialog), stateKey_(stateKey), lineEdit_(lineEdit)
  {
    connect(lineEdit_, SIGNAL(textChanged(const QString&)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = QString::fromStdString(state_->getValue(stateKey_).toString());
    if (newValue != lineEdit_->text())
    {
      lineEdit_->setText(newValue);
      LOG_DEBUG("In new version of pull code for LineEdit: " << newValue.toStdString());
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for LineEdit: " << lineEdit_->text().toStdString());
    state_->setValue(stateKey_, lineEdit_->text().toStdString());
  }
private:
  AlgorithmParameterName stateKey_;
  QLineEdit* lineEdit_;
};

void ModuleDialogGeneric::addLineEditManager(QLineEdit* lineEdit, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<LineEditSlotManager>(state_, *this, stateKey, lineEdit));
}

class DoubleLineEditSlotManager : public WidgetSlotManager
{
public:
  DoubleLineEditSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QLineEdit* lineEdit) :
      WidgetSlotManager(state, dialog), stateKey_(stateKey), lineEdit_(lineEdit)
      {
        connect(lineEdit_, SIGNAL(textChanged(const QString&)), this, SLOT(push()));
      }
      virtual void pull() override
      {
        auto newValue = QString::number(state_->getValue(stateKey_).toDouble());
        if (newValue != lineEdit_->text())
        {
          lineEdit_->setText(newValue);
          LOG_DEBUG("In new version of pull code for DoubleLineEdit: " << newValue.toStdString());
        }
      }
      virtual void pushImpl() override
      {
        LOG_DEBUG("In new version of push code for LineEdit: " << lineEdit_->text().toStdString());
        state_->setValue(stateKey_, boost::lexical_cast<double>(lineEdit_->text().toStdString()));
      }
private:
  AlgorithmParameterName stateKey_;
  QLineEdit* lineEdit_;
};

void ModuleDialogGeneric::addDoubleLineEditManager(QLineEdit* lineEdit, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<DoubleLineEditSlotManager>(state_, *this, stateKey, lineEdit));
}

class SpinBoxSlotManager : public WidgetSlotManager
{
public:
  SpinBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QSpinBox* spinBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), spinBox_(spinBox)
  {
    connect(spinBox_, SIGNAL(valueChanged(int)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = state_->getValue(stateKey_).toInt();
    if (newValue != spinBox_->value())
    {
      spinBox_->setValue(newValue);
      LOG_DEBUG("In new version of pull code for SpinBox: " << newValue);
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for SpinBox: " << spinBox_->value());
    state_->setValue(stateKey_, spinBox_->value());
  }
private:
  AlgorithmParameterName stateKey_;
  QSpinBox* spinBox_;
};

void ModuleDialogGeneric::addSpinBoxManager(QSpinBox* spinBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<SpinBoxSlotManager>(state_, *this, stateKey, spinBox));
}

class DoubleSpinBoxSlotManager : public WidgetSlotManager
{
public:
  DoubleSpinBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QDoubleSpinBox* spinBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), spinBox_(spinBox)
  {
    connect(spinBox_, SIGNAL(valueChanged(double)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    auto newValue = state_->getValue(stateKey_).toDouble();
    if (newValue != spinBox_->value())
    {
      spinBox_->setValue(newValue);
      LOG_DEBUG("In new version of pull code for DoubleSpinBox: " << newValue);
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for DoubleSpinBox: " << spinBox_->value());
    state_->setValue(stateKey_, spinBox_->value());
  }
private:
  AlgorithmParameterName stateKey_;
  QDoubleSpinBox* spinBox_;
};

void ModuleDialogGeneric::addDoubleSpinBoxManager(QDoubleSpinBox* spinBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<DoubleSpinBoxSlotManager>(state_, *this, stateKey, spinBox));
}

class CheckBoxSlotManager : public WidgetSlotManager
{
public:
  CheckBoxSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QCheckBox* checkBox) :
    WidgetSlotManager(state, dialog), stateKey_(stateKey), checkBox_(checkBox)
  {
    connect(checkBox_, SIGNAL(stateChanged(int)), this, SLOT(push()));
  }
  virtual void pull() override
  {
    bool newValue = state_->getValue(stateKey_).toBool();
    if (newValue != checkBox_->isChecked())
    {
      LOG_DEBUG("In new version of pull code for CheckBox: " << newValue);
      checkBox_->setChecked(newValue);
    }
  }
  virtual void pushImpl() override
  {
    LOG_DEBUG("In new version of push code for CheckBox: " << checkBox_->isChecked());
    state_->setValue(stateKey_, checkBox_->isChecked());
  }
private:
  AlgorithmParameterName stateKey_;
  QCheckBox* checkBox_;
};

void ModuleDialogGeneric::addCheckBoxManager(QCheckBox* checkBox, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<CheckBoxSlotManager>(state_, *this, stateKey, checkBox));
}

class CheckableButtonSlotManager : public WidgetSlotManager
{
public:
  CheckableButtonSlotManager(ModuleStateHandle state, ModuleDialogGeneric& dialog, const AlgorithmParameterName& stateKey, QAbstractButton* checkable) :
      WidgetSlotManager(state, dialog), stateKey_(stateKey), checkable_(checkable)
      {
        connect(checkable_, SIGNAL(clicked()), this, SLOT(push()));
      }
      virtual void pull() override
      {
        bool newValue = state_->getValue(stateKey_).toBool();
        if (newValue != checkable_->isChecked())
        {
          LOG_DEBUG("In new version of pull code for checkable QAbstractButton: " << newValue);
          checkable_->setChecked(newValue);
        }
      }
      virtual void pushImpl() override
      {
        LOG_DEBUG("In new version of push code for checkable QAbstractButton: " << checkable_->isChecked());
        state_->setValue(stateKey_, checkable_->isChecked());
      }
private:
  AlgorithmParameterName stateKey_;
  QAbstractButton* checkable_;
};

void ModuleDialogGeneric::addCheckableButtonManager(QAbstractButton* checkable, const AlgorithmParameterName& stateKey)
{
  addWidgetSlotManager(boost::make_shared<CheckableButtonSlotManager>(state_, *this, stateKey, checkable));
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Name:      process.cpp
// Purpose:   Implementation of class wxExProcess
// Author:    Anton van Wezenbeek
// Copyright: (c) 2016 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <vector>
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/config.h>
#include <wx/process.h>
#include <wx/timer.h>
#include <wx/txtstrm.h> // for wxTextInputStream
#include <wx/extension/process.h>
#include <wx/extension/debug.h>
#include <wx/extension/defs.h>
#include <wx/extension/itemdlg.h>
#include <wx/extension/managedframe.h>
#include <wx/extension/shell.h>
#include <wx/extension/util.h> // for wxExConfigFirstOf

#define GET_STREAM(SCOPE)                         \
{                                                 \
  if (Is##SCOPE##Available())                     \
  {                                               \
    wxTextInputStream tis(*Get##SCOPE##Stream()); \
                                                  \
    while (Is##SCOPE##Available())                \
    {                                             \
      const char c = tis.GetChar();               \
                                                  \
      if (c != 0)                                 \
      {                                           \
        text += c;                                \
      }                                           \
    }                                             \
  }                                               \
};                                                \

class wxExProcessImp : public wxProcess
{
public:
  wxExProcessImp(wxExManagedFrame* frame, wxExShell* shell, bool debug)
    : wxProcess(wxPROCESS_REDIRECT)
    , m_Debug(debug)
    , m_Frame(frame)
    , m_Shell(shell)
    , m_Timer(std::make_unique<wxTimer>(this)) {
    Bind(wxEVT_TIMER, [=](wxTimerEvent& event) {Read();});};
  virtual ~wxExProcessImp() {;};

  bool Execute(const std::string& command, const std::string& path);
  bool Kill();
  static int KillAll();
  void Read();
  bool Write(const std::string& text);
private:
  void HandleCommand(const wxString& command);
  virtual void OnTerminate(int pid, int status) override {
    const auto it = find (m_pids.begin(), m_pids.end(), pid);
    if (it != m_pids.end())
    {
      m_pids.erase(it);
    }
    m_Timer->Stop();
    Read();};

  const bool m_Debug;
  std::string m_Command, m_StdIn;
  wxExManagedFrame* m_Frame;
  wxExShell* m_Shell;
  std::unique_ptr<wxTimer> m_Timer;
  wxCriticalSection m_Critical;
  static std::vector<int> m_pids;
};

bool ShowProcess(wxExManagedFrame* frame, bool show)
{
  if (frame != nullptr)
  {
    frame->ShowPane("PROCESS", show);
    return true;
  }

  return false;
}

std::vector<int> wxExProcessImp::m_pids;

wxExShell* wxExProcess::m_Shell = nullptr;
wxString wxExProcess::m_WorkingDirKey = _("Process folder");

wxExProcess::wxExProcess()
  : m_Command(wxExConfigFirstOf(_("Process")))
  , m_Frame(dynamic_cast<wxExManagedFrame*>(wxTheApp->GetTopWindow()))
{
}

wxExProcess::~wxExProcess()
{
}

wxExProcess::wxExProcess(const wxExProcess& process)
{
  *this = process;
}

wxExProcess& wxExProcess::operator=(const wxExProcess& p)
{
  if (this != &p)
  {
    m_Command = p.m_Command;
    m_Error = p.m_Error;
    m_StdErr = p.m_StdErr;
    m_StdOut = p.m_StdOut;
  }

  return *this;
}

int wxExProcess::ConfigDialog(
  wxWindow* parent,
  const wxString& title,
  bool modal)
{
  wxExItem ci(_("Process"), ITEM_COMBOBOX, wxAny(), true);

  wxTextValidator validator(wxFILTER_EXCLUDE_CHAR_LIST);
  validator.SetCharExcludes("?%*\"");
  ci.SetValidator(&validator);

  const std::vector<wxExItem> v {
    ci,
    {m_WorkingDirKey, ITEM_COMBOBOX_DIR, wxAny(), true, wxWindow::NewControlId()}};

  if (modal)
  {
    return wxExItemDialog(parent, v, title).ShowModal();
  }
  else
  {
    wxExItemDialog* dlg = new wxExItemDialog(parent, v, title);
    return dlg->Show();
  }
}

bool wxExProcess::Execute(
  const std::string& command,
  bool wait,
  const std::string& wd)
{
  m_Error = false;

  std::string cwd(wd);

  if (command.empty())
  {
    if (wxExConfigFirstOf(_("Process")).empty())
    {
      if (ConfigDialog(wxTheApp->GetTopWindow()) == wxID_CANCEL)
      {
        return false;
      }
    }

    m_Command = wxExConfigFirstOf(_("Process"));
    cwd = wxExConfigFirstOf(m_WorkingDirKey);
  }
  else
  {
    m_Command = command;
  }

  if (!wait)
  {
    // We need a shell for output.
    if (m_Shell == nullptr) return false;

    m_Shell->EnableShell(true);
    m_Shell->SetProcess(this);
    m_Shell->SetName(m_Command);
    m_Shell->SetPrompt(
      // a unix shell itself has no prompt, so put one here
      m_Command.find("bash") == 0 ||
      m_Command.find("csh") == 0 ||
      m_Command.find("ksh") == 0 ||
      m_Command.find("tcsh") == 0 ||
      m_Command.find("sh") == 0 ? ">" : "");

    m_Process = std::make_unique<wxExProcessImp>(m_Frame, m_Shell, command == "gdb");

    if (!m_Process->Execute(m_Command, wd))
    {
      m_Process.release();
      m_Error = true;
    }
  }
  else
  {
    wxArrayString output;
    wxArrayString errors;
    struct wxExecuteEnv env;
    env.cwd = wd;

    if (wxExecute(m_Command, output, errors, wxEXEC_SYNC, &env) == -1)
    {
      m_StdErr.clear();
      m_StdOut.clear();
      m_Error = true;
    }
    else
    {
      // Set output by converting array strings into normal strings.
      m_StdOut = wxJoin(output, '\n', '\n');
      m_StdErr = wxJoin(errors, '\n', '\n');
    }

    if (m_Shell != nullptr)
    {
      m_Shell->EnableShell(false);
    }
  }

  return !m_Error;
}

bool wxExProcess::IsRunning() const
{
  return m_Process != nullptr && wxProcess::Exists(m_Process->GetPid());
}

bool wxExProcess::Kill()
{
  bool killed = false;

  if (m_Process != nullptr)
  {
    killed = m_Process->Kill();
    m_Process.release();
  }

  ShowProcess(m_Frame, false);

  return killed;
}

int wxExProcess::KillAll()
{
  return wxExProcessImp::KillAll();
}

void wxExProcess::PrepareOutput(wxWindow* parent)
{
  if (m_Shell == nullptr)
  {
    m_Shell = new wxExShell(parent,
      std::string(),
      std::string(),
      true,
      100);
  }
}

#if wxUSE_GUI
void wxExProcess::ShowOutput(const wxString& caption) const
{
  if (!m_Error)
  {
    if (m_Shell != nullptr && ShowProcess(m_Frame, true))
    {
      m_Shell->AppendText(m_StdOut);
    }
    else
    {
      std::cout << m_StdOut << "\n";
    }
  }
  else
  {
    // Executing command failed, so no output,
    // show failing command.
    wxLogError("Could not execute: %s", m_Command.c_str());
  }
}
#endif

bool wxExProcess::Write(const std::string& text)
{
  if (!IsRunning())
  {
    wxLogStatus("Process is not running");
    return false;
  }

  m_Process->Write(text);

  if (!IsRunning())
  {
    ShowProcess(m_Frame, false);
  }

  return true;
}

// Implementation.

bool wxExProcessImp::Execute(
  const std::string& command, const std::string& path)
{
  struct wxExecuteEnv env;
  env.cwd = path;
  m_Command = command;

  if (wxExecute(command, wxEXEC_ASYNC, this, &env) <= 0)
  {
    return false;
  }

  m_pids.push_back(GetPid());

  ShowProcess(m_Frame, true);
  m_Timer->Start(100); // milliseconds
  m_Shell->SetFocus();

  return true;
}

void wxExProcessImp::HandleCommand(const wxString& command)
{
  wxString rest;

  if (
         command.StartsWith("cd", &rest)
#ifdef __WXMSW__
      || command.StartsWith("chdir", &rest)
#endif
     )
  {
    wxLogNull logNo;
    rest.Trim(false);
    rest.Trim(true);

    if (rest.empty() || rest == "~")
    {
#ifdef __WXMSW__
#else
      wxSetWorkingDirectory(wxGetHomeDir());
#endif
    }
    else
    {
      wxSetWorkingDirectory(rest);
    }
  }
}

bool wxExProcessImp::Kill()
{
  int pid = GetPid();

  if (wxProcess::Kill(pid, wxSIGKILL) != wxKILL_OK)
  {
    return false;
  }

  const auto it = find (m_pids.begin(), m_pids.end(), pid);

  if (it != m_pids.end())
  {
    m_pids.erase(it);
  }

  return true;
}

int wxExProcessImp::KillAll()
{
  int killed = 0;

  for (auto pid : m_pids)
  {
    if (wxProcess::Kill(pid, wxSIGKILL) == wxKILL_OK)
    {
      killed++;
    }
  }

  if (killed != m_pids.size())
  {
    std::cout << "could not kill all processes\n";
  }

  return killed;
}

void wxExProcessImp::Read()
{
  wxCriticalSectionLocker lock(m_Critical);

  std::string text;
  GET_STREAM(Input);
  GET_STREAM(Error);

  if (!text.empty())
  {
    m_Shell->AppendText(
      // prevent echo of last input
      !m_StdIn.empty() && text.find(m_StdIn) == 0 ?
        text.substr(m_StdIn.length()):
        text);

    if (m_Debug && m_Frame != nullptr)
    {
      m_Frame->GetDebug()->ProcessStdOut(text);
    }
  }

  if (!m_StdIn.empty())
  {
    m_StdIn.clear();
    m_Shell->Prompt(std::string(), false);
  }
}

bool wxExProcessImp::Write(const std::string& text)
{
  m_Timer->Stop();

  if (m_Command.find("cmd") == 0 ||
      m_Command.find("powershell") == 0)
  {
    m_Shell->DocumentEnd();
  }

  // Write text to process and restart timer.
  wxOutputStream* os = GetOutputStream();

  if (os != nullptr)
  {
    HandleCommand(text);

    if (m_Debug && m_Frame != nullptr)
    {
      m_Frame->GetDebug()->ProcessStdIn(text);
    }

    const std::string el = (text.size() == 1 && text[0] == 3 ? std::string(): std::string("\n"));
    wxTextOutputStream(*os).WriteString(text + el);
    m_StdIn = text;
    wxMilliSleep(10);
    Read();
    m_Timer->Start();
  }

  return true;
}
<commit_msg>use std::find<commit_after>////////////////////////////////////////////////////////////////////////////////
// Name:      process.cpp
// Purpose:   Implementation of class wxExProcess
// Author:    Anton van Wezenbeek
// Copyright: (c) 2016 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <vector>
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/config.h>
#include <wx/process.h>
#include <wx/timer.h>
#include <wx/txtstrm.h> // for wxTextInputStream
#include <wx/extension/process.h>
#include <wx/extension/debug.h>
#include <wx/extension/defs.h>
#include <wx/extension/itemdlg.h>
#include <wx/extension/managedframe.h>
#include <wx/extension/shell.h>
#include <wx/extension/util.h> // for wxExConfigFirstOf

#define GET_STREAM(SCOPE)                         \
{                                                 \
  if (Is##SCOPE##Available())                     \
  {                                               \
    wxTextInputStream tis(*Get##SCOPE##Stream()); \
                                                  \
    while (Is##SCOPE##Available())                \
    {                                             \
      const char c = tis.GetChar();               \
                                                  \
      if (c != 0)                                 \
      {                                           \
        text += c;                                \
      }                                           \
    }                                             \
  }                                               \
};                                                \

class wxExProcessImp : public wxProcess
{
public:
  wxExProcessImp(wxExManagedFrame* frame, wxExShell* shell, bool debug)
    : wxProcess(wxPROCESS_REDIRECT)
    , m_Debug(debug)
    , m_Frame(frame)
    , m_Shell(shell)
    , m_Timer(std::make_unique<wxTimer>(this)) {
    Bind(wxEVT_TIMER, [=](wxTimerEvent& event) {Read();});};
  virtual ~wxExProcessImp() {;};

  bool Execute(const std::string& command, const std::string& path);
  bool Kill();
  static int KillAll();
  void Read();
  bool Write(const std::string& text);
private:
  void HandleCommand(const wxString& command);
  virtual void OnTerminate(int pid, int status) override {
    const auto it = std::find(m_pids.begin(), m_pids.end(), pid);
    if (it != m_pids.end())
    {
      m_pids.erase(it);
    }
    m_Timer->Stop();
    Read();};

  const bool m_Debug;
  std::string m_Command, m_StdIn;
  wxExManagedFrame* m_Frame;
  wxExShell* m_Shell;
  std::unique_ptr<wxTimer> m_Timer;
  wxCriticalSection m_Critical;
  static std::vector<int> m_pids;
};

bool ShowProcess(wxExManagedFrame* frame, bool show)
{
  if (frame != nullptr)
  {
    frame->ShowPane("PROCESS", show);
    return true;
  }

  return false;
}

std::vector<int> wxExProcessImp::m_pids;

wxExShell* wxExProcess::m_Shell = nullptr;
wxString wxExProcess::m_WorkingDirKey = _("Process folder");

wxExProcess::wxExProcess()
  : m_Command(wxExConfigFirstOf(_("Process")))
  , m_Frame(dynamic_cast<wxExManagedFrame*>(wxTheApp->GetTopWindow()))
{
}

wxExProcess::~wxExProcess()
{
}

wxExProcess::wxExProcess(const wxExProcess& process)
{
  *this = process;
}

wxExProcess& wxExProcess::operator=(const wxExProcess& p)
{
  if (this != &p)
  {
    m_Command = p.m_Command;
    m_Error = p.m_Error;
    m_StdErr = p.m_StdErr;
    m_StdOut = p.m_StdOut;
  }

  return *this;
}

int wxExProcess::ConfigDialog(
  wxWindow* parent,
  const wxString& title,
  bool modal)
{
  wxExItem ci(_("Process"), ITEM_COMBOBOX, wxAny(), true);

  wxTextValidator validator(wxFILTER_EXCLUDE_CHAR_LIST);
  validator.SetCharExcludes("?%*\"");
  ci.SetValidator(&validator);

  const std::vector<wxExItem> v {
    ci,
    {m_WorkingDirKey, ITEM_COMBOBOX_DIR, wxAny(), true, wxWindow::NewControlId()}};

  if (modal)
  {
    return wxExItemDialog(parent, v, title).ShowModal();
  }
  else
  {
    wxExItemDialog* dlg = new wxExItemDialog(parent, v, title);
    return dlg->Show();
  }
}

bool wxExProcess::Execute(
  const std::string& command,
  bool wait,
  const std::string& wd)
{
  m_Error = false;

  std::string cwd(wd);

  if (command.empty())
  {
    if (wxExConfigFirstOf(_("Process")).empty())
    {
      if (ConfigDialog(wxTheApp->GetTopWindow()) == wxID_CANCEL)
      {
        return false;
      }
    }

    m_Command = wxExConfigFirstOf(_("Process"));
    cwd = wxExConfigFirstOf(m_WorkingDirKey);
  }
  else
  {
    m_Command = command;
  }

  if (!wait)
  {
    // We need a shell for output.
    if (m_Shell == nullptr) return false;

    m_Shell->EnableShell(true);
    m_Shell->SetProcess(this);
    m_Shell->SetName(m_Command);
    m_Shell->SetPrompt(
      // a unix shell itself has no prompt, so put one here
      m_Command.find("bash") == 0 ||
      m_Command.find("csh") == 0 ||
      m_Command.find("ksh") == 0 ||
      m_Command.find("tcsh") == 0 ||
      m_Command.find("sh") == 0 ? ">" : "");

    m_Process = std::make_unique<wxExProcessImp>(m_Frame, m_Shell, command == "gdb");

    if (!m_Process->Execute(m_Command, wd))
    {
      m_Process.release();
      m_Error = true;
    }
  }
  else
  {
    wxArrayString output;
    wxArrayString errors;
    struct wxExecuteEnv env;
    env.cwd = wd;

    if (wxExecute(m_Command, output, errors, wxEXEC_SYNC, &env) == -1)
    {
      m_StdErr.clear();
      m_StdOut.clear();
      m_Error = true;
    }
    else
    {
      // Set output by converting array strings into normal strings.
      m_StdOut = wxJoin(output, '\n', '\n');
      m_StdErr = wxJoin(errors, '\n', '\n');
    }

    if (m_Shell != nullptr)
    {
      m_Shell->EnableShell(false);
    }
  }

  return !m_Error;
}

bool wxExProcess::IsRunning() const
{
  return m_Process != nullptr && wxProcess::Exists(m_Process->GetPid());
}

bool wxExProcess::Kill()
{
  bool killed = false;

  if (m_Process != nullptr)
  {
    killed = m_Process->Kill();
    m_Process.release();
  }

  ShowProcess(m_Frame, false);

  return killed;
}

int wxExProcess::KillAll()
{
  return wxExProcessImp::KillAll();
}

void wxExProcess::PrepareOutput(wxWindow* parent)
{
  if (m_Shell == nullptr)
  {
    m_Shell = new wxExShell(parent,
      std::string(),
      std::string(),
      true,
      100);
  }
}

#if wxUSE_GUI
void wxExProcess::ShowOutput(const wxString& caption) const
{
  if (!m_Error)
  {
    if (m_Shell != nullptr && ShowProcess(m_Frame, true))
    {
      m_Shell->AppendText(m_StdOut);
    }
    else
    {
      std::cout << m_StdOut << "\n";
    }
  }
  else
  {
    // Executing command failed, so no output,
    // show failing command.
    wxLogError("Could not execute: %s", m_Command.c_str());
  }
}
#endif

bool wxExProcess::Write(const std::string& text)
{
  if (!IsRunning())
  {
    wxLogStatus("Process is not running");
    return false;
  }

  m_Process->Write(text);

  if (!IsRunning())
  {
    ShowProcess(m_Frame, false);
  }

  return true;
}

// Implementation.

bool wxExProcessImp::Execute(
  const std::string& command, const std::string& path)
{
  struct wxExecuteEnv env;
  env.cwd = path;
  m_Command = command;

  if (wxExecute(command, wxEXEC_ASYNC, this, &env) <= 0)
  {
    return false;
  }

  m_pids.push_back(GetPid());

  ShowProcess(m_Frame, true);
  m_Timer->Start(100); // milliseconds
  m_Shell->SetFocus();

  return true;
}

void wxExProcessImp::HandleCommand(const wxString& command)
{
  wxString rest;

  if (
         command.StartsWith("cd", &rest)
#ifdef __WXMSW__
      || command.StartsWith("chdir", &rest)
#endif
     )
  {
    wxLogNull logNo;
    rest.Trim(false);
    rest.Trim(true);

    if (rest.empty() || rest == "~")
    {
#ifdef __WXMSW__
#else
      wxSetWorkingDirectory(wxGetHomeDir());
#endif
    }
    else
    {
      wxSetWorkingDirectory(rest);
    }
  }
}

bool wxExProcessImp::Kill()
{
  int pid = GetPid();

  if (wxProcess::Kill(pid, wxSIGKILL) != wxKILL_OK)
  {
    return false;
  }

  const auto it = std::find(m_pids.begin(), m_pids.end(), pid);

  if (it != m_pids.end())
  {
    m_pids.erase(it);
  }

  return true;
}

int wxExProcessImp::KillAll()
{
  int killed = 0;

  for (auto pid : m_pids)
  {
    if (wxProcess::Kill(pid, wxSIGKILL) == wxKILL_OK)
    {
      killed++;
    }
  }

  if (killed != m_pids.size())
  {
    std::cout << "could not kill all processes\n";
  }

  return killed;
}

void wxExProcessImp::Read()
{
  wxCriticalSectionLocker lock(m_Critical);

  std::string text;
  GET_STREAM(Input);
  GET_STREAM(Error);

  if (!text.empty())
  {
    m_Shell->AppendText(
      // prevent echo of last input
      !m_StdIn.empty() && text.find(m_StdIn) == 0 ?
        text.substr(m_StdIn.length()):
        text);

    if (m_Debug && m_Frame != nullptr)
    {
      m_Frame->GetDebug()->ProcessStdOut(text);
    }
  }

  if (!m_StdIn.empty())
  {
    m_StdIn.clear();
    m_Shell->Prompt(std::string(), false);
  }
}

bool wxExProcessImp::Write(const std::string& text)
{
  m_Timer->Stop();

  if (m_Command.find("cmd") == 0 ||
      m_Command.find("powershell") == 0)
  {
    m_Shell->DocumentEnd();
  }

  // Write text to process and restart timer.
  wxOutputStream* os = GetOutputStream();

  if (os != nullptr)
  {
    HandleCommand(text);

    if (m_Debug && m_Frame != nullptr)
    {
      m_Frame->GetDebug()->ProcessStdIn(text);
    }

    const std::string el = (text.size() == 1 && text[0] == 3 ? std::string(): std::string("\n"));
    wxTextOutputStream(*os).WriteString(text + el);
    m_StdIn = text;
    wxMilliSleep(10);
    Read();
    m_Timer->Start();
  }

  return true;
}
<|endoftext|>


<commit_before>#include "time.hpp"

#include <inttypes.h>

#ifndef _MSC_VER
#include <sys/time.h>
#endif

#ifdef __MACH__
#include <mach/mach_time.h>
#include "thread_local.hpp"
#include "utils.hpp"
#endif

#include "config/args.hpp"
#include "errors.hpp"

#ifdef _MSC_VER

microtime_t current_microtime() {
    FILETIME time;
#if _WIN32_WINNT >= 0x602 // Windows 8
    GetSystemTimePreciseAsFileTime(&time);
#else
    GetSystemTimeAsFileTime(&time);
#endif
    ULARGE_INTEGER nanos100;
    nanos100.LowPart = time.dwLowDateTime;
    nanos100.HighPart = time.dwHighDateTime;
    return nanos100.QuadPart / 10;
}

#else

microtime_t current_microtime() {
    // This could be done more efficiently, surely.
    struct timeval t;
    DEBUG_VAR int res = gettimeofday(&t, nullptr);
    rassert(0 == res);
    return uint64_t(t.tv_sec) * MILLION + t.tv_usec;
}

#endif

ticks_t secs_to_ticks(time_t secs) {
    return static_cast<ticks_t>(secs) * BILLION;
}

#ifdef __MACH__
TLS(mach_timebase_info_data_t, mach_time_info);
#endif  // __MACH__

timespec clock_monotonic() {
#ifdef __MACH__
    mach_timebase_info_data_t mach_time_info = TLS_get_mach_time_info();
    if (mach_time_info.denom == 0) {
        mach_timebase_info(&mach_time_info);
        guarantee(mach_time_info.denom != 0);
        TLS_set_mach_time_info(mach_time_info);
    }
    const uint64_t t = mach_absolute_time();
    uint64_t nanosecs = t * mach_time_info.numer / mach_time_info.denom;
    timespec ret;
    ret.tv_sec = nanosecs / BILLION;
    ret.tv_nsec = nanosecs % BILLION;
    return ret;
#elif defined(_WIN32)
    timespec ret;
    static THREAD_LOCAL LARGE_INTEGER frequency_hz = {0};
    if (frequency_hz.QuadPart == 0) {
        BOOL res = QueryPerformanceFrequency(&frequency_hz);
        guarantee_winerr(res, "QueryPerformanceFrequency failed");
    }
    LARGE_INTEGER counter;
    BOOL res = QueryPerformanceCounter(&counter);
    guarantee_winerr(res, "QueryPerformanceCounter failed");
    ret.tv_sec = counter.QuadPart / frequency_hz.QuadPart;
    ret.tv_nsec = (counter.QuadPart - ret.tv_sec * frequency_hz.QuadPart) * BILLION / frequency_hz.QuadPart;
    return ret;
#else
    timespec ret;
    int res = clock_gettime(CLOCK_MONOTONIC, &ret);
    guarantee_err(res == 0, "clock_gettime(CLOCK_MONOTIC, ...) failed");
    return ret;
#endif
}

timespec clock_realtime() {
#ifdef __MACH__
    struct timeval tv;
    int res = gettimeofday(&tv, nullptr);
    guarantee_err(res == 0, "gettimeofday failed");

    timespec ret;
    ret.tv_sec = tv.tv_sec;
    ret.tv_nsec = THOUSAND * tv.tv_usec;
    return ret;
#elif defined(_MSC_VER)
    FILETIME time;
#if _WIN32_WINNT >= 0x602 // Windows 8
    GetSystemTimePreciseAsFileTime(&time);
#else
    GetSystemTimeAsFileTime(&time);
#endif
    ULARGE_INTEGER nanos100;
    nanos100.LowPart = time.dwLowDateTime;
    nanos100.HighPart = time.dwHighDateTime;
    timespec ret;
    ret.tv_sec = nanos100.QuadPart / (MILLION * 10);
    ret.tv_nsec = (nanos100.QuadPart % (MILLION * 10)) * 100;
    return ret;
#else
    timespec ret;
    int res = clock_gettime(CLOCK_REALTIME, &ret);
    guarantee_err(res == 0, "clock_gettime(CLOCK_REALTIME) failed");
    return ret;
#endif
}

void add_to_timespec(timespec *ts, int32_t nanoseconds) {
    guarantee(ts->tv_nsec >= 0 && ts->tv_nsec < BILLION);
    int64_t new_tv_nsec = ts->tv_nsec + nanoseconds;
    if (new_tv_nsec >= 0 || new_tv_nsec % BILLION == 0) {
        ts->tv_sec += new_tv_nsec / BILLION;
        ts->tv_nsec = new_tv_nsec % BILLION;
    } else {
        ts->tv_sec += new_tv_nsec / BILLION - 1;
        ts->tv_nsec = BILLION + new_tv_nsec % BILLION;
    }
    guarantee(ts->tv_nsec >= 0 && ts->tv_nsec < BILLION);
}

timespec subtract_timespecs(const timespec &t1, const timespec &t2) {
    guarantee(t1.tv_nsec >= 0 && t1.tv_nsec < BILLION);
    guarantee(t2.tv_nsec >= 0 && t2.tv_nsec < BILLION);
    timespec res;
    res.tv_sec = t1.tv_sec - t2.tv_sec;
    if (t2.tv_nsec > t1.tv_nsec) {
        --res.tv_sec;
        res.tv_nsec = t1.tv_nsec + BILLION - t2.tv_nsec;
    } else {
        res.tv_nsec = t1.tv_nsec - t2.tv_nsec;
    }
    guarantee(res.tv_nsec >= 0 && res.tv_nsec < BILLION);
    return res;
}

bool operator<(const struct timespec &t1, const struct timespec &t2) {
    guarantee(t1.tv_nsec >= 0 && t1.tv_nsec < BILLION);
    guarantee(t2.tv_nsec >= 0 && t2.tv_nsec < BILLION);
    return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec);
}

bool operator>(const struct timespec &t1, const struct timespec &t2) {
    return t2 < t1;
}

bool operator<=(const struct timespec &t1, const struct timespec &t2) {
    guarantee(t1.tv_nsec >= 0 && t1.tv_nsec < BILLION);
    guarantee(t2.tv_nsec >= 0 && t2.tv_nsec < BILLION);
    return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec <= t2.tv_nsec);
}

bool operator>=(const struct timespec &t1, const struct timespec &t2) {
    return t2 <= t1;
}

ticks_t get_ticks() {
    timespec tv = clock_monotonic();
    ticks_t ticks = secs_to_ticks(tv.tv_sec) + tv.tv_nsec;
    return ticks;
}

time_t get_secs() {
    timespec tv = clock_realtime();
    return tv.tv_sec;
}

double ticks_to_secs(ticks_t ticks) {
    return ticks / static_cast<double>(BILLION);
}

<commit_msg>Fix current_microtime on Windows<commit_after>#include "time.hpp"

#include <inttypes.h>

#ifndef _MSC_VER
#include <sys/time.h>
#endif

#ifdef __MACH__
#include <mach/mach_time.h>
#include "thread_local.hpp"
#include "utils.hpp"
#endif

#include "config/args.hpp"
#include "errors.hpp"

#ifdef _MSC_VER

microtime_t current_microtime() {
    FILETIME time;
#if _WIN32_WINNT >= 0x602 // Windows 8
    GetSystemTimePreciseAsFileTime(&time);
#else
    GetSystemTimeAsFileTime(&time);
#endif
    ULARGE_INTEGER nanos100;
    nanos100.LowPart = time.dwLowDateTime;
    nanos100.HighPart = time.dwHighDateTime;
    // Convert to Unix epoch
    nanos100.QuadPart -= 116444736000000000ULL;
    return nanos100.QuadPart / 10;
}

#else

microtime_t current_microtime() {
    // This could be done more efficiently, surely.
    struct timeval t;
    DEBUG_VAR int res = gettimeofday(&t, nullptr);
    rassert(0 == res);
    return uint64_t(t.tv_sec) * MILLION + t.tv_usec;
}

#endif

ticks_t secs_to_ticks(time_t secs) {
    return static_cast<ticks_t>(secs) * BILLION;
}

#ifdef __MACH__
TLS(mach_timebase_info_data_t, mach_time_info);
#endif  // __MACH__

timespec clock_monotonic() {
#ifdef __MACH__
    mach_timebase_info_data_t mach_time_info = TLS_get_mach_time_info();
    if (mach_time_info.denom == 0) {
        mach_timebase_info(&mach_time_info);
        guarantee(mach_time_info.denom != 0);
        TLS_set_mach_time_info(mach_time_info);
    }
    const uint64_t t = mach_absolute_time();
    uint64_t nanosecs = t * mach_time_info.numer / mach_time_info.denom;
    timespec ret;
    ret.tv_sec = nanosecs / BILLION;
    ret.tv_nsec = nanosecs % BILLION;
    return ret;
#elif defined(_WIN32)
    timespec ret;
    static THREAD_LOCAL LARGE_INTEGER frequency_hz = {0};
    if (frequency_hz.QuadPart == 0) {
        BOOL res = QueryPerformanceFrequency(&frequency_hz);
        guarantee_winerr(res, "QueryPerformanceFrequency failed");
    }
    LARGE_INTEGER counter;
    BOOL res = QueryPerformanceCounter(&counter);
    guarantee_winerr(res, "QueryPerformanceCounter failed");
    ret.tv_sec = counter.QuadPart / frequency_hz.QuadPart;
    ret.tv_nsec = (counter.QuadPart - ret.tv_sec * frequency_hz.QuadPart) * BILLION / frequency_hz.QuadPart;
    return ret;
#else
    timespec ret;
    int res = clock_gettime(CLOCK_MONOTONIC, &ret);
    guarantee_err(res == 0, "clock_gettime(CLOCK_MONOTIC, ...) failed");
    return ret;
#endif
}

timespec clock_realtime() {
#ifdef __MACH__
    struct timeval tv;
    int res = gettimeofday(&tv, nullptr);
    guarantee_err(res == 0, "gettimeofday failed");

    timespec ret;
    ret.tv_sec = tv.tv_sec;
    ret.tv_nsec = THOUSAND * tv.tv_usec;
    return ret;
#elif defined(_MSC_VER)
    FILETIME time;
#if _WIN32_WINNT >= 0x602 // Windows 8
    GetSystemTimePreciseAsFileTime(&time);
#else
    GetSystemTimeAsFileTime(&time);
#endif
    ULARGE_INTEGER nanos100;
    nanos100.LowPart = time.dwLowDateTime;
    nanos100.HighPart = time.dwHighDateTime;
    // Convert to Unix epoch
    nanos100.QuadPart -= 116444736000000000ULL;
    timespec ret;
    ret.tv_sec = nanos100.QuadPart / (MILLION * 10);
    ret.tv_nsec = (nanos100.QuadPart % (MILLION * 10)) * 100;
    return ret;
#else
    timespec ret;
    int res = clock_gettime(CLOCK_REALTIME, &ret);
    guarantee_err(res == 0, "clock_gettime(CLOCK_REALTIME) failed");
    return ret;
#endif
}

void add_to_timespec(timespec *ts, int32_t nanoseconds) {
    guarantee(ts->tv_nsec >= 0 && ts->tv_nsec < BILLION);
    int64_t new_tv_nsec = ts->tv_nsec + nanoseconds;
    if (new_tv_nsec >= 0 || new_tv_nsec % BILLION == 0) {
        ts->tv_sec += new_tv_nsec / BILLION;
        ts->tv_nsec = new_tv_nsec % BILLION;
    } else {
        ts->tv_sec += new_tv_nsec / BILLION - 1;
        ts->tv_nsec = BILLION + new_tv_nsec % BILLION;
    }
    guarantee(ts->tv_nsec >= 0 && ts->tv_nsec < BILLION);
}

timespec subtract_timespecs(const timespec &t1, const timespec &t2) {
    guarantee(t1.tv_nsec >= 0 && t1.tv_nsec < BILLION);
    guarantee(t2.tv_nsec >= 0 && t2.tv_nsec < BILLION);
    timespec res;
    res.tv_sec = t1.tv_sec - t2.tv_sec;
    if (t2.tv_nsec > t1.tv_nsec) {
        --res.tv_sec;
        res.tv_nsec = t1.tv_nsec + BILLION - t2.tv_nsec;
    } else {
        res.tv_nsec = t1.tv_nsec - t2.tv_nsec;
    }
    guarantee(res.tv_nsec >= 0 && res.tv_nsec < BILLION);
    return res;
}

bool operator<(const struct timespec &t1, const struct timespec &t2) {
    guarantee(t1.tv_nsec >= 0 && t1.tv_nsec < BILLION);
    guarantee(t2.tv_nsec >= 0 && t2.tv_nsec < BILLION);
    return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec);
}

bool operator>(const struct timespec &t1, const struct timespec &t2) {
    return t2 < t1;
}

bool operator<=(const struct timespec &t1, const struct timespec &t2) {
    guarantee(t1.tv_nsec >= 0 && t1.tv_nsec < BILLION);
    guarantee(t2.tv_nsec >= 0 && t2.tv_nsec < BILLION);
    return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec <= t2.tv_nsec);
}

bool operator>=(const struct timespec &t1, const struct timespec &t2) {
    return t2 <= t1;
}

ticks_t get_ticks() {
    timespec tv = clock_monotonic();
    ticks_t ticks = secs_to_ticks(tv.tv_sec) + tv.tv_nsec;
    return ticks;
}

time_t get_secs() {
    timespec tv = clock_realtime();
    return tv.tv_sec;
}

double ticks_to_secs(ticks_t ticks) {
    return ticks / static_cast<double>(BILLION);
}

<|endoftext|>


<commit_before>#include "catch_ext.hpp"

#include <mapnik/expression.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/wkt/wkt_factory.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/unicode.hpp>

#include <functional>
#include <map>

namespace {

template <typename Properties>
mapnik::feature_ptr make_test_feature(mapnik::value_integer id, std::string const& wkt, Properties const& prop)
{
    auto ctx = std::make_shared<mapnik::context_type>();
    mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx, id));
    mapnik::geometry::geometry<double> geom;
    if (mapnik::from_wkt(wkt, geom))
    {
        feature->set_geometry(std::move(geom));
    }

    for (auto const& kv : prop)
    {
        feature->put_new(kv.first, kv.second);
    }
    return feature;
}

template <typename Feature, typename Expression>
mapnik::value_type evaluate(Feature const& feature, Expression const& expr)
{
    auto value = mapnik::util::apply_visitor(
        mapnik::evaluate<Feature, mapnik::value_type, mapnik::attributes>(
            feature, mapnik::attributes()), expr);
    return value;
}

mapnik::value evaluate_string(mapnik::feature_ptr const& feature, std::string const& str)
{
    auto expr = mapnik::parse_expression(str);
    return evaluate(*feature, *expr);
}

std::string parse_and_dump(std::string const& str)
{
    auto expr = mapnik::parse_expression(str);
    return mapnik::to_expression_string(*expr);
}

} // namespace

TEST_CASE("expressions")
{
    using namespace std::placeholders;
    using properties_type = std::map<std::string, mapnik::value>;
    mapnik::transcoder tr("utf8");

    properties_type prop = {{ "foo"   , tr.transcode("bar") },
                            { "name"  , tr.transcode("Québec")},
                            { "grass" , tr.transcode("grow")},
                            { "wind"  , tr.transcode("blow")},
                            { "sky"   , tr.transcode("is blue")},
                            { "τ"     , mapnik::value_double(6.2831853)},
                            { "double", mapnik::value_double(1.23456)},
                            { "int"   , mapnik::value_integer(123)},
                            { "bool"  , mapnik::value_bool(true)},
                            { "null"  , mapnik::value_null()}};

    auto feature = make_test_feature(1, "POINT(100 200)", prop);
    auto eval = std::bind(evaluate_string, feature, _1);
    auto approx = Approx::custom().epsilon(1e-6);

    // primary expressions
    // null
    TRY_CHECK(parse_and_dump("null") == "null");
    // boolean
    TRY_CHECK(parse_and_dump("true") == "true");
    TRY_CHECK(parse_and_dump("false") == "false");
    // floating point
    TRY_CHECK(parse_and_dump("3.14159") == "3.14159");
    // integer
    TRY_CHECK(parse_and_dump("123") == "123");
    // unicode
    TRY_CHECK(parse_and_dump("''") == "''");
    TRY_CHECK(parse_and_dump("'single-quoted string'") == "'single-quoted string'");
    TRY_CHECK(parse_and_dump("\"double-quoted string\"") == "'double-quoted string'");
    TRY_CHECK(parse_and_dump("'escaped \\' apostrophe'") == "'escaped \\' apostrophe'");
    TRY_CHECK(parse_and_dump("'escaped \\\\ backslash'") == "'escaped \\\\ backslash'");

    // floating point constants
    TRY_CHECK(parse_and_dump("pi") == "3.14159");
    TRY_CHECK(parse_and_dump("deg_to_rad") == "0.0174533");
    TRY_CHECK(parse_and_dump("rad_to_deg") == "57.2958");

    // ascii attribute name
    TRY_CHECK(eval(" [foo]='bar' ") == true);

    // unicode attribute name
    TRY_CHECK(eval("[τ]") == prop.at("τ"));
    TRY_CHECK(eval("[τ]") == eval(u8"[\u03C4]"));

    // change to TRY_CHECK once \u1234 escape sequence in attribute name
    // is implemented in expression grammar
    CHECK_NOFAIL(eval("[τ]") == eval("[\\u03C3]"));

    // unary functions
    // sin / cos
    TRY_CHECK(eval(" sin(0.25 * pi) / cos(0.25 * pi) ").to_double() == approx(1.0));
    // tan
    TRY_CHECK(eval(" tan(0.25 * pi) ").to_double() == approx(1.0));
    // atan
    TRY_CHECK(eval(" rad_to_deg * atan(1.0) ").to_double() == approx(45.0));
    // exp
    TRY_CHECK(eval(" exp(0.0) ") == 1.0);
    // log
    TRY_CHECK(eval(" log(1.0) ") == 0.0);
    TRY_CHECK(eval(" log(exp(1.0)) ") == 1.0);
    // abs
    TRY_CHECK(eval(" abs(cos(-pi)) ") == 1.0);
    // length (string)
    TRY_CHECK(eval(" length('1234567890') ").to_int() == 10);

    // binary functions
    // min
    TRY_CHECK(eval(" min(-0.01, 0.001) ") == -0.01);
    // max
    TRY_CHECK(eval(" max(0.01, -0.1) ") == 0.01);
    // pow
    TRY_CHECK(eval(" pow(2, 32) ") == 4294967296.0);

    // geometry types
    TRY_CHECK(eval(" [mapnik::geometry_type] = point ") == true);
    TRY_CHECK(eval(" [ mapnik::geometry_type] <> linestring ") == true);
    TRY_CHECK(eval(" [mapnik::geometry_type ] != polygon ") == true);
    TRY_CHECK(eval(" [ mapnik::geometry_type ] neq collection ") == true);
    TRY_CHECK(eval(" [mapnik::geometry_type] eq collection ") == false);

    //unary expression
    TRY_CHECK(eval(" -123.456 ") == -123.456);
    TRY_CHECK(eval(" +123.456 ") == 123.456);

    // multiplicative/additive
    auto expr = mapnik::parse_expression("(2.0 * 2.0 + 3.0 * 3.0)/(2.0 * 2.0 - 3.0 * 3.0)");
    TRY_CHECK(evaluate(*feature, *expr) == -2.6);
    auto expr2 = mapnik::parse_expression("(2.0 * 2.0 + 3.0 * 3.0)/((2.0 - 3.0) * (2.0 + 3.0))");
    TRY_CHECK(evaluate(*feature, *expr) == evaluate(*feature, *expr2));

    // logical
    TRY_CHECK(eval(" [int] = 123 and [double] = 1.23456 && [bool] = true and [null] = null && [foo] = 'bar' ") == true);
    TRY_CHECK(eval(" [int] = 456 or [foo].match('foo') || length([foo]) = 3 ") == true);
    TRY_CHECK(eval(" not true  and not true  ") == eval(" (not true ) and (not true ) "));
    TRY_CHECK(eval(" not true  or  not true  ") == eval(" (not true ) or  (not true ) "));
    TRY_CHECK(eval(" not false and not false ") == eval(" (not false) and (not false) "));
    TRY_CHECK(eval(" not false or  not false ") == eval(" (not false) or  (not false) "));

    // test not/and/or precedence using combinations of "not EQ1 OP1 not EQ2 OP2 not EQ3"
    TRY_CHECK(eval(" not [grass] = 'grow' and not [wind] = 'blow' and not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grow' and not [wind] = 'blow' or  not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grow' or  not [wind] = 'blow' and not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grow' or  not [wind] = 'blow' or  not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grew' and not [wind] = 'blew' and not [sky] = 'was blue' ") == true);
    TRY_CHECK(eval(" not [grass] = 'grew' and not [wind] = 'blew' or  not [sky] = 'was blue' ") == true);
    TRY_CHECK(eval(" not [grass] = 'grew' or  not [wind] = 'blew' and not [sky] = 'was blue' ") == true);
    TRY_CHECK(eval(" not [grass] = 'grew' or  not [wind] = 'blew' or  not [sky] = 'was blue' ") == true);

    // relational
    TRY_CHECK(eval(" [int] > 100 and [int] gt 100.0 and [double] < 2 and [double] lt 2.0 ") == true);
    TRY_CHECK(eval(" [int] >= 123 and [int] ge 123.0 and [double] <= 1.23456 and [double] le 1.23456 ") == true);

    // empty string/null equality
    TRY_CHECK(eval("[null] = null") == true);
    TRY_CHECK(eval("[null] != null") == false);
    TRY_CHECK(eval("[null] = ''") == false);
    ///////////////////// ref: https://github.com/mapnik/mapnik/issues/1859
    TRY_CHECK(eval("[null] != ''") == false); // back compatible - will be changed in 3.1.x
    //////////////////////
    TRY_CHECK(eval("'' = [null]") == false);
    TRY_CHECK(eval("'' != [null]") == true);

    // regex
    // replace
    TRY_CHECK(eval(" [foo].replace(\"(\\B)|( )\",'$1 ') ") == tr.transcode("b a r"));

    // https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode
    //'\u265C\u265E\u265D\u265B\u265A\u265D\u265E\u265C' - black chess figures
    // replace black knights with white knights
    auto val0 = eval(u8"'\u265C\u265E\u265D\u265B\u265A\u265D\u265E\u265C'.replace('\u265E','\u2658')");
    auto val1 = eval("'♜♞♝♛♚♝♞♜'.replace('♞','♘')"); // ==> expected ♜♘♝♛♚♝♘♜
    TRY_CHECK(val0 == val1);
    TRY_CHECK(val0.to_string() == val1.to_string()); // UTF-8
    TRY_CHECK(val0.to_unicode() == val1.to_unicode()); // Unicode
    // \u+NNNN \U+NNNNNNNN \xNN\xNN
    auto val3 = eval("'\\u262f\\xF0\\x9F\\x8D\\xB7'");
    auto val4 = eval("'\\U0000262f\\U0001F377'");
    auto val5 = eval("\"\\u262f\\xF0\\x9F\\x8D\\xB7\"");
    auto val6 = eval("\"\\U0000262f\\U0001F377\"");
    // UTF16 surrogate pairs work also ;)
    // \ud83d\udd7a\ud83c\udffc => \U0001F57A\U0001F3FC works also
    // TODO: find a way to enter UTF16 pairs
    TRY_CHECK(val3 == val4);
    TRY_CHECK(val5 == val6);
    TRY_CHECK(val3.to_string() == val4.to_string()); // UTF-8
    TRY_CHECK(val3.to_unicode() == val4.to_unicode()); // Unicod
    TRY_CHECK(val5.to_string() == val6.to_string()); // UTF-8
    TRY_CHECK(val5.to_unicode() == val6.to_unicode()); // Unicode

    // following test will fail if boost_regex is built without ICU support (unpaired surrogates in output)
    TRY_CHECK(eval("[name].replace('(\\B)|( )',' ') ") == tr.transcode("Q u é b e c"));
    TRY_CHECK(eval("'Москва'.replace('(?<!^)(\\B|b)(?!$)',' ')") == tr.transcode("М о с к в а"));
    // 'foo' =~ s:(\w)\1:$1x:r
    TRY_CHECK(eval(" 'foo'.replace('(\\w)\\1', '$1x') ") == tr.transcode("fox"));
    TRY_CHECK(parse_and_dump(" 'foo'.replace('(\\w)\\1', '$1x') ") == "'foo'.replace('(\\w)\\1','$1x')");

    // match
    TRY_CHECK(eval(" [name].match('Québec') ") == true);
    // 'Québec' =~ m:^Q\S*$:
    TRY_CHECK(eval(" [name].match('^Q\\S*$') ") == true);
    TRY_CHECK(parse_and_dump(" [name].match('^Q\\S*$') ") == "[name].match('^Q\\S*$')");

    // string & value concatenation
    // this should evaluate as two strings concatenating
    TRY_CHECK(eval("Hello + '!'") == eval("'Hello!'"));
    // this should evaulate as a combination of an int value and string
    TRY_CHECK(eval("[int]+m") == eval("'123m'"));
}
<commit_msg>add more single/double quotes tests<commit_after>#include "catch_ext.hpp"

#include <mapnik/expression.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/wkt/wkt_factory.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/unicode.hpp>

#include <functional>
#include <map>

namespace {

template <typename Properties>
mapnik::feature_ptr make_test_feature(mapnik::value_integer id, std::string const& wkt, Properties const& prop)
{
    auto ctx = std::make_shared<mapnik::context_type>();
    mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx, id));
    mapnik::geometry::geometry<double> geom;
    if (mapnik::from_wkt(wkt, geom))
    {
        feature->set_geometry(std::move(geom));
    }

    for (auto const& kv : prop)
    {
        feature->put_new(kv.first, kv.second);
    }
    return feature;
}

template <typename Feature, typename Expression>
mapnik::value_type evaluate(Feature const& feature, Expression const& expr)
{
    auto value = mapnik::util::apply_visitor(
        mapnik::evaluate<Feature, mapnik::value_type, mapnik::attributes>(
            feature, mapnik::attributes()), expr);
    return value;
}

mapnik::value evaluate_string(mapnik::feature_ptr const& feature, std::string const& str)
{
    auto expr = mapnik::parse_expression(str);
    return evaluate(*feature, *expr);
}

std::string parse_and_dump(std::string const& str)
{
    auto expr = mapnik::parse_expression(str);
    return mapnik::to_expression_string(*expr);
}

} // namespace

TEST_CASE("expressions")
{
    using namespace std::placeholders;
    using properties_type = std::map<std::string, mapnik::value>;
    mapnik::transcoder tr("utf8");

    properties_type prop = {{ "foo"   , tr.transcode("bar") },
                            { "name"  , tr.transcode("Québec")},
                            { "grass" , tr.transcode("grow")},
                            { "wind"  , tr.transcode("blow")},
                            { "sky"   , tr.transcode("is blue")},
                            { "τ"     , mapnik::value_double(6.2831853)},
                            { "double", mapnik::value_double(1.23456)},
                            { "int"   , mapnik::value_integer(123)},
                            { "bool"  , mapnik::value_bool(true)},
                            { "null"  , mapnik::value_null()}};

    auto feature = make_test_feature(1, "POINT(100 200)", prop);
    auto eval = std::bind(evaluate_string, feature, _1);
    auto approx = Approx::custom().epsilon(1e-6);

    // primary expressions
    // null
    TRY_CHECK(parse_and_dump("null") == "null");
    // boolean
    TRY_CHECK(parse_and_dump("true") == "true");
    TRY_CHECK(parse_and_dump("false") == "false");
    // floating point
    TRY_CHECK(parse_and_dump("3.14159") == "3.14159");
    // integer
    TRY_CHECK(parse_and_dump("123") == "123");
    // unicode
    TRY_CHECK(parse_and_dump("''") == "''");
    TRY_CHECK(parse_and_dump("'single-quoted string'") == "'single-quoted string'");
    TRY_CHECK(parse_and_dump("\"double-quoted string\"") == "'double-quoted string'");
    TRY_CHECK(parse_and_dump("'escaped \\' apostrophe'") == "'escaped \\' apostrophe'");
    TRY_CHECK(parse_and_dump("'escaped \\\\ backslash'") == "'escaped \\\\ backslash'");

    // floating point constants
    TRY_CHECK(parse_and_dump("pi") == "3.14159");
    TRY_CHECK(parse_and_dump("deg_to_rad") == "0.0174533");
    TRY_CHECK(parse_and_dump("rad_to_deg") == "57.2958");

    // ascii attribute name
    TRY_CHECK(eval(" [foo]='bar' ") == true);

    // unicode attribute name
    TRY_CHECK(eval("[τ]") == prop.at("τ"));
    TRY_CHECK(eval("[τ]") == eval(u8"[\u03C4]"));

    // change to TRY_CHECK once \u1234 escape sequence in attribute name
    // is implemented in expression grammar
    CHECK_NOFAIL(eval("[τ]") == eval("[\\u03C3]"));

    // unary functions
    // sin / cos
    TRY_CHECK(eval(" sin(0.25 * pi) / cos(0.25 * pi) ").to_double() == approx(1.0));
    // tan
    TRY_CHECK(eval(" tan(0.25 * pi) ").to_double() == approx(1.0));
    // atan
    TRY_CHECK(eval(" rad_to_deg * atan(1.0) ").to_double() == approx(45.0));
    // exp
    TRY_CHECK(eval(" exp(0.0) ") == 1.0);
    // log
    TRY_CHECK(eval(" log(1.0) ") == 0.0);
    TRY_CHECK(eval(" log(exp(1.0)) ") == 1.0);
    // abs
    TRY_CHECK(eval(" abs(cos(-pi)) ") == 1.0);
    // length (string)
    TRY_CHECK(eval(" length('1234567890') ").to_int() == 10);

    // binary functions
    // min
    TRY_CHECK(eval(" min(-0.01, 0.001) ") == -0.01);
    // max
    TRY_CHECK(eval(" max(0.01, -0.1) ") == 0.01);
    // pow
    TRY_CHECK(eval(" pow(2, 32) ") == 4294967296.0);

    // geometry types
    TRY_CHECK(eval(" [mapnik::geometry_type] = point ") == true);
    TRY_CHECK(eval(" [ mapnik::geometry_type] <> linestring ") == true);
    TRY_CHECK(eval(" [mapnik::geometry_type ] != polygon ") == true);
    TRY_CHECK(eval(" [ mapnik::geometry_type ] neq collection ") == true);
    TRY_CHECK(eval(" [mapnik::geometry_type] eq collection ") == false);

    //unary expression
    TRY_CHECK(eval(" -123.456 ") == -123.456);
    TRY_CHECK(eval(" +123.456 ") == 123.456);

    // multiplicative/additive
    auto expr = mapnik::parse_expression("(2.0 * 2.0 + 3.0 * 3.0)/(2.0 * 2.0 - 3.0 * 3.0)");
    TRY_CHECK(evaluate(*feature, *expr) == -2.6);
    auto expr2 = mapnik::parse_expression("(2.0 * 2.0 + 3.0 * 3.0)/((2.0 - 3.0) * (2.0 + 3.0))");
    TRY_CHECK(evaluate(*feature, *expr) == evaluate(*feature, *expr2));

    // logical
    TRY_CHECK(eval(" [int] = 123 and [double] = 1.23456 && [bool] = true and [null] = null && [foo] = 'bar' ") == true);
    TRY_CHECK(eval(" [int] = 456 or [foo].match('foo') || length([foo]) = 3 ") == true);
    TRY_CHECK(eval(" not true  and not true  ") == eval(" (not true ) and (not true ) "));
    TRY_CHECK(eval(" not true  or  not true  ") == eval(" (not true ) or  (not true ) "));
    TRY_CHECK(eval(" not false and not false ") == eval(" (not false) and (not false) "));
    TRY_CHECK(eval(" not false or  not false ") == eval(" (not false) or  (not false) "));

    // test not/and/or precedence using combinations of "not EQ1 OP1 not EQ2 OP2 not EQ3"
    TRY_CHECK(eval(" not [grass] = 'grow' and not [wind] = 'blow' and not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grow' and not [wind] = 'blow' or  not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grow' or  not [wind] = 'blow' and not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grow' or  not [wind] = 'blow' or  not [sky] = 'is blue' ") == false);
    TRY_CHECK(eval(" not [grass] = 'grew' and not [wind] = 'blew' and not [sky] = 'was blue' ") == true);
    TRY_CHECK(eval(" not [grass] = 'grew' and not [wind] = 'blew' or  not [sky] = 'was blue' ") == true);
    TRY_CHECK(eval(" not [grass] = 'grew' or  not [wind] = 'blew' and not [sky] = 'was blue' ") == true);
    TRY_CHECK(eval(" not [grass] = 'grew' or  not [wind] = 'blew' or  not [sky] = 'was blue' ") == true);

    // relational
    TRY_CHECK(eval(" [int] > 100 and [int] gt 100.0 and [double] < 2 and [double] lt 2.0 ") == true);
    TRY_CHECK(eval(" [int] >= 123 and [int] ge 123.0 and [double] <= 1.23456 and [double] le 1.23456 ") == true);

    // empty string/null equality
    TRY_CHECK(eval("[null] = null") == true);
    TRY_CHECK(eval("[null] != null") == false);
    TRY_CHECK(eval("[null] = ''") == false);
    ///////////////////// ref: https://github.com/mapnik/mapnik/issues/1859
    TRY_CHECK(eval("[null] != ''") == false); // back compatible - will be changed in 3.1.x
    //////////////////////
    TRY_CHECK(eval("'' = [null]") == false);
    TRY_CHECK(eval("'' != [null]") == true);

    // regex
    // replace
    TRY_CHECK(eval(" [foo].replace('(\\B)|( )','$1 ') ") == tr.transcode("b a r")); // single quotes
    TRY_CHECK(eval(" [foo].replace(\"(\\B)|( )\",\"$1 \") ") == tr.transcode("b a r")); // double quotes

    // https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode
    //'\u265C\u265E\u265D\u265B\u265A\u265D\u265E\u265C' - black chess figures
    // replace black knights with white knights
    auto val0 = eval(u8"'\u265C\u265E\u265D\u265B\u265A\u265D\u265E\u265C'.replace('\u265E','\u2658')");
    auto val1 = eval("'♜♞♝♛♚♝♞♜'.replace('♞','♘')"); // ==> expected ♜♘♝♛♚♝♘♜
    TRY_CHECK(val0 == val1);
    TRY_CHECK(val0.to_string() == val1.to_string()); // UTF-8
    TRY_CHECK(val0.to_unicode() == val1.to_unicode()); // Unicode
    // \u+NNNN \U+NNNNNNNN \xNN\xNN
    // single quotes
    auto val3 = eval("'\\u262f\\xF0\\x9F\\x8D\\xB7'");
    auto val4 = eval("'\\U0000262f\\U0001F377'");
    // double quotes
    auto val5 = eval("\"\\u262f\\xF0\\x9F\\x8D\\xB7\"");
    auto val6 = eval("\"\\U0000262f\\U0001F377\"");
    // UTF16 surrogate pairs work also ;)
    // \ud83d\udd7a\ud83c\udffc => \U0001F57A\U0001F3FC works also
    // TODO: find a way to enter UTF16 pairs
    TRY_CHECK(val3 == val4);
    TRY_CHECK(val5 == val6);
    TRY_CHECK(val3.to_string() == val4.to_string()); // UTF-8
    TRY_CHECK(val3.to_unicode() == val4.to_unicode()); // Unicod
    TRY_CHECK(val5.to_string() == val6.to_string()); // UTF-8
    TRY_CHECK(val5.to_unicode() == val6.to_unicode()); // Unicode

    // following test will fail if boost_regex is built without ICU support (unpaired surrogates in output)
    TRY_CHECK(eval("[name].replace('(\\B)|( )',' ') ") == tr.transcode("Q u é b e c"));
    TRY_CHECK(eval("'Москва'.replace('(?<!^)(\\B|b)(?!$)',' ')") == tr.transcode("М о с к в а"));
    // 'foo' =~ s:(\w)\1:$1x:r
    TRY_CHECK(eval(" 'foo'.replace('(\\w)\\1', '$1x') ") == tr.transcode("fox"));
    TRY_CHECK(parse_and_dump(" 'foo'.replace('(\\w)\\1', '$1x') ") == "'foo'.replace('(\\w)\\1','$1x')");

    // match
    TRY_CHECK(eval(" [name].match('Québec') ") == true);
    // 'Québec' =~ m:^Q\S*$:
    TRY_CHECK(eval(" [name].match('^Q\\S*$') ") == true);
    TRY_CHECK(parse_and_dump(" [name].match('^Q\\S*$') ") == "[name].match('^Q\\S*$')");

    // string & value concatenation
    // this should evaluate as two strings concatenating
    TRY_CHECK(eval("Hello + '!'") == eval("'Hello!'"));
    // this should evaulate as a combination of an int value and string
    TRY_CHECK(eval("[int]+m") == eval("'123m'"));
}
<|endoftext|>


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

#include <iostream> // TODO: remove me

#include "../../cvmfs/upload_file_processing/chunk_detector.h"
#include "../../cvmfs/upload_file_processing/char_buffer.h"

class T_ChunkDetectors : public ::testing::Test {
 protected:
  void CreateBuffers(const size_t buffer_size) {
    ClearBuffers();

    const size_t MB          = 1048576;
    const size_t full_size   = 100 * MB;

    // make sure we always produce the same test data
    srand(42);

    // produce some test data
    size_t i = 0;
    while (i < full_size) {
      upload::CharBuffer * buffer = new upload::CharBuffer(buffer_size);
      buffer->SetUsedBytes(std::min(full_size - i, buffer_size));
      buffer->SetBaseOffset(i);

      for (size_t j = 0; j < buffer->size(); ++j) {
        *(buffer->ptr() + j) = rand() % 256;
      }

      buffers_.push_back(buffer);
      i += buffer->used_bytes();
    }
  }

  virtual void TearDown() {
    ClearBuffers();
  }

 private:
  void ClearBuffers() {
    Buffers::const_iterator i    = buffers_.begin();
    Buffers::const_iterator iend = buffers_.end();
    for (; i != iend; ++i) {
      delete *i;
    }
    buffers_.clear();
  }

 protected:
  typedef std::vector<upload::CharBuffer*> Buffers;
  Buffers buffers_;
};

TEST_F(T_ChunkDetectors, StaticOffsetChunkDetector) {
  const size_t static_chunk_size = 1024;

  upload::StaticOffsetDetector static_offset_detector(static_chunk_size);
  EXPECT_FALSE (static_offset_detector.MightFindChunks(static_chunk_size));
  EXPECT_TRUE  (static_offset_detector.MightFindChunks(static_chunk_size + 1));

  upload::CharBuffer buffer(static_chunk_size);
  buffer.SetUsedBytes(static_chunk_size / 2);

  off_t next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (0, next_cut_mark);

  buffer.SetBaseOffset(buffer.used_bytes());
  next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (0, next_cut_mark);

  buffer.SetBaseOffset(buffer.used_bytes() * 2);
  next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (static_cast<off_t>(static_chunk_size), next_cut_mark);

  buffer.SetBaseOffset(buffer.used_bytes() * 3);
  next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (0, next_cut_mark);

  CreateBuffers(1048576);

  off_t next_cut = 0;
  int   runs     = 2;
  Buffers::const_iterator i    = buffers_.begin();
  Buffers::const_iterator iend = buffers_.end();
  for (; i != iend; ++i) {
    while ((next_cut = static_offset_detector.FindNextCutMark(*i)) != 0) {
      EXPECT_EQ (static_cast<off_t>(static_chunk_size) * runs, next_cut);
      ++runs;
    }
  }
}

TEST_F(T_ChunkDetectors, Xor32ChunkDetector) {
  const size_t base = 512000;
  upload::Xor32Detector xor32_detector(base, base * 2, base * 4);

  EXPECT_FALSE (xor32_detector.MightFindChunks(0));
  EXPECT_FALSE (xor32_detector.MightFindChunks(base));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base + 1));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base * 2));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base * 3));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base * 3 + 1));

  // expected cut marks
  const off_t expected[] = {
      591544,  1112589,  1716139,   2677179,   3473340,   4075967,   4832346,
     5467359,  6584843,  7233161,   8237075,   8911472,  10211539,  11708173,
    12545542, 13150940, 13746430,  14902448,  15841437,  16458881,  17123801,
    17959953, 19894517, 21942517,  22888955,  23820032,  24355174,  25084484,
    25791530, 27398981, 28841447,  29685225,  30533040,  32581040,  33565322,
    34846885, 35378061, 36354275,  37469200,  38049328,  38748025,  40526310,
    41244625, 41925007, 42497297,  43830589,  44347080,  44978978,  45843784,
    46489702, 47049471, 47713371,  48414503,  50118479,  50834131,  52830209,
    53505714, 54385890, 55416918,  56736198,  57516639,  58112403,  58705576,
    59540049, 60904495, 61734883,  62667100,  63296723,  63908168,  64952264,
    66536570, 67562340, 68734626,  69540127,  71148388,  71905512,  72813152,
    73372989, 74072441, 74621306,  75181178,  75996775,  76631479,  77206009,
    77724169, 79104681, 79710589,  81014713,  81612478,  82360921,  83350791,
    84079423, 84809053, 85439794,  86132602,  87432725,  88277767,  88889874,
    90188927, 90949653, 91472336,  92216519,  92985916,  94199809,  95523530,
    96884562, 98506308, 99403409, 101084089, 101672598, 102271189, 103238158,
    104050039
  };

  std::vector<size_t> buffer_sizes;
  buffer_sizes.push_back(102400);   // 100kB
  buffer_sizes.push_back(base);     // same as minimal chunk size
  buffer_sizes.push_back(base * 2); // same as average chunk size
  buffer_sizes.push_back(10485760); // 10MB

  std::vector<size_t>::const_iterator i    = buffer_sizes.begin();
  std::vector<size_t>::const_iterator iend = buffer_sizes.end();
  for (; i != iend; ++i) {
    // create the test data chunked into buffers of size *i
    CreateBuffers(*i);

    // go through all the buffers and check if the chunker produces the
    // expected results
    upload::Xor32Detector detector(base, base * 2, base * 4);
    off_t next_cut = 0;
    int   cut      = 0;
    bool fail      = false;
    Buffers::const_iterator j    = buffers_.begin();
    Buffers::const_iterator jend = buffers_.end();
    for (; ! fail && j != jend; ++j) {
      while ((next_cut = detector.FindNextCutMark(*j)) != 0) {
        const int index = cut++;
        if (expected[index] != next_cut) {
          EXPECT_EQ(expected[index], next_cut) << "failed with buffer size "
                                               << *i << " byte... ";
          fail = true;
        }
      }
    }
  }
}
<commit_msg>use internal PRNG instead of cstdlib's<commit_after>#include <gtest/gtest.h>
#include <vector>

#include <iostream> // TODO: remove me

#include "../../cvmfs/upload_file_processing/chunk_detector.h"
#include "../../cvmfs/upload_file_processing/char_buffer.h"
#include "../../cvmfs/prng.h"

class T_ChunkDetectors : public ::testing::Test {
 protected:
  void CreateBuffers(const size_t buffer_size) {
    ClearBuffers();

    const size_t MB          = 1048576;
    const size_t full_size   = 100 * MB;

    // make sure we always produce the same test data
    rng_.InitSeed(42);

    // produce some test data
    size_t i = 0;
    while (i < full_size) {
      upload::CharBuffer * buffer = new upload::CharBuffer(buffer_size);
      buffer->SetUsedBytes(std::min(full_size - i, buffer_size));
      buffer->SetBaseOffset(i);

      for (size_t j = 0; j < buffer->size(); ++j) {
        *(buffer->ptr() + j) = static_cast<unsigned char>(rng_.Next(256));
      }

      buffers_.push_back(buffer);
      i += buffer->used_bytes();
    }
  }

  virtual void TearDown() {
    ClearBuffers();
  }

 private:
  void ClearBuffers() {
    Buffers::const_iterator i    = buffers_.begin();
    Buffers::const_iterator iend = buffers_.end();
    for (; i != iend; ++i) {
      delete *i;
    }
    buffers_.clear();
  }

 protected:
  typedef std::vector<upload::CharBuffer*> Buffers;
  Buffers buffers_;

 private:
  Prng rng_;
};

TEST_F(T_ChunkDetectors, StaticOffsetChunkDetector) {
  const size_t static_chunk_size = 1024;

  upload::StaticOffsetDetector static_offset_detector(static_chunk_size);
  EXPECT_FALSE (static_offset_detector.MightFindChunks(static_chunk_size));
  EXPECT_TRUE  (static_offset_detector.MightFindChunks(static_chunk_size + 1));

  upload::CharBuffer buffer(static_chunk_size);
  buffer.SetUsedBytes(static_chunk_size / 2);

  off_t next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (0, next_cut_mark);

  buffer.SetBaseOffset(buffer.used_bytes());
  next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (0, next_cut_mark);

  buffer.SetBaseOffset(buffer.used_bytes() * 2);
  next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (static_cast<off_t>(static_chunk_size), next_cut_mark);

  buffer.SetBaseOffset(buffer.used_bytes() * 3);
  next_cut_mark = static_offset_detector.FindNextCutMark(&buffer);
  EXPECT_EQ (0, next_cut_mark);

  CreateBuffers(1048576);

  off_t next_cut = 0;
  int   runs     = 2;
  Buffers::const_iterator i    = buffers_.begin();
  Buffers::const_iterator iend = buffers_.end();
  for (; i != iend; ++i) {
    while ((next_cut = static_offset_detector.FindNextCutMark(*i)) != 0) {
      EXPECT_EQ (static_cast<off_t>(static_chunk_size) * runs, next_cut);
      ++runs;
    }
  }
}

TEST_F(T_ChunkDetectors, Xor32ChunkDetector) {
  const size_t base = 512000;
  upload::Xor32Detector xor32_detector(base, base * 2, base * 4);

  EXPECT_FALSE (xor32_detector.MightFindChunks(0));
  EXPECT_FALSE (xor32_detector.MightFindChunks(base));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base + 1));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base * 2));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base * 3));
  EXPECT_TRUE  (xor32_detector.MightFindChunks(base * 3 + 1));

  // expected cut marks
  const off_t expected[] = {
      1478173,   3106048,   4774918,   5483479,   6145474,   6815524,   7993818,
      9827816,  10901280,  11472834,  12866669,  14914669,  15680095,  17728095,
     18270545,  20003471,  20608171,  21331281,  21978222,  22534719,  23082248,
     24152259,  25139243,  26454712,  28211455,  29378773,  30048887,  31106301,
     32584060,  33220713,  33782665,  34554410,  36509543,  37845212,  38357766,
     39026052,  39717365,  40902090,  41894611,  42565784,  43651833,  45340938,
     45957448,  46753480,  47668586,  48650727,  49349827,  50044561,  50609473,
     52448783,  53114708,  53757146,  54854971,  55503055,  56082722,  57245560,
     58088890,  59568818,  60827431,  62875431,  63539342,  64281928,  64832251,
     65513397,  67561397,  69265229,  70379107,  71426608,  72421269,  72983868,
     73937161,  74486883,  75507129,  76825236,  77691932,  79497499,  80381772,
     81515814,  82632302,  83187470,  83940560,  84919129,  85716196,  87018583,
     88055868,  90103868,  91215622,  93088799,  94465150,  95279802,  95895281,
     96736014,  97377339,  99274429, 100477630, 101018222, 101607802, 102485227,
    103100922, 103673232, 104338021
  };

  std::vector<size_t> buffer_sizes;
  buffer_sizes.push_back(102400);   // 100kB
  buffer_sizes.push_back(base);     // same as minimal chunk size
  buffer_sizes.push_back(base * 2); // same as average chunk size
  buffer_sizes.push_back(10485760); // 10MB

  std::vector<size_t>::const_iterator i    = buffer_sizes.begin();
  std::vector<size_t>::const_iterator iend = buffer_sizes.end();
  for (; i != iend; ++i) {
    // create the test data chunked into buffers of size *i
    CreateBuffers(*i);

    // go through all the buffers and check if the chunker produces the
    // expected results
    upload::Xor32Detector detector(base, base * 2, base * 4);
    off_t next_cut = 0;
    int   cut      = 0;
    bool fail      = false;
    Buffers::const_iterator j    = buffers_.begin();
    Buffers::const_iterator jend = buffers_.end();
    for (; ! fail && j != jend; ++j) {
      while ((next_cut = detector.FindNextCutMark(*j)) != 0) {
        const int index = cut++;
        if (expected[index] != next_cut) {
          EXPECT_EQ(expected[index], next_cut) << "failed with buffer size "
                                               << *i << " byte... ";
          fail = true;
          break;
        }
      }
    }
  }
}
<|endoftext|>