<commit_before>#include "copasi.h"

#include <math.h>
#include <vector>
#include <set>

#include "utilities/utilities.h"
#include "function/function.h"
#include "CStochSolver.h"
#include "CTrajectory.h"

CStochSolver::CStochSolver()
    : mMethodType(CTrajectory::STOCH_DIRECT),
    mMethod(0)
{}

CStochSolver::CStochSolver(C_INT32 method_type)
    : mMethodType(method_type),
    mMethod(0)
{}

void CStochSolver::initialize(CModel *model)
{
  if (mMethodType == CTrajectory::STOCH_NEXTREACTION)
    {
      //        mMethod = new CStochNextReactionMethod(model);
    }
  else
    {
      mMethod = new CStochDirectMethod(model);
    }

  mMethod->initMethod();
  cout << "Done initializing stochastic method\n";
}
CStochSolver::~CStochSolver() {cleanup(); }

void CStochSolver::cleanup()
{
  if (mMethod)
    {
      mMethod->cleanup();
      delete mMethod;
    }

  mMethod = NULL;
}

CStochMethod *CStochSolver::getStochMethod()
{
  return mMethod;
}

CStochMethod::CStochMethod()
    : mModel(NULL),
    mFail(0)
{
  mRandomGenerator = new CRandom();
}

CStochMethod::CStochMethod(CModel *model)
    : mModel(model),
    mFail(0)
{
  mRandomGenerator = new CRandom();
}
CStochMethod::~CStochMethod() {cleanup(); }

void CStochMethod::cleanup()
{
  delete mRandomGenerator;
  mRandomGenerator = 0;
}

C_INT32 CStochMethod::updatePropensities()
{
  mA0 = 0;

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      calculateAmu(i);
      mA0 += mAmu[i];
    }

  return 0;
}

C_INT32 CStochMethod::calculateAmu(C_INT32 index)
{
  // We need the product of the cmu and hmu for this step.
  // We calculate this in one go, as there are fewer steps to
  // perform and we eliminate some possible rounding errors.
  C_FLOAT32 amu = 1; // initially
  C_INT32 total_substrates = 0;
  C_INT32 num_ident = 0;
  C_INT32 number = 0;
  C_INT32 lower_bound;
  // substrate_factor - The substrates, raised to their multiplicities,
  // multiplied with one another. If there are, e.g. m substrates of type m,
  // and n of type N, then substrate_factor = M^m * N^n.
  C_FLOAT64 substrate_factor = 1;
  // First, find the reaction associated with this index.
  // Keep a pointer to this.
  CChemEq *chemeq = &mModel->getReactions()[index]->getChemEq();
  // Iterate through each substrate in the reaction
  CCopasiVector < CChemEqElement > & substrates =
    const_cast < CCopasiVector < CChemEqElement > & > (chemeq->getSubstrates());

  for (unsigned C_INT32 i = 0; i < substrates.size(); i++)
    {
      num_ident = static_cast<C_INT32>(substrates[i]->getMultiplicity());
      cout << "Num ident = " << num_ident << endl;
      total_substrates += num_ident;
      number = static_cast<C_INT32> (substrates[i]->getMetabolite().getNumber());
      lower_bound = number - num_ident;
      cout << "Number = " << number << "  Lower bound = " << lower_bound << endl;
      substrate_factor = substrate_factor * pow(number, num_ident);
      cout << "Substrate factor = " << substrate_factor << endl;

      while (number > lower_bound)
        {
          amu *= number;
          number--;
        }
    }

  // We assume that all substrates are in the same compartment.
  // If there are no substrates, then volume is irrelevant. Otherwise,
  // we can use the volume of the compartment for the first substrate.
  if (substrates.size() > 0)
    {
      C_FLOAT64 volume =
        substrates[0]->getMetabolite().getCompartment()->getVolume();
      amu = amu / pow(volume, total_substrates - 1);
    }

  // rate_factor is the rate function divided by substrate_factor.
  // It would be more efficient if this was generated directly, since in effect we
  // are multiplying and then dividing by the same thing (substrate_factor)!
  C_FLOAT64 rate_factor = mModel->getReactions()[index]->calculate() / substrate_factor;

  cout << "Rate factor = " << rate_factor << endl;

  amu *= rate_factor;

  mAmu[index] = amu;

  cout << "Index = " << index << "  Amu = " << amu << endl;

  return 0;
}

C_INT32 CStochMethod::updateSystemState(C_INT32 rxn)
{
  // Change the particle numbers according to which step took place.
  // First, get the vector of balances in the reaction we've got.
  // (This vector expresses the number change of each metabolite
  // in the reaction.) Then step through each balance, using its
  // multiplicity to calculate a new value for the associated
  // metabolite. Finally, update the metabolite.
  CCopasiVector <CChemEqElement> & balances =
    const_cast < CCopasiVector <CChemEqElement> & >
    (mModel->getReactions()[rxn]->getChemEq().getBalances());
  CChemEqElement *bal = 0;
  C_FLOAT32 new_num;

  for (unsigned C_INT32 i = 0; i < balances.size(); i++)
    {
      bal = balances[i];
      new_num = bal->getMetabolite().getNumber() + bal->getMultiplicity();
      bal->getMetabolite().setNumber(new_num);
    }

  // Update the model to take into account the new particle numbers
  mModel->setConcentrations(mModel->getNumbers());

  return 0;
}

C_INT32 CStochMethod::generateReactionIndex()
{
  C_FLOAT64 rand1 = mRandomGenerator->getUniformRandom();
  C_FLOAT64 sum = 0;
  unsigned C_INT32 index = 0;

  while (index < mModel->getReactions().size())
    {
      sum += mAmu[index] / mA0;

      if (rand1 <= sum)
        {
          return index;
        }

      index++;
    }

  // shouldn't get here
  return mFail;
}

C_FLOAT64 CStochMethod::generateReactionTime()
{
  C_FLOAT32 rand2 = mRandomGenerator->getUniformRandom();
  return -1 * log(rand2 / mA0);
}

CStochDirectMethod::CStochDirectMethod()
    : CStochMethod()
{}

CStochDirectMethod::CStochDirectMethod(CModel *model)
    : CStochMethod(model)
{}
CStochDirectMethod::~CStochDirectMethod() {cleanup(); }

C_INT32 CStochDirectMethod::initMethod()
{
  // Populate the vector of propensities

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      mAmu.push_back(0);
    }

  return 0;
}

C_FLOAT64 CStochDirectMethod::doStep(C_FLOAT64 initial_time)
{
  updatePropensities();
  C_INT32 rxn = generateReactionIndex();
  C_FLOAT64 step_time = generateReactionTime();
  updateSystemState(rxn);
  return initial_time + step_time;
}

CStochNextReactionMethod::CStochNextReactionMethod(CModel *model)
    : CStochMethod(model)
{}
CStochNextReactionMethod::~CStochNextReactionMethod() {cleanup(); }

C_INT32 CStochNextReactionMethod::initMethod(C_FLOAT64 start_time)
{
  // Populate the vector of propensities

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      mAmu.push_back(0);
    }

  setupDependencyGraph();
  updatePropensities();
  setupPriorityQueue(start_time);
  return 0;
}

C_FLOAT64 CStochNextReactionMethod::doStep()
{
  C_FLOAT64 time = mPQ.topKey();
  C_INT32 reaction_index = mPQ.topIndex();
  updateSystemState(reaction_index);
  updatePriorityQueue(reaction_index, time);
  return time;
}

void CStochNextReactionMethod::setupDependencyGraph()
{
  vector< set<CMetab>* > DependsOn;
  vector< set<CMetab>* > Affects;
  //    set<CMetab> *tmpdepends = 0;
  //    set<CMetab> *tmpaffects = 0;
  C_INT32 num_reactions = mModel->getReactions().size();
  C_INT32 i, j;
  // Do for each reaction:

  for (i = 0; i < num_reactions; i++)
    {
      // Get the set of metabolites  which affect the value of amu for this
      // reaction i.e. the set on which amu depends. This may be  more than
      // the set of substrates, since the kinetics can involve other
      // reactants, e.g. catalysts. We thus need to step through the
      // rate function and pick out every reactant which can vary.
      DependsOn.push_back(getDependsOn(i));
      // Get the set of metabolites which are affected when this reaction takes place
      Affects.push_back(getAffects(i));
    }

  // For each possible pair of reactions i and j, if the intersection of
  // Affects(i) with DependsOn(j) is non-empty, add a dependency edge from i to j.
  for (i = 0; i < num_reactions; i++)
    {
      for (j = 0; j < num_reactions; j++)
        {
          // Determine whether the intersection of these two sets is non-empty
          // Could also do this with set_intersection generic algorithm, but that
          // would require operator<() to be defined on the set elements.

          set
            <CMetab>::iterator iter = Affects[i]->begin();

          for (; iter != Affects[i]->end(); iter++)
            {
              if (DependsOn[j]->count(*iter))
                {
                  // The set intersection is non-empty
                  mDG.addDependent(i, j);
                  break;
                }
            }
        }

      // Ensure that self edges are included
      mDG.addDependent(i, i);
    }

  // Delete the memory allocated in getDependsOn() and getAffects()
  // since this is allocated in other functions.
  for (i = 0; i < num_reactions; i++)
    {
      delete DependsOn[i];
      delete Affects[i];
    }
}

void CStochNextReactionMethod::setupPriorityQueue(C_FLOAT64 start_time)
{
  C_INT32 rxn;
  C_FLOAT64 time;

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      rxn = generateReactionIndex();
      time = start_time + generateReactionTime();
      mPQ.pushPair(rxn, time);
    }

  mPQ.buildHeap();
}

void CStochNextReactionMethod::updatePriorityQueue(C_INT32 reaction_index, C_FLOAT64 time)
{
  vector<C_INT32> dep_nodes = mDG.getDependents(reaction_index);

  for (unsigned int i = 0; i < dep_nodes.size(); i++)
    {
      C_FLOAT64 new_time = time + generateReactionTime();
      mPQ.updateNode(dep_nodes[i], new_time);
    }
}

set
  <CMetab> *CStochNextReactionMethod::getDependsOn(C_INT32 reaction_index)
  {
    set
      <CMetab> *retset = new set
                           <CMetab>;

    //#if 0    // New way of doing this; supercedes bit #if'ed out
    // Get chemical equation balances
    CCopasiVector<CChemEqElement> balances = mModel->getReactions()[reaction_index]->getChemEq().getBalances();

    for (unsigned C_INT32 i = 0; i < balances.size(); i++)
      {
        retset->insert(balances[i]->getMetabolite());
      }

    //#endif // 0
#if 0
    // Get the reaction associated with this index
    CReaction *react = mModel->getReactions()[reaction_index];

    // Get the kinetic function associated with the reaction_index'th reaction in the model.
    CKinFunction *pfunction = dynamic_cast<CKinFunction*>(&react->getFunction());

    if (!pfunction)
      {
        // the dynamic cast didn't work
        CCopasiMessage(CCopasiMessage::ERROR, "Dynamic cast of CFunction to CKinFunction failed");
        return 0;
      }

    // Get the vector of nodes associated with this function
    CCopasiVectorS<CNodeK> node_vec = pfunction->getNodes();

    CNodeK *tmpnode;

    char subtype;

    CMetab *pmetab = 0;

    // Step through the nodes. If the node is an identifier and corresponds
    // to a substrate or modifier, then add it to the set.

    for (unsigned C_INT32 i = 0; i < node_vec.size(); i++)
      {
        pmetab = 0;
        tmpnode = node_vec[i];

        if ((tmpnode->getType() == N_IDENTIFIER) &&
            (((subtype = tmpnode->getSubtype()) == N_SUBSTRATE) ||
             (subtype == N_MODIFIER)))
          {
            // Get the identifier as known to the node.
            string id_name = tmpnode->getName();
            // Now try to find this, first amongst the substrates, then the products

            if ((pmetab = react->findSubstrate(id_name)) == 0)
              {
                pmetab = react->findModifier(id_name);
              }

            if (pmetab == 0)
              {
                // Uh oh, we haven't found a metabolite corresponding to this node.
                CCopasiMessage(CCopasiMessage::ERROR, "Couldn't find a substrate or modifier named %s", id_name.c_str());
              }

            // we found it
            retset->insert(*pmetab);
          }
      }

#endif // 0
    return retset;
  }

