<commit_before>/*
 * substitutionMap.cpp
 *
 */

#include "SubstitutionMap.h"
#include "simplifier.h"
#include "../AST/ArrayTransformer.h"

namespace BEEV
{

SubstitutionMap::~SubstitutionMap()
{
	delete SolverMap;
}

// if false. Don't simplify while creating the substitution map.
// This makes it easier to spot how long is spent in the simplifier.
const bool simplify_during_create_subM = false;

//if a is READ(Arr,const) and b is BVCONST then return 1.
//if a is a symbol SYMBOL, return 1.
//if b is READ(Arr,const) and a is BVCONST then return -1
// if b is a symbol return -1.
//
//else return 0 by default
int TermOrder(const ASTNode& a, const ASTNode& b)
{
  const Kind k1 = a.GetKind();
  const Kind k2 = b.GetKind();

  if (k1 == SYMBOL)
  	return 1;

  if (k2 == SYMBOL)
  	return -1;

  //a is of the form READ(Arr,const), and b is const, or
  if ((k1 == READ
       && a[0].GetKind() == SYMBOL
       && a[1].GetKind() == BVCONST
       && (k2 == BVCONST)))
    return 1;

  //b is of the form READ(Arr,const), and a is const, or
  //b is of the form var, and a is const
  if ((k1 == BVCONST)
      && ((k2 == READ
           && b[0].GetKind() == SYMBOL
           && b[1].GetKind() == BVCONST)))
    return -1;

  return 0;
} //End of TermOrder()

//This finds conjuncts which are one of: (= SYMBOL BVCONST), (= BVCONST (READ SYMBOL BVCONST)),
// (IFF SYMBOL TRUE), (IFF SYMBOL FALSE), (IFF SYMBOL SYMBOL), (=SYMBOL SYMBOL)
// or (=SYMBOL BVCONST).
// It tries to remove the conjunct, storing it in the substitutionmap. It replaces it in the
// formula by true.
// The bvsolver puts expressions of the form (= SYMBOL TERM) into the solverMap.

ASTNode SubstitutionMap::CreateSubstitutionMap(const ASTNode& a,  ArrayTransformer*at)
{
  if (!bm->UserFlags.wordlevel_solve_flag)
    return a;

  ASTNode output;
  //if the variable has been solved for, then simply return it
  if (CheckSubstitutionMap(a, output))
    return output;

  if (!alreadyVisited.insert(a.GetNodeNum()).second)
  {
    return a;
  }

  output = a;

  //traverse a and populate the SubstitutionMap
  const Kind k = a.GetKind();
  if (SYMBOL == k && BOOLEAN_TYPE == a.GetType())
    {
      bool updated = UpdateSubstitutionMap(a, ASTTrue);
      output = updated ? ASTTrue : a;
    }
  else if (NOT == k && SYMBOL == a[0].GetKind())
    {
      bool updated = UpdateSubstitutionMap(a[0], ASTFalse);
      output = updated ? ASTTrue : a;
    }
  else if (IFF == k || EQ == k)
    {
	  ASTVec c = a.GetChildren();

	  if (simplify_during_create_subM)
		  SortByArith(c);

	  if (c[0] == c[1])
		  return ASTTrue;

      ASTNode c1;
      if (EQ == k)
    	  c1 = simplify_during_create_subM ? simp->SimplifyTerm(c[1]) : c[1];
      else// (IFF == k )
    	  c1= simplify_during_create_subM ?  simp->SimplifyFormula_NoRemoveWrites(c[1], false) : c[1];

      bool updated = UpdateSubstitutionMap(c[0], c1);
      output = updated ? ASTTrue : a;

      if (updated)
        {
          //fill the arrayname readindices vector if e0 is a
          //READ(Arr,index) and index is a BVCONST
          int to;
          if ((to =TermOrder(c[0],c1))==1 && c[0].GetKind() == READ)
              at->FillUp_ArrReadIndex_Vec(c[0], c1);
          else if (to ==-1 && c1.GetKind() == READ)
                    at->FillUp_ArrReadIndex_Vec(c1,c[0]);
        }
    }
  else if (XOR == k)
  {
	  if (a.Degree() !=2)
		  return output;

	  int to = TermOrder(a[0],a[1]);
	  if (0 == to)
		  return output;

	  ASTNode symbol,rhs;
	  if (to==1)
	  {
		  symbol = a[0];
		  rhs = a[1];
	  }
	  else
	  {
		  symbol = a[0];
		  rhs = a[1];
	  }

	  assert(symbol.GetKind() == SYMBOL);

	  // If either side is already solved for.
	  if (CheckSubstitutionMap(symbol) || CheckSubstitutionMap(rhs))
	  {}
	  else if (UpdateSolverMap(symbol, bm->CreateNode(NOT, rhs)))
			output =  ASTTrue;
  }
  else if (AND == k)
    {
	  const ASTVec& c = a.GetChildren();
	  ASTVec o;
      o.reserve(c.size());

      for (ASTVec::const_iterator
             it = c.begin(), itend = c.end();
           it != itend; it++)
        {
          simp->UpdateAlwaysTrueFormSet(*it);
          ASTNode aaa = CreateSubstitutionMap(*it,at);

          if (ASTTrue != aaa)
            {
              if (ASTFalse == aaa)
                return ASTFalse;
              else
                o.push_back(aaa);
            }
        }
      if (o.size() == 0)
        output =  ASTTrue;
      else if (o.size() == 1)
        output = o[0];
      else if (o != c)
    	output = bm->CreateNode(AND, o);
      else
    	output = a;
    }

  return output;
} //end of CreateSubstitutionMap()

ASTNode SubstitutionMap::applySubstitutionMap(const ASTNode& n)
{
	bm->GetRunTimes()->start(RunTimes::ApplyingSubstitutions);
	ASTNodeMap cache;
	SimplifyingNodeFactory nf(*bm->hashingNodeFactory, *bm);
	ASTNode result =  replace(n,*SolverMap,cache,&nf, false);
	bm->GetRunTimes()->stop(RunTimes::ApplyingSubstitutions);
	return result;
}

ASTNode SubstitutionMap::applySubstitutionMapUntilArrays(const ASTNode& n)
{
	bm->GetRunTimes()->start(RunTimes::ApplyingSubstitutions);
	ASTNodeMap cache;
	SimplifyingNodeFactory nf(*bm->hashingNodeFactory, *bm);
	ASTNode result = replace(n,*SolverMap,cache,&nf, true);
	bm->GetRunTimes()->stop(RunTimes::ApplyingSubstitutions);
	return result;
}

ASTNode SubstitutionMap::replace(const ASTNode& n, ASTNodeMap& fromTo,
                ASTNodeMap& cache, NodeFactory * nf)
{
    return replace(n,fromTo,cache,nf,false);
}

// NOTE the fromTo map is changed as we traverse downwards.
// We call replace on each of the things in the fromTo map aswell.
// This is in case we have a fromTo map: (x maps to y), (y maps to 5),
// and pass the replace() function the node "x" to replace, then it
// will return 5, rather than y.

// NB: You can't use this to map from "5" to the symbol "x" say.
// It's optimised for the symbol to something case.

ASTNode SubstitutionMap::replace(const ASTNode& n, ASTNodeMap& fromTo,
		ASTNodeMap& cache, NodeFactory * nf, bool stopAtArrays)
{
    const Kind k = n.GetKind();
	if (k == BVCONST || k == TRUE || k == FALSE)
    	return n;

	ASTNodeMap::const_iterator it;

    if ((it = cache.find(n)) != cache.end())
		return it->second;

	if ((it = fromTo.find(n)) != fromTo.end())
	{
		const ASTNode& r = it->second;
		assert(r.GetIndexWidth() == n.GetIndexWidth());
		ASTNode replaced = replace(r, fromTo, cache,nf,stopAtArrays);
		if (replaced != r)
			fromTo[n] = replaced;

		cache.insert(make_pair(n,replaced));
		return replaced;
	}

	// These can't be created like regular nodes are
	if (k == SYMBOL )
		return n;

	const unsigned int indexWidth = n.GetIndexWidth();
	if (stopAtArrays && indexWidth > 0) // is an array.
	{
	   return n;
	}

	const ASTVec& children = n.GetChildren();
	assert(children.size() > 0); // Should have no leaves left here.

	ASTVec new_children;
	new_children.reserve(children.size());

	for (ASTVec::const_iterator it = children.begin(); it != children.end(); it++)
	{
		new_children.push_back(replace(*it, fromTo, cache,nf,stopAtArrays));
	}

	assert(new_children.size() == children.size());

	// This code short-cuts if the children are the same. Nodes with the same children,
	// won't have necessarily given the same node if the simplifyingNodeFactory is enabled
	// now, but wasn't enabled when the node was created. Shortcutting saves lots of time.
	if (new_children == children)
	{
		cache.insert(make_pair(n,n));
		return n;
	}

	ASTNode result;
	const unsigned int valueWidth = n.GetValueWidth();

	if (valueWidth == 0 ) // n.GetType() == BOOLEAN_TYPE
	{
		result = nf->CreateNode(k, new_children);
	}
	else
	{
		// If the index and value width aren't saved, they are reset sometimes (??)
		result = nf->CreateArrayTerm(k,indexWidth, valueWidth,new_children);
	}

	assert(result.GetValueWidth() == valueWidth);
	assert(result.GetIndexWidth() == indexWidth);

	cache.insert(make_pair(n,result));
	return result;
}

// Adds to the dependency graph that n0 depends on the variables in n1.
// It's not the transitive closure of the dependencies. Just the variables in the expression "n1".
// This is only needed as long as all the substitution rules haven't been written through.
void SubstitutionMap::buildDepends(const ASTNode& n0, const ASTNode& n1)
{
	if (n0.GetKind() != SYMBOL)
		return;

	if (n1.isConstant())
		return;

	vector<Symbols*> av;
	vars.VarSeenInTerm(vars.getSymbol(n1), rhs_visited, rhs, av);

	sort(av.begin(), av.end());
	for (int i =0; i < av.size();i++)
	{
		if (i!=0 && av[i] == av[i-1])
			continue; // Treat it like a set of Symbol* in effect.

		ASTNodeSet* sym = (vars.TermsAlreadySeenMap.find(av[i])->second);
		if(rhsAlreadyAdded.find(sym) != rhsAlreadyAdded.end())
			continue;
		rhsAlreadyAdded.insert(sym);

		//cout << loopCount++ << " ";
		//cout << "initial" << rhs.size() << " Adding: " <<sym->size();
		rhs.insert(sym->begin(), sym->end());
		//cout << "final:" << rhs.size();
		//cout << "added:" << sym << endl;

	}

	assert (dependsOn.find(n0) == dependsOn.end());
	dependsOn.insert(make_pair(n0,vars.getSymbol(n1)));
}

// Take the transitive closure of the varsToCheck. Storing the result in visited.
void SubstitutionMap::loops_helper(const set<ASTNode>& varsToCheck, set<ASTNode>& visited)
{
	set<ASTNode>::const_iterator visitedIt = visited.begin();

	set<ASTNode> toVisit;
	vector<ASTNode> visitedN;

	// for each variable.
	for (set<ASTNode>::const_iterator varIt = varsToCheck.begin(); varIt != varsToCheck.end(); varIt++)
	{
		while (*visitedIt < *varIt && visitedIt != visited.end())
			visitedIt++;

		if (*visitedIt == *varIt)
			continue;

		visitedN.push_back(*varIt);
		DependsType::iterator it;
		if ((it = dependsOn.find(*varIt)) != dependsOn.end())
		{
			Symbols* s= it->second;
			bool destruct;
			ASTNodeSet* varsSeen = vars.SetofVarsSeenInTerm(s,destruct);
			toVisit.insert(varsSeen->begin(), varsSeen->end());

			if (destruct)
				delete varsSeen;
		}
	}

	visited.insert(visitedN.begin(), visitedN.end());

	visitedN.clear();

	if (toVisit.size() !=0)
		loops_helper(toVisit, visited);
}

// If n0 is replaced by n1 in the substitution map. Will it cause a loop?
// i.e. will the dependency graph be an acyclic graph still.
// For example, if we have x = F(y,z,w), it would make the substitutionMap loop
// if there's already z = F(x).
bool SubstitutionMap::loops(const ASTNode& n0, const ASTNode& n1)
	{
	if (n0.GetKind() != SYMBOL)
		return false; // sometimes this function is called with constants on the lhs.

	if (n1.isConstant())
		return false; // constants contain no variables. Can't loop.

	// We are adding an edge FROM n0, so unless there is already an edge TO n0,
	// there is no change it can loop. Unless adding this would add a TO and FROM edge.
	if (rhs.find(n0) == rhs.end())
	{
		return vars.VarSeenInTerm(n0,n1);
	}

	if (n1.GetKind() == SYMBOL && dependsOn.find(n1) == dependsOn.end() )
		return false; // The rhs is a symbol and doesn't appear.

	if (debug_substn)
		cout << loopCount++  << endl;

	bool destruct = true;
	ASTNodeSet* dependN = vars.SetofVarsSeenInTerm(n1,destruct);

	if (debug_substn)
	{
		cout << n0 << " " <<  n1.GetNodeNum(); //<< " Expression size:" << bm->NodeSize(n1,true);
		cout << "Variables in expression: "<< dependN->size() << endl;
	}

	set<ASTNode> depend(dependN->begin(), dependN->end());

	if (destruct)
		delete dependN;

	set<ASTNode> visited;
	loops_helper(depend,visited);

	bool loops = visited.find(n0) != visited.end();

	if (debug_substn)
		cout << "Visited:" << visited.size() << "Loops:" << loops << endl;

	return (loops);
	}

};
<commit_msg>Make applySubstitutionMap idempotent (I hope).<commit_after>/*
 * substitutionMap.cpp
 *
 */