set
  <CMetab> *CStochNextReactionMethod::getAffects(C_INT32 reaction_index)
  {
    set
      <CMetab> *retset = new set
                           <CMetab>;

    // Get the balances  associated with the reaction at this index
    // XXX We first get the chemical equation, then the balances, since the getBalances method in CReaction is unimplemented!
    CCopasiVector<CChemEqElement> balances = mModel->getReactions()[reaction_index]->getChemEq().getBalances();

    for (unsigned C_INT32 i = 0; i < balances.size(); i++)
      {
        if (balances[i]->getMultiplicity() != 0)
          {
            retset->insert(balances[i]->getMetabolite());
          }
      }

    return retset;
  }
<commit_msg>fixed calculation of reaktion time<commit_after>#include "copasi.h"

#include <math.h>
#include <vector>
#include <set>

#include "utilities/utilities.h"
#include "function/function.h"
#include "CStochSolver.h"
#include "CTrajectory.h"

CStochSolver::CStochSolver()
    : mMethodType(CTrajectory::STOCH_DIRECT),
    mMethod(0)
{}

CStochSolver::CStochSolver(C_INT32 method_type)
    : mMethodType(method_type),
    mMethod(0)
{}

void CStochSolver::initialize(CModel *model)
{
  if (mMethodType == CTrajectory::STOCH_NEXTREACTION)
    {
      //        mMethod = new CStochNextReactionMethod(model);
    }
  else
    {
      mMethod = new CStochDirectMethod(model);
    }

  mMethod->initMethod();
  cout << "Done initializing stochastic method\n";
}
CStochSolver::~CStochSolver() {cleanup(); }

void CStochSolver::cleanup()
{
  if (mMethod)
    {
      mMethod->cleanup();
      delete mMethod;
    }

  mMethod = NULL;
}

CStochMethod *CStochSolver::getStochMethod()
{
  return mMethod;
}

CStochMethod::CStochMethod()
    : mModel(NULL),
    mFail(0)
{
  mRandomGenerator = new CRandom();
}

CStochMethod::CStochMethod(CModel *model)
    : mModel(model),
    mFail(0)
{
  mRandomGenerator = new CRandom();
}
CStochMethod::~CStochMethod() {cleanup(); }

void CStochMethod::cleanup()
{
  delete mRandomGenerator;
  mRandomGenerator = 0;
}

C_INT32 CStochMethod::updatePropensities()
{
  mA0 = 0;
  //cout << "        updatePropensities: ";

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      calculateAmu(i);
      //cout << mAmu[i] << " ";
      mA0 += mAmu[i];
    }

  //cout << endl;
  return 0;
}

C_INT32 CStochMethod::calculateAmu(C_INT32 index)
{
  // We need the product of the cmu and hmu for this step.
  // We calculate this in one go, as there are fewer steps to
  // perform and we eliminate some possible rounding errors.
  C_FLOAT32 amu = 1; // initially
  C_INT32 total_substrates = 0;
  C_INT32 num_ident = 0;
  C_INT32 number = 0;
  C_INT32 lower_bound;
  // substrate_factor - The substrates, raised to their multiplicities,
  // multiplied with one another. If there are, e.g. m substrates of type m,
  // and n of type N, then substrate_factor = M^m * N^n.
  C_FLOAT64 substrate_factor = 1;
  // First, find the reaction associated with this index.
  // Keep a pointer to this.
  CChemEq *chemeq = &mModel->getReactions()[index]->getChemEq();
  // Iterate through each substrate in the reaction
  CCopasiVector < CChemEqElement > & substrates =
    const_cast < CCopasiVector < CChemEqElement > & > (chemeq->getSubstrates());

  for (unsigned C_INT32 i = 0; i < substrates.size(); i++)
    {
      num_ident = static_cast<C_INT32>(substrates[i]->getMultiplicity());
      //cout << "Num ident = " << num_ident << endl;
      total_substrates += num_ident;
      number = static_cast<C_INT32> (substrates[i]->getMetabolite().getNumber());
      lower_bound = number - num_ident;
      //cout << "Number = " << number << "  Lower bound = " << lower_bound << endl;
      substrate_factor = substrate_factor * pow(number, num_ident);
      //cout << "Substrate factor = " << substrate_factor << endl;

      while (number > lower_bound)
        {
          amu *= number;
          number--;
        }
    }

  if (amu == 0)  // at least one substrate particle number is zero
    {
      mAmu[index] = 0;
      return 0;
    }

  // We assume that all substrates are in the same compartment.
  // If there are no substrates, then volume is irrelevant. Otherwise,
  // we can use the volume of the compartment for the first substrate.
  if (substrates.size() > 0)
    {
      C_FLOAT64 volume =
        substrates[0]->getMetabolite().getCompartment()->getVolume();
      amu = amu / pow(volume, total_substrates - 1);
    }

  // rate_factor is the rate function divided by substrate_factor.
  // It would be more efficient if this was generated directly, since in effect we
  // are multiplying and then dividing by the same thing (substrate_factor)!
  C_FLOAT64 dummy = mModel->getReactions()[index]->calculate() ;

  C_FLOAT64 rate_factor = mModel->getReactions()[index]->calculate() / substrate_factor;

  //cout << "Rate factor = " << rate_factor << endl;
  amu *= rate_factor;

  mAmu[index] = amu;

  //cout << "Index = " << index << "  Amu = " << amu << endl;
  return 0;
}

C_INT32 CStochMethod::updateSystemState(C_INT32 rxn)
{
  // Change the particle numbers according to which step took place.
  // First, get the vector of balances in the reaction we've got.
  // (This vector expresses the number change of each metabolite
  // in the reaction.) Then step through each balance, using its
  // multiplicity to calculate a new value for the associated
  // metabolite. Finally, update the metabolite.
  CCopasiVector <CChemEqElement> & balances =
    const_cast < CCopasiVector <CChemEqElement> & >
    (mModel->getReactions()[rxn]->getChemEq().getBalances());
  CChemEqElement *bal = 0;
  C_FLOAT32 new_num;

  for (unsigned C_INT32 i = 0; i < balances.size(); i++)
    {
      bal = balances[i];
      new_num = bal->getMetabolite().getNumber() + bal->getMultiplicity();
      bal->getMetabolite().setNumber(new_num);
    }

  // Update the model to take into account the new particle numbers
  mModel->setConcentrations(mModel->getNumbers());

  //cout << "Reaktion " << rxn << " new state: " ;
  for (int j = 0 ; j < 2 ; j++)
    cout << mModel->getMetabolites()[j]->getNumber() << "  ";

  cout << endl;

  return 0;
}

C_INT32 CStochMethod::generateReactionIndex()
{
  C_FLOAT64 rand1 = mRandomGenerator->getUniformRandom();
  C_FLOAT64 sum = 0;
  unsigned C_INT32 index = 0;

  while (index < mModel->getReactions().size())
    {
      sum += mAmu[index] / mA0;

      if (rand1 <= sum)
        {
          return index;
        }

      index++;
    }

  // shouldn't get here
  return mFail;
}

C_FLOAT64 CStochMethod::generateReactionTime()
{
  C_FLOAT32 rand2 = mRandomGenerator->getUniformRandom();
  return -1 * log(rand2) / mA0;
}

CStochDirectMethod::CStochDirectMethod()
    : CStochMethod()
{}

CStochDirectMethod::CStochDirectMethod(CModel *model)
    : CStochMethod(model)
{}
CStochDirectMethod::~CStochDirectMethod() {cleanup(); }

C_INT32 CStochDirectMethod::initMethod()
{
  // Populate the vector of propensities

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      mAmu.push_back(0);
    }

  return 0;
}

C_FLOAT64 CStochDirectMethod::doStep(C_FLOAT64 initial_time)
{
  updatePropensities();
  C_INT32 rxn = generateReactionIndex();
  C_FLOAT64 step_time = generateReactionTime();
  updateSystemState(rxn);
  return initial_time + step_time;
}

CStochNextReactionMethod::CStochNextReactionMethod(CModel *model)
    : CStochMethod(model)
{}
CStochNextReactionMethod::~CStochNextReactionMethod() {cleanup(); }

C_INT32 CStochNextReactionMethod::initMethod(C_FLOAT64 start_time)
{
  // Populate the vector of propensities

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      mAmu.push_back(0);
    }

  setupDependencyGraph();
  updatePropensities();
  setupPriorityQueue(start_time);
  return 0;
}

C_FLOAT64 CStochNextReactionMethod::doStep()
{
  C_FLOAT64 time = mPQ.topKey();
  C_INT32 reaction_index = mPQ.topIndex();
  updateSystemState(reaction_index);
  updatePriorityQueue(reaction_index, time);
  return time;
}

void CStochNextReactionMethod::setupDependencyGraph()
{
  vector< set<CMetab>* > DependsOn;
  vector< set<CMetab>* > Affects;
  //    set<CMetab> *tmpdepends = 0;
  //    set<CMetab> *tmpaffects = 0;
  C_INT32 num_reactions = mModel->getReactions().size();
  C_INT32 i, j;
  // Do for each reaction:

  for (i = 0; i < num_reactions; i++)
    {
      // Get the set of metabolites  which affect the value of amu for this
      // reaction i.e. the set on which amu depends. This may be  more than
      // the set of substrates, since the kinetics can involve other
      // reactants, e.g. catalysts. We thus need to step through the
      // rate function and pick out every reactant which can vary.
      DependsOn.push_back(getDependsOn(i));
      // Get the set of metabolites which are affected when this reaction takes place
      Affects.push_back(getAffects(i));
    }

  // For each possible pair of reactions i and j, if the intersection of
  // Affects(i) with DependsOn(j) is non-empty, add a dependency edge from i to j.
  for (i = 0; i < num_reactions; i++)
    {
      for (j = 0; j < num_reactions; j++)
        {
          // Determine whether the intersection of these two sets is non-empty
          // Could also do this with set_intersection generic algorithm, but that
          // would require operator<() to be defined on the set elements.

          set
            <CMetab>::iterator iter = Affects[i]->begin();

          for (; iter != Affects[i]->end(); iter++)
            {
              if (DependsOn[j]->count(*iter))
                {
                  // The set intersection is non-empty
                  mDG.addDependent(i, j);
                  break;
                }
            }
        }

      // Ensure that self edges are included
      mDG.addDependent(i, i);
    }

  // Delete the memory allocated in getDependsOn() and getAffects()
  // since this is allocated in other functions.
  for (i = 0; i < num_reactions; i++)
    {
      delete DependsOn[i];
      delete Affects[i];
    }
}

void CStochNextReactionMethod::setupPriorityQueue(C_FLOAT64 start_time)
{
  C_INT32 rxn;
  C_FLOAT64 time;

  for (unsigned C_INT32 i = 0; i < mModel->getReactions().size(); i++)
    {
      rxn = generateReactionIndex();
      time = start_time + generateReactionTime();
      mPQ.pushPair(rxn, time);
    }

  mPQ.buildHeap();
}

void CStochNextReactionMethod::updatePriorityQueue(C_INT32 reaction_index, C_FLOAT64 time)
{
  vector<C_INT32> dep_nodes = mDG.getDependents(reaction_index);

  for (unsigned int i = 0; i < dep_nodes.size(); i++)
    {
      C_FLOAT64 new_time = time + generateReactionTime();
      mPQ.updateNode(dep_nodes[i], new_time);
    }
}

set
  <CMetab> *CStochNextReactionMethod::getDependsOn(C_INT32 reaction_index)
  {
    set
      <CMetab> *retset = new set
                           <CMetab>;

    //#if 0    // New way of doing this; supercedes bit #if'ed out
    // Get chemical equation balances
    CCopasiVector<CChemEqElement> balances = mModel->getReactions()[reaction_index]->getChemEq().getBalances();

    for (unsigned C_INT32 i = 0; i < balances.size(); i++)
      {
        retset->insert(balances[i]->getMetabolite());
      }

    //#endif // 0
#if 0
    // Get the reaction associated with this index
    CReaction *react = mModel->getReactions()[reaction_index];

    // Get the kinetic function associated with the reaction_index'th reaction in the model.
    CKinFunction *pfunction = dynamic_cast<CKinFunction*>(&react->getFunction());

    if (!pfunction)
      {
        // the dynamic cast didn't work
        CCopasiMessage(CCopasiMessage::ERROR, "Dynamic cast of CFunction to CKinFunction failed");
        return 0;
      }

    // Get the vector of nodes associated with this function
    CCopasiVectorS<CNodeK> node_vec = pfunction->getNodes();

    CNodeK *tmpnode;

    char subtype;

    CMetab *pmetab = 0;

    // Step through the nodes. If the node is an identifier and corresponds
    // to a substrate or modifier, then add it to the set.

    for (unsigned C_INT32 i = 0; i < node_vec.size(); i++)
      {
        pmetab = 0;
        tmpnode = node_vec[i];

        if ((tmpnode->getType() == N_IDENTIFIER) &&
            (((subtype = tmpnode->getSubtype()) == N_SUBSTRATE) ||
             (subtype == N_MODIFIER)))
          {
            // Get the identifier as known to the node.
            string id_name = tmpnode->getName();
            // Now try to find this, first amongst the substrates, then the products

            if ((pmetab = react->findSubstrate(id_name)) == 0)
              {
                pmetab = react->findModifier(id_name);
              }

            if (pmetab == 0)
              {
                // Uh oh, we haven't found a metabolite corresponding to this node.
                CCopasiMessage(CCopasiMessage::ERROR, "Couldn't find a substrate or modifier named %s", id_name.c_str());
              }

            // we found it
            retset->insert(*pmetab);
          }
      }

#endif // 0
    return retset;
  }

set
  <CMetab> *CStochNextReactionMethod::getAffects(C_INT32 reaction_index)
  {
    set
      <CMetab> *retset = new set
                           <CMetab>;

    // Get the balances  associated with the reaction at this index
    // XXX We first get the chemical equation, then the balances, since the getBalances method in CReaction is unimplemented!
    CCopasiVector<CChemEqElement> balances = mModel->getReactions()[reaction_index]->getChemEq().getBalances();

    for (unsigned C_INT32 i = 0; i < balances.size(); i++)
      {
        if (balances[i]->getMultiplicity() != 0)
          {
            retset->insert(balances[i]->getMetabolite());
          }
      }

    return retset;
  }
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <assert.h>
#include <dirent.h>
#include <pthread.h>
#include <pty.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "sandbox_impl.h"

// This is basically a marker to grep for.
#define TEST(name) void name()

TEST(test_dup) {
  StartSeccompSandbox();
  // Test a simple syscall that is marked as UNRESTRICTED_SYSCALL.
  int fd = dup(1);
  assert(fd >= 0);
  int rc = close(fd);
  assert(rc == 0);
}

// This has an off-by-three error because it counts ".", "..", and the
// FD for the /proc/self/fd directory.  This doesn't matter because it
// is only used to check for differences in the number of open FDs.
int count_fds() {
  DIR *dir = opendir("/proc/self/fd");
  assert(dir != NULL);
  int count = 0;
  while (1) {
    struct dirent *d = readdir(dir);
    if (d == NULL)
      break;
    count++;
  }
  int rc = closedir(dir);
  assert(rc == 0);
  return count;
}

void *thread_func(void *x) {
  int *ptr = (int *) x;
  *ptr = 123;
  printf("In new thread\n");
  return (void *) 456;
}

TEST(test_thread) {
  StartSeccompSandbox();
  int fd_count1 = count_fds();
  pthread_t tid;
  int x = 999;
  void *result;
  pthread_create(&tid, NULL, thread_func, &x);
  printf("Waiting for thread\n");
  pthread_join(tid, &result);
  assert(result == (void *) 456);
  assert(x == 123);
  // Check that the process has not leaked FDs.
  int fd_count2 = count_fds();
  assert(fd_count2 == fd_count1);
}

int clone_func(void *x) {
  int *ptr = (int *) x;
  *ptr = 124;
  printf("In thread\n");
  // On x86-64, returning from this function calls the __NR_exit_group
  // syscall instead of __NR_exit.
  syscall(__NR_exit, 100);
  // Not reached.
  return 200;
}

#if defined(__i386__)
int get_gs() {
  int gs;
  asm volatile("mov %%gs, %0" : "=r"(gs));
  return gs;
}
#endif

void *get_tls_base() {
  void *base;
#if defined(__x86_64__)
  asm volatile("mov %%fs:0, %0" : "=r"(base));
#elif defined(__i386__)
  asm volatile("mov %%gs:0, %0" : "=r"(base));
#else
#error Unsupported target platform
#endif
  return base;
}

TEST(test_clone) {
  StartSeccompSandbox();
  int fd_count1 = count_fds();
  int stack_size = 0x1000;
  char *stack = (char *) malloc(stack_size);
  assert(stack != NULL);
  int flags = CLONE_VM | CLONE_FS | CLONE_FILES |
    CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM |
    CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
  int tid = -1;
  int x = 999;

  // The sandbox requires us to pass CLONE_TLS.  Pass settings that
  // are enough to copy the parent thread's TLS setup.  This allows us
  // to invoke libc in the child thread.
#if defined(__x86_64__)
  void *tls = get_tls_base();
#elif defined(__i386__)
  struct user_desc tls_desc, *tls = &tls_desc;
  tls_desc.entry_number = get_gs() >> 3;
  tls_desc.base_addr = (long) get_tls_base();
  tls_desc.limit = 0xfffff;
  tls_desc.seg_32bit = 1;
  tls_desc.contents = 0;
  tls_desc.read_exec_only = 0;
  tls_desc.limit_in_pages = 1;
  tls_desc.seg_not_present = 0;
  tls_desc.useable = 1;
#else
#error Unsupported target platform
#endif

  int rc = clone(clone_func, (void *) (stack + stack_size), flags, &x,
                 &tid, tls, &tid);
  assert(rc > 0);
  while (tid == rc) {
    syscall(__NR_futex, &tid, FUTEX_WAIT, rc, NULL);
  }
  assert(tid == 0);
  assert(x == 124);
  // Check that the process has not leaked FDs.
  int fd_count2 = count_fds();
  assert(fd_count2 == fd_count1);
}

int uncalled_clone_func(void *x) {
  printf("In thread func, which shouldn't happen\n");
  return 1;
}

TEST(test_clone_disallowed_flags) {
  StartSeccompSandbox();
  int stack_size = 4096;
  char *stack = (char *) malloc(stack_size);
  assert(stack != NULL);
  /* We omit the flags CLONE_SETTLS, CLONE_PARENT_SETTID and
     CLONE_CHILD_CLEARTID, which is disallowed by the sandbox. */
  int flags = CLONE_VM | CLONE_FS | CLONE_FILES |
    CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM;
  int rc = clone(uncalled_clone_func, (void *) (stack + stack_size),
                 flags, NULL, NULL, NULL, NULL);
  assert(rc == -1);
  assert(errno == EPERM);
}

long long read_tsc() {
  long long rc;
  asm volatile(
      "rdtsc\n"
      "mov %%eax, (%0)\n"
      "mov %%edx, 4(%0)\n"
      :
      : "c"(&rc), "a"(-1), "d"(-1));
  return rc;
}

TEST(test_rdtsc) {
  StartSeccompSandbox();
  // Just check that we can do the instruction.
  read_tsc();
}

TEST(test_getpid) {
  int pid1 = getpid();
  StartSeccompSandbox();
  int pid2 = getpid();
  assert(pid1 == pid2);
  // Bypass any caching that glibc's getpid() wrapper might do.
  int pid3 = syscall(__NR_getpid);
  assert(pid1 == pid3);
}

TEST(test_gettid) {
  // glibc doesn't provide a gettid() wrapper.
  int tid1 = syscall(__NR_gettid);
  assert(tid1 > 0);
  StartSeccompSandbox();
  int tid2 = syscall(__NR_gettid);
  assert(tid1 == tid2);
}