#include "SubstitutionMap.h"
#include "simplifier.h"
#include "../AST/ArrayTransformer.h"

namespace BEEV
{

SubstitutionMap::~SubstitutionMap()
{
	delete SolverMap;
}

// if false. Don't simplify while creating the substitution map.
// This makes it easier to spot how long is spent in the simplifier.
const bool simplify_during_create_subM = false;

//if a is READ(Arr,const) and b is BVCONST then return 1.
//if a is a symbol SYMBOL, return 1.
//if b is READ(Arr,const) and a is BVCONST then return -1
// if b is a symbol return -1.
//
//else return 0 by default
int TermOrder(const ASTNode& a, const ASTNode& b)
{
  const Kind k1 = a.GetKind();
  const Kind k2 = b.GetKind();

  if (k1 == SYMBOL)
  	return 1;

  if (k2 == SYMBOL)
  	return -1;

  //a is of the form READ(Arr,const), and b is const, or
  if ((k1 == READ
       && a[0].GetKind() == SYMBOL
       && a[1].GetKind() == BVCONST
       && (k2 == BVCONST)))
    return 1;

  //b is of the form READ(Arr,const), and a is const, or
  //b is of the form var, and a is const
  if ((k1 == BVCONST)
      && ((k2 == READ
           && b[0].GetKind() == SYMBOL
           && b[1].GetKind() == BVCONST)))
    return -1;

  return 0;
} //End of TermOrder()

//This finds conjuncts which are one of: (= SYMBOL BVCONST), (= BVCONST (READ SYMBOL BVCONST)),
// (IFF SYMBOL TRUE), (IFF SYMBOL FALSE), (IFF SYMBOL SYMBOL), (=SYMBOL SYMBOL)
// or (=SYMBOL BVCONST).
// It tries to remove the conjunct, storing it in the substitutionmap. It replaces it in the
// formula by true.
// The bvsolver puts expressions of the form (= SYMBOL TERM) into the solverMap.

ASTNode SubstitutionMap::CreateSubstitutionMap(const ASTNode& a,  ArrayTransformer*at)
{
  if (!bm->UserFlags.wordlevel_solve_flag)
    return a;

  ASTNode output;
  //if the variable has been solved for, then simply return it
  if (CheckSubstitutionMap(a, output))
    return output;

  if (!alreadyVisited.insert(a.GetNodeNum()).second)
  {
    return a;
  }

  output = a;

  //traverse a and populate the SubstitutionMap
  const Kind k = a.GetKind();
  if (SYMBOL == k && BOOLEAN_TYPE == a.GetType())
    {
      bool updated = UpdateSubstitutionMap(a, ASTTrue);
      output = updated ? ASTTrue : a;
    }
  else if (NOT == k && SYMBOL == a[0].GetKind())
    {
      bool updated = UpdateSubstitutionMap(a[0], ASTFalse);
      output = updated ? ASTTrue : a;
    }
  else if (IFF == k || EQ == k)
    {
	  ASTVec c = a.GetChildren();

	  if (simplify_during_create_subM)
		  SortByArith(c);

	  if (c[0] == c[1])
		  return ASTTrue;

      ASTNode c1;
      if (EQ == k)
    	  c1 = simplify_during_create_subM ? simp->SimplifyTerm(c[1]) : c[1];
      else// (IFF == k )
    	  c1= simplify_during_create_subM ?  simp->SimplifyFormula_NoRemoveWrites(c[1], false) : c[1];

      bool updated = UpdateSubstitutionMap(c[0], c1);
      output = updated ? ASTTrue : a;

      if (updated)
        {
          //fill the arrayname readindices vector if e0 is a
          //READ(Arr,index) and index is a BVCONST
          int to;
          if ((to =TermOrder(c[0],c1))==1 && c[0].GetKind() == READ)
              at->FillUp_ArrReadIndex_Vec(c[0], c1);
          else if (to ==-1 && c1.GetKind() == READ)
                    at->FillUp_ArrReadIndex_Vec(c1,c[0]);
        }
    }
  else if (XOR == k)
  {
	  if (a.Degree() !=2)
		  return output;

	  int to = TermOrder(a[0],a[1]);
	  if (0 == to)
		  return output;

	  ASTNode symbol,rhs;
	  if (to==1)
	  {
		  symbol = a[0];
		  rhs = a[1];
	  }
	  else
	  {
		  symbol = a[0];
		  rhs = a[1];
	  }

	  assert(symbol.GetKind() == SYMBOL);

	  // If either side is already solved for.
	  if (CheckSubstitutionMap(symbol) || CheckSubstitutionMap(rhs))
	  {}
	  else if (UpdateSolverMap(symbol, bm->CreateNode(NOT, rhs)))
			output =  ASTTrue;
  }
  else if (AND == k)
    {
	  const ASTVec& c = a.GetChildren();
	  ASTVec o;
      o.reserve(c.size());

      for (ASTVec::const_iterator
             it = c.begin(), itend = c.end();
           it != itend; it++)
        {
          simp->UpdateAlwaysTrueFormSet(*it);
          ASTNode aaa = CreateSubstitutionMap(*it,at);

          if (ASTTrue != aaa)
            {
              if (ASTFalse == aaa)
                return ASTFalse;
              else
                o.push_back(aaa);
            }
        }
      if (o.size() == 0)
        output =  ASTTrue;
      else if (o.size() == 1)
        output = o[0];
      else if (o != c)
    	output = bm->CreateNode(AND, o);
      else
    	output = a;
    }

  return output;
} //end of CreateSubstitutionMap()

// idempotent.
ASTNode SubstitutionMap::applySubstitutionMap(const ASTNode& n)
{
	bm->GetRunTimes()->start(RunTimes::ApplyingSubstitutions);
	ASTNodeMap cache;
	SimplifyingNodeFactory nf(*bm->hashingNodeFactory, *bm);
	ASTNode result =  replace(n,*SolverMap,cache,&nf, false);

	// NB. This is an expensive check. Remove it after it's been idempotent
	// for a while.
        #ifndef NDEBUG
              cache.clear();
              assert( result ==  replace(result,*SolverMap,cache,&nf, false));
        #endif

	bm->GetRunTimes()->stop(RunTimes::ApplyingSubstitutions);
	return result;
}

// not always idempotent.
ASTNode SubstitutionMap::applySubstitutionMapUntilArrays(const ASTNode& n)
{
	bm->GetRunTimes()->start(RunTimes::ApplyingSubstitutions);
	ASTNodeMap cache;
	SimplifyingNodeFactory nf(*bm->hashingNodeFactory, *bm);
	ASTNode result = replace(n,*SolverMap,cache,&nf, true);
	bm->GetRunTimes()->stop(RunTimes::ApplyingSubstitutions);
	return result;
}

ASTNode SubstitutionMap::replace(const ASTNode& n, ASTNodeMap& fromTo,
                ASTNodeMap& cache, NodeFactory * nf)
{
    return replace(n,fromTo,cache,nf,false);
}

// NOTE the fromTo map is changed as we traverse downwards.
// We call replace on each of the things in the fromTo map aswell.
// This is in case we have a fromTo map: (x maps to y), (y maps to 5),
// and pass the replace() function the node "x" to replace, then it
// will return 5, rather than y.

// NB: You can't use this to map from "5" to the symbol "x" say.
// It's optimised for the symbol to something case.

ASTNode SubstitutionMap::replace(const ASTNode& n, ASTNodeMap& fromTo,
		ASTNodeMap& cache, NodeFactory * nf, bool stopAtArrays)
{
    const Kind k = n.GetKind();
	if (k == BVCONST || k == TRUE || k == FALSE)
    	return n;

	ASTNodeMap::const_iterator it;

    if ((it = cache.find(n)) != cache.end())
		return it->second;

	if ((it = fromTo.find(n)) != fromTo.end())
	{
		const ASTNode& r = it->second;
		assert(r.GetIndexWidth() == n.GetIndexWidth());
		ASTNode replaced = replace(r, fromTo, cache,nf,stopAtArrays);
		if (replaced != r)
			fromTo[n] = replaced;

		cache.insert(make_pair(n,replaced));
		return replaced;
	}

	// These can't be created like regular nodes are
	if (k == SYMBOL )
		return n;

	const unsigned int indexWidth = n.GetIndexWidth();
	if (stopAtArrays && indexWidth > 0) // is an array.
	{
	   return n;
	}

	const ASTVec& children = n.GetChildren();
	assert(children.size() > 0); // Should have no leaves left here.

	ASTVec new_children;
	new_children.reserve(children.size());

	for (ASTVec::const_iterator it = children.begin(); it != children.end(); it++)
	{
		new_children.push_back(replace(*it, fromTo, cache,nf,stopAtArrays));
	}

	assert(new_children.size() == children.size());

	// This code short-cuts if the children are the same. Nodes with the same children,
	// won't have necessarily given the same node if the simplifyingNodeFactory is enabled
	// now, but wasn't enabled when the node was created. Shortcutting saves lots of time.
	if (new_children == children)
	{
		cache.insert(make_pair(n,n));
		return n;
	}

	ASTNode result;
	const unsigned int valueWidth = n.GetValueWidth();

	if (valueWidth == 0 ) // n.GetType() == BOOLEAN_TYPE
	{
		result = nf->CreateNode(k, new_children);
	}
	else
	{
		// If the index and value width aren't saved, they are reset sometimes (??)
		result = nf->CreateArrayTerm(k,indexWidth, valueWidth,new_children);
	}

	// We may have created something that should be mapped. For instance,
	// if n is READ(A, x), and the fromTo is: {x==0, READ(A,0) == 1}, then
	// by here the result will be READ(A,0). Which needs to be mapped again..
	// I hope that this makes it idempotent.

	if (fromTo.find(result) != fromTo.end())
	  result = replace(result, fromTo, cache,nf,stopAtArrays);

	assert(result.GetValueWidth() == valueWidth);
	assert(result.GetIndexWidth() == indexWidth);

	cache.insert(make_pair(n,result));
	return result;
}

// Adds to the dependency graph that n0 depends on the variables in n1.
// It's not the transitive closure of the dependencies. Just the variables in the expression "n1".
// This is only needed as long as all the substitution rules haven't been written through.
void SubstitutionMap::buildDepends(const ASTNode& n0, const ASTNode& n1)
{
	if (n0.GetKind() != SYMBOL)
		return;

	if (n1.isConstant())
		return;

	vector<Symbols*> av;
	vars.VarSeenInTerm(vars.getSymbol(n1), rhs_visited, rhs, av);

	sort(av.begin(), av.end());
	for (int i =0; i < av.size();i++)
	{
		if (i!=0 && av[i] == av[i-1])
			continue; // Treat it like a set of Symbol* in effect.

		ASTNodeSet* sym = (vars.TermsAlreadySeenMap.find(av[i])->second);
		if(rhsAlreadyAdded.find(sym) != rhsAlreadyAdded.end())
			continue;
		rhsAlreadyAdded.insert(sym);

		//cout << loopCount++ << " ";
		//cout << "initial" << rhs.size() << " Adding: " <<sym->size();
		rhs.insert(sym->begin(), sym->end());
		//cout << "final:" << rhs.size();
		//cout << "added:" << sym << endl;

	}

	assert (dependsOn.find(n0) == dependsOn.end());
	dependsOn.insert(make_pair(n0,vars.getSymbol(n1)));
}

// Take the transitive closure of the varsToCheck. Storing the result in visited.
void SubstitutionMap::loops_helper(const set<ASTNode>& varsToCheck, set<ASTNode>& visited)
{
	set<ASTNode>::const_iterator visitedIt = visited.begin();

	set<ASTNode> toVisit;
	vector<ASTNode> visitedN;

	// for each variable.
	for (set<ASTNode>::const_iterator varIt = varsToCheck.begin(); varIt != varsToCheck.end(); varIt++)
	{
		while (*visitedIt < *varIt && visitedIt != visited.end())
			visitedIt++;

		if (*visitedIt == *varIt)
			continue;

		visitedN.push_back(*varIt);
		DependsType::iterator it;
		if ((it = dependsOn.find(*varIt)) != dependsOn.end())
		{
			Symbols* s= it->second;
			bool destruct;
			ASTNodeSet* varsSeen = vars.SetofVarsSeenInTerm(s,destruct);
			toVisit.insert(varsSeen->begin(), varsSeen->end());

			if (destruct)
				delete varsSeen;
		}
	}

	visited.insert(visitedN.begin(), visitedN.end());

	visitedN.clear();

	if (toVisit.size() !=0)
		loops_helper(toVisit, visited);
}

// If n0 is replaced by n1 in the substitution map. Will it cause a loop?
// i.e. will the dependency graph be an acyclic graph still.
// For example, if we have x = F(y,z,w), it would make the substitutionMap loop
// if there's already z = F(x).
bool SubstitutionMap::loops(const ASTNode& n0, const ASTNode& n1)
	{
	if (n0.GetKind() != SYMBOL)
		return false; // sometimes this function is called with constants on the lhs.

	if (n1.isConstant())
		return false; // constants contain no variables. Can't loop.

	// We are adding an edge FROM n0, so unless there is already an edge TO n0,
	// there is no change it can loop. Unless adding this would add a TO and FROM edge.
	if (rhs.find(n0) == rhs.end())
	{
		return vars.VarSeenInTerm(n0,n1);
	}

	if (n1.GetKind() == SYMBOL && dependsOn.find(n1) == dependsOn.end() )
		return false; // The rhs is a symbol and doesn't appear.

	if (debug_substn)
		cout << loopCount++  << endl;

	bool destruct = true;
	ASTNodeSet* dependN = vars.SetofVarsSeenInTerm(n1,destruct);

	if (debug_substn)
	{
		cout << n0 << " " <<  n1.GetNodeNum(); //<< " Expression size:" << bm->NodeSize(n1,true);
		cout << "Variables in expression: "<< dependN->size() << endl;
	}

	set<ASTNode> depend(dependN->begin(), dependN->end());

	if (destruct)
		delete dependN;

	set<ASTNode> visited;
	loops_helper(depend,visited);

	bool loops = visited.find(n0) != visited.end();

	if (debug_substn)
		cout << "Visited:" << visited.size() << "Loops:" << loops << endl;

	return (loops);
	}

};
<|endoftext|>