void *map_something() {
  void *addr = mmap(NULL, 0x1000, PROT_READ,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  assert(addr != MAP_FAILED);
  return addr;
}

TEST(test_mmap_disallows_remapping) {
  void *addr = map_something();
  StartSeccompSandbox();
  // Overwriting a mapping that was created before the sandbox was
  // enabled is not allowed.
  void *result = mmap(addr, 0x1000, PROT_READ,
                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  assert(result == MAP_FAILED);
  assert(errno == EINVAL);
}

TEST(test_mmap_disallows_low_address) {
  StartSeccompSandbox();
  // Mapping pages at low addresses is not allowed because this helps
  // with exploiting buggy kernels.
  void *result = mmap(NULL, 0x1000, PROT_READ,
                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  assert(result == MAP_FAILED);
  assert(errno == EINVAL);
}

TEST(test_munmap_allowed) {
  StartSeccompSandbox();
  void *addr = map_something();
  int result = munmap(addr, 0x1000);
  assert(result == 0);
}

TEST(test_munmap_disallowed) {
  void *addr = map_something();
  StartSeccompSandbox();
  int result = munmap(addr, 0x1000);
  assert(result == -1);
  assert(errno == EINVAL);
}

TEST(test_mprotect_allowed) {
  StartSeccompSandbox();
  void *addr = map_something();
  int result = mprotect(addr, 0x1000, PROT_READ | PROT_WRITE);
  assert(result == 0);
}

TEST(test_mprotect_disallowed) {
  void *addr = map_something();
  StartSeccompSandbox();
  int result = mprotect(addr, 0x1000, PROT_READ | PROT_WRITE);
  assert(result == -1);
  assert(errno == EINVAL);
}

int get_tty_fd() {
  int master_fd, tty_fd;
  int rc = openpty(&master_fd, &tty_fd, NULL, NULL, NULL);
  assert(rc == 0);
  return tty_fd;
}

TEST(test_ioctl_tiocgwinsz_allowed) {
  int tty_fd = get_tty_fd();
  StartSeccompSandbox();
  int size[2];
  // Get terminal width and height.
  int result = ioctl(tty_fd, TIOCGWINSZ, size);
  assert(result == 0);
}

TEST(test_ioctl_disallowed) {
  int tty_fd = get_tty_fd();
  StartSeccompSandbox();
  // This ioctl call inserts a character into the tty's input queue,
  // which provides a way to send commands to an interactive shell.
  char c = 'x';
  int result = ioctl(tty_fd, TIOCSTI, &c);
  assert(result == -1);
  assert(errno == EINVAL);
}

TEST(test_socket) {
  StartSeccompSandbox();
  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
  assert(fd == -1);
  // TODO: Make it consistent between i386 and x86-64.
  assert(errno == EINVAL || errno == ENOSYS);
}

TEST(test_open) {
  StartSeccompSandbox();
  int fd = open("/dev/null", O_RDONLY);
  assert(fd >= 0);
  int rc = close(fd);
  assert(rc == 0);
  fd = open("/dev/null", O_WRONLY);
  assert(fd == -1);
  assert(errno == EACCES);
}

TEST(test_access) {
  StartSeccompSandbox();
  int rc = access("/dev/null", R_OK);
  assert(rc == 0);
  rc = access("path-that-does-not-exist", R_OK);
  assert(rc == -1);
  assert(errno == ENOENT);
}

TEST(test_stat) {
  StartSeccompSandbox();
  struct stat st;
  int rc = stat("/dev/null", &st);
  assert(rc == 0);
  rc = stat("path-that-does-not-exist", &st);
  assert(rc == -1);
  assert(errno == ENOENT);
}

struct testcase {
  const char *test_name;
  void (*test_func)();
};

struct testcase all_tests[] = {
#include "test-list.h"
  { NULL, NULL },
};

int run_test_forked(struct testcase *test) {
  printf("** %s\n", test->test_name);
  int pid = fork();
  if (pid == 0) {
    test->test_func();
    _exit(0);
  }
  int status;
  waitpid(pid, &status, 0);
  if (status != 0) {
    printf("Test failed with exit status %i\n", status);
    return 1;
  }
  else {
    return 0;
  }
}

int run_test_by_name(const char *name) {
  struct testcase *test;
  for (test = all_tests; test->test_name != NULL; test++) {
    if (strcmp(name, test->test_name) == 0) {
      test->test_func();
      return 0;
    }
  }
  fprintf(stderr, "Test '%s' not found\n", name);
  return 1;
}

int main(int argc, char **argv) {
  if (argc == 2) {
    // Run one test without forking, to aid debugging.
    return run_test_by_name(argv[1]);
  }
  else if (argc > 2) {
    // TODO: run multiple tests.
    fprintf(stderr, "Too many arguments\n");
    return 1;
  }
  else {
    // Run all tests.
    struct testcase *test;
    int failures = 0;
    for (test = all_tests; test->test_name != NULL; test++) {
      failures += run_test_forked(test);
    }
    if (failures == 0) {
      printf("OK\n");
      return 0;
    }
    else {
      printf("%i FAILURE(S)\n", failures);
      return 1;
    }
  }
}
<commit_msg>Add tests for signal handling<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <assert.h>
#include <dirent.h>
#include <pthread.h>
#include <pty.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "sandbox_impl.h"

int g_intended_status_fd = -1;

// Declares the wait() status that the test subprocess intends to exit with.
void intend_exit_status(int val) {
  if (g_intended_status_fd != -1) {
    int sent = write(g_intended_status_fd, &val, sizeof(val));
    assert(sent == sizeof(val));
  }
  else {
    printf("Intending to exit with status %i...\n", val);
  }
}

// This is basically a marker to grep for.
#define TEST(name) void name()

TEST(test_dup) {
  StartSeccompSandbox();
  // Test a simple syscall that is marked as UNRESTRICTED_SYSCALL.
  int fd = dup(1);
  assert(fd >= 0);
  int rc = close(fd);
  assert(rc == 0);
}

TEST(test_segfault) {
  // Check that the sandbox's SIGSEGV handler does not stop the
  // process from dying cleanly in the event of a real segfault.
  intend_exit_status(SIGSEGV);
  asm("hlt");
}

TEST(test_exit) {
  intend_exit_status(123 << 8);
  _exit(123);
}

// This has an off-by-three error because it counts ".", "..", and the
// FD for the /proc/self/fd directory.  This doesn't matter because it
// is only used to check for differences in the number of open FDs.
int count_fds() {
  DIR *dir = opendir("/proc/self/fd");
  assert(dir != NULL);
  int count = 0;
  while (1) {
    struct dirent *d = readdir(dir);
    if (d == NULL)
      break;
    count++;
  }
  int rc = closedir(dir);
  assert(rc == 0);
  return count;
}

void *thread_func(void *x) {
  int *ptr = (int *) x;
  *ptr = 123;
  printf("In new thread\n");
  return (void *) 456;
}

TEST(test_thread) {
  StartSeccompSandbox();
  int fd_count1 = count_fds();
  pthread_t tid;
  int x = 999;
  void *result;
  pthread_create(&tid, NULL, thread_func, &x);
  printf("Waiting for thread\n");
  pthread_join(tid, &result);
  assert(result == (void *) 456);
  assert(x == 123);
  // Check that the process has not leaked FDs.
  int fd_count2 = count_fds();
  assert(fd_count2 == fd_count1);
}

int clone_func(void *x) {
  int *ptr = (int *) x;
  *ptr = 124;
  printf("In thread\n");
  // On x86-64, returning from this function calls the __NR_exit_group
  // syscall instead of __NR_exit.
  syscall(__NR_exit, 100);
  // Not reached.
  return 200;
}

#if defined(__i386__)
int get_gs() {
  int gs;
  asm volatile("mov %%gs, %0" : "=r"(gs));
  return gs;
}
#endif

void *get_tls_base() {
  void *base;
#if defined(__x86_64__)
  asm volatile("mov %%fs:0, %0" : "=r"(base));
#elif defined(__i386__)
  asm volatile("mov %%gs:0, %0" : "=r"(base));
#else
#error Unsupported target platform
#endif
  return base;
}

TEST(test_clone) {
  StartSeccompSandbox();
  int fd_count1 = count_fds();
  int stack_size = 0x1000;
  char *stack = (char *) malloc(stack_size);
  assert(stack != NULL);
  int flags = CLONE_VM | CLONE_FS | CLONE_FILES |
    CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM |
    CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
  int tid = -1;
  int x = 999;

  // The sandbox requires us to pass CLONE_TLS.  Pass settings that
  // are enough to copy the parent thread's TLS setup.  This allows us
  // to invoke libc in the child thread.
#if defined(__x86_64__)
  void *tls = get_tls_base();
#elif defined(__i386__)
  struct user_desc tls_desc, *tls = &tls_desc;
  tls_desc.entry_number = get_gs() >> 3;
  tls_desc.base_addr = (long) get_tls_base();
  tls_desc.limit = 0xfffff;
  tls_desc.seg_32bit = 1;
  tls_desc.contents = 0;
  tls_desc.read_exec_only = 0;
  tls_desc.limit_in_pages = 1;
  tls_desc.seg_not_present = 0;
  tls_desc.useable = 1;
#else
#error Unsupported target platform
#endif

  int rc = clone(clone_func, (void *) (stack + stack_size), flags, &x,
                 &tid, tls, &tid);
  assert(rc > 0);
  while (tid == rc) {
    syscall(__NR_futex, &tid, FUTEX_WAIT, rc, NULL);
  }
  assert(tid == 0);
  assert(x == 124);
  // Check that the process has not leaked FDs.
  int fd_count2 = count_fds();
  assert(fd_count2 == fd_count1);
}

int uncalled_clone_func(void *x) {
  printf("In thread func, which shouldn't happen\n");
  return 1;
}

TEST(test_clone_disallowed_flags) {
  StartSeccompSandbox();
  int stack_size = 4096;
  char *stack = (char *) malloc(stack_size);
  assert(stack != NULL);
  /* We omit the flags CLONE_SETTLS, CLONE_PARENT_SETTID and
     CLONE_CHILD_CLEARTID, which is disallowed by the sandbox. */
  int flags = CLONE_VM | CLONE_FS | CLONE_FILES |
    CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM;
  int rc = clone(uncalled_clone_func, (void *) (stack + stack_size),
                 flags, NULL, NULL, NULL, NULL);
  assert(rc == -1);
  assert(errno == EPERM);
}

long long read_tsc() {
  long long rc;
  asm volatile(
      "rdtsc\n"
      "mov %%eax, (%0)\n"
      "mov %%edx, 4(%0)\n"
      :
      : "c"(&rc), "a"(-1), "d"(-1));
  return rc;
}

TEST(test_rdtsc) {
  StartSeccompSandbox();
  // Just check that we can do the instruction.
  read_tsc();
}

TEST(test_getpid) {
  int pid1 = getpid();
  StartSeccompSandbox();
  int pid2 = getpid();
  assert(pid1 == pid2);
  // Bypass any caching that glibc's getpid() wrapper might do.
  int pid3 = syscall(__NR_getpid);
  assert(pid1 == pid3);
}

TEST(test_gettid) {
  // glibc doesn't provide a gettid() wrapper.
  int tid1 = syscall(__NR_gettid);
  assert(tid1 > 0);
  StartSeccompSandbox();
  int tid2 = syscall(__NR_gettid);
  assert(tid1 == tid2);
}

void *map_something() {
  void *addr = mmap(NULL, 0x1000, PROT_READ,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  assert(addr != MAP_FAILED);
  return addr;
}

TEST(test_mmap_disallows_remapping) {
  void *addr = map_something();
  StartSeccompSandbox();
  // Overwriting a mapping that was created before the sandbox was
  // enabled is not allowed.
  void *result = mmap(addr, 0x1000, PROT_READ,
                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  assert(result == MAP_FAILED);
  assert(errno == EINVAL);
}

TEST(test_mmap_disallows_low_address) {
  StartSeccompSandbox();
  // Mapping pages at low addresses is not allowed because this helps
  // with exploiting buggy kernels.
  void *result = mmap(NULL, 0x1000, PROT_READ,
                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
  assert(result == MAP_FAILED);
  assert(errno == EINVAL);
}

TEST(test_munmap_allowed) {
  StartSeccompSandbox();
  void *addr = map_something();
  int result = munmap(addr, 0x1000);
  assert(result == 0);
}

TEST(test_munmap_disallowed) {
  void *addr = map_something();
  StartSeccompSandbox();
  int result = munmap(addr, 0x1000);
  assert(result == -1);
  assert(errno == EINVAL);
}

TEST(test_mprotect_allowed) {
  StartSeccompSandbox();
  void *addr = map_something();
  int result = mprotect(addr, 0x1000, PROT_READ | PROT_WRITE);
  assert(result == 0);
}

TEST(test_mprotect_disallowed) {
  void *addr = map_something();
  StartSeccompSandbox();
  int result = mprotect(addr, 0x1000, PROT_READ | PROT_WRITE);
  assert(result == -1);
  assert(errno == EINVAL);
}

int get_tty_fd() {
  int master_fd, tty_fd;
  int rc = openpty(&master_fd, &tty_fd, NULL, NULL, NULL);
  assert(rc == 0);
  return tty_fd;
}

TEST(test_ioctl_tiocgwinsz_allowed) {
  int tty_fd = get_tty_fd();
  StartSeccompSandbox();
  int size[2];
  // Get terminal width and height.
  int result = ioctl(tty_fd, TIOCGWINSZ, size);
  assert(result == 0);
}

TEST(test_ioctl_disallowed) {
  int tty_fd = get_tty_fd();
  StartSeccompSandbox();
  // This ioctl call inserts a character into the tty's input queue,
  // which provides a way to send commands to an interactive shell.
  char c = 'x';
  int result = ioctl(tty_fd, TIOCSTI, &c);
  assert(result == -1);
  assert(errno == EINVAL);
}

TEST(test_socket) {
  StartSeccompSandbox();
  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
  assert(fd == -1);
  // TODO: Make it consistent between i386 and x86-64.
  assert(errno == EINVAL || errno == ENOSYS);
}

TEST(test_open) {
  StartSeccompSandbox();
  int fd = open("/dev/null", O_RDONLY);
  assert(fd >= 0);
  int rc = close(fd);
  assert(rc == 0);
  fd = open("/dev/null", O_WRONLY);
  assert(fd == -1);
  assert(errno == EACCES);
}

TEST(test_access) {
  StartSeccompSandbox();
  int rc = access("/dev/null", R_OK);
  assert(rc == 0);
  rc = access("path-that-does-not-exist", R_OK);
  assert(rc == -1);
  assert(errno == ENOENT);
}

TEST(test_stat) {
  StartSeccompSandbox();
  struct stat st;
  int rc = stat("/dev/null", &st);
  assert(rc == 0);
  rc = stat("path-that-does-not-exist", &st);
  assert(rc == -1);
  assert(errno == ENOENT);
}

int g_value;

void signal_handler(int sig) {
  g_value = 300;
  printf("In signal handler\n");
}

void sigaction_handler(int sig, siginfo_t *a, void *b) {
  g_value = 300;
  printf("In sigaction handler\n");
}

TEST(test_signal_handler) {
  sighandler_t result = signal(SIGTRAP, signal_handler);
  assert(result != SIG_ERR);

  StartSeccompSandbox();

  // signal() is not allowed inside the sandbox yet.
  result = signal(SIGTRAP, signal_handler);
  assert(result == SIG_ERR);
  assert(errno == ENOSYS);

  g_value = 200;
  asm("int3");
  assert(g_value == 300);
}

TEST(test_sigaction_handler) {
  struct sigaction act;
  act.sa_sigaction = sigaction_handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_SIGINFO;
  int rc = sigaction(SIGTRAP, &act, NULL);
  assert(rc == 0);

  StartSeccompSandbox();

  // sigaction() is not allowed inside the sandbox yet.
  rc = sigaction(SIGTRAP, &act, NULL);
  assert(rc == -1);
  assert(errno == ENOSYS);

  g_value = 200;
  asm("int3");
  assert(g_value == 300);
}

TEST(test_blocked_signal) {
  sighandler_t result = signal(SIGTRAP, signal_handler);
  assert(result != SIG_ERR);
  StartSeccompSandbox();

  // Initially the signal should not be blocked.
  sigset_t sigs;
  sigfillset(&sigs);
  int rc = sigprocmask(0, NULL, &sigs);
  assert(rc == 0);
  assert(!sigismember(&sigs, SIGTRAP));

  sigemptyset(&sigs);
  sigaddset(&sigs, SIGTRAP);
  rc = sigprocmask(SIG_BLOCK, &sigs, NULL);
  assert(rc == 0);

  // Check that we can read back the blocked status.
  sigemptyset(&sigs);
  rc = sigprocmask(0, NULL, &sigs);
  assert(rc == 0);
  assert(sigismember(&sigs, SIGTRAP));

  // Check that the signal handler really is blocked.
  intend_exit_status(SIGTRAP);
  asm("int3");
}

TEST(test_sigaltstack) {
  // The sandbox does not support sigaltstack() yet.  Just test that
  // it returns an error.
  StartSeccompSandbox();
  stack_t st;
  st.ss_size = 0x4000;
  st.ss_sp = malloc(st.ss_size);
  assert(st.ss_sp != NULL);
  st.ss_flags = 0;
  int rc = sigaltstack(&st, NULL);
  assert(rc == -1);
  assert(errno == ENOSYS);
}

struct testcase {
  const char *test_name;
  void (*test_func)();
};

struct testcase all_tests[] = {
#include "test-list.h"
  { NULL, NULL },
};

int run_test_forked(struct testcase *test) {
  printf("** %s\n", test->test_name);
  int pipe_fds[2];
  int rc = pipe(pipe_fds);
  assert(rc == 0);
  int pid = fork();
  if (pid == 0) {
    rc = close(pipe_fds[0]);
    assert(rc == 0);
    g_intended_status_fd = pipe_fds[1];

    test->test_func();
    intend_exit_status(0);
    _exit(0);
  }
  rc = close(pipe_fds[1]);
  assert(rc == 0);

  int intended_status;
  int got = read(pipe_fds[0], &intended_status, sizeof(intended_status));
  bool got_intended_status = got == sizeof(intended_status);
  if (!got_intended_status) {
    printf("Test runner: Did not receive intended status\n");
  }

  int status;
  int pid2 = waitpid(pid, &status, 0);
  assert(pid2 == pid);
  if (!got_intended_status) {
    printf("Test returned exit status %i\n", status);
    return 1;
  }
  else if (status != intended_status) {
    printf("Test failed with exit status %i, expected %i\n",
           status, intended_status);
    return 1;
  }
  else {
    return 0;
  }
}

int run_test_by_name(const char *name) {
  struct testcase *test;
  for (test = all_tests; test->test_name != NULL; test++) {
    if (strcmp(name, test->test_name) == 0) {
      printf("Running test %s...\n", name);
      test->test_func();
      printf("OK\n");
      return 0;
    }
  }
  fprintf(stderr, "Test '%s' not found\n", name);
  return 1;
}

int main(int argc, char **argv) {
  if (argc == 2) {
    // Run one test without forking, to aid debugging.
    return run_test_by_name(argv[1]);
  }
  else if (argc > 2) {
    // TODO: run multiple tests.
    fprintf(stderr, "Too many arguments\n");
    return 1;
  }
  else {
    // Run all tests.
    struct testcase *test;
    int failures = 0;
    for (test = all_tests; test->test_name != NULL; test++) {
      failures += run_test_forked(test);
    }
    if (failures == 0) {
      printf("OK\n");
      return 0;
    }
    else {
      printf("%i FAILURE(S)\n", failures);
      return 1;
    }
  }
}
<|endoftext|>


<commit_before>#include "samples.hpp"
#include <powerset.hpp>
#include <range.hpp>
#include <vector>
#include <iostream>

using iter::powerset;

int main() {
    std::vector<int> vec {1,2,3,4,5,6,7,8,9};
    for (auto v : powerset(vec)) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }
    std::cout << "with temporary\n";
    for (auto v : powerset(std::vector<int>{1,2,3})) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }
    std::cout << "with initializer_list\n";
    for (auto v : powerset({1,2,3})) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }

    std::cout << "with container of move-only objects\n";
    std::vector<itertest::MoveOnly> mv;
    for (auto i : iter::range(3)) {
        mv.emplace_back(i);
    }
    for (auto v : powerset(mv)) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }
    return 0;
}
<commit_msg>adds test with deref-by-value iterator<commit_after>#include "samples.hpp"
#include <powerset.hpp>
#include <range.hpp>
#include <vector>
#include <iostream>

using iter::powerset;

int main() {
    std::vector<int> vec {1,2,3,4,5,6,7,8,9};
    for (auto v : powerset(vec)) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }
    std::cout << "with temporary\n";
    for (auto v : powerset(std::vector<int>{1,2,3})) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }
    std::cout << "with initializer_list\n";
    for (auto v : powerset({1,2,3})) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }

    std::cout << "with container of move-only objects\n";
    std::vector<itertest::MoveOnly> mv;
    for (auto i : iter::range(3)) {
        mv.emplace_back(i);
    }
    for (auto v : powerset(mv)) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }

    std::cout << "with deref-by-value iterator\n";
    itertest::DerefByValue dbv;
    for (auto v : powerset(dbv)) {
        for (auto i : v) std::cout << i << " ";
        std::cout << std::endl;
    }

    return 0;
}
<|endoftext|>


<commit_before>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2011 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "Timeout.h"

#include "../base/Exception.h"
#include "../base/ObjectCounter.h"

#include "BoostPython.h"

#include <iostream>

using namespace boost::python;
using namespace std;

namespace avg {

int Timeout::s_LastID = 0;

Timeout::Timeout(int time, PyObject * pyfunc, bool isInterval, long long startTime)
    : m_Interval(time),
      m_PyFunc(pyfunc),
      m_IsInterval(isInterval)
{
    ObjectCounter::get()->incRef(&typeid(*this));
    m_NextTimeout = m_Interval+startTime;
    s_LastID++;
    m_ID = s_LastID;

    Py_INCREF(m_PyFunc);
}

Timeout::~Timeout()
{
    Py_DECREF(m_PyFunc);
    ObjectCounter::get()->decRef(&typeid(*this));
}

bool Timeout::IsReady(long long time) const
{
    return m_NextTimeout <= time;
}

bool Timeout::IsInterval() const
{
    return m_IsInterval;
}

void Timeout::Fire(long long curTime)
{
    if (m_IsInterval) {
        m_NextTimeout = m_Interval + curTime;
    }
    PyObject * arglist = Py_BuildValue("()");
    PyObject * result = PyEval_CallObject(m_PyFunc, arglist);
    // XXX: After the call to python, this might have been deleted
    // by a call to clearTimeout()!
    Py_DECREF(arglist);
    if (!result) {
        throw error_already_set();
    }
    Py_DECREF(result);
}

int Timeout::GetID() const
{
    return m_ID;
}

bool Timeout::operator <(const Timeout& other) const
{
    return m_NextTimeout < other.m_NextTimeout;
}

}
<commit_msg>Coding conventions.<commit_after>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2011 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "Timeout.h"

#include "../base/Exception.h"
#include "../base/ObjectCounter.h"

#include "BoostPython.h"

#include <iostream>

using namespace boost::python;
using namespace std;

namespace avg {

int Timeout::s_LastID = 0;

Timeout::Timeout(int time, PyObject * pyfunc, bool isInterval, long long startTime)
    : m_Interval(time),
      m_PyFunc(pyfunc),
      m_IsInterval(isInterval)
{
    ObjectCounter::get()->incRef(&typeid(*this));
    m_NextTimeout = m_Interval+startTime;
    s_LastID++;
    m_ID = s_LastID;

    Py_INCREF(m_PyFunc);
}

Timeout::~Timeout()
{
    Py_DECREF(m_PyFunc);
    ObjectCounter::get()->decRef(&typeid(*this));
}

bool Timeout::isReady(long long time) const
{
    return m_NextTimeout <= time;
}

bool Timeout::isInterval() const
{
    return m_IsInterval;
}

void Timeout::fire(long long curTime)
{
    if (m_IsInterval) {
        m_NextTimeout = m_Interval + curTime;
    }
    PyObject * arglist = Py_BuildValue("()");
    PyObject * result = PyEval_CallObject(m_PyFunc, arglist);
    // XXX: After the call to python, this might have been deleted
    // by a call to clearTimeout()!
    Py_DECREF(arglist);
    if (!result) {
        throw error_already_set();
    }
    Py_DECREF(result);
}

int Timeout::getID() const
{
    return m_ID;
}

bool Timeout::operator <(const Timeout& other) const
{
    return m_NextTimeout < other.m_NextTimeout;
}

}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Djaodjin Inc.
   All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdlib.h>