<commit_before><commit_msg>Update known conformance version for SC 1.0.1.0<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM 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) version 2.1 dated February 1999.

#ifndef MFEM_MM
#define MFEM_MM

#include <list>
#include <cstddef> // for size_t
#include <unordered_map>

using std::size_t;

#include "occa.hpp" // for OccaMemory

namespace mfem
{

// *****************************************************************************
// * Memory Manager Singleton
// *****************************************************************************
class mm
{
public:
   // **************************************************************************
   struct alias;

   // TODO: Change this to ptr
   struct memory
   {
      const size_t bytes;
      void *const h_ptr;
      void *d_ptr;
      OccaMemory o_ptr;
      std::list<const alias *> aliases;
      bool host;
      bool padding[7];
      memory(void* const h, const size_t b):
         bytes(b), h_ptr(h), d_ptr(nullptr), aliases(), host(true) {}
   };

   struct alias
   {
      memory *const mem;
      const long offset;
   };

   // **************************************************************************
   typedef std::unordered_map<const void*, memory> memory_map;
   typedef std::unordered_map<const void*, const alias*> alias_map;

   struct ledger
   {
      memory_map memories;
      alias_map aliases;
   };

   // **************************************************************************
   // * Main malloc template function
   // * Allocates n*size bytes and returns a pointer to the allocated memory
   // **************************************************************************
   template<class T>
   static inline T* malloc(const size_t n, const size_t size = sizeof(T))
   { return static_cast<T*>(MM().Insert(::new T[n], n*size)); }

   // **************************************************************************
   // * Frees the memory space pointed to by ptr, which must have been
   // * returned by a previous call to mm::malloc
   // **************************************************************************
   template<class T>
   static inline void free(void *ptr)
   {
      if (!ptr) { return; }
      ::delete[] static_cast<T*>(ptr);
      mm::MM().Erase(ptr);
   }

   // **************************************************************************
   // * Translates ptr to host or device address,
   // * depending on config::Cuda() and the ptr' state
   // **************************************************************************
   static inline void *ptr(void *a) { return MM().Ptr(a); }
   static inline const void* ptr(const void *a) { return MM().Ptr(a); }
   static inline OccaMemory occaPtr(const void *a) { return MM().Memory(a); }

   // **************************************************************************
   static inline void push(const void *ptr, const size_t bytes = 0)
   {
      return MM().Push(ptr, bytes);
   }

   // **************************************************************************
   static inline void pull(const void *ptr, const size_t bytes = 0)
   {
      return MM().Pull(ptr, bytes);
   }

   // **************************************************************************
   static void* memcpy(void *dst, const void *src,
                       size_t bytes, const bool async = false);

   // **************************************************************************
   static inline bool known(const void *a)
   {
      return MM().Known(a);
   }

   // **************************************************************************
   template<class T>
   static inline void RegisterHostPtr(T * ptr_host, const size_t size)
   {
     MM().Insert(ptr_host, size*sizeof(T));
   }

   // **************************************************************************
   template<class T>
   static void RegisterHostAndDevicePtr(T * ptr_host, T * ptr_device, const size_t size)
   {
     MM().Insert(ptr_host, size*sizeof(T));
     mm::memory &base = MM().maps.memories.at(ptr_host);
     base.d_ptr = ptr_device;
     base.host = false;
   }

private:
   ledger maps;
   mm() {}
   mm(mm const&) = delete;
   void operator=(mm const&) = delete;
   static inline mm& MM() { static mm *singleton = new mm(); return *singleton; }

   // **************************************************************************
   void *Insert(void *ptr, const size_t bytes);
   void *Erase(void *ptr);
   void *Ptr(void *ptr);
   const void *Ptr(const void *ptr);
   bool Known(const void *ptr);
   bool Alias(const void *ptr);
   OccaMemory Memory(const void *ptr);

   // **************************************************************************
   void Push(const void *ptr, const size_t bytes = 0);
   void Pull(const void *ptr, const size_t bytes = 0);
};
  //Check if method has been registered
  void RegisterCheck(void *ptr);
} // namespace mfem

#endif
<commit_msg>comment fix<commit_after>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM 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) version 2.1 dated February 1999.

#ifndef MFEM_MM
#define MFEM_MM

#include <list>
#include <cstddef> // for size_t
#include <unordered_map>

using std::size_t;

#include "occa.hpp" // for OccaMemory

namespace mfem
{

// *****************************************************************************
// * Memory Manager Singleton
// *****************************************************************************
class mm
{
public:
   // **************************************************************************
   struct alias;

   // TODO: Change this to ptr
   struct memory
   {
      const size_t bytes;
      void *const h_ptr;
      void *d_ptr;
      OccaMemory o_ptr;
      std::list<const alias *> aliases;
      bool host;
      bool padding[7];
      memory(void* const h, const size_t b):
         bytes(b), h_ptr(h), d_ptr(nullptr), aliases(), host(true) {}
   };

   struct alias
   {
      memory *const mem;
      const long offset;
   };

   // **************************************************************************
   typedef std::unordered_map<const void*, memory> memory_map;
   typedef std::unordered_map<const void*, const alias*> alias_map;

   struct ledger
   {
      memory_map memories;
      alias_map aliases;
   };

   // **************************************************************************
   // * Main malloc template function
   // * Allocates n*size bytes and returns a pointer to the allocated memory
   // **************************************************************************
   template<class T>
   static inline T* malloc(const size_t n, const size_t size = sizeof(T))
   { return static_cast<T*>(MM().Insert(::new T[n], n*size)); }

   // **************************************************************************
   // * Frees the memory space pointed to by ptr, which must have been
   // * returned by a previous call to mm::malloc
   // **************************************************************************
   template<class T>
   static inline void free(void *ptr)
   {
      if (!ptr) { return; }
      ::delete[] static_cast<T*>(ptr);
      mm::MM().Erase(ptr);
   }

   // **************************************************************************
   // * Translates ptr to host or device address,
   // * depending on config::Cuda() and the ptr' state
   // **************************************************************************
   static inline void *ptr(void *a) { return MM().Ptr(a); }
   static inline const void* ptr(const void *a) { return MM().Ptr(a); }
   static inline OccaMemory occaPtr(const void *a) { return MM().Memory(a); }

   // **************************************************************************
   static inline void push(const void *ptr, const size_t bytes = 0)
   {
      return MM().Push(ptr, bytes);
   }

   // **************************************************************************
   static inline void pull(const void *ptr, const size_t bytes = 0)
   {
      return MM().Pull(ptr, bytes);
   }

   // **************************************************************************
   static void* memcpy(void *dst, const void *src,
                       size_t bytes, const bool async = false);

   // **************************************************************************
   static inline bool known(const void *a)
   {
      return MM().Known(a);
   }

   // **************************************************************************
   template<class T>
   static inline void RegisterHostPtr(T * ptr_host, const size_t size)
   {
     MM().Insert(ptr_host, size*sizeof(T));
   }

   // **************************************************************************
   template<class T>
   static void RegisterHostAndDevicePtr(T * ptr_host, T * ptr_device, const size_t size)
   {
     MM().Insert(ptr_host, size*sizeof(T));
     mm::memory &base = MM().maps.memories.at(ptr_host);
     base.d_ptr = ptr_device;
     base.host = false;
   }

private:
   ledger maps;
   mm() {}
   mm(mm const&) = delete;
   void operator=(mm const&) = delete;
   static inline mm& MM() { static mm *singleton = new mm(); return *singleton; }

   // **************************************************************************
   void *Insert(void *ptr, const size_t bytes);
   void *Erase(void *ptr);
   void *Ptr(void *ptr);
   const void *Ptr(const void *ptr);
   bool Known(const void *ptr);
   bool Alias(const void *ptr);
   OccaMemory Memory(const void *ptr);

   // **************************************************************************
   void Push(const void *ptr, const size_t bytes = 0);
   void Pull(const void *ptr, const size_t bytes = 0);
};
  //Check if pointer has been registered with the okina memory manager
  void RegisterCheck(void *ptr);
} // namespace mfem

#endif
<|endoftext|>


<commit_before>/*
* CommonCrypto Hash Functions
* (C) 2018 Jose Pereira
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/commoncrypto.h>
#include <botan/hash.h>
#include <unordered_map>

#include <CommonCrypto/CommonCrypto.h>

namespace Botan {

namespace {

template <class CTX>
class CommonCrypto_HashFunction final : public HashFunction
   {
   public:

      struct digest_config_t {
         std::string name;
         size_t digestLength;
         size_t blockSize;
         int (*init)(CTX *);
         int (*update)(CTX *, const void *, CC_LONG len);
         int (*final)(unsigned char *, CTX*);
         };

      void clear() override
         {
         if(m_info.init(&m_ctx) != 1)
            throw CommonCrypto_Error("CC_" + m_info.name + "_Init");
         }

      std::string provider() const override { return "commoncrypto"; }
      std::string name() const override { return m_info.name; }

      HashFunction* clone() const override
         {
         return new CommonCrypto_HashFunction(m_info);
         }

      std::unique_ptr<HashFunction> copy_state() const override
         {
         return std::unique_ptr<CommonCrypto_HashFunction>(
            new CommonCrypto_HashFunction(m_info, m_ctx));
         }

      size_t output_length() const override
         {
         return m_info.digestLength;
         }

      size_t hash_block_size() const override
         {
         return m_info.blockSize;
         }

      CommonCrypto_HashFunction(const digest_config_t& info) :
         m_info(info)
         {
         clear();
         }

      CommonCrypto_HashFunction(const digest_config_t& info, const CTX &ctx) :
         m_ctx(ctx), m_info(info) {}

   private:
      void add_data(const uint8_t input[], size_t length) override
         {
         /* update len parameter is 32 bit unsigned integer, feed input in parts */
         while (length > 0)
            {
            CC_LONG update_len = (length > 0xFFFFFFFFUL) ? 0xFFFFFFFFUL : static_cast<CC_LONG>(length);
            m_info.update(&m_ctx, input, update_len);
            input += update_len;
            length -= update_len;
            }
         }

      void final_result(uint8_t output[]) override
         {
         if(m_info.final(output, &m_ctx) != 1)
            throw CommonCrypto_Error("CC_" + m_info.name + "_Final");
         clear();
         }

      CTX m_ctx;
      digest_config_t m_info;
   };
}

std::unique_ptr<HashFunction>
make_commoncrypto_hash(const std::string& name)
   {
#define MAKE_COMMONCRYPTO_HASH_3(name, hash, ctx)               \
   std::unique_ptr<HashFunction>(                               \
      new CommonCrypto_HashFunction<CC_ ## ctx ## _CTX >({      \
            name,                                               \
            CC_ ## hash ## _DIGEST_LENGTH,                      \
            CC_ ## hash ## _BLOCK_BYTES,                        \
            CC_ ## hash ## _Init,                               \
            CC_ ## hash ## _Update,                             \
            CC_ ## hash ## _Final                               \
         }));

#define MAKE_COMMONCRYPTO_HASH_2(name, id)      \
   MAKE_COMMONCRYPTO_HASH_3(name, id, id)