#include <iostream>
#include <cstdio>
#include <podofo/podofo.h>

#ifndef PRINT_DEBUG_MSG
#define PRINT_DEBUG_MSG           0
#endif

using namespace PoDoFo;

/** For debugging, print (key, data_type) on stderr.
 */
void show_dict( const PdfDictionary& dict, const char *indent = "" )
{
    TKeyMap keys = dict.GetKeys();
    for( TCIKeyMap iter = keys.begin(); iter != keys.end(); ++iter ) {
        const char *str = iter->second->GetDataTypeString();
        std::cerr << indent << iter->first.GetName()
                  << ":" << str
                  << std::endl;
    }
}

/** Set the value in an input text *field*. The value is taken from *fills*,
    a map indexed by field names.

    In some PDF files the field_name is not directly associated to the input
    object ("T") but to its "Parent".

    Chrome does not respect the read-only flag (Preview.app does),
    so we typically don't use this function in production. We prefer
    ``flat_text_field`` instead.
 */
void
fill_text_field( PdfTextField& field,
    const std::map<std::string, std::string>& fills )
{
#if PRINT_DEBUG_MSG
    std::cerr << "field:\n";
    TKeyMap keys = field.GetFieldObject()->GetDictionary().GetKeys();
    for( TCIKeyMap iter = keys.begin(); iter != keys.end(); ++iter ) {
        std::cerr << iter->first.GetName();
        try {
            if( iter->second->IsDictionary() ) {
                const PdfDictionary& dict = iter->second->GetDictionary();
                show_dict(dict, "\t\t");

            } else if( iter->second->IsReference() ) {
                const PdfReference& ref = iter->second->GetReference();
                PdfObject *obj = field.GetFieldObject()->GetOwner()->GetObject(ref);
                if( obj->IsDictionary() ) {
                    TKeyMap parent_keys = obj->GetDictionary().GetKeys();
                    for( TCIKeyMap parent_iter = parent_keys.begin();
                         parent_iter != parent_keys.end(); ++parent_iter ) {
                        std::cerr << "\t" << parent_iter->first.GetName();
                        const char *str = parent_iter->second->GetDataTypeString();
                        std::cerr << ":" << str;
                        if( parent_iter->first.GetName() == "T" ) {
                            std::cerr << ":"
                                << parent_iter->second->GetString().GetString();
                        }
                        std::cerr << std::endl;
                    }
                } else {
                    std::cerr << ":" << obj->GetDataTypeString();
                    std::cerr << " :" << ref.ToString() << std::endl;
                }
            } else {
                const char *str = iter->second->GetDataTypeString();
                std::cerr << ":" << str << std::endl;
            }
        } catch( PdfError& err ) {
            std::cerr << ":" << "<error>" << std::endl;
        }
    }
#endif

    // PdfTextField::GetFieldName()
    const char* field_name = "";
    if( field.GetFieldName().IsValid() ) {
        field_name = field.GetFieldName().GetString();
    } else {
        PdfObject *obj = field.GetFieldObject();
        if( obj->GetDictionary().HasKey(PdfName("Parent")) ) {
            const PdfReference& ref = obj->GetDictionary().GetKey(
                PdfName("Parent"))->GetReference();
            obj = obj->GetOwner()->GetObject(ref);
            field_name = obj->GetDictionary().GetKey(
                PdfName("T"))->GetString().GetString();
        }
    }

    std::map<std::string, std::string>::const_iterator found
        = fills.find(std::string(field_name));
    if( found != fills.end() ) {
#if PRINT_DEBUG_MSG
        std::cerr << "Set '"<< field_name << "' to '" << found->second << "'\n";
#endif
        // PdfTextField::SetText()
        PdfName key = field.IsRichText() ? PdfName("RV") : PdfName("V");
        PdfObject *obj = field.GetFieldObject();
        if( !obj->GetDictionary().HasKey(PdfName("T")) ) {
            const PdfReference& ref = obj->GetDictionary().GetKey(
                PdfName("Parent"))->GetReference();
            obj = obj->GetOwner()->GetObject(ref);
        }
        // if field value is longer than maxlen, truncate it
        pdf_long nMax = field.GetMaxLen();
        if( nMax != -1 && found->second.size() > nMax )
            obj->GetDictionary().AddKey(
                key, PdfString(found->second.c_str(), nMax));
        else
            obj->GetDictionary().AddKey(key, PdfString(found->second.c_str()));
#if PRINT_DEBUG_MSG
    } else {
        std::cerr << "warning: no value for field '"<< field_name << "'\n";
#endif
    }
    field.SetReadOnly(true);
}

/** Draw an input field (*annot*) value taken from *fills*, a map indexed by
    field names, at the position the input field appears in the document.

    In some PDF files the field_name is not directly associated to the input
    object ("T") but to its "Parent".
*/
void flat_text_field( PdfAnnotation* annot,
    const std::map<std::string, std::string>& fills,
    PdfPainter& painter,
    PdfDocument& doc ) {
    std::string font_family("Helvetica");
    int font_size = 12;

    if( annot->GetObject()->GetDictionary().HasKey(PdfName("DA")) ) {
        PdfObject *da_obj
            = annot->GetObject()->GetDictionary().GetKey(PdfName("DA"));
        std::string da_value(da_obj->GetString().GetString());
        size_t start = da_value.find('/');
        size_t end = da_value.find("Tf");
        for( --end; end > 0 && (
                da_value[end] == ' ' || isdigit(da_value[end])); --end ) {}
        std::string font_family = da_value.substr(start + 1, end - start);
        font_size = atoi(da_value.substr(end + 1).c_str());
#if PRINT_DEBUG_MSG
        std::cerr << "field DA: " << da_obj->GetString().GetString()
                  << std::endl;
        std::cerr << "\tfont-family: " << font_family
                  << ", font-size: " << font_size
                  << std::endl;
#endif
    }

    painter.SetFont(doc.CreateFont(font_family.c_str()));
    painter.GetFont()->SetFontSize(font_size);
    const PdfRect& rect = annot->GetRect();

    // PdfTextField::GetFieldName() for Annotation
    const char* field_name = "";
    PdfString pdf_field_name;
    PdfObject *obj = annot->GetObject();
    if( obj->GetDictionary().HasKey(PdfName("T")) ) {
        pdf_field_name = obj->GetDictionary().GetKey(PdfName("T"))->GetString();
    }
    if( pdf_field_name.IsValid() ) {
        field_name = pdf_field_name.GetString();
    } else {
        if( obj->GetDictionary().HasKey(PdfName("Parent")) ) {
            const PdfReference& ref = obj->GetDictionary().GetKey(
                PdfName("Parent"))->GetReference();
            obj = obj->GetOwner()->GetObject(ref);
            field_name = obj->GetDictionary().GetKey(
                PdfName("T"))->GetString().GetString();
        }
    }

    std::map<std::string, std::string>::const_iterator found
        = fills.find(std::string(field_name));
    if( found != fills.end() ) {
#if PRINT_DEBUG_MSG
        std::cerr << "Set '"<< field_name << "' to '" << found->second << "'\n";
#endif
        painter.DrawText(
            rect.GetLeft(), rect.GetBottom() + 1, found->second.c_str());
#if PRINT_DEBUG_MSG
    } else {
        std::cerr << "warning: no value for field '"<< field_name << "'\n";
#endif
    }
}

void
flat_form( const std::string& output_path, const std::string& input_path,
    const std::map<std::string, std::string>& fills )
{
    PdfMemDocument doc(input_path.c_str());
    PdfPainter painter;

    int nb_pages = doc.GetPageCount();
    for( int i = 0; i < nb_pages; ++i ) {
        PdfPage *page = doc.GetPage(i);
        painter.SetPage(page);

        int nb_fields = page->GetNumFields();
        for( int n = 0; n < nb_fields; ++n ) {
            PdfField field = page->GetField(n);
            EPdfField eType = field.GetType();

            switch( eType ) {
               case ePdfField_TextField:
                {
                    PdfTextField text(field);
                    flat_text_field(
                        page->GetAnnotation(n), fills, painter, doc);
                    break;
                }
                default:
                    break;
            }
        }
        painter.FinishPage();

        /* Delete annotations to make sure fields cannot be written over. */
        for( int n = 0; n < nb_fields; ++n ) {
            page->DeleteAnnotation(0);
        }
    }

    if( output_path != "-" ) {
        doc.Write(output_path.c_str());
    } else {
        PdfOutputDevice out(&std::cout);
        doc.Write(&out);
    }
}

int main( int argc, char* argv[] )
{
    int idx = 1;
    std::string input_path, output_path;
    std::map<std::string, std::string> fills;

    while( idx < argc ) {
        if( strncmp(argv[idx], "--fill", 6) == 0 ) {
            ++idx;
            if( idx >= argc ) {
                std::cerr << "missing field_name=field_value after --fill\n";
                return 1;
            }
            const char *sep = strchr(argv[idx], '=');
            if( !sep ) {
                std::cerr
                    << "no separator field_name=field_value after --fill\n";
                return 1;
            }
            fills[std::string(argv[idx], sep - argv[idx])]
                = std::string(sep + 1);
        } else if( input_path.empty() ) {
            input_path = std::string(argv[idx]);
        } else {
            output_path = std::string(argv[idx]);
        }
        ++idx;
    }

    if( input_path.empty() || output_path.empty() ) {
        std::cout << "Usage: " << argv[0]
            << " [--fill field_name=field_value] input_file output_file\n";
        std::cout << "Fill out an existing form and save it to a PDF file\n";
        return 1;
    }

    flat_form(output_path, input_path, fills);
    return 0;
}
<commit_msg>clean compilation warning<commit_after>/* Copyright (c) 2015, Djaodjin Inc.
   All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdlib.h>
#include <iostream>
#include <cstdio>
#include <podofo/podofo.h>

#ifndef PRINT_DEBUG_MSG
#define PRINT_DEBUG_MSG           0
#endif

using namespace PoDoFo;

/** For debugging, print (key, data_type) on stderr.
 */
void show_dict( const PdfDictionary& dict, const char *indent = "" )
{
    TKeyMap keys = dict.GetKeys();
    for( TCIKeyMap iter = keys.begin(); iter != keys.end(); ++iter ) {
        const char *str = iter->second->GetDataTypeString();
        std::cerr << indent << iter->first.GetName()
                  << ":" << str
                  << std::endl;
    }
}

/** Set the value in an input text *field*. The value is taken from *fills*,
    a map indexed by field names.

    In some PDF files the field_name is not directly associated to the input
    object ("T") but to its "Parent".

    Chrome does not respect the read-only flag (Preview.app does),
    so we typically don't use this function in production. We prefer
    ``flat_text_field`` instead.
 */