#define MAKE_COMMONCRYPTO_HASH_1(id)            \
   MAKE_COMMONCRYPTO_HASH_2(#id, id)

#if defined(BOTAN_HAS_SHA2_32)
   if(name == "SHA-224")
      return MAKE_COMMONCRYPTO_HASH_3(name, SHA224, SHA256);
   if(name == "SHA-256")
      return MAKE_COMMONCRYPTO_HASH_2(name, SHA256);
#endif
#if defined(BOTAN_HAS_SHA2_64)
   if(name == "SHA-384")
      return MAKE_COMMONCRYPTO_HASH_3(name, SHA384, SHA512);
   if(name == "SHA-512")
      return MAKE_COMMONCRYPTO_HASH_2(name, SHA512);
#endif

#if defined(BOTAN_HAS_SHA1)
   if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
      return MAKE_COMMONCRYPTO_HASH_2(name, SHA1);
#endif

#if defined(BOTAN_HAS_MD5)
   if(name == "MD5")
      return MAKE_COMMONCRYPTO_HASH_1(MD5);
#endif

#if defined(BOTAN_HAS_MD4)
   if(name == "MD4")
      return MAKE_COMMONCRYPTO_HASH_1(MD4);
#endif
   return nullptr;
   }

}
<commit_msg>Disable MD4/MD5 support in CommonCrypto<commit_after>/*
* CommonCrypto Hash Functions
* (C) 2018 Jose Pereira
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/commoncrypto.h>
#include <botan/hash.h>
#include <unordered_map>

#include <CommonCrypto/CommonCrypto.h>

namespace Botan {

namespace {

template <class CTX>
class CommonCrypto_HashFunction final : public HashFunction
   {
   public:

      struct digest_config_t {
         std::string name;
         size_t digestLength;
         size_t blockSize;
         int (*init)(CTX *);
         int (*update)(CTX *, const void *, CC_LONG len);
         int (*final)(unsigned char *, CTX*);
         };

      void clear() override
         {
         if(m_info.init(&m_ctx) != 1)
            throw CommonCrypto_Error("CC_" + m_info.name + "_Init");
         }

      std::string provider() const override { return "commoncrypto"; }
      std::string name() const override { return m_info.name; }

      HashFunction* clone() const override
         {
         return new CommonCrypto_HashFunction(m_info);
         }

      std::unique_ptr<HashFunction> copy_state() const override
         {
         return std::unique_ptr<CommonCrypto_HashFunction>(
            new CommonCrypto_HashFunction(m_info, m_ctx));
         }

      size_t output_length() const override
         {
         return m_info.digestLength;
         }

      size_t hash_block_size() const override
         {
         return m_info.blockSize;
         }

      CommonCrypto_HashFunction(const digest_config_t& info) :
         m_info(info)
         {
         clear();
         }

      CommonCrypto_HashFunction(const digest_config_t& info, const CTX &ctx) :
         m_ctx(ctx), m_info(info) {}

   private:
      void add_data(const uint8_t input[], size_t length) override
         {
         /* update len parameter is 32 bit unsigned integer, feed input in parts */
         while (length > 0)
            {
            CC_LONG update_len = (length > 0xFFFFFFFFUL) ? 0xFFFFFFFFUL : static_cast<CC_LONG>(length);
            m_info.update(&m_ctx, input, update_len);
            input += update_len;
            length -= update_len;
            }
         }

      void final_result(uint8_t output[]) override
         {
         if(m_info.final(output, &m_ctx) != 1)
            throw CommonCrypto_Error("CC_" + m_info.name + "_Final");
         clear();
         }

      CTX m_ctx;
      digest_config_t m_info;
   };
}

std::unique_ptr<HashFunction>
make_commoncrypto_hash(const std::string& name)
   {
#define MAKE_COMMONCRYPTO_HASH_3(name, hash, ctx)               \
   std::unique_ptr<HashFunction>(                               \
      new CommonCrypto_HashFunction<CC_ ## ctx ## _CTX >({      \
            name,                                               \
            CC_ ## hash ## _DIGEST_LENGTH,                      \
            CC_ ## hash ## _BLOCK_BYTES,                        \
            CC_ ## hash ## _Init,                               \
            CC_ ## hash ## _Update,                             \
            CC_ ## hash ## _Final                               \
         }));

#define MAKE_COMMONCRYPTO_HASH_2(name, id)      \
   MAKE_COMMONCRYPTO_HASH_3(name, id, id)

#define MAKE_COMMONCRYPTO_HASH_1(id)            \
   MAKE_COMMONCRYPTO_HASH_2(#id, id)

#if defined(BOTAN_HAS_SHA2_32)
   if(name == "SHA-224")
      return MAKE_COMMONCRYPTO_HASH_3(name, SHA224, SHA256);
   if(name == "SHA-256")
      return MAKE_COMMONCRYPTO_HASH_2(name, SHA256);
#endif
#if defined(BOTAN_HAS_SHA2_64)
   if(name == "SHA-384")
      return MAKE_COMMONCRYPTO_HASH_3(name, SHA384, SHA512);
   if(name == "SHA-512")
      return MAKE_COMMONCRYPTO_HASH_2(name, SHA512);
#endif

#if defined(BOTAN_HAS_SHA1)
   if(name == "SHA-160" || name == "SHA-1" || name == "SHA1")
      return MAKE_COMMONCRYPTO_HASH_2(name, SHA1);
#endif

   return nullptr;
   }

}
<|endoftext|>


<commit_before>// Authors: see AUTHORS.md at project root.
// CopyPolicy: released under the terms of the LGPLv2.1, see LICENSE at project root.
// URL: https://github.com/asrob-uc3m/robotDevastation

#include "YarpNetworkManager.hpp"

#include <sstream>
#include <cstring> // strcmp()

#include <yarp/os/Network.h>

#include <ColorDebug.hpp>

#include "Vocabs.hpp"

//-- Initialize static members
rd::YarpNetworkManager * rd::YarpNetworkManager::uniqueInstance = NULL;
const std::string rd::YarpNetworkManager::id = "YARP";
const int rd::YarpNetworkManager::KEEPALIVE_RATE_MS = 1000;

bool rd::YarpNetworkManager::RegisterManager()
{
    if (uniqueInstance == NULL)
    {
        uniqueInstance = new YarpNetworkManager();
    }

    return Register( uniqueInstance, id);
}

rd::YarpNetworkManager::YarpNetworkManager() : RateThread(KEEPALIVE_RATE_MS)
{
    started = false;
}

void rd::YarpNetworkManager::run()
{
    keepAlive();
}

rd::YarpNetworkManager::~YarpNetworkManager()
{
    uniqueInstance = NULL;
}

bool rd::YarpNetworkManager::start()
{
    if (player.getId() == -1)
    {
        CD_ERROR("NetworkManager not initialized, player id not set\n");
        return false;
    }

    if (started)
    {
        CD_ERROR("NetworkManager already started\n");
        return false;
    }

    yarp::os::NetworkBase::initMinimum();

    if ( ! yarp::os::NetworkBase::checkNetwork() )
    {
        CD_ERROR("Found no yarp network to connect to rdServer (try running 'yarpserver &'). Bye!\n");
        return false;
    }

    //-- Open the rcpClient port with this player's id
    std::ostringstream rpc_str;
    rpc_str << "/robotDevastation/";
    rpc_str << player.getId();
    rpc_str << "/rdServer/rpc:c";
    if( ! rpcClient.open( rpc_str.str() ) )
    {
        CD_ERROR("Could not open '%s'. Bye!\n",rpc_str.str().c_str());
        return false;
    }

    //-- Open the callback port with this player's id
    std::ostringstream callback_str;
    callback_str << "/robotDevastation/";
    callback_str << player.getId();
    callback_str << "/rdServer/info:i";
    if( ! callbackPort.open( callback_str.str() ) )
    {
        CD_ERROR("Could not open '%s'. Bye!\n",callback_str.str().c_str());
        return false;
    }

    //-- Connect robotDevastation RpcClient to rdServer RpcServer
    if( ! yarp::os::Network::connect( rpc_str.str() , "/rdServer/rpc:s" ) )
    {
        CD_ERROR("Could not connect robotDevastation RpcClient to rdServer RpcServer (launch 'rdServer' if not already launched).\n");
        return false;
    }
    CD_SUCCESS("Connected robotDevastation RpcClient to rdServer RpcServer!\n");

    //-- Connect from rdServer info to robotDevastation callbackPort
    if ( !yarp::os::Network::connect( "/rdServer/info:o", callback_str.str() ))
    {
        CD_ERROR("Could not connect from rdServer info to robotDevastation callbackPort (launch 'rdServer' if not already launched).\n");
        return false;
    }
    CD_SUCCESS("Connected from rdServer info to robotDevastation callbackPort!\n");

    callbackPort.useCallback(*this);

    RateThread::start();

    started = true;
    return true;
}

void rd::YarpNetworkManager::onRead(yarp::os::Bottle &b)
{
    //CD_INFO("Got %s\n", b.toString().c_str());
    if ((b.get(0).asString() == "players")||(b.get(0).asVocab() == VOCAB_RD_PLAYERS)) {  // players //
        //CD_INFO("Number of players: %d\n",b.size()-1);  // -1 because of vocab.
        std::vector< Player > players;
        for (int i = 1; i < b.size(); i++)
        {
            Player rdPlayer(b.get(i).asList()->get(0).asInt(),
                              b.get(i).asList()->get(1).asString().c_str(),
                              b.get(i).asList()->get(2).asInt(),
                              b.get(i).asList()->get(3).asInt(),
                              b.get(i).asList()->get(4).asInt(),
                              b.get(i).asList()->get(5).asInt()
                              );
            players.push_back(rdPlayer);
        }

        //-- Notify listeners
        for (std::vector<NetworkEventListener *>::iterator it = listeners.begin(); it != listeners.end(); ++it)
        {
            (*it)->onDataArrived(players);
        }
    }
    else
    {
        CD_ERROR("What?\n");
    }

}

bool rd::YarpNetworkManager::stop()
{
    if (!started)
    {
        CD_ERROR("Already stopped\n");
        return false;
    }

    RateThread::askToStop();

    rpcClient.close();

    callbackPort.disableCallback();
    callbackPort.interrupt();
    callbackPort.close();

    yarp::os::NetworkBase::finiMinimum();

    started = false;
    return true;
}

bool rd::YarpNetworkManager::isStopped() const
{
    return !started;
}

bool rd::YarpNetworkManager::configure(const std::string & parameter, const Player & value)
{
    if (parameter.compare("player") == 0)
    {
        player = value;
        return true;
    }

    return NetworkManager::configure(parameter, value);
}

bool rd::YarpNetworkManager::sendPlayerHit(const Player & player, int damage)
{
    if (!started)
    {
        CD_ERROR("NetworkManager has not been started\n");
        return false;
    }

    //-- Send a message to the server with the player Id and the damage done:
    yarp::os::Bottle msg_player_hit, response;
    msg_player_hit.addVocab(VOCAB_RD_HIT);
    msg_player_hit.addInt(player.getId());
    msg_player_hit.addInt(damage);
    rpcClient.write(msg_player_hit,response);
    CD_INFO("rdServer response from hit: %s\n",response.toString().c_str());

    //-- Check response
    if (std::strcmp(response.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::YarpNetworkManager::login()
{
    if (!started)
    {
        CD_WARNING("NetworkManager has not been started\n");
        if(!start())
        {
            CD_ERROR("NetworkManager could not be started for player %d\n", player.getId() );
            return false;
        }
    }

    //-- Start network system
    std::stringstream ss;
    ss << player.getId();

    //-- Send login message
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGIN);
    msgRdPlayer.addInt(player.getId());
    msgRdPlayer.addString(player.getName().c_str());
    msgRdPlayer.addInt(player.getTeamId());
    rpcClient.write(msgRdPlayer,res);
    CD_INFO("rdServer response from login: %s\n",res.toString().c_str());

    //-- Check response
    if (std::strcmp(res.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::YarpNetworkManager::logout()
{
    if (!started)
    {
        CD_ERROR("NetworkManager has not been started\n");
        return false;
    }

    CD_INFO("Logout...\n");
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGOUT);
    msgRdPlayer.addInt(player.getId());
    rpcClient.write(msgRdPlayer,res);
    CD_INFO("rdServer response from logout: %s\n",res.toString().c_str());

    //-- Check response
    if (std::strcmp(res.toString().c_str(), "[ok]") == 0)
    {
        CD_SUCCESS("Logout ok\n");
        return true;
    }
    else
    {
        CD_ERROR("Logout failed\n");
        return false;
    }
}

bool rd::YarpNetworkManager::keepAlive()
{
    if (!started)
    {
        CD_ERROR("NetworkManager has not been started\n");
        return false;
    }

    CD_INFO("Keep alive...\n");
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_KEEPALIVE);
    msgRdPlayer.addInt(player.getId());
    rpcClient.write(msgRdPlayer,res);

    //-- Check response
    if (std::strcmp(res.toString().c_str(), "[ok]") == 0)
    {
        return true;
    }
    else
    {
        CD_ERROR("Keep alive failed\n");
        return false;
    }
}
<commit_msg>match syntax of main<commit_after>// Authors: see AUTHORS.md at project root.
// CopyPolicy: released under the terms of the LGPLv2.1, see LICENSE at project root.
// URL: https://github.com/asrob-uc3m/robotDevastation

#include "YarpNetworkManager.hpp"

#include <sstream>
#include <cstring> // strcmp()

#include <yarp/os/Network.h>

#include <ColorDebug.hpp>

#include "Vocabs.hpp"

//-- Initialize static members
rd::YarpNetworkManager * rd::YarpNetworkManager::uniqueInstance = NULL;
const std::string rd::YarpNetworkManager::id = "YARP";
const int rd::YarpNetworkManager::KEEPALIVE_RATE_MS = 1000;

bool rd::YarpNetworkManager::RegisterManager()
{
    if (uniqueInstance == NULL)
    {
        uniqueInstance = new YarpNetworkManager();
    }

    return Register( uniqueInstance, id);
}

rd::YarpNetworkManager::YarpNetworkManager() : RateThread(KEEPALIVE_RATE_MS)
{
    started = false;
}

void rd::YarpNetworkManager::run()
{
    keepAlive();
}

rd::YarpNetworkManager::~YarpNetworkManager()
{
    uniqueInstance = NULL;
}

bool rd::YarpNetworkManager::start()
{
    if (player.getId() == -1)
    {
        CD_ERROR("NetworkManager not initialized, player id not set\n");
        return false;
    }

    if (started)
    {
        CD_ERROR("NetworkManager already started\n");
        return false;
    }

    yarp::os::Network::initMinimum();

    if ( ! yarp::os::Network::checkNetwork() )
    {
        CD_ERROR("Found no yarp network to connect to rdServer (try running 'yarpserver &'). Bye!\n");
        return false;
    }

    //-- Open the rcpClient port with this player's id
    std::ostringstream rpc_str;
    rpc_str << "/robotDevastation/";
    rpc_str << player.getId();
    rpc_str << "/rdServer/rpc:c";
    if( ! rpcClient.open( rpc_str.str() ) )
    {
        CD_ERROR("Could not open '%s'. Bye!\n",rpc_str.str().c_str());
        return false;
    }

    //-- Open the callback port with this player's id
    std::ostringstream callback_str;
    callback_str << "/robotDevastation/";
    callback_str << player.getId();
    callback_str << "/rdServer/info:i";
    if( ! callbackPort.open( callback_str.str() ) )
    {
        CD_ERROR("Could not open '%s'. Bye!\n",callback_str.str().c_str());
        return false;
    }

    //-- Connect robotDevastation RpcClient to rdServer RpcServer
    if( ! yarp::os::Network::connect( rpc_str.str() , "/rdServer/rpc:s" ) )
    {
        CD_ERROR("Could not connect robotDevastation RpcClient to rdServer RpcServer (launch 'rdServer' if not already launched).\n");
        return false;
    }
    CD_SUCCESS("Connected robotDevastation RpcClient to rdServer RpcServer!\n");

    //-- Connect from rdServer info to robotDevastation callbackPort
    if ( !yarp::os::Network::connect( "/rdServer/info:o", callback_str.str() ))
    {
        CD_ERROR("Could not connect from rdServer info to robotDevastation callbackPort (launch 'rdServer' if not already launched).\n");
        return false;
    }
    CD_SUCCESS("Connected from rdServer info to robotDevastation callbackPort!\n");

    callbackPort.useCallback(*this);

    RateThread::start();

    started = true;
    return true;
}

void rd::YarpNetworkManager::onRead(yarp::os::Bottle &b)
{
    //CD_INFO("Got %s\n", b.toString().c_str());
    if ((b.get(0).asString() == "players")||(b.get(0).asVocab() == VOCAB_RD_PLAYERS)) {  // players //
        //CD_INFO("Number of players: %d\n",b.size()-1);  // -1 because of vocab.
        std::vector< Player > players;
        for (int i = 1; i < b.size(); i++)
        {
            Player rdPlayer(b.get(i).asList()->get(0).asInt(),
                              b.get(i).asList()->get(1).asString().c_str(),
                              b.get(i).asList()->get(2).asInt(),
                              b.get(i).asList()->get(3).asInt(),
                              b.get(i).asList()->get(4).asInt(),
                              b.get(i).asList()->get(5).asInt()
                              );
            players.push_back(rdPlayer);
        }

        //-- Notify listeners
        for (std::vector<NetworkEventListener *>::iterator it = listeners.begin(); it != listeners.end(); ++it)
        {
            (*it)->onDataArrived(players);
        }
    }
    else
    {
        CD_ERROR("What?\n");
    }

}

bool rd::YarpNetworkManager::stop()
{
    if (!started)
    {
        CD_ERROR("Already stopped\n");
        return false;
    }

    RateThread::askToStop();

    rpcClient.close();

    callbackPort.disableCallback();
    callbackPort.interrupt();
    callbackPort.close();

    yarp::os::NetworkBase::finiMinimum();

    started = false;
    return true;
}

bool rd::YarpNetworkManager::isStopped() const
{
    return !started;
}

bool rd::YarpNetworkManager::configure(const std::string & parameter, const Player & value)
{
    if (parameter.compare("player") == 0)
    {
        player = value;
        return true;
    }

    return NetworkManager::configure(parameter, value);
}

bool rd::YarpNetworkManager::sendPlayerHit(const Player & player, int damage)
{
    if (!started)
    {
        CD_ERROR("NetworkManager has not been started\n");
        return false;
    }

    //-- Send a message to the server with the player Id and the damage done:
    yarp::os::Bottle msg_player_hit, response;
    msg_player_hit.addVocab(VOCAB_RD_HIT);
    msg_player_hit.addInt(player.getId());
    msg_player_hit.addInt(damage);
    rpcClient.write(msg_player_hit,response);
    CD_INFO("rdServer response from hit: %s\n",response.toString().c_str());

    //-- Check response
    if (std::strcmp(response.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::YarpNetworkManager::login()
{
    if (!started)
    {
        CD_WARNING("NetworkManager has not been started\n");
        if(!start())
        {
            CD_ERROR("NetworkManager could not be started for player %d\n", player.getId() );
            return false;
        }
    }

    //-- Start network system
    std::stringstream ss;
    ss << player.getId();

    //-- Send login message
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGIN);
    msgRdPlayer.addInt(player.getId());
    msgRdPlayer.addString(player.getName().c_str());
    msgRdPlayer.addInt(player.getTeamId());
    rpcClient.write(msgRdPlayer,res);
    CD_INFO("rdServer response from login: %s\n",res.toString().c_str());

    //-- Check response
    if (std::strcmp(res.toString().c_str(), "[ok]") == 0)
        return true;
    else
        return false;
}

bool rd::YarpNetworkManager::logout()
{
    if (!started)
    {
        CD_ERROR("NetworkManager has not been started\n");
        return false;
    }

    CD_INFO("Logout...\n");
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_LOGOUT);
    msgRdPlayer.addInt(player.getId());
    rpcClient.write(msgRdPlayer,res);
    CD_INFO("rdServer response from logout: %s\n",res.toString().c_str());

    //-- Check response
    if (std::strcmp(res.toString().c_str(), "[ok]") == 0)
    {
        CD_SUCCESS("Logout ok\n");
        return true;
    }
    else
    {
        CD_ERROR("Logout failed\n");
        return false;
    }
}

bool rd::YarpNetworkManager::keepAlive()
{
    if (!started)
    {
        CD_ERROR("NetworkManager has not been started\n");
        return false;
    }

    CD_INFO("Keep alive...\n");
    yarp::os::Bottle msgRdPlayer,res;
    msgRdPlayer.addVocab(VOCAB_RD_KEEPALIVE);
    msgRdPlayer.addInt(player.getId());
    rpcClient.write(msgRdPlayer,res);

    //-- Check response
    if (std::strcmp(res.toString().c_str(), "[ok]") == 0)
    {
        return true;
    }
    else
    {
        CD_ERROR("Keep alive failed\n");
        return false;
    }
}
<|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 "base/command_line.h"
#include "base/message_loop_proxy.h"
#include "base/ref_counted.h"
#include "base/thread.h"
#include "base/waitable_event.h"
#include "chrome/common/net/url_fetcher_protect.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "chrome/service/service_process.h"
#include "chrome/service/cloud_print/cloud_print_url_fetcher.h"
#include "googleurl/src/gurl.h"
#include "net/test/test_server.h"
#include "net/url_request/url_request_unittest.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::Time;
using base::TimeDelta;

namespace {

const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");

int g_request_context_getter_instances = 0;
class TestURLRequestContextGetter : public URLRequestContextGetter {
 public:
  explicit TestURLRequestContextGetter(
      base::MessageLoopProxy* io_message_loop_proxy)
          : io_message_loop_proxy_(io_message_loop_proxy) {
    g_request_context_getter_instances++;
  }
  virtual URLRequestContext* GetURLRequestContext() {
    if (!context_)
      context_ = new TestURLRequestContext();
    return context_;
  }
  virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const {
    return io_message_loop_proxy_;
  }

 protected:
  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;

 private:
  ~TestURLRequestContextGetter() {
    g_request_context_getter_instances--;
  }

  scoped_refptr<URLRequestContext> context_;
};

class TestCloudPrintURLFetcher : public CloudPrintURLFetcher {
 public:
  explicit TestCloudPrintURLFetcher(
      base::MessageLoopProxy* io_message_loop_proxy)
          : io_message_loop_proxy_(io_message_loop_proxy) {
  }

  virtual URLRequestContextGetter* GetRequestContextGetter() {
    return new TestURLRequestContextGetter(io_message_loop_proxy_.get());
  }
 private:
  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
};

class CloudPrintURLFetcherTest : public testing::Test,
                                 public CloudPrintURLFetcher::Delegate {
 public:
  CloudPrintURLFetcherTest() : fetcher_(NULL) { }

  // Creates a URLFetcher, using the program's main thread to do IO.
  virtual void CreateFetcher(const GURL& url, const std::string& retry_policy);

  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse(
      const URLFetcher* source,
      const GURL& url,
      const URLRequestStatus& status,
      int response_code,
      const ResponseCookies& cookies,
      const std::string& data);

  virtual void OnRequestAuthError() {
    ADD_FAILURE();
  }

  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() {
    return io_message_loop_proxy_;
  }

 protected:
  virtual void SetUp() {
    testing::Test::SetUp();

    io_message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread();
  }

  virtual void TearDown() {
    fetcher_ = NULL;
    // Deleting the fetcher causes a task to be posted to the IO thread to
    // release references to the URLRequestContextGetter. We need to run all
    // pending tasks to execute that (this is the IO thread).
    MessageLoop::current()->RunAllPending();
    EXPECT_EQ(0, g_request_context_getter_instances);
  }

  // URLFetcher is designed to run on the main UI thread, but in our tests
  // we assume that the current thread is the IO thread where the URLFetcher
  // dispatches its requests to.  When we wish to simulate being used from
  // a UI thread, we dispatch a worker thread to do so.
  MessageLoopForIO io_loop_;
  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
  std::string retry_policy_;
  Time start_time_;
  scoped_refptr<CloudPrintURLFetcher> fetcher_;
};

class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest {
 public:
  CloudPrintURLFetcherBasicTest()
      : handle_raw_response_(false), handle_raw_data_(false) { }
  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse(
      const URLFetcher* source,
      const GURL& url,
      const URLRequestStatus& status,
      int response_code,
      const ResponseCookies& cookies,
      const std::string& data);

  virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
      const URLFetcher* source,
      const GURL& url,
      const std::string& data);

  virtual CloudPrintURLFetcher::ResponseAction HandleJSONData(
      const URLFetcher* source,
      const GURL& url,
      DictionaryValue* json_data,
      bool succeeded);

  void SetHandleRawResponse(bool handle_raw_response) {
    handle_raw_response_ = handle_raw_response;
  }
  void SetHandleRawData(bool handle_raw_data) {
    handle_raw_data_ = handle_raw_data;
  }
 private:
  bool handle_raw_response_;
  bool handle_raw_data_;
};

// Version of CloudPrintURLFetcherTest that tests overload protection.
class CloudPrintURLFetcherOverloadTest : public CloudPrintURLFetcherTest {
 public:
  CloudPrintURLFetcherOverloadTest() : response_count_(0) {
  }

  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
      const URLFetcher* source,
      const GURL& url,
      const std::string& data);

 private:
  int response_count_;
};

// Version of CloudPrintURLFetcherTest that tests backoff protection.
class CloudPrintURLFetcherRetryBackoffTest : public CloudPrintURLFetcherTest {
 public:
  CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) {
  }

  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
      const URLFetcher* source,
      const GURL& url,
      const std::string& data);

  virtual void OnRequestGiveUp();

 private:
  int response_count_;
};

void CloudPrintURLFetcherTest::CreateFetcher(const GURL& url,
                                             const std::string& retry_policy) {
  fetcher_ = new TestCloudPrintURLFetcher(io_message_loop_proxy());
  retry_policy_ = retry_policy;
  start_time_ = Time::Now();
  fetcher_->StartGetRequest(url, this, "", retry_policy_);
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherTest::HandleRawResponse(
    const URLFetcher* source,
    const GURL& url,
    const URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  EXPECT_TRUE(status.is_success());
  EXPECT_EQ(200, response_code);  // HTTP OK
  EXPECT_FALSE(data.empty());
  return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleRawResponse(
    const URLFetcher* source,
    const GURL& url,
    const URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  EXPECT_TRUE(status.is_success());
  EXPECT_EQ(200, response_code);  // HTTP OK
  EXPECT_FALSE(data.empty());

  if (handle_raw_response_) {
    // If the current message loop is not the IO loop, it will be shut down when
    // the main loop returns and this thread subsequently goes out of scope.
    io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
    return CloudPrintURLFetcher::STOP_PROCESSING;
  }
  return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleRawData(
    const URLFetcher* source,
    const GURL& url,
    const std::string& data) {
  // We should never get here if we returned true in HandleRawResponse
  EXPECT_FALSE(handle_raw_response_);
  if (handle_raw_data_) {
    io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
    return CloudPrintURLFetcher::STOP_PROCESSING;
  }
  return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleJSONData(
    const URLFetcher* source,
    const GURL& url,
    DictionaryValue* json_data,
    bool succeeded) {
  // We should never get here if we returned true in one of the above methods.
  EXPECT_FALSE(handle_raw_response_);
  EXPECT_FALSE(handle_raw_data_);
  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
  return CloudPrintURLFetcher::STOP_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherOverloadTest::HandleRawData(const URLFetcher* source,
                                                const GURL& url,
                                                const std::string& data) {
  const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
  response_count_++;
  if (response_count_ < 20) {
    fetcher_->StartGetRequest(url, this, "", retry_policy_);
  } else {
    // We have already sent 20 requests continuously. And we expect that
    // it takes more than 1 second due to the overload pretection settings.
    EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
    io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
  }
  return CloudPrintURLFetcher::STOP_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherRetryBackoffTest::HandleRawData(const URLFetcher* source,
                                                    const GURL& url,
                                                    const std::string& data) {
  response_count_++;
  // First attempt + 11 retries = 12 total responses.
  EXPECT_LE(response_count_, 12);
  return CloudPrintURLFetcher::RETRY_REQUEST;
}

void CloudPrintURLFetcherRetryBackoffTest::OnRequestGiveUp() {
  const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
  // It takes more than 1 second to finish all 11 requests.
  EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}

TEST_F(CloudPrintURLFetcherBasicTest, HandleRawResponse) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());
  SetHandleRawResponse(true);

  CreateFetcher(test_server.GetURL("echo"), "DummyRetryPolicy");
  MessageLoop::current()->Run();
}

TEST_F(CloudPrintURLFetcherBasicTest, HandleRawData) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());

  SetHandleRawData(true);
  CreateFetcher(test_server.GetURL("echo"), "DummyRetryPolicy");
  MessageLoop::current()->Run();
}

TEST_F(CloudPrintURLFetcherOverloadTest, Protect) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());

  GURL url(test_server.GetURL("defaultresponse"));

  // Registers an entry for test url. It only allows 3 requests to be sent
  // in 200 milliseconds.
  std::string retry_policy = "OverloadTestPolicy";
  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
  URLFetcherProtectEntry* entry =
      new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256);
  manager->Register(retry_policy, entry);

  CreateFetcher(url, retry_policy);

  MessageLoop::current()->Run();
}