void
fill_text_field( PdfTextField& field,
    const std::map<std::string, std::string>& fills )
{
#if PRINT_DEBUG_MSG
    std::cerr << "field:\n";
    TKeyMap keys = field.GetFieldObject()->GetDictionary().GetKeys();
    for( TCIKeyMap iter = keys.begin(); iter != keys.end(); ++iter ) {
        std::cerr << iter->first.GetName();
        try {
            if( iter->second->IsDictionary() ) {
                const PdfDictionary& dict = iter->second->GetDictionary();
                show_dict(dict, "\t\t");

            } else if( iter->second->IsReference() ) {
                const PdfReference& ref = iter->second->GetReference();
                PdfObject *obj = field.GetFieldObject()->GetOwner()->GetObject(ref);
                if( obj->IsDictionary() ) {
                    TKeyMap parent_keys = obj->GetDictionary().GetKeys();
                    for( TCIKeyMap parent_iter = parent_keys.begin();
                         parent_iter != parent_keys.end(); ++parent_iter ) {
                        std::cerr << "\t" << parent_iter->first.GetName();
                        const char *str = parent_iter->second->GetDataTypeString();
                        std::cerr << ":" << str;
                        if( parent_iter->first.GetName() == "T" ) {
                            std::cerr << ":"
                                << parent_iter->second->GetString().GetString();
                        }
                        std::cerr << std::endl;
                    }
                } else {
                    std::cerr << ":" << obj->GetDataTypeString();
                    std::cerr << " :" << ref.ToString() << std::endl;
                }
            } else {
                const char *str = iter->second->GetDataTypeString();
                std::cerr << ":" << str << std::endl;
            }
        } catch( PdfError& err ) {
            std::cerr << ":" << "<error>" << std::endl;
        }
    }
#endif

    // PdfTextField::GetFieldName()
    const char* field_name = "";
    if( field.GetFieldName().IsValid() ) {
        field_name = field.GetFieldName().GetString();
    } else {
        PdfObject *obj = field.GetFieldObject();
        if( obj->GetDictionary().HasKey(PdfName("Parent")) ) {
            const PdfReference& ref = obj->GetDictionary().GetKey(
                PdfName("Parent"))->GetReference();
            obj = obj->GetOwner()->GetObject(ref);
            field_name = obj->GetDictionary().GetKey(
                PdfName("T"))->GetString().GetString();
        }
    }

    std::map<std::string, std::string>::const_iterator found
        = fills.find(std::string(field_name));
    if( found != fills.end() ) {
#if PRINT_DEBUG_MSG
        std::cerr << "Set '"<< field_name << "' to '" << found->second << "'\n";
#endif
        // PdfTextField::SetText()
        PdfName key = field.IsRichText() ? PdfName("RV") : PdfName("V");
        PdfObject *obj = field.GetFieldObject();
        if( !obj->GetDictionary().HasKey(PdfName("T")) ) {
            const PdfReference& ref = obj->GetDictionary().GetKey(
                PdfName("Parent"))->GetReference();
            obj = obj->GetOwner()->GetObject(ref);
        }
        // if field value is longer than maxlen, truncate it
        pdf_long nMax = field.GetMaxLen();
        if( (nMax != -1) && (found->second.size() > nMax) )
            obj->GetDictionary().AddKey(
                key, PdfString(found->second.c_str(), nMax));
        else
            obj->GetDictionary().AddKey(key, PdfString(found->second.c_str()));
#if PRINT_DEBUG_MSG
    } else {
        std::cerr << "warning: no value for field '"<< field_name << "'\n";
#endif
    }
    field.SetReadOnly(true);
}

/** Draw an input field (*annot*) value taken from *fills*, a map indexed by
    field names, at the position the input field appears in the document.

    In some PDF files the field_name is not directly associated to the input
    object ("T") but to its "Parent".
*/
void flat_text_field( PdfAnnotation* annot,
    const std::map<std::string, std::string>& fills,
    PdfPainter& painter,
    PdfDocument& doc ) {
    std::string font_family("Helvetica");
    int font_size = 12;

    if( annot->GetObject()->GetDictionary().HasKey(PdfName("DA")) ) {
        PdfObject *da_obj
            = annot->GetObject()->GetDictionary().GetKey(PdfName("DA"));
        std::string da_value(da_obj->GetString().GetString());
        size_t start = da_value.find('/');
        size_t end = da_value.find("Tf");
        for( --end; end > 0 && (
                da_value[end] == ' ' || isdigit(da_value[end])); --end ) {}
        std::string font_family = da_value.substr(start + 1, end - start);
        font_size = atoi(da_value.substr(end + 1).c_str());
#if PRINT_DEBUG_MSG
        std::cerr << "field DA: " << da_obj->GetString().GetString()
                  << std::endl;
        std::cerr << "\tfont-family: " << font_family
                  << ", font-size: " << font_size
                  << std::endl;
#endif
    }

    painter.SetFont(doc.CreateFont(font_family.c_str()));
    painter.GetFont()->SetFontSize(font_size);
    const PdfRect& rect = annot->GetRect();

    // PdfTextField::GetFieldName() for Annotation
    const char* field_name = "";
    PdfString pdf_field_name;
    PdfObject *obj = annot->GetObject();
    if( obj->GetDictionary().HasKey(PdfName("T")) ) {
        pdf_field_name = obj->GetDictionary().GetKey(PdfName("T"))->GetString();
    }
    if( pdf_field_name.IsValid() ) {
        field_name = pdf_field_name.GetString();
    } else {
        if( obj->GetDictionary().HasKey(PdfName("Parent")) ) {
            const PdfReference& ref = obj->GetDictionary().GetKey(
                PdfName("Parent"))->GetReference();
            obj = obj->GetOwner()->GetObject(ref);
            field_name = obj->GetDictionary().GetKey(
                PdfName("T"))->GetString().GetString();
        }
    }

    std::map<std::string, std::string>::const_iterator found
        = fills.find(std::string(field_name));
    if( found != fills.end() ) {
#if PRINT_DEBUG_MSG
        std::cerr << "Set '"<< field_name << "' to '" << found->second << "'\n";
#endif
        painter.DrawText(
            rect.GetLeft(), rect.GetBottom() + 1, found->second.c_str());
#if PRINT_DEBUG_MSG
    } else {
        std::cerr << "warning: no value for field '"<< field_name << "'\n";
#endif
    }
}

void
flat_form( const std::string& output_path, const std::string& input_path,
    const std::map<std::string, std::string>& fills )
{
    PdfMemDocument doc(input_path.c_str());
    PdfPainter painter;

    int nb_pages = doc.GetPageCount();
    for( int i = 0; i < nb_pages; ++i ) {
        PdfPage *page = doc.GetPage(i);
        painter.SetPage(page);

        int nb_fields = page->GetNumFields();
        for( int n = 0; n < nb_fields; ++n ) {
            PdfField field = page->GetField(n);
            EPdfField eType = field.GetType();

            switch( eType ) {
               case ePdfField_TextField:
                {
                    PdfTextField text(field);
                    flat_text_field(
                        page->GetAnnotation(n), fills, painter, doc);
                    break;
                }
                default:
                    break;
            }
        }
        painter.FinishPage();

        /* Delete annotations to make sure fields cannot be written over. */
        for( int n = 0; n < nb_fields; ++n ) {
            page->DeleteAnnotation(0);
        }
    }

    if( output_path != "-" ) {
        doc.Write(output_path.c_str());
    } else {
        PdfOutputDevice out(&std::cout);
        doc.Write(&out);
    }
}