TEST_F(CloudPrintURLFetcherRetryBackoffTest, GiveUp) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());

  GURL url(test_server.GetURL("defaultresponse"));

  // Registers an entry for test url. The backoff time is calculated by:
  //     new_backoff = 2.0 * old_backoff + 0
  // and maximum backoff time is 256 milliseconds.
  // Maximum retries allowed is set to 11.
  std::string retry_policy = "BackoffTestPolicy";
  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
  URLFetcherProtectEntry* entry =
      new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256);
  manager->Register(retry_policy, entry);

  CreateFetcher(url, retry_policy);

  MessageLoop::current()->Run();
}

}  // namespace.
<commit_msg>Mark the CloudPrintURLFetcherBasicTest.HandleRawData and CloudPrintURLFetcherRetryBackoffTest.GiveUp tests as flaky.<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 "base/command_line.h"
#include "base/message_loop_proxy.h"
#include "base/ref_counted.h"
#include "base/thread.h"
#include "base/waitable_event.h"
#include "chrome/common/net/url_fetcher_protect.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "chrome/service/service_process.h"
#include "chrome/service/cloud_print/cloud_print_url_fetcher.h"
#include "googleurl/src/gurl.h"
#include "net/test/test_server.h"
#include "net/url_request/url_request_unittest.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::Time;
using base::TimeDelta;