int main( int argc, char* argv[] )
{
    int idx = 1;
    std::string input_path, output_path;
    std::map<std::string, std::string> fills;

    while( idx < argc ) {
        if( strncmp(argv[idx], "--fill", 6) == 0 ) {
            ++idx;
            if( idx >= argc ) {
                std::cerr << "missing field_name=field_value after --fill\n";
                return 1;
            }
            const char *sep = strchr(argv[idx], '=');
            if( !sep ) {
                std::cerr
                    << "no separator field_name=field_value after --fill\n";
                return 1;
            }
            fills[std::string(argv[idx], sep - argv[idx])]
                = std::string(sep + 1);
        } else if( input_path.empty() ) {
            input_path = std::string(argv[idx]);
        } else {
            output_path = std::string(argv[idx]);
        }
        ++idx;
    }

    if( input_path.empty() || output_path.empty() ) {
        std::cout << "Usage: " << argv[0]
            << " [--fill field_name=field_value] input_file output_file\n";
        std::cout << "Fill out an existing form and save it to a PDF file\n";
        return 1;
    }

    flat_form(output_path, input_path, fills);
    return 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: md5.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: hr $ $Date: 2004-12-10 18:05:15 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <rtl/digest.h>
class ByteString;

rtlDigestError calc_md5_checksum( const char *filename, ByteString &aChecksum );

<commit_msg>INTEGRATION: CWS ooo19126 (1.2.80); FILE MERGED 2005/09/05 13:58:17 rt 1.2.80.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: md5.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 13:30:57 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <rtl/digest.h>
class ByteString;

rtlDigestError calc_md5_checksum( const char *filename, ByteString &aChecksum );

<|endoftext|>


<commit_before>/******************************************************************************\
* File:          test.cpp
* Purpose:       Implementation for wxExtension cpp unit testing
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
* Created:       za 17 jan 2009 11:51:20 CET
*
* Copyright (c) 2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <TestCaller.h>
#include <wx/extension/extension.h>
#include "test.h"

#define TEST_FILE "./test.h"
#define TEST_BIN "./test.bin"

void wxExTestFixture::testDir()
{
  wxExDir dir("./", "*.h");

  CPPUNIT_ASSERT(dir.IsOpened());
  CPPUNIT_ASSERT(dir.GetFileSpec() == "*.h");
  printf("%s %d\n", dir.GetName().c_str(), dir.FindFiles());
//  CPPUNIT_ASSERT(dir.FindFiles() == 1);
}

void wxExTestFixture::testFile()
{
  wxExFile file(wxExFileName(TEST_FILE));

  CPPUNIT_ASSERT(file.GetFileName().GetStat().IsOk());
  // The fullpath should be normalized, test it.
  CPPUNIT_ASSERT(file.GetFileName().GetFullPath() != TEST_FILE);
  CPPUNIT_ASSERT(!file.GetFileName().GetStat().IsReadOnly());
  file.CheckSync();
  CPPUNIT_ASSERT(file.FileLoad(wxExFileName(TEST_BIN)));
  CPPUNIT_ASSERT(!file.IsOpened());
  CPPUNIT_ASSERT(file.Open(wxExFileName(TEST_BIN).GetFullPath()));
  wxCharBuffer buffer = file.Read();
  CPPUNIT_ASSERT(buffer.length() == 40);
}

void wxExTestFixture::testFileName()
{
  wxExFileName fileName(TEST_FILE);

  CPPUNIT_ASSERT(fileName.GetLexer().GetScintillaLexer().empty());
  CPPUNIT_ASSERT(fileName.GetStat().IsOk());
  fileName.Assign("xxx");
  CPPUNIT_ASSERT(fileName.GetStat().IsOk());
}

void wxExTestFixture::testFileStatistics()
{
  wxExFileStatistics fileStatistics;

  CPPUNIT_ASSERT(fileStatistics.Get().empty());
  CPPUNIT_ASSERT(fileStatistics.Get("xx") == 0);
}

void wxExTestFixture::testRCS()
{
  wxExRCS rcs;
  CPPUNIT_ASSERT(rcs.GetDescription().empty());
  CPPUNIT_ASSERT(rcs.GetUser().empty());
}

void wxExTestFixture::testStat()
{
  wxExStat stat(TEST_FILE);
  CPPUNIT_ASSERT(stat.IsOk());
  CPPUNIT_ASSERT(!stat.IsReadOnly());
  CPPUNIT_ASSERT(stat.Sync("./test-base.link"));
}

void wxExTestFixture::testStatistics()
{
  wxExStatistics<long> statistics;
  statistics.Inc("test");
  CPPUNIT_ASSERT(statistics.Get("test") == 1);
  statistics.Inc("test");
  CPPUNIT_ASSERT(statistics.Get("test") == 2);
  statistics.Set("test", 13);
  CPPUNIT_ASSERT(statistics.Get("test") == 13);
  statistics.Dec("test");
  CPPUNIT_ASSERT(statistics.Get("test") == 12);
  statistics.Inc("test2");
  CPPUNIT_ASSERT(statistics.Get("test2") == 1);
  wxExStatistics<long> copy(statistics);
  CPPUNIT_ASSERT(copy.Get("test2") == 1);
  statistics.Clear();
  CPPUNIT_ASSERT(statistics.GetItems().empty());
}

void wxExTestFixture::testTextFile()
{
  wxExTextFile textFile(wxExFileName(TEST_FILE), ID_TOOL_REPORT_COUNT);
  CPPUNIT_ASSERT(textFile.RunTool());
  CPPUNIT_ASSERT(!textFile.GetStatistics().GetElements().GetItems().empty());
  CPPUNIT_ASSERT(!textFile.IsOpened()); // file should be closed after running tool

  CPPUNIT_ASSERT(textFile.RunTool()); // do the same test
  CPPUNIT_ASSERT(!textFile.GetStatistics().GetElements().GetItems().empty());
  CPPUNIT_ASSERT(!textFile.IsOpened()); // file should be closed after running tool

  wxExTextFile textFile2(wxExFileName(TEST_FILE), ID_TOOL_REPORT_KEYWORD);
  CPPUNIT_ASSERT(textFile2.RunTool());
//  CPPUNIT_ASSERT(!m_TextFile->GetStatistics().GetKeywords().GetItems().empty());
}

void wxExTestFixture::testTiming()
{
  wxExFile file(wxExFileName(TEST_FILE));

  CPPUNIT_ASSERT(file.IsOpened());

  wxStopWatch sw;

  const int max = 10000;

  for (int i = 0; i < max; i++)
  {
    CPPUNIT_ASSERT(file.Read().length() > 0);
  }

  const long exfile_read = sw.Time();

  sw.Start();

  wxFile wxfile(TEST_FILE);

  for (int j = 0; j < max; j++)
  {
    char* charbuffer = new char[wxfile.Length()];
    wxfile.Read(charbuffer, wxfile.Length());
    wxString* buffer = new wxString(charbuffer, wxfile.Length());
    CPPUNIT_ASSERT(buffer->length() > 0);
    delete charbuffer;
    delete buffer;
  }

  const long file_read = sw.Time();

  printf(
    "wxExFile::Read:%ld wxFile::Read:%ld\n",
    exfile_read,
    file_read);
}

void wxExTestFixture::testTimingAttrib()
{
  const int max = 1000;

  wxStopWatch sw;

  const wxExFileName exfile(TEST_FILE);

  int checked = 0;

  for (int i = 0; i < max; i++)
  {
    checked += exfile.GetStat().IsReadOnly();
  }

  const long exfile_time = sw.Time();

  sw.Start();

  const wxFileName file(TEST_FILE);

  for (int j = 0; j < max; j++)
  {
    checked += file.IsFileWritable();
  }

  const long file_time = sw.Time();

  printf(
    "wxExFileName::IsReadOnly:%ld wxFileName::IsFileWritable:%ld\n",
    exfile_time,
    file_time);
}

void wxExTestFixture::testTool()
{
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_COUNT).IsCount());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_FIND).IsFindType());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_REPLACE).IsFindType());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_COUNT).IsStatisticsType());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_COUNT).IsReportType());
}

wxExTestSuite::wxExTestSuite()
  : CppUnit::TestSuite("wxExtension test suite")
{
  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testDir",
    &wxExTestFixture::testDir));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testFile",
    &wxExTestFixture::testFile));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testFileName",
    &wxExTestFixture::testFileName));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testFileStatistics",
    &wxExTestFixture::testFileStatistics));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testRCS",
    &wxExTestFixture::testRCS));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testStat",
    &wxExTestFixture::testStat));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testStatistics",
    &wxExTestFixture::testStatistics));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testTiming",
    &wxExTestFixture::testTiming));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testTimingAttrib",
    &wxExTestFixture::testTimingAttrib));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testTool",
    &wxExTestFixture::testTool));
}
<commit_msg>add files only<commit_after>/******************************************************************************\
* File:          test.cpp
* Purpose:       Implementation for wxExtension cpp unit testing
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
* Created:       za 17 jan 2009 11:51:20 CET
*
* Copyright (c) 2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <TestCaller.h>
#include <wx/extension/extension.h>
#include "test.h"

#define TEST_FILE "./test.h"
#define TEST_BIN "./test.bin"

void wxExTestFixture::testDir()
{
  wxExDir dir("./", "*.h", wxDIR_FILES);

  CPPUNIT_ASSERT(dir.IsOpened());
  CPPUNIT_ASSERT(dir.GetFileSpec() == "*.h");
  printf("%s %d\n", dir.GetName().c_str(), dir.FindFiles());
  CPPUNIT_ASSERT(dir.FindFiles() == 1);
}

void wxExTestFixture::testFile()
{
  wxExFile file(wxExFileName(TEST_FILE));

  CPPUNIT_ASSERT(file.GetFileName().GetStat().IsOk());
  // The fullpath should be normalized, test it.
  CPPUNIT_ASSERT(file.GetFileName().GetFullPath() != TEST_FILE);
  CPPUNIT_ASSERT(!file.GetFileName().GetStat().IsReadOnly());
  file.CheckSync();
  CPPUNIT_ASSERT(file.FileLoad(wxExFileName(TEST_BIN)));
  CPPUNIT_ASSERT(!file.IsOpened());
  CPPUNIT_ASSERT(file.Open(wxExFileName(TEST_BIN).GetFullPath()));
  wxCharBuffer buffer = file.Read();
  CPPUNIT_ASSERT(buffer.length() == 40);
}

void wxExTestFixture::testFileName()
{
  wxExFileName fileName(TEST_FILE);

  CPPUNIT_ASSERT(fileName.GetLexer().GetScintillaLexer().empty());
  CPPUNIT_ASSERT(fileName.GetStat().IsOk());
  fileName.Assign("xxx");
  CPPUNIT_ASSERT(fileName.GetStat().IsOk());
}

void wxExTestFixture::testFileStatistics()
{
  wxExFileStatistics fileStatistics;

  CPPUNIT_ASSERT(fileStatistics.Get().empty());
  CPPUNIT_ASSERT(fileStatistics.Get("xx") == 0);
}

void wxExTestFixture::testRCS()
{
  wxExRCS rcs;
  CPPUNIT_ASSERT(rcs.GetDescription().empty());
  CPPUNIT_ASSERT(rcs.GetUser().empty());
}

void wxExTestFixture::testStat()
{
  wxExStat stat(TEST_FILE);
  CPPUNIT_ASSERT(stat.IsOk());
  CPPUNIT_ASSERT(!stat.IsReadOnly());
  CPPUNIT_ASSERT(stat.Sync("./test-base.link"));
}

void wxExTestFixture::testStatistics()
{
  wxExStatistics<long> statistics;
  statistics.Inc("test");
  CPPUNIT_ASSERT(statistics.Get("test") == 1);
  statistics.Inc("test");
  CPPUNIT_ASSERT(statistics.Get("test") == 2);
  statistics.Set("test", 13);
  CPPUNIT_ASSERT(statistics.Get("test") == 13);
  statistics.Dec("test");
  CPPUNIT_ASSERT(statistics.Get("test") == 12);
  statistics.Inc("test2");
  CPPUNIT_ASSERT(statistics.Get("test2") == 1);
  wxExStatistics<long> copy(statistics);
  CPPUNIT_ASSERT(copy.Get("test2") == 1);
  statistics.Clear();
  CPPUNIT_ASSERT(statistics.GetItems().empty());
}

void wxExTestFixture::testTextFile()
{
  wxExTextFile textFile(wxExFileName(TEST_FILE), ID_TOOL_REPORT_COUNT);
  CPPUNIT_ASSERT(textFile.RunTool());
  CPPUNIT_ASSERT(!textFile.GetStatistics().GetElements().GetItems().empty());
  CPPUNIT_ASSERT(!textFile.IsOpened()); // file should be closed after running tool

  CPPUNIT_ASSERT(textFile.RunTool()); // do the same test
  CPPUNIT_ASSERT(!textFile.GetStatistics().GetElements().GetItems().empty());
  CPPUNIT_ASSERT(!textFile.IsOpened()); // file should be closed after running tool

  wxExTextFile textFile2(wxExFileName(TEST_FILE), ID_TOOL_REPORT_KEYWORD);
  CPPUNIT_ASSERT(textFile2.RunTool());
//  CPPUNIT_ASSERT(!m_TextFile->GetStatistics().GetKeywords().GetItems().empty());
}

void wxExTestFixture::testTiming()
{
  wxExFile file(wxExFileName(TEST_FILE));

  CPPUNIT_ASSERT(file.IsOpened());

  wxStopWatch sw;

  const int max = 10000;

  for (int i = 0; i < max; i++)
  {
    CPPUNIT_ASSERT(file.Read().length() > 0);
  }

  const long exfile_read = sw.Time();

  sw.Start();

  wxFile wxfile(TEST_FILE);

  for (int j = 0; j < max; j++)
  {
    char* charbuffer = new char[wxfile.Length()];
    wxfile.Read(charbuffer, wxfile.Length());
    wxString* buffer = new wxString(charbuffer, wxfile.Length());
    CPPUNIT_ASSERT(buffer->length() > 0);
    delete charbuffer;
    delete buffer;
  }

  const long file_read = sw.Time();

  printf(
    "wxExFile::Read:%ld wxFile::Read:%ld\n",
    exfile_read,
    file_read);
}

void wxExTestFixture::testTimingAttrib()
{
  const int max = 1000;

  wxStopWatch sw;

  const wxExFileName exfile(TEST_FILE);

  int checked = 0;

  for (int i = 0; i < max; i++)
  {
    checked += exfile.GetStat().IsReadOnly();
  }

  const long exfile_time = sw.Time();

  sw.Start();

  const wxFileName file(TEST_FILE);

  for (int j = 0; j < max; j++)
  {
    checked += file.IsFileWritable();
  }

  const long file_time = sw.Time();

  printf(
    "wxExFileName::IsReadOnly:%ld wxFileName::IsFileWritable:%ld\n",
    exfile_time,
    file_time);
}

void wxExTestFixture::testTool()
{
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_COUNT).IsCount());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_FIND).IsFindType());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_REPLACE).IsFindType());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_COUNT).IsStatisticsType());
  CPPUNIT_ASSERT(wxExTool(ID_TOOL_REPORT_COUNT).IsReportType());
}

wxExTestSuite::wxExTestSuite()
  : CppUnit::TestSuite("wxExtension test suite")
{
  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testDir",
    &wxExTestFixture::testDir));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testFile",
    &wxExTestFixture::testFile));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testFileName",
    &wxExTestFixture::testFileName));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testFileStatistics",
    &wxExTestFixture::testFileStatistics));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testRCS",
    &wxExTestFixture::testRCS));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testStat",
    &wxExTestFixture::testStat));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testStatistics",
    &wxExTestFixture::testStatistics));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testTiming",
    &wxExTestFixture::testTiming));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testTimingAttrib",
    &wxExTestFixture::testTimingAttrib));

  addTest(new CppUnit::TestCaller<wxExTestFixture>(
    "testTool",
    &wxExTestFixture::testTool));
}
<|endoftext|>