namespace {

const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");

int g_request_context_getter_instances = 0;
class TestURLRequestContextGetter : public URLRequestContextGetter {
 public:
  explicit TestURLRequestContextGetter(
      base::MessageLoopProxy* io_message_loop_proxy)
          : io_message_loop_proxy_(io_message_loop_proxy) {
    g_request_context_getter_instances++;
  }
  virtual URLRequestContext* GetURLRequestContext() {
    if (!context_)
      context_ = new TestURLRequestContext();
    return context_;
  }
  virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const {
    return io_message_loop_proxy_;
  }

 protected:
  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;

 private:
  ~TestURLRequestContextGetter() {
    g_request_context_getter_instances--;
  }

  scoped_refptr<URLRequestContext> context_;
};

class TestCloudPrintURLFetcher : public CloudPrintURLFetcher {
 public:
  explicit TestCloudPrintURLFetcher(
      base::MessageLoopProxy* io_message_loop_proxy)
          : io_message_loop_proxy_(io_message_loop_proxy) {
  }

  virtual URLRequestContextGetter* GetRequestContextGetter() {
    return new TestURLRequestContextGetter(io_message_loop_proxy_.get());
  }
 private:
  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
};

class CloudPrintURLFetcherTest : public testing::Test,
                                 public CloudPrintURLFetcher::Delegate {
 public:
  CloudPrintURLFetcherTest() : fetcher_(NULL) { }

  // Creates a URLFetcher, using the program's main thread to do IO.
  virtual void CreateFetcher(const GURL& url, const std::string& retry_policy);

  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse(
      const URLFetcher* source,
      const GURL& url,
      const URLRequestStatus& status,
      int response_code,
      const ResponseCookies& cookies,
      const std::string& data);

  virtual void OnRequestAuthError() {
    ADD_FAILURE();
  }

  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() {
    return io_message_loop_proxy_;
  }

 protected:
  virtual void SetUp() {
    testing::Test::SetUp();

    io_message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread();
  }

  virtual void TearDown() {
    fetcher_ = NULL;
    // Deleting the fetcher causes a task to be posted to the IO thread to
    // release references to the URLRequestContextGetter. We need to run all
    // pending tasks to execute that (this is the IO thread).
    MessageLoop::current()->RunAllPending();
    EXPECT_EQ(0, g_request_context_getter_instances);
  }

  // URLFetcher is designed to run on the main UI thread, but in our tests
  // we assume that the current thread is the IO thread where the URLFetcher
  // dispatches its requests to.  When we wish to simulate being used from
  // a UI thread, we dispatch a worker thread to do so.
  MessageLoopForIO io_loop_;
  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
  std::string retry_policy_;
  Time start_time_;
  scoped_refptr<CloudPrintURLFetcher> fetcher_;
};

class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest {
 public:
  CloudPrintURLFetcherBasicTest()
      : handle_raw_response_(false), handle_raw_data_(false) { }
  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse(
      const URLFetcher* source,
      const GURL& url,
      const URLRequestStatus& status,
      int response_code,
      const ResponseCookies& cookies,
      const std::string& data);

  virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
      const URLFetcher* source,
      const GURL& url,
      const std::string& data);

  virtual CloudPrintURLFetcher::ResponseAction HandleJSONData(
      const URLFetcher* source,
      const GURL& url,
      DictionaryValue* json_data,
      bool succeeded);

  void SetHandleRawResponse(bool handle_raw_response) {
    handle_raw_response_ = handle_raw_response;
  }
  void SetHandleRawData(bool handle_raw_data) {
    handle_raw_data_ = handle_raw_data;
  }
 private:
  bool handle_raw_response_;
  bool handle_raw_data_;
};

// Version of CloudPrintURLFetcherTest that tests overload protection.
class CloudPrintURLFetcherOverloadTest : public CloudPrintURLFetcherTest {
 public:
  CloudPrintURLFetcherOverloadTest() : response_count_(0) {
  }

  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
      const URLFetcher* source,
      const GURL& url,
      const std::string& data);

 private:
  int response_count_;
};

// Version of CloudPrintURLFetcherTest that tests backoff protection.
class CloudPrintURLFetcherRetryBackoffTest : public CloudPrintURLFetcherTest {
 public:
  CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) {
  }

  // CloudPrintURLFetcher::Delegate
  virtual CloudPrintURLFetcher::ResponseAction HandleRawData(
      const URLFetcher* source,
      const GURL& url,
      const std::string& data);

  virtual void OnRequestGiveUp();

 private:
  int response_count_;
};

void CloudPrintURLFetcherTest::CreateFetcher(const GURL& url,
                                             const std::string& retry_policy) {
  fetcher_ = new TestCloudPrintURLFetcher(io_message_loop_proxy());
  retry_policy_ = retry_policy;
  start_time_ = Time::Now();
  fetcher_->StartGetRequest(url, this, "", retry_policy_);
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherTest::HandleRawResponse(
    const URLFetcher* source,
    const GURL& url,
    const URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  EXPECT_TRUE(status.is_success());
  EXPECT_EQ(200, response_code);  // HTTP OK
  EXPECT_FALSE(data.empty());
  return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleRawResponse(
    const URLFetcher* source,
    const GURL& url,
    const URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  EXPECT_TRUE(status.is_success());
  EXPECT_EQ(200, response_code);  // HTTP OK
  EXPECT_FALSE(data.empty());

  if (handle_raw_response_) {
    // If the current message loop is not the IO loop, it will be shut down when
    // the main loop returns and this thread subsequently goes out of scope.
    io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
    return CloudPrintURLFetcher::STOP_PROCESSING;
  }
  return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleRawData(
    const URLFetcher* source,
    const GURL& url,
    const std::string& data) {
  // We should never get here if we returned true in HandleRawResponse
  EXPECT_FALSE(handle_raw_response_);
  if (handle_raw_data_) {
    io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
    return CloudPrintURLFetcher::STOP_PROCESSING;
  }
  return CloudPrintURLFetcher::CONTINUE_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherBasicTest::HandleJSONData(
    const URLFetcher* source,
    const GURL& url,
    DictionaryValue* json_data,
    bool succeeded) {
  // We should never get here if we returned true in one of the above methods.
  EXPECT_FALSE(handle_raw_response_);
  EXPECT_FALSE(handle_raw_data_);
  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
  return CloudPrintURLFetcher::STOP_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherOverloadTest::HandleRawData(const URLFetcher* source,
                                                const GURL& url,
                                                const std::string& data) {
  const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
  response_count_++;
  if (response_count_ < 20) {
    fetcher_->StartGetRequest(url, this, "", retry_policy_);
  } else {
    // We have already sent 20 requests continuously. And we expect that
    // it takes more than 1 second due to the overload pretection settings.
    EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
    io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
  }
  return CloudPrintURLFetcher::STOP_PROCESSING;
}

CloudPrintURLFetcher::ResponseAction
CloudPrintURLFetcherRetryBackoffTest::HandleRawData(const URLFetcher* source,
                                                    const GURL& url,
                                                    const std::string& data) {
  response_count_++;
  // First attempt + 11 retries = 12 total responses.
  EXPECT_LE(response_count_, 12);
  return CloudPrintURLFetcher::RETRY_REQUEST;
}

void CloudPrintURLFetcherRetryBackoffTest::OnRequestGiveUp() {
  const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
  // It takes more than 1 second to finish all 11 requests.
  EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}

TEST_F(CloudPrintURLFetcherBasicTest, HandleRawResponse) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());
  SetHandleRawResponse(true);

  CreateFetcher(test_server.GetURL("echo"), "DummyRetryPolicy");
  MessageLoop::current()->Run();
}

// http://code.google.com/p/chromium/issues/detail?id=62758
TEST_F(CloudPrintURLFetcherBasicTest, FLAKY_HandleRawData) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());

  SetHandleRawData(true);
  CreateFetcher(test_server.GetURL("echo"), "DummyRetryPolicy");
  MessageLoop::current()->Run();
}

TEST_F(CloudPrintURLFetcherOverloadTest, Protect) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());

  GURL url(test_server.GetURL("defaultresponse"));

  // Registers an entry for test url. It only allows 3 requests to be sent
  // in 200 milliseconds.
  std::string retry_policy = "OverloadTestPolicy";
  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
  URLFetcherProtectEntry* entry =
      new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256);
  manager->Register(retry_policy, entry);

  CreateFetcher(url, retry_policy);

  MessageLoop::current()->Run();
}

// http://code.google.com/p/chromium/issues/detail?id=62758
TEST_F(CloudPrintURLFetcherRetryBackoffTest, FLAKY_GiveUp) {
  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
  ASSERT_TRUE(test_server.Start());

  GURL url(test_server.GetURL("defaultresponse"));

  // Registers an entry for test url. The backoff time is calculated by:
  //     new_backoff = 2.0 * old_backoff + 0
  // and maximum backoff time is 256 milliseconds.
  // Maximum retries allowed is set to 11.
  std::string retry_policy = "BackoffTestPolicy";
  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
  URLFetcherProtectEntry* entry =
      new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256);
  manager->Register(retry_policy, entry);

  CreateFetcher(url, retry_policy);

  MessageLoop::current()->Run();
}

}  // namespace.
<|endoftext|>