<commit_before>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// object that creates and contains a spawn position

#include "common.h"

#include <string>

#include "SpawnPosition.h"
#include "DropGeometry.h"
#include "Obstacle.h"
#include "FlagInfo.h"
#include "TeamBases.h"
#include "WorldInfo.h"
#include "PlayerInfo.h"
#include "PlayerState.h"
#include "GameKeeper.h"
#include "BZDBCache.h"

// FIXME: from bzfs.cxx
extern int getCurMaxPlayers();
extern bool areFoes(TeamColor team1, TeamColor team2);
extern BasesList bases;
extern WorldInfo *world;
extern PlayerState lastState[];

SpawnPosition::SpawnPosition(int playerId, bool onGroundOnly, bool notNearEdges) :
		curMaxPlayers(getCurMaxPlayers())
{
  GameKeeper::Player *playerData
    = GameKeeper::Player::getPlayerByIndex(playerId);
  if (!playerData)
    return;

  team = playerData->player.getTeam();
  azimuth = (float)(bzfrand() * 2.0 * M_PI);

  if (playerData->player.shouldRestartAtBase() &&
      (team >= RedTeam) && (team <= PurpleTeam) &&
      (bases.find(team) != bases.end())) {
    TeamBases &teamBases = bases[team];
    const TeamBase &base = teamBases.getRandomBase((int)(bzfrand() * 100));
    base.getRandomPosition(pos[0], pos[1], pos[2]);
    playerData->player.setRestartOnBase(false);
  } else {
    const float tankRadius = BZDBCache::tankRadius;
    safeSWRadius = (float)((BZDB.eval(StateDatabase::BZDB_SHOCKOUTRADIUS) + BZDBCache::tankRadius) * 1.5);
    safeDistance = tankRadius * 20; // FIXME: is this a good value?
    const float size = BZDBCache::worldSize;
    const float maxWorldHeight = world->getMaxWorldHeight();

    // keep track of how much time we spend searching for a location
    TimeKeeper start = TimeKeeper::getCurrent();

    int tries = 0;
    float minProximity = size / 3.0f;
    float bestDist = -1.0f;
    bool foundspot = false;
    while (!foundspot) {
      if (!world->getZonePoint(std::string(Team::getName(team)), testPos)) {
	if (notNearEdges) {
	  // don't spawn close to map edges in CTF mode
	  testPos[0] = ((float)bzfrand() - 0.5f) * size * 0.6f;
	  testPos[1] = ((float)bzfrand() - 0.5f) * size * 0.6f;
	} else {
	  testPos[0] = ((float)bzfrand() - 0.5f) * (size - 2.0f * tankRadius);
	  testPos[1] = ((float)bzfrand() - 0.5f) * (size - 2.0f * tankRadius);
	}
	testPos[2] = onGroundOnly ? 0.0f : ((float)bzfrand() * maxWorldHeight);
      }
      tries++;

      const float waterLevel = world->getWaterLevel();
      float minZ = 0.0f;
      if (waterLevel > minZ) {
	minZ = waterLevel;
      }
      float maxZ = maxWorldHeight;
      if (onGroundOnly) {
	maxZ = 0.0f;
      }

      if (DropGeometry::dropPlayer(testPos, minZ, maxZ)) {
	foundspot = true;
      }

      // check every now and then if we have already used up 10ms of time
      if (tries >= 50) {
	tries = 0;
	if (TimeKeeper::getCurrent() - start > 0.01f) {
	  if (bestDist < 0.0f) { // haven't found a single spot
	    //Just drop the sucka in, and pray
	    pos[0] = testPos[0];
	    pos[1] = testPos[1];
	    pos[2] = maxWorldHeight;
	    DEBUG1("Warning: getSpawnLocation ran out of time, just dropping the sucker in\n");
	  }
	  break;
	}
      }

      // check if spot is safe enough
      bool dangerous = isImminentlyDangerous();
      if (foundspot && !dangerous) {
	float enemyAngle;
	float dist = enemyProximityCheck(enemyAngle);
	if (dist > bestDist) { // best so far
	  bestDist = dist;
	  pos[0] = testPos[0];
	  pos[1] = testPos[1];
	  pos[2] = testPos[2];
	  azimuth = fmod((float)(enemyAngle + M_PI), (float)(2.0 * M_PI));
	}
	if (bestDist < minProximity) { // not good enough, keep looking
	  foundspot = false;
	  minProximity *= 0.99f; // relax requirements a little
	}
      } else if (dangerous) {
	foundspot = false;
      }
    }
  }
}

SpawnPosition::~SpawnPosition()
{
}

const bool SpawnPosition::isFacing(const float *enemyPos, const float enemyAzimuth,
				   const float deviation) const
{
  // vector points from test to enemy
  float dx = enemyPos[0] - testPos[0];
  float dy = enemyPos[0] - testPos[1];
  float angActual = atan2f (dy, dx);
  float diff = fmodf(enemyAzimuth - angActual, (float)M_PI * 2.0f);

  // now diff is between {-PI*2 and +PI*2}, and we're looking for values around
  // -PI or +PI, because that's when the enemy is facing the source.
  diff = fabsf (diff); // between {0 and +PI*2}
  diff = fabsf ((float)(diff - M_PI));

  if (diff < (deviation / 2.0f)) {
    return true;
  } else {
    return false;
  }
}

const bool SpawnPosition::isImminentlyDangerous() const
{
  GameKeeper::Player *playerData;
  for (int i = 0; i < curMaxPlayers; i++) {
    playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if (playerData->player.isAlive()) {
      float *enemyPos = lastState[i].pos;
      float enemyAngle = lastState[i].azimuth;
      if (playerData->player.getFlag() >= 0) {
	// check for dangerous flags
	const FlagInfo *finfo = FlagInfo::get(playerData->player.getFlag());
	const FlagType *ftype = finfo->flag.type;
	// FIXME: any more?
	if (ftype == Flags::Laser) {  // don't spawn in the line of sight of an L
	  if (isFacing(enemyPos, enemyAngle, (float)(M_PI / 9.0))) { // he's looking within 20 degrees of spawn point
	    return true;	// eek, don't spawn here
	  }
	} else if (ftype == Flags::ShockWave) {  // don't spawn next to a SW
	  if (distanceFrom(enemyPos) < safeSWRadius) { // too close to SW
	    return true;	// eek, don't spawn here
	  }
	}
      }
      // don't spawn in the line of sight of a normal-shot tank within a certain distance
      if (distanceFrom(enemyPos) < safeDistance) { // within danger zone?
	if (isFacing(enemyPos, enemyAngle, (float)(M_PI / 9.0))) { //and he's looking at me
	  return true;
	}
      }
    }
  }

  // TODO: should check world weapons also

  return false;
}

const float SpawnPosition::enemyProximityCheck(float &enemyAngle) const
{
  GameKeeper::Player *playerData;
  float worstDist = 1e12f; // huge number
  bool noEnemy    = true;

  for (int i = 0; i < curMaxPlayers; i++) {
    playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if (playerData->player.isAlive()
	&& areFoes(playerData->player.getTeam(), team)) {
      float *enemyPos = lastState[i].pos;
      if (fabs(enemyPos[2] - testPos[2]) < 1.0f) {
	float x = enemyPos[0] - testPos[0];
	float y = enemyPos[1] - testPos[1];
	float distSq = x * x + y * y;
	if (distSq < worstDist) {
	  worstDist  = distSq;
	  enemyAngle = lastState[i].azimuth;
	  noEnemy    = false;
	}
      }
    }
  }
  if (noEnemy)
    enemyAngle = (float)(bzfrand() * 2.0 * M_PI);
  return sqrtf(worstDist);
}

const float SpawnPosition::distanceFrom(const float* farPos) const
{
  float dx = farPos[0] - testPos[0];
  float dy = farPos[1] - testPos[1];
  float dz = farPos[2] - testPos[2];
  return (float)sqrt(dx*dx + dy*dy + dz*dz);
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>make SpawnPosition use playerData->lastState rather than directly accessing the lastState[] array<commit_after>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// object that creates and contains a spawn position

#include "common.h"

#include <string>

#include "SpawnPosition.h"
#include "DropGeometry.h"
#include "Obstacle.h"
#include "FlagInfo.h"
#include "TeamBases.h"
#include "WorldInfo.h"
#include "PlayerInfo.h"
#include "PlayerState.h"
#include "GameKeeper.h"
#include "BZDBCache.h"

// FIXME: from bzfs.cxx
extern int getCurMaxPlayers();
extern bool areFoes(TeamColor team1, TeamColor team2);
extern BasesList bases;
extern WorldInfo *world;

SpawnPosition::SpawnPosition(int playerId, bool onGroundOnly, bool notNearEdges) :
		curMaxPlayers(getCurMaxPlayers())
{
  GameKeeper::Player *playerData
    = GameKeeper::Player::getPlayerByIndex(playerId);
  if (!playerData)
    return;

  team = playerData->player.getTeam();
  azimuth = (float)(bzfrand() * 2.0 * M_PI);

  if (playerData->player.shouldRestartAtBase() &&
      (team >= RedTeam) && (team <= PurpleTeam) &&
      (bases.find(team) != bases.end())) {
    TeamBases &teamBases = bases[team];
    const TeamBase &base = teamBases.getRandomBase((int)(bzfrand() * 100));
    base.getRandomPosition(pos[0], pos[1], pos[2]);
    playerData->player.setRestartOnBase(false);
  } else {
    const float tankRadius = BZDBCache::tankRadius;
    safeSWRadius = (float)((BZDB.eval(StateDatabase::BZDB_SHOCKOUTRADIUS) + BZDBCache::tankRadius) * 1.5);
    safeDistance = tankRadius * 20; // FIXME: is this a good value?
    const float size = BZDBCache::worldSize;
    const float maxWorldHeight = world->getMaxWorldHeight();

    // keep track of how much time we spend searching for a location
    TimeKeeper start = TimeKeeper::getCurrent();

    int tries = 0;
    float minProximity = size / 3.0f;
    float bestDist = -1.0f;
    bool foundspot = false;
    while (!foundspot) {
      if (!world->getZonePoint(std::string(Team::getName(team)), testPos)) {
	if (notNearEdges) {
	  // don't spawn close to map edges in CTF mode
	  testPos[0] = ((float)bzfrand() - 0.5f) * size * 0.6f;
	  testPos[1] = ((float)bzfrand() - 0.5f) * size * 0.6f;
	} else {
	  testPos[0] = ((float)bzfrand() - 0.5f) * (size - 2.0f * tankRadius);
	  testPos[1] = ((float)bzfrand() - 0.5f) * (size - 2.0f * tankRadius);
	}
	testPos[2] = onGroundOnly ? 0.0f : ((float)bzfrand() * maxWorldHeight);
      }
      tries++;

      const float waterLevel = world->getWaterLevel();
      float minZ = 0.0f;
      if (waterLevel > minZ) {
	minZ = waterLevel;
      }
      float maxZ = maxWorldHeight;
      if (onGroundOnly) {
	maxZ = 0.0f;
      }

      if (DropGeometry::dropPlayer(testPos, minZ, maxZ)) {
	foundspot = true;
      }

      // check every now and then if we have already used up 10ms of time
      if (tries >= 50) {
	tries = 0;
	if (TimeKeeper::getCurrent() - start > 0.01f) {
	  if (bestDist < 0.0f) { // haven't found a single spot
	    //Just drop the sucka in, and pray
	    pos[0] = testPos[0];
	    pos[1] = testPos[1];
	    pos[2] = maxWorldHeight;
	    DEBUG1("Warning: getSpawnLocation ran out of time, just dropping the sucker in\n");
	  }
	  break;
	}
      }

      // check if spot is safe enough
      bool dangerous = isImminentlyDangerous();
      if (foundspot && !dangerous) {
	float enemyAngle;
	float dist = enemyProximityCheck(enemyAngle);
	if (dist > bestDist) { // best so far
	  bestDist = dist;
	  pos[0] = testPos[0];
	  pos[1] = testPos[1];
	  pos[2] = testPos[2];
	  azimuth = fmod((float)(enemyAngle + M_PI), (float)(2.0 * M_PI));
	}
	if (bestDist < minProximity) { // not good enough, keep looking
	  foundspot = false;
	  minProximity *= 0.99f; // relax requirements a little
	}
      } else if (dangerous) {
	foundspot = false;
      }
    }
  }
}

SpawnPosition::~SpawnPosition()
{
}

const bool SpawnPosition::isFacing(const float *enemyPos, const float enemyAzimuth,
				   const float deviation) const
{
  // vector points from test to enemy
  float dx = enemyPos[0] - testPos[0];
  float dy = enemyPos[0] - testPos[1];
  float angActual = atan2f (dy, dx);
  float diff = fmodf(enemyAzimuth - angActual, (float)M_PI * 2.0f);

  // now diff is between {-PI*2 and +PI*2}, and we're looking for values around
  // -PI or +PI, because that's when the enemy is facing the source.
  diff = fabsf (diff); // between {0 and +PI*2}
  diff = fabsf ((float)(diff - M_PI));

  if (diff < (deviation / 2.0f)) {
    return true;
  } else {
    return false;
  }
}

const bool SpawnPosition::isImminentlyDangerous() const
{
  GameKeeper::Player *playerData;
  for (int i = 0; i < curMaxPlayers; i++) {
    playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if (playerData->player.isAlive()) {
      float *enemyPos = playerData->lastState->pos;
      float enemyAngle = playerData->lastState->azimuth;
      if (playerData->player.getFlag() >= 0) {
	// check for dangerous flags
	const FlagInfo *finfo = FlagInfo::get(playerData->player.getFlag());
	const FlagType *ftype = finfo->flag.type;
	// FIXME: any more?
	if (ftype == Flags::Laser) {  // don't spawn in the line of sight of an L
	  if (isFacing(enemyPos, enemyAngle, (float)(M_PI / 9.0))) { // he's looking within 20 degrees of spawn point
	    return true;	// eek, don't spawn here
	  }
	} else if (ftype == Flags::ShockWave) {  // don't spawn next to a SW
	  if (distanceFrom(enemyPos) < safeSWRadius) { // too close to SW
	    return true;	// eek, don't spawn here
	  }
	}
      }
      // don't spawn in the line of sight of a normal-shot tank within a certain distance
      if (distanceFrom(enemyPos) < safeDistance) { // within danger zone?
	if (isFacing(enemyPos, enemyAngle, (float)(M_PI / 9.0))) { //and he's looking at me
	  return true;
	}
      }
    }
  }

  // TODO: should check world weapons also

  return false;
}

const float SpawnPosition::enemyProximityCheck(float &enemyAngle) const
{
  GameKeeper::Player *playerData;
  float worstDist = 1e12f; // huge number
  bool noEnemy    = true;

  for (int i = 0; i < curMaxPlayers; i++) {
    playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if (playerData->player.isAlive()
	&& areFoes(playerData->player.getTeam(), team)) {
      float *enemyPos = playerData->lastState->pos;
      if (fabs(enemyPos[2] - testPos[2]) < 1.0f) {
	float x = enemyPos[0] - testPos[0];
	float y = enemyPos[1] - testPos[1];
	float distSq = x * x + y * y;
	if (distSq < worstDist) {
	  worstDist  = distSq;
	  enemyAngle = playerData->lastState->azimuth;
	  noEnemy    = false;
	}
      }
    }
  }
  if (noEnemy)
    enemyAngle = (float)(bzfrand() * 2.0 * M_PI);
  return sqrtf(worstDist);
}

const float SpawnPosition::distanceFrom(const float* farPos) const
{
  float dx = farPos[0] - testPos[0];
  float dy = farPos[1] - testPos[1];
  float dz = farPos[2] - testPos[2];
  return (float)sqrt(dx*dx + dy*dy + dz*dz);
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>#include "ShardQuorumProcessor.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "Application/Common/DatabaseConsts.h"
#include "Application/Common/ContextTransport.h"
#include "ShardServer.h"

ShardQuorumProcessor::ShardQuorumProcessor()
{
    prev = next = this;

    requestLeaseTimeout.SetCallable(MFUNC(ShardQuorumProcessor, OnRequestLeaseTimeout));
    requestLeaseTimeout.SetDelay(PRIMARYLEASE_REQUEST_TIMEOUT);

    leaseTimeout.SetCallable(MFUNC(ShardQuorumProcessor, OnLeaseTimeout));
    tryAppend.SetCallable(MFUNC(ShardQuorumProcessor, TryAppend));
    resumeAppend.SetCallable(MFUNC(ShardQuorumProcessor, OnResumeAppend));
}

void ShardQuorumProcessor::Init(ConfigQuorum* configQuorum, ShardServer* shardServer_)
{
    shardServer = shardServer_;
    catchupReader.Init(this);
    isPrimary = false;
    proposalID = 0;
    configID = 0;
    requestedLeaseExpireTime = 0;
    shardMessagesLength = 0;
    ownAppend = false;
    paxosID = 0;
    commandID = 0;
    quorumContext.Init(configQuorum, this);
    CONTEXT_TRANSPORT->AddQuorumContext(&quorumContext);
    EventLoop::Add(&requestLeaseTimeout);
}

ShardServer* ShardQuorumProcessor::GetShardServer()
{
    return shardServer;
}

bool ShardQuorumProcessor::IsPrimary()
{
    return isPrimary;
}

uint64_t ShardQuorumProcessor::GetQuorumID()
{
    return quorumContext.GetQuorumID();
}

uint64_t ShardQuorumProcessor::GetPaxosID()
{
    return quorumContext.GetPaxosID();
}

ConfigQuorum* ShardQuorumProcessor::GetConfigQuorum()
{
    return shardServer->GetConfigState()->GetQuorum(GetQuorumID());
}

void ShardQuorumProcessor::OnReceiveLease(ClusterMessage& message)
{
    if (proposalID != message.proposalID)
        return;

    SortedList<uint64_t>& shards = shardServer->GetConfigState()->GetQuorum(GetQuorumID())->shards;
    if (shards != message.shards)
        return;

    isPrimary = true;
    configID = message.configID;
    activeNodes = message.activeNodes;
    quorumContext.SetActiveNodes(activeNodes);

    shards = message.shards;

    leaseTimeout.SetExpireTime(requestedLeaseExpireTime);
    EventLoop::Reset(&leaseTimeout);

    quorumContext.OnLearnLease();

    if (message.watchingPaxosID && shardMessages.GetLength() == 0)
        quorumContext.AppendDummy();
}

void ShardQuorumProcessor::OnAppend(uint64_t paxosID_, ReadBuffer& value_, bool ownAppend_)
{
    paxosID = paxosID_;
    commandID = 0;
    valueBuffer.Write(value_);
    value.Wrap(valueBuffer);
    ownAppend = ownAppend_;

    OnResumeAppend();
}

void ShardQuorumProcessor::OnStartCatchup()
{
    CatchupMessage  msg;

    if (catchupReader.IsActive() || !quorumContext.IsLeaseKnown())
        return;

    quorumContext.StopReplication();

    msg.CatchupRequest(MY_NODEID, quorumContext.GetQuorumID());

    CONTEXT_TRANSPORT->SendQuorumMessage(
     quorumContext.GetLeaseOwner(), quorumContext.GetQuorumID(), msg);

    catchupReader.Begin();

    Log_Message("Catchup started from node %U", quorumContext.GetLeaseOwner());
}

void ShardQuorumProcessor::OnCatchupMessage(CatchupMessage& message)
{
    switch (message.type)
    {
        case CATCHUPMESSAGE_REQUEST:
            if (!catchupWriter.IsActive())
                catchupWriter.Begin(message);
            break;
        case CATCHUPMESSAGE_BEGIN_SHARD:
            if (catchupReader.IsActive())
                catchupReader.OnBeginShard(message);
            break;
        case CATCHUPMESSAGE_SET:
            if (catchupReader.IsActive())
                catchupReader.OnSet(message);
            break;
        case CATCHUPMESSAGE_DELETE:
            if (catchupReader.IsActive())
                catchupReader.OnDelete(message);
            break;
        case CATCHUPMESSAGE_COMMIT:
            if (catchupReader.IsActive())
            {
                catchupReader.OnCommit(message);
                quorumContext.OnCatchupComplete(message.paxosID); // this commits
                quorumContext.ContinueReplication();
            }
            break;
        case CATCHUPMESSAGE_ABORT:
            if (catchupReader.IsActive())
            {
                catchupReader.OnAbort(message);
                quorumContext.ContinueReplication();
            }
            break;
        default:
            ASSERT_FAIL();
    }
}

bool ShardQuorumProcessor::IsPaxosBlocked()
{
    return resumeAppend.IsActive();
}

void ShardQuorumProcessor::OnResumeAppend()
{
    int             read;
    uint64_t        valueLength;
    uint64_t        start;
    ShardMessage    message;

    Log_Trace();

    valueLength = value.GetLength();

    start = NowClock();
    while (value.GetLength() > 0)
    {
        read = message.Read(value);
        assert(read > 0);
        value.Advance(read);

        ExecuteMessage(message, paxosID, commandID, ownAppend);
        commandID++;

        if (NowClock() - start >= YIELD_TIME)
        {
            // let other code run every YIELD_TIME msec
            if (resumeAppend.IsActive())
                STOP_FAIL(1, "Program bug: resumeAppend.IsActive() should be false.");
            EventLoop::Add(&resumeAppend);
            return;
        }
    }

    // TODO:
    if (ownAppend && shardMessages.GetLength() > 0)
    {
        shardMessagesLength -= valueLength;
        Log_Trace("shardMessagesLength: %U", shardMessagesLength);
        assert(shardMessagesLength >= 0);
    }

    ownAppend = false;
    paxosID = 0;
    commandID = 0;
    valueBuffer.Reset();
    value.Reset();

    if (!tryAppend.IsActive() && shardMessages.GetLength() > 0)
        EventLoop::Add(&tryAppend);

    quorumContext.OnAppendComplete();
}

void ShardQuorumProcessor::OnRequestLeaseTimeout()
{
    ClusterMessage msg;

    Log_Trace();

    proposalID = REPLICATION_CONFIG->NextProposalID(proposalID);
    msg.RequestLease(MY_NODEID, quorumContext.GetQuorumID(), proposalID,
     GetPaxosID(), configID, PAXOSLEASE_MAX_LEASE_TIME);

    shardServer->BroadcastToControllers(msg);

    requestedLeaseExpireTime = EventLoop::Now() + PAXOSLEASE_MAX_LEASE_TIME;
    if (!requestLeaseTimeout.IsActive())
        EventLoop::Add(&requestLeaseTimeout);
}

void ShardQuorumProcessor::OnLeaseTimeout()
{
    ClientRequest*  itRequest;
    ShardMessage*   itMessage;

    for (itRequest = clientRequests.First(); itRequest != NULL; itRequest = clientRequests.First())
    {
        clientRequests.Remove(itRequest);
        itRequest->response.NoService();
        itRequest->OnComplete();
    }

    for (itMessage = shardMessages.First(); itMessage != NULL; itMessage = shardMessages.First())
    {
        shardMessages.Remove(itMessage);
        delete itMessage;
    }

    shardMessagesLength = 0;

    isPrimary = false;
}

void ShardQuorumProcessor::OnClientRequest(ClientRequest* request)
{
    ShardMessage*   message;
    Buffer          singleBuffer;

    if (!quorumContext.IsLeader())
    {
        Log_Trace();
        request->response.Failed();
        request->OnComplete();
        return;
    }

    if (request->type == CLIENTREQUEST_GET)
    {
        shardServer->GetDatabaseManager()->OnClientReadRequest(request);
        return;
    }

    if (request->type == CLIENTREQUEST_SUBMIT)
    {
        if (!tryAppend.IsActive() && shardMessages.GetLength() > 0)
            EventLoop::Add(&tryAppend);
        request->response.NoResponse();
        request->OnComplete();
        return;
    }

    message = new ShardMessage;
    TransformRequest(request, message);

    Log_Trace("message.type = %c, message.key = %R", message->type, &message->key);

    clientRequests.Append(request);
    shardMessages.Append(message);

    message->Write(singleBuffer);
    shardMessagesLength += singleBuffer.GetLength();

    assert(shardMessagesLength >= 0);

    if (!tryAppend.IsActive())
    {
        Log_Trace("shardMessagesLength: %U", shardMessagesLength);
        EventLoop::Add(&tryAppend);
    }
}

void ShardQuorumProcessor::OnClientClose(ClientSession* /*session*/)
{
}

void ShardQuorumProcessor::SetActiveNodes(List<uint64_t>& activeNodes)
{
    quorumContext.SetActiveNodes(activeNodes);
}

void ShardQuorumProcessor::RegisterPaxosID(uint64_t paxosID)
{
    quorumContext.RegisterPaxosID(paxosID);
}

void ShardQuorumProcessor::TryReplicationCatchup()
{
    // this is called if we're an inactive node and we should probably try to catchup

    if (catchupReader.IsActive())
        return;

    quorumContext.TryReplicationCatchup();
}

void ShardQuorumProcessor::TrySplitShard(uint64_t shardID, uint64_t newShardID, ReadBuffer& splitKey)
{
    ShardMessage*   it;
    Buffer          singleBuffer;

    FOREACH(it, shardMessages)
    {
        if (it->type == SHARDMESSAGE_SPLIT_SHARD && it->shardID == shardID)
        {
            Log_Trace("Not appending shard split");
            return;
        }
    }

    Log_Trace("Appending shard split");

    it = new ShardMessage;
    it->SplitShard(shardID, newShardID, splitKey);
    it->fromClient = false;
    shardMessages.Append(it);

    it->Write(singleBuffer);
    shardMessagesLength += singleBuffer.GetLength();

    if (!tryAppend.IsActive() && shardMessages.GetLength() > 0)
        EventLoop::Add(&tryAppend);
}

void ShardQuorumProcessor::TransformRequest(ClientRequest* request, ShardMessage* message)
{
    message->fromClient = true;

    switch (request->type)
    {
        case CLIENTREQUEST_SET:
            message->type = SHARDMESSAGE_SET;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_SET_IF_NOT_EXISTS:
            message->type = SHARDMESSAGE_SET_IF_NOT_EXISTS;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_TEST_AND_SET:
            message->type = SHARDMESSAGE_TEST_AND_SET;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->test.Wrap(request->test);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_GET_AND_SET:
            message->type = SHARDMESSAGE_GET_AND_SET;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_ADD:
            message->type = SHARDMESSAGE_ADD;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->number = request->number;
            break;
        case CLIENTREQUEST_APPEND:
            message->type = SHARDMESSAGE_APPEND;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_DELETE:
            message->type = SHARDMESSAGE_DELETE;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            break;
        case CLIENTREQUEST_REMOVE:
            message->type = SHARDMESSAGE_REMOVE;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            break;
        default:
            ASSERT_FAIL();
    }
}

void ShardQuorumProcessor::ExecuteMessage(ShardMessage& message,
 uint64_t paxosID, uint64_t commandID, bool ownAppend)
{
    ClientRequest*  request;
    bool            fromClient;

    request = NULL;

    // TODO: this is not a complete solution
    if (shardMessages.GetLength() == 0)
    {
        shardServer->GetDatabaseManager()->ExecuteMessage(paxosID, commandID, message, request);
        return;
    }

    if (ownAppend)
        fromClient = shardMessages.First()->fromClient;
    else
        fromClient = false;

    if (ownAppend && fromClient)
    {
        assert(shardMessages.GetLength() > 0);
        assert(shardMessages.First()->type == message.type);
        assert(clientRequests.GetLength() > 0);
        request = clientRequests.First();
        request->response.OK();
    }

    shardServer->GetDatabaseManager()->ExecuteMessage(paxosID, commandID, message, request);

    if (ownAppend)
    {
        if (fromClient)
        {
            clientRequests.Remove(request);
            request->OnComplete(); // request deletes itself
        }
        shardMessages.Delete(shardMessages.First());
    }
}

void ShardQuorumProcessor::TryAppend()
{
    Buffer          singleBuffer;
    ShardMessage*   it;
    bool            first;
    unsigned        numMessages;

    if (shardMessages.GetLength() == 0)
        return;

    if (resumeAppend.IsActive())
    {
        EventLoop::Add(&tryAppend);
        return;
    }

    numMessages = 0;

    if (!quorumContext.IsAppending() && shardMessages.GetLength() > 0)
    {
        Buffer& value = quorumContext.GetNextValue();

        first = true;
        FOREACH(it, shardMessages)
        {
            // make sure split shard messages are replicated by themselves
            if (!first && it->type == SHARDMESSAGE_SPLIT_SHARD)
                break;

            if (first)
                first = false;

            singleBuffer.Clear();
            it->Write(singleBuffer);
            if (value.GetLength() + 1 + singleBuffer.GetLength() < DATABASE_REPLICATION_SIZE)
            {
                value.Appendf("%B", &singleBuffer);
                numMessages++;
                // make sure split shard messages are replicated by themselves
                if (it->type == SHARDMESSAGE_SPLIT_SHARD)
                    break;
            }
            else
                break;
        }

        Log_Debug("numMessages = %u", numMessages);

        assert(value.GetLength() > 0);

        quorumContext.Append();
    }
}
<commit_msg>Improved client failover.<commit_after>#include "ShardQuorumProcessor.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "Application/Common/DatabaseConsts.h"
#include "Application/Common/ContextTransport.h"
#include "ShardServer.h"

ShardQuorumProcessor::ShardQuorumProcessor()
{
    prev = next = this;

    requestLeaseTimeout.SetCallable(MFUNC(ShardQuorumProcessor, OnRequestLeaseTimeout));
    requestLeaseTimeout.SetDelay(PRIMARYLEASE_REQUEST_TIMEOUT);

    leaseTimeout.SetCallable(MFUNC(ShardQuorumProcessor, OnLeaseTimeout));
    tryAppend.SetCallable(MFUNC(ShardQuorumProcessor, TryAppend));
    resumeAppend.SetCallable(MFUNC(ShardQuorumProcessor, OnResumeAppend));
}

void ShardQuorumProcessor::Init(ConfigQuorum* configQuorum, ShardServer* shardServer_)
{
    shardServer = shardServer_;
    catchupReader.Init(this);
    isPrimary = false;
    proposalID = 0;
    configID = 0;
    requestedLeaseExpireTime = 0;
    shardMessagesLength = 0;
    ownAppend = false;
    paxosID = 0;
    commandID = 0;
    quorumContext.Init(configQuorum, this);
    CONTEXT_TRANSPORT->AddQuorumContext(&quorumContext);
    EventLoop::Add(&requestLeaseTimeout);
}

ShardServer* ShardQuorumProcessor::GetShardServer()
{
    return shardServer;
}

bool ShardQuorumProcessor::IsPrimary()
{
    return isPrimary;
}

uint64_t ShardQuorumProcessor::GetQuorumID()
{
    return quorumContext.GetQuorumID();
}

uint64_t ShardQuorumProcessor::GetPaxosID()
{
    return quorumContext.GetPaxosID();
}

ConfigQuorum* ShardQuorumProcessor::GetConfigQuorum()
{
    return shardServer->GetConfigState()->GetQuorum(GetQuorumID());
}

void ShardQuorumProcessor::OnReceiveLease(ClusterMessage& message)
{
    if (proposalID != message.proposalID)
        return;

    Log_Debug("received lease for quorum %U", GetQuorumID());

    SortedList<uint64_t>& shards = shardServer->GetConfigState()->GetQuorum(GetQuorumID())->shards;
    if (shards != message.shards)
        return;

    isPrimary = true;
    configID = message.configID;
    activeNodes = message.activeNodes;
    quorumContext.SetActiveNodes(activeNodes);

    shards = message.shards;

    leaseTimeout.SetExpireTime(requestedLeaseExpireTime);
    EventLoop::Reset(&leaseTimeout);

    quorumContext.OnLearnLease();

    if (message.watchingPaxosID && shardMessages.GetLength() == 0)
        quorumContext.AppendDummy();
}

void ShardQuorumProcessor::OnAppend(uint64_t paxosID_, ReadBuffer& value_, bool ownAppend_)
{
    paxosID = paxosID_;
    commandID = 0;
    valueBuffer.Write(value_);
    value.Wrap(valueBuffer);
    ownAppend = ownAppend_;

    OnResumeAppend();
}

void ShardQuorumProcessor::OnStartCatchup()
{
    CatchupMessage  msg;

    if (catchupReader.IsActive() || !quorumContext.IsLeaseKnown())
        return;

    quorumContext.StopReplication();

    msg.CatchupRequest(MY_NODEID, quorumContext.GetQuorumID());

    CONTEXT_TRANSPORT->SendQuorumMessage(
     quorumContext.GetLeaseOwner(), quorumContext.GetQuorumID(), msg);

    catchupReader.Begin();

    Log_Message("Catchup started from node %U", quorumContext.GetLeaseOwner());
}

void ShardQuorumProcessor::OnCatchupMessage(CatchupMessage& message)
{
    switch (message.type)
    {
        case CATCHUPMESSAGE_REQUEST:
            if (!catchupWriter.IsActive())
                catchupWriter.Begin(message);
            break;
        case CATCHUPMESSAGE_BEGIN_SHARD:
            if (catchupReader.IsActive())
                catchupReader.OnBeginShard(message);
            break;
        case CATCHUPMESSAGE_SET:
            if (catchupReader.IsActive())
                catchupReader.OnSet(message);
            break;
        case CATCHUPMESSAGE_DELETE:
            if (catchupReader.IsActive())
                catchupReader.OnDelete(message);
            break;
        case CATCHUPMESSAGE_COMMIT:
            if (catchupReader.IsActive())
            {
                catchupReader.OnCommit(message);
                quorumContext.OnCatchupComplete(message.paxosID); // this commits
                quorumContext.ContinueReplication();
            }
            break;
        case CATCHUPMESSAGE_ABORT:
            if (catchupReader.IsActive())
            {
                catchupReader.OnAbort(message);
                quorumContext.ContinueReplication();
            }
            break;
        default:
            ASSERT_FAIL();
    }
}

bool ShardQuorumProcessor::IsPaxosBlocked()
{
    return resumeAppend.IsActive();
}

void ShardQuorumProcessor::OnResumeAppend()
{
    int             read;
    uint64_t        valueLength;
    uint64_t        start;
    ShardMessage    message;

    Log_Trace();

    valueLength = value.GetLength();

    start = NowClock();
    while (value.GetLength() > 0)
    {
        read = message.Read(value);
        assert(read > 0);
        value.Advance(read);

        ExecuteMessage(message, paxosID, commandID, ownAppend);
        commandID++;

        if (NowClock() - start >= YIELD_TIME)
        {
            // let other code run every YIELD_TIME msec
            if (resumeAppend.IsActive())
                STOP_FAIL(1, "Program bug: resumeAppend.IsActive() should be false.");
            EventLoop::Add(&resumeAppend);
            return;
        }
    }

    // TODO:
    if (ownAppend && shardMessages.GetLength() > 0)
    {
        shardMessagesLength -= valueLength;
        Log_Trace("shardMessagesLength: %U", shardMessagesLength);
        assert(shardMessagesLength >= 0);
    }

    ownAppend = false;
    paxosID = 0;
    commandID = 0;
    valueBuffer.Reset();
    value.Reset();

    if (!tryAppend.IsActive() && shardMessages.GetLength() > 0)
        EventLoop::Add(&tryAppend);

    quorumContext.OnAppendComplete();
}

void ShardQuorumProcessor::OnRequestLeaseTimeout()
{
    ClusterMessage msg;

    Log_Trace();

    proposalID = REPLICATION_CONFIG->NextProposalID(proposalID);
    msg.RequestLease(MY_NODEID, quorumContext.GetQuorumID(), proposalID,
     GetPaxosID(), configID, PAXOSLEASE_MAX_LEASE_TIME);

    shardServer->BroadcastToControllers(msg);

    requestedLeaseExpireTime = EventLoop::Now() + PAXOSLEASE_MAX_LEASE_TIME;
    if (!requestLeaseTimeout.IsActive())
        EventLoop::Add(&requestLeaseTimeout);
}

void ShardQuorumProcessor::OnLeaseTimeout()
{
    ClientRequest*  itRequest;
    ShardMessage*   itMessage;

    for (itRequest = clientRequests.First(); itRequest != NULL; itRequest = clientRequests.First())
    {
        clientRequests.Remove(itRequest);
        itRequest->response.NoService();
        itRequest->OnComplete();
    }

    for (itMessage = shardMessages.First(); itMessage != NULL; itMessage = shardMessages.First())
    {
        shardMessages.Remove(itMessage);
        delete itMessage;
    }

    shardMessagesLength = 0;

    isPrimary = false;
}

void ShardQuorumProcessor::OnClientRequest(ClientRequest* request)
{
    ShardMessage*   message;
    Buffer          singleBuffer;

    if (!quorumContext.IsLeader())
    {
        Log_Trace();
        request->response.Failed();
        request->OnComplete();
        return;
    }

    if (request->type == CLIENTREQUEST_GET)
    {
        shardServer->GetDatabaseManager()->OnClientReadRequest(request);
        return;
    }

    if (request->type == CLIENTREQUEST_SUBMIT)
    {
        if (!tryAppend.IsActive() && shardMessages.GetLength() > 0)
            EventLoop::Add(&tryAppend);
        request->response.NoResponse();
        request->OnComplete();
        return;
    }

    message = new ShardMessage;
    TransformRequest(request, message);

    Log_Trace("message.type = %c, message.key = %R", message->type, &message->key);

    clientRequests.Append(request);
    shardMessages.Append(message);

    message->Write(singleBuffer);
    shardMessagesLength += singleBuffer.GetLength();

    assert(shardMessagesLength >= 0);

    if (!tryAppend.IsActive())
    {
        Log_Trace("shardMessagesLength: %U", shardMessagesLength);
        EventLoop::Add(&tryAppend);
    }
}

void ShardQuorumProcessor::OnClientClose(ClientSession* /*session*/)
{
}

void ShardQuorumProcessor::SetActiveNodes(List<uint64_t>& activeNodes)
{
    quorumContext.SetActiveNodes(activeNodes);
}

void ShardQuorumProcessor::RegisterPaxosID(uint64_t paxosID)
{
    quorumContext.RegisterPaxosID(paxosID);
}

void ShardQuorumProcessor::TryReplicationCatchup()
{
    // this is called if we're an inactive node and we should probably try to catchup

    if (catchupReader.IsActive())
        return;

    quorumContext.TryReplicationCatchup();
}

void ShardQuorumProcessor::TrySplitShard(uint64_t shardID, uint64_t newShardID, ReadBuffer& splitKey)
{
    ShardMessage*   it;
    Buffer          singleBuffer;

    FOREACH(it, shardMessages)
    {
        if (it->type == SHARDMESSAGE_SPLIT_SHARD && it->shardID == shardID)
        {
            Log_Trace("Not appending shard split");
            return;
        }
    }

    Log_Trace("Appending shard split");

    it = new ShardMessage;
    it->SplitShard(shardID, newShardID, splitKey);
    it->fromClient = false;
    shardMessages.Append(it);

    it->Write(singleBuffer);
    shardMessagesLength += singleBuffer.GetLength();

    if (!tryAppend.IsActive() && shardMessages.GetLength() > 0)
        EventLoop::Add(&tryAppend);
}

void ShardQuorumProcessor::TransformRequest(ClientRequest* request, ShardMessage* message)
{
    message->fromClient = true;

    switch (request->type)
    {
        case CLIENTREQUEST_SET:
            message->type = SHARDMESSAGE_SET;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_SET_IF_NOT_EXISTS:
            message->type = SHARDMESSAGE_SET_IF_NOT_EXISTS;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_TEST_AND_SET:
            message->type = SHARDMESSAGE_TEST_AND_SET;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->test.Wrap(request->test);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_GET_AND_SET:
            message->type = SHARDMESSAGE_GET_AND_SET;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_ADD:
            message->type = SHARDMESSAGE_ADD;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->number = request->number;
            break;
        case CLIENTREQUEST_APPEND:
            message->type = SHARDMESSAGE_APPEND;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            message->value.Wrap(request->value);
            break;
        case CLIENTREQUEST_DELETE:
            message->type = SHARDMESSAGE_DELETE;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            break;
        case CLIENTREQUEST_REMOVE:
            message->type = SHARDMESSAGE_REMOVE;
            message->tableID = request->tableID;
            message->key.Wrap(request->key);
            break;
        default:
            ASSERT_FAIL();
    }
}

void ShardQuorumProcessor::ExecuteMessage(ShardMessage& message,
 uint64_t paxosID, uint64_t commandID, bool ownAppend)
{
    ClientRequest*  request;
    bool            fromClient;

    request = NULL;

    // TODO: this is not a complete solution
    if (shardMessages.GetLength() == 0)
    {
        shardServer->GetDatabaseManager()->ExecuteMessage(paxosID, commandID, message, request);
        return;
    }

    if (ownAppend)
        fromClient = shardMessages.First()->fromClient;
    else
        fromClient = false;

    if (ownAppend && fromClient)
    {
        assert(shardMessages.GetLength() > 0);
        assert(shardMessages.First()->type == message.type);
        assert(clientRequests.GetLength() > 0);
        request = clientRequests.First();
        request->response.OK();
    }

    shardServer->GetDatabaseManager()->ExecuteMessage(paxosID, commandID, message, request);

    if (ownAppend)
    {
        if (fromClient)
        {
            clientRequests.Remove(request);
            request->OnComplete(); // request deletes itself
        }
        shardMessages.Delete(shardMessages.First());
    }
}

void ShardQuorumProcessor::TryAppend()
{
    Buffer          singleBuffer;
    ShardMessage*   it;
    bool            first;
    unsigned        numMessages;

    if (shardMessages.GetLength() == 0)
        return;

    if (resumeAppend.IsActive())
    {
        EventLoop::Add(&tryAppend);
        return;
    }

    numMessages = 0;

    if (!quorumContext.IsAppending() && shardMessages.GetLength() > 0)
    {
        Buffer& value = quorumContext.GetNextValue();

        first = true;
        FOREACH(it, shardMessages)
        {
            // make sure split shard messages are replicated by themselves
            if (!first && it->type == SHARDMESSAGE_SPLIT_SHARD)
                break;

            if (first)
                first = false;

            singleBuffer.Clear();
            it->Write(singleBuffer);
            if (value.GetLength() + 1 + singleBuffer.GetLength() < DATABASE_REPLICATION_SIZE)
            {
                value.Appendf("%B", &singleBuffer);
                numMessages++;
                // make sure split shard messages are replicated by themselves
                if (it->type == SHARDMESSAGE_SPLIT_SHARD)
                    break;
            }
            else
                break;
        }

        Log_Debug("numMessages = %u", numMessages);

        assert(value.GetLength() > 0);

        quorumContext.Append();
    }
}
<|endoftext|>


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

#ifndef PARAMETER_FIXTURES_HPP_
#define PARAMETER_FIXTURES_HPP_

#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include "gtest/gtest.h"

#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rclcpp/rclcpp.hpp"

const double test_epsilon = 1e-6;

void set_test_parameters(
  std::shared_ptr<rclcpp::parameter_client::SyncParametersClient> parameters_client)
{
  printf("Setting parameters\n");
  // Set several differnet types of parameters.
  auto set_parameters_results = parameters_client->set_parameters({
    rclcpp::parameter::ParameterVariant("foo", 2),
    rclcpp::parameter::ParameterVariant("bar", "hello"),
    rclcpp::parameter::ParameterVariant("barstr", std::string("hello_str")),
    rclcpp::parameter::ParameterVariant("baz", 1.45),
    rclcpp::parameter::ParameterVariant("foo.first", 8),
    rclcpp::parameter::ParameterVariant("foo.second", 42),
    rclcpp::parameter::ParameterVariant("foobar", true),
  });
  printf("Got set_parameters result\n");

  // Check to see if they were set.
  for (auto & result : set_parameters_results) {
    ASSERT_TRUE(result.successful);
  }
}

void verify_set_parameters_async(
  std::shared_ptr<rclcpp::Node> node,
  std::shared_ptr<rclcpp::parameter_client::AsyncParametersClient> parameters_client)
{
  printf("Setting parameters\n");
  // Set several differnet types of parameters.
  auto set_parameters_results = parameters_client->set_parameters({
    rclcpp::parameter::ParameterVariant("foo", 2),
    rclcpp::parameter::ParameterVariant("bar", "hello"),
    rclcpp::parameter::ParameterVariant("barstr", std::string("hello_str")),
    rclcpp::parameter::ParameterVariant("baz", 1.45),
    rclcpp::parameter::ParameterVariant("foo.first", 8),
    rclcpp::parameter::ParameterVariant("foo.second", 42),
    rclcpp::parameter::ParameterVariant("foobar", true),
  });
  rclcpp::spin_until_future_complete(node, set_parameters_results);  // Wait for the results.
  printf("Got set_parameters result\n");

  // Check to see if they were set.
  for (auto & result : set_parameters_results.get()) {
    ASSERT_TRUE(result.successful);
  }
}

void verify_test_parameters(
  std::shared_ptr<rclcpp::parameter_client::SyncParametersClient> parameters_client)
{
  printf("Listing parameters with recursive depth\n");
  // Test recursive depth (=0)
  auto parameters_and_prefixes = parameters_client->list_parameters({"foo", "bar"},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  for (auto & name : parameters_and_prefixes.names) {
    EXPECT_TRUE(name == "foo" || name == "bar" || name == "foo.first" || name == "foo.second");
  }
  for (auto & prefix : parameters_and_prefixes.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Listing parameters with depth of 1\n");
  // Test different depth
  auto parameters_and_prefixes4 = parameters_client->list_parameters({"foo"}, 1);
  for (auto & name : parameters_and_prefixes4.names) {
    EXPECT_EQ(name, "foo");
  }
  for (auto & prefix : parameters_and_prefixes4.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Listing parameters with depth of 2\n");
  // Test different depth
  auto parameters_and_prefixes5 = parameters_client->list_parameters({"foo"}, 2);
  for (auto & name : parameters_and_prefixes5.names) {
    EXPECT_TRUE(name == "foo" || name == "foo.first" || name == "foo.second");
  }
  for (auto & prefix : parameters_and_prefixes5.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Getting parameters\n");
  // Get a few of the parameters just set.
  for (auto & parameter : parameters_client->get_parameters({"foo", "bar", "baz"})) {
    // std::cout << "Parameter is:" << std::endl << parameter.to_yaml() << std::endl;
    if (parameter.get_name() == "foo") {
      EXPECT_STREQ(
        "{\"name\": \"foo\", \"type\": \"integer\", \"value\": \"2\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("integer", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "bar") {
      EXPECT_STREQ(
        "{\"name\": \"bar\", \"type\": \"string\", \"value\": \"hello\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("string", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "baz") {
      EXPECT_STREQ("double", parameter.get_type_name().c_str());
      EXPECT_NEAR(1.45, parameter.as_double(), test_epsilon);
    } else {
      ASSERT_FALSE("you should never hit this");
    }
  }

  printf("Getting nonexistent parameters\n");
  // Get a few non existant parameters
  for (auto & parameter : parameters_client->get_parameters({"not_foo", "not_baz"})) {
    EXPECT_STREQ("There should be no matches", parameter.get_name().c_str());
  }

  printf("Listing parameters with recursive depth\n");
  // List all of the parameters, using an empty prefix list and depth=0
  parameters_and_prefixes = parameters_client->list_parameters({},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  std::vector<std::string> all_names = {
    "foo", "bar", "barstr", "baz", "foo.first", "foo.second", "foobar"
  };
  EXPECT_EQ(parameters_and_prefixes.names.size(), all_names.size());
  for (auto & name : all_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
  printf("Listing parameters with depth 100\n");
  // List all of the parameters, using an empty prefix list and large depth
  parameters_and_prefixes = parameters_client->list_parameters({}, 100);
  EXPECT_EQ(parameters_and_prefixes.names.size(), all_names.size());
  for (auto & name : all_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
  printf("Listing parameters with depth 1\n");
  // List most of the parameters, using an empty prefix list and depth=1
  parameters_and_prefixes = parameters_client->list_parameters({}, 1);
  std::vector<std::string> depth_one_names = {
    "foo", "bar", "barstr", "baz", "foobar"
  };
  EXPECT_EQ(parameters_and_prefixes.names.size(), depth_one_names.size());
  for (auto & name : depth_one_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
}

void verify_get_parameters_async(
  std::shared_ptr<rclcpp::Node> node,
  std::shared_ptr<rclcpp::parameter_client::AsyncParametersClient> parameters_client)
{
  printf("Listing parameters with recursive depth\n");
  // Test recursive depth (=0)
  auto result = parameters_client->list_parameters({"foo", "bar"},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  rclcpp::spin_until_future_complete(node, result);
  auto parameters_and_prefixes = result.get();
  for (auto & name : parameters_and_prefixes.names) {
    EXPECT_TRUE(name == "foo" || name == "bar" || name == "foo.first" || name == "foo.second");
  }
  for (auto & prefix : parameters_and_prefixes.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Listing parameters with depth 1\n");
  // Test different depth
  auto result4 = parameters_client->list_parameters({"foo"}, 1);
  rclcpp::spin_until_future_complete(node, result4);
  auto parameters_and_prefixes4 = result4.get();
  for (auto & name : parameters_and_prefixes4.names) {
    EXPECT_EQ(name, "foo");
  }
  for (auto & prefix : parameters_and_prefixes4.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  // Test different depth
  printf("Listing parameters with depth 2\n");
  auto result4a = parameters_client->list_parameters({"foo"}, 2);
  rclcpp::spin_until_future_complete(node, result4a);
  auto parameters_and_prefixes4a = result4a.get();
  for (auto & name : parameters_and_prefixes4a.names) {
    EXPECT_TRUE(name == "foo" || name == "foo.first" || name == "foo.second");
  }
  for (auto & prefix : parameters_and_prefixes4a.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Getting parameters\n");
  // Get a few of the parameters just set.
  auto result2 = parameters_client->get_parameters({"foo", "bar", "baz"});
  rclcpp::spin_until_future_complete(node, result2);
  for (auto & parameter : result2.get()) {
    if (parameter.get_name() == "foo") {
      EXPECT_STREQ("foo", parameter.get_name().c_str());
      EXPECT_STREQ(
        "{\"name\": \"foo\", \"type\": \"integer\", \"value\": \"2\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("integer", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "bar") {
      EXPECT_STREQ(
        "{\"name\": \"bar\", \"type\": \"string\", \"value\": \"hello\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("string", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "baz") {
      EXPECT_STREQ("double", parameter.get_type_name().c_str());
      EXPECT_NEAR(1.45, parameter.as_double(), test_epsilon);
    } else {
      ASSERT_FALSE("you should never hit this");
    }
  }

  printf("Getting nonexistent parameters\n");
  // Get a few non existant parameters
  auto result3 = parameters_client->get_parameters({"not_foo", "not_baz"});
  rclcpp::spin_until_future_complete(node, result3);
  for (auto & parameter : result3.get()) {
    EXPECT_STREQ("There should be no matches", parameter.get_name().c_str());
  }

  printf("Listing parameters with recursive depth\n");
  // List all of the parameters, using an empty prefix list
  auto result5 = parameters_client->list_parameters({},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  rclcpp::spin_until_future_complete(node, result5);
  parameters_and_prefixes = result5.get();
  std::vector<std::string> all_names = {
    "foo", "bar", "barstr", "baz", "foo.first", "foo.second", "foobar"
  };
  EXPECT_EQ(parameters_and_prefixes.names.size(), all_names.size());
  for (auto & name : all_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
}

#endif  // PARAMETER_FIXTURES_HPP_
<commit_msg>apply forward slash for list_parameters (#224)<commit_after>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PARAMETER_FIXTURES_HPP_
#define PARAMETER_FIXTURES_HPP_

#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include "gtest/gtest.h"

#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rclcpp/rclcpp.hpp"

const double test_epsilon = 1e-6;

void set_test_parameters(
  std::shared_ptr<rclcpp::parameter_client::SyncParametersClient> parameters_client)
{
  printf("Setting parameters\n");
  // Set several differnet types of parameters.
  auto set_parameters_results = parameters_client->set_parameters({
    rclcpp::parameter::ParameterVariant("foo", 2),
    rclcpp::parameter::ParameterVariant("bar", "hello"),
    rclcpp::parameter::ParameterVariant("barstr", std::string("hello_str")),
    rclcpp::parameter::ParameterVariant("baz", 1.45),
    rclcpp::parameter::ParameterVariant("foo/first", 8),
    rclcpp::parameter::ParameterVariant("foo/second", 42),
    rclcpp::parameter::ParameterVariant("foobar", true),
  });
  printf("Got set_parameters result\n");

  // Check to see if they were set.
  for (auto & result : set_parameters_results) {
    ASSERT_TRUE(result.successful);
  }
}

void verify_set_parameters_async(
  std::shared_ptr<rclcpp::Node> node,
  std::shared_ptr<rclcpp::parameter_client::AsyncParametersClient> parameters_client)
{
  printf("Setting parameters\n");
  // Set several differnet types of parameters.
  auto set_parameters_results = parameters_client->set_parameters({
    rclcpp::parameter::ParameterVariant("foo", 2),
    rclcpp::parameter::ParameterVariant("bar", "hello"),
    rclcpp::parameter::ParameterVariant("barstr", std::string("hello_str")),
    rclcpp::parameter::ParameterVariant("baz", 1.45),
    rclcpp::parameter::ParameterVariant("foo/first", 8),
    rclcpp::parameter::ParameterVariant("foo/second", 42),
    rclcpp::parameter::ParameterVariant("foobar", true),
  });
  rclcpp::spin_until_future_complete(node, set_parameters_results);  // Wait for the results.
  printf("Got set_parameters result\n");

  // Check to see if they were set.
  for (auto & result : set_parameters_results.get()) {
    ASSERT_TRUE(result.successful);
  }
}

void verify_test_parameters(
  std::shared_ptr<rclcpp::parameter_client::SyncParametersClient> parameters_client)
{
  printf("Listing parameters with recursive depth\n");
  // Test recursive depth (=0)
  auto parameters_and_prefixes = parameters_client->list_parameters({"foo", "bar"},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  for (auto & name : parameters_and_prefixes.names) {
    EXPECT_TRUE(name == "foo" || name == "bar" || name == "foo/first" || name == "foo/second");
  }
  for (auto & prefix : parameters_and_prefixes.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Listing parameters with depth of 1\n");
  // Test different depth
  auto parameters_and_prefixes4 = parameters_client->list_parameters({"foo"}, 1);
  for (auto & name : parameters_and_prefixes4.names) {
    EXPECT_EQ(name, "foo");
  }
  for (auto & prefix : parameters_and_prefixes4.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Listing parameters with depth of 2\n");
  // Test different depth
  auto parameters_and_prefixes5 = parameters_client->list_parameters({"foo"}, 2);
  for (auto & name : parameters_and_prefixes5.names) {
    EXPECT_TRUE(name == "foo" || name == "foo/first" || name == "foo/second");
  }
  for (auto & prefix : parameters_and_prefixes5.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Getting parameters\n");
  // Get a few of the parameters just set.
  for (auto & parameter : parameters_client->get_parameters({"foo", "bar", "baz"})) {
    // std::cout << "Parameter is:" << std::endl << parameter.to_yaml() << std::endl;
    if (parameter.get_name() == "foo") {
      EXPECT_STREQ(
        "{\"name\": \"foo\", \"type\": \"integer\", \"value\": \"2\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("integer", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "bar") {
      EXPECT_STREQ(
        "{\"name\": \"bar\", \"type\": \"string\", \"value\": \"hello\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("string", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "baz") {
      EXPECT_STREQ("double", parameter.get_type_name().c_str());
      EXPECT_NEAR(1.45, parameter.as_double(), test_epsilon);
    } else {
      ASSERT_FALSE("you should never hit this");
    }
  }

  printf("Getting nonexistent parameters\n");
  // Get a few non existant parameters
  for (auto & parameter : parameters_client->get_parameters({"not_foo", "not_baz"})) {
    EXPECT_STREQ("There should be no matches", parameter.get_name().c_str());
  }

  printf("Listing parameters with recursive depth\n");
  // List all of the parameters, using an empty prefix list and depth=0
  parameters_and_prefixes = parameters_client->list_parameters({},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  std::vector<std::string> all_names = {
    "foo", "bar", "barstr", "baz", "foo/first", "foo/second", "foobar"
  };
  EXPECT_EQ(parameters_and_prefixes.names.size(), all_names.size());
  for (auto & name : all_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
  printf("Listing parameters with depth 100\n");
  // List all of the parameters, using an empty prefix list and large depth
  parameters_and_prefixes = parameters_client->list_parameters({}, 100);
  EXPECT_EQ(parameters_and_prefixes.names.size(), all_names.size());
  for (auto & name : all_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
  printf("Listing parameters with depth 1\n");
  // List most of the parameters, using an empty prefix list and depth=1
  parameters_and_prefixes = parameters_client->list_parameters({}, 1);
  std::vector<std::string> depth_one_names = {
    "foo", "bar", "barstr", "baz", "foobar"
  };
  EXPECT_EQ(parameters_and_prefixes.names.size(), depth_one_names.size());
  for (auto & name : depth_one_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
}

void verify_get_parameters_async(
  std::shared_ptr<rclcpp::Node> node,
  std::shared_ptr<rclcpp::parameter_client::AsyncParametersClient> parameters_client)
{
  printf("Listing parameters with recursive depth\n");
  // Test recursive depth (=0)
  auto result = parameters_client->list_parameters({"foo", "bar"},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  rclcpp::spin_until_future_complete(node, result);
  auto parameters_and_prefixes = result.get();
  for (auto & name : parameters_and_prefixes.names) {
    EXPECT_TRUE(name == "foo" || name == "bar" || name == "foo/first" || name == "foo/second");
  }
  for (auto & prefix : parameters_and_prefixes.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Listing parameters with depth 1\n");
  // Test different depth
  auto result4 = parameters_client->list_parameters({"foo"}, 1);
  rclcpp::spin_until_future_complete(node, result4);
  auto parameters_and_prefixes4 = result4.get();
  for (auto & name : parameters_and_prefixes4.names) {
    EXPECT_EQ(name, "foo");
  }
  for (auto & prefix : parameters_and_prefixes4.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  // Test different depth
  printf("Listing parameters with depth 2\n");
  auto result4a = parameters_client->list_parameters({"foo"}, 2);
  rclcpp::spin_until_future_complete(node, result4a);
  auto parameters_and_prefixes4a = result4a.get();
  for (auto & name : parameters_and_prefixes4a.names) {
    EXPECT_TRUE(name == "foo" || name == "foo/first" || name == "foo/second");
  }
  for (auto & prefix : parameters_and_prefixes4a.prefixes) {
    EXPECT_STREQ("foo", prefix.c_str());
  }

  printf("Getting parameters\n");
  // Get a few of the parameters just set.
  auto result2 = parameters_client->get_parameters({"foo", "bar", "baz"});
  rclcpp::spin_until_future_complete(node, result2);
  for (auto & parameter : result2.get()) {
    if (parameter.get_name() == "foo") {
      EXPECT_STREQ("foo", parameter.get_name().c_str());
      EXPECT_STREQ(
        "{\"name\": \"foo\", \"type\": \"integer\", \"value\": \"2\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("integer", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "bar") {
      EXPECT_STREQ(
        "{\"name\": \"bar\", \"type\": \"string\", \"value\": \"hello\"}",
        std::to_string(parameter).c_str());
      EXPECT_STREQ("string", parameter.get_type_name().c_str());
    } else if (parameter.get_name() == "baz") {
      EXPECT_STREQ("double", parameter.get_type_name().c_str());
      EXPECT_NEAR(1.45, parameter.as_double(), test_epsilon);
    } else {
      ASSERT_FALSE("you should never hit this");
    }
  }

  printf("Getting nonexistent parameters\n");
  // Get a few non existant parameters
  auto result3 = parameters_client->get_parameters({"not_foo", "not_baz"});
  rclcpp::spin_until_future_complete(node, result3);
  for (auto & parameter : result3.get()) {
    EXPECT_STREQ("There should be no matches", parameter.get_name().c_str());
  }

  printf("Listing parameters with recursive depth\n");
  // List all of the parameters, using an empty prefix list
  auto result5 = parameters_client->list_parameters({},
      rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
  rclcpp::spin_until_future_complete(node, result5);
  parameters_and_prefixes = result5.get();
  std::vector<std::string> all_names = {
    "foo", "bar", "barstr", "baz", "foo/first", "foo/second", "foobar"
  };
  EXPECT_EQ(parameters_and_prefixes.names.size(), all_names.size());
  for (auto & name : all_names) {
    EXPECT_NE(std::find(
        parameters_and_prefixes.names.cbegin(),
        parameters_and_prefixes.names.cend(),
        name),
      parameters_and_prefixes.names.cend());
  }
}

#endif  // PARAMETER_FIXTURES_HPP_
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of the WebSocket++ Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * 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 PETER THORSON 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 "../../src/endpoint.hpp"
#include "../../src/roles/client.hpp"

#include <iostream>

typedef websocketpp::endpoint<websocketpp::role::client,websocketpp::socket::plain> plain_endpoint_type;
typedef plain_endpoint_type::handler_ptr plain_handler_ptr;
typedef plain_endpoint_type::connection_ptr connection_ptr;

// base test case handler
class test_case_handler : public plain_endpoint_type::handler {
public:
    typedef test_case_handler type;
    typedef plain_endpoint_type::connection_ptr connection_ptr;

    virtual void end(connection_ptr connection) {
        // test is over, give control back to controlling handler
        boost::posix_time::time_period len(m_start_time,m_end_time);

        if (m_pass) {
            std::cout << " passes in " << len.length() << std::endl;
        } else {
            std::cout << " fails in " << len.length() << std::endl;
        }

        connection->close(websocketpp::close::status::NORMAL,"");
    }
protected:
    bool                        m_pass;
    boost::posix_time::ptime    m_start_time;
    boost::posix_time::ptime    m_end_time;
};

// test class for 9.1.* and 9.2.*
class test_9_1_X : public test_case_handler {
public:
    test_9_1_X(int minor, int subtest) : m_minor(minor),m_subtest(subtest){
        // needs more C++11 intializer lists =(
        m_test_sizes[0] = 65536;
        m_test_sizes[1] = 262144;
        m_test_sizes[2] = 1048576;
        m_test_sizes[3] = 4194304;
        m_test_sizes[4] = 8388608;
        m_test_sizes[5] = 16777216;
    }

    void on_open(connection_ptr connection) {
        std::cout << "Test 9." << m_minor << "." << m_subtest;

        m_data.reserve(m_test_sizes[m_subtest-1]);

        if (m_minor == 1) {
            fill_utf8(connection,true);
            m_start_time = boost::posix_time::microsec_clock::local_time();
            connection->send(m_data,websocketpp::frame::opcode::TEXT);
        } else if (m_minor == 2) {
            fill_binary(connection,true);
            m_start_time = boost::posix_time::microsec_clock::local_time();
            connection->send(m_data,websocketpp::frame::opcode::BINARY);
        } else {
            std::cout << " has unknown definition." << std::endl;
        }
    }

    // Just does random ascii right now. True random UTF8 with multi-byte stuff
    // would probably be better
    void fill_utf8(connection_ptr connection,bool random = true) {
        if (random) {
            uint32_t data;
            for (int i = 0; i < m_test_sizes[m_subtest-1]; i++) {
                if (i%4 == 0) {
                    data = uint32_t(connection->rand());
                }

                m_data.push_back(char(((reinterpret_cast<uint8_t*>(&data)[i%4])%95)+32));
            }
        } else {
            m_data.assign(m_test_sizes[m_subtest-1],'*');
        }
    }

    void fill_binary(connection_ptr connection, bool random = true) {
        if (random) {
            int32_t data;
            for (int i = 0; i < m_test_sizes[m_subtest-1]; i++) {
                if (i%4 == 0) {
                    data = connection->rand();
                }

                m_data.push_back((reinterpret_cast<char*>(&data))[i%4]);
            }
        } else {
            m_data.assign(m_test_sizes[m_subtest-1],'*');
        }
    }

    void on_message(connection_ptr connection,websocketpp::message::data_ptr msg) {
        m_end_time = boost::posix_time::microsec_clock::local_time();

        // Check whether the echoed data matches exactly
        m_pass = (msg->get_payload() == m_data);

        this->end(connection);
    }
private:
    int         m_minor;
    int         m_subtest;
    size_t      m_test_sizes[6];
    std::string m_data;
};

// test class for 9.7.* and 9.8.*
/*class test_9_7_X : public test_case_handler {
public:
    test_9_1_X(int minor, int subtest) : m_minor(minor),m_subtest(subtest){
        // needs more C++11 intializer lists =(
        m_test_sizes[0] = 65536;
        m_test_sizes[1] = 262144;
        m_test_sizes[2] = 1048576;
        m_test_sizes[3] = 4194304;
        m_test_sizes[4] = 8388608;
        m_test_sizes[5] = 16777216;
    }

    void on_open(connection_ptr connection) {
        std::cout << "Test 9." << m_minor << "." << m_subtest;

        m_data.reserve(m_test_sizes[m_subtest-1]);

        if (m_minor == 1) {
            fill_utf8(connection,true);
            m_start_time = boost::posix_time::microsec_clock::local_time();
            connection->send(m_data,false);
        } else if (m_minor == 2) {
            fill_binary(connection,true);
            m_start_time = boost::posix_time::microsec_clock::local_time();
            connection->send(m_data,true);
        } else {
            std::cout << " has unknown definition." << std::endl;
        }
    }

    // Just does random ascii right now. True random UTF8 with multi-byte stuff
    // would probably be better
    void fill_utf8(connection_ptr connection,bool random = true) {
        if (random) {
            uint32_t data;
            for (int i = 0; i < m_test_sizes[m_subtest-1]; i++) {
                if (i%4 == 0) {
                    data = uint32_t(connection->rand());
                }

                m_data.push_back(char(((reinterpret_cast<uint8_t*>(&data)[i%4])%95)+32));
            }
        } else {
            m_data.assign(m_test_sizes[m_subtest-1],'*');
        }
    }

    void fill_binary(connection_ptr connection, bool random = true) {
        if (random) {
            int32_t data;
            for (int i = 0; i < m_test_sizes[m_subtest-1]; i++) {
                if (i%4 == 0) {
                    data = connection->rand();
                }

                m_data.push_back((reinterpret_cast<char*>(&data))[i%4]);
            }
        } else {
            m_data.assign(m_test_sizes[m_subtest-1],'*');
        }
    }

    void on_message(connection_ptr connection,websocketpp::message::data_ptr msg) {
        m_end_time = boost::posix_time::microsec_clock::local_time();

        // Check whether the echoed data matches exactly
        m_pass = (msg->get_payload() == m_data);

        connection->recycle(msg);
        this->end(connection);
    }
private:
    int         m_minor;
    int         m_subtest;
    size_t      m_test_sizes[6];
    std::string m_data;
};*/

int main(int argc, char* argv[]) {
    std::string uri;

    if (argc != 2) {
        uri = "ws://localhost:9002/";
    } else {
        uri = argv[1];
    }

    std::vector<plain_handler_ptr>  tests;

    for (int i = 1; i <= 2; i++) {
        for (int j = 1; j <= 6; j++) {
            tests.push_back(plain_handler_ptr(new test_9_1_X(i,j)));
        }
    }

    try {
        plain_endpoint_type endpoint(tests[0]);

        endpoint.alog().unset_level(websocketpp::log::alevel::ALL);
        endpoint.elog().unset_level(websocketpp::log::elevel::ALL);

        for (int i = 0; i < tests.size(); i++) {
            if (i > 0) {
                endpoint.reset();
                endpoint.set_handler(tests[i]);
            }

            endpoint.connect(uri);
            endpoint.run();
        }

        std::cout << "done" << std::endl;

    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}
<commit_msg>fuzzing client fully implements 9.1.x, 9.2.x, 9.7.x, 9.8.x<commit_after>/*
 * Copyright (c) 2011, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of the WebSocket++ Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * 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 PETER THORSON 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 "../../src/roles/client.hpp"
#include "../../src/websocketpp.hpp"

#include <cstdlib>
#include <ctime>
#include <iostream>

using websocketpp::client;

// base test case handler
class test_case_handler : public client::handler {
public:
    typedef test_case_handler type;

    void start(connection_ptr con, int timeout) {
        m_timer.reset(new boost::asio::deadline_timer(
            con->get_io_service(),
            boost::posix_time::seconds(0))
        );
        m_timer->expires_from_now(boost::posix_time::milliseconds(timeout));
        m_timer->async_wait(boost::bind(&type::on_timer,
                                        this,
                                        con,
                                        boost::asio::placeholders::error));

        m_start_time = boost::posix_time::microsec_clock::local_time();
    }

    virtual void end(connection_ptr con) {
        // test is over, give control back to controlling handler
        boost::posix_time::time_period len(m_start_time,m_end_time);

        switch (m_pass) {
            case FAIL:
                std::cout << " fails in " << len.length() << std::endl;
                break;
            case PASS:
                std::cout << " passes in " << len.length() << std::endl;
                break;
            case TIME_OUT:
                std::cout << " times out in " << len.length() << std::endl;
                break;
        }

        con->close(websocketpp::close::status::NORMAL,"");
    }

    // Just does random ascii right now. True random UTF8 with multi-byte stuff
    // would probably be better
    void fill_utf8(std::string& data,size_t size,bool random = true) {
        if (random) {
            uint32_t val;
            for (int i = 0; i < size; i++) {
                if (i%4 == 0) {
                    val = uint32_t(rand());
                }

                data.push_back(char(((reinterpret_cast<uint8_t*>(&val)[i%4])%95)+32));
            }
        } else {
            data.assign(size,'*');
        }
    }

    void fill_binary(std::string& data,size_t size,bool random = true) {
        if (random) {
            int32_t val;
            for (int i = 0; i < size; i++) {
                if (i%4 == 0) {
                    val = rand();
                }

                data.push_back((reinterpret_cast<char*>(&val))[i%4]);
            }
        } else {
            data.assign(size,'*');
        }
    }

    void on_timer(connection_ptr con,const boost::system::error_code& error) {
        if (error == boost::system::errc::operation_canceled) {
            return; // timer was canceled because test finished
        }

        // time out
        m_end_time = boost::posix_time::microsec_clock::local_time();
        m_pass = TIME_OUT;
        this->end(con);
    }
protected:
    enum status {
        FAIL = 0,
        PASS = 1,
        TIME_OUT = 2
    };

    status                      m_pass;
    boost::posix_time::ptime    m_start_time;
    boost::posix_time::ptime    m_end_time;
    boost::shared_ptr<boost::asio::deadline_timer> m_timer;
};

// test class for 9.1.* and 9.2.*
class test_9_1_X : public test_case_handler {
public:
    test_9_1_X(int minor, int subtest) : m_minor(minor),m_subtest(subtest){
        // needs more C++11 intializer lists =(
        m_test_sizes[0] = 65536;
        m_test_sizes[1] = 262144;
        m_test_sizes[2] = 1048576;
        m_test_sizes[3] = 4194304;
        m_test_sizes[4] = 8388608;
        m_test_sizes[5] = 16777216;
    }

    void on_open(connection_ptr con) {
        std::cout << "Test 9." << m_minor << "." << m_subtest;

        m_data.reserve(m_test_sizes[m_subtest-1]);

        int timeout = 10000; // 10 sec

        // extend timeout to 100 sec for the longer tests
        if ((m_minor == 1 && m_subtest >= 3) || (m_minor == 2 && m_subtest >= 5)) {
            timeout = 100000;
        }

        if (m_minor == 1) {
            fill_utf8(m_data,m_test_sizes[m_subtest-1],true);
            start(con,timeout);
            con->send(m_data,websocketpp::frame::opcode::TEXT);
        } else if (m_minor == 2) {
            fill_binary(m_data,m_test_sizes[m_subtest-1],true);
            start(con,timeout);
            con->send(m_data,websocketpp::frame::opcode::BINARY);
        } else {
            std::cout << " has unknown definition." << std::endl;
        }
    }

    void on_message(connection_ptr con,websocketpp::message::data_ptr msg) {
        m_timer->cancel();
        m_end_time = boost::posix_time::microsec_clock::local_time();

        // Check whether the echoed data matches exactly
        m_pass = ((msg->get_payload() == m_data) ? PASS : FAIL);
        this->end(con);
    }
private:
    int         m_minor;
    int         m_subtest;
    int         m_test_sizes[6];
    std::string m_data;
};

// test class for 9.7.* and 9.8.*
class test_9_7_X : public test_case_handler {
public:
    test_9_7_X(int minor, int subtest)
     : m_minor(minor),
       m_subtest(subtest),
       m_iterations(1000),
       m_acks(0)
    {
        // needs more C++11 intializer lists =(
        m_test_sizes[0] = 0;
        m_test_sizes[1] = 16;
        m_test_sizes[2] = 64;
        m_test_sizes[3] = 256;
        m_test_sizes[4] = 1024;
        m_test_sizes[5] = 4096;

        m_test_timeouts[0] = 60000;
        m_test_timeouts[1] = 60000;
        m_test_timeouts[2] = 60000;
        m_test_timeouts[3] = 120000;
        m_test_timeouts[4] = 240000;
        m_test_timeouts[5] = 480000;
    }

    void on_open(connection_ptr con) {
        std::cout << "Test 9." << m_minor << "." << m_subtest;

        // Get a message buffer from the connection
        message_ptr msg = con->get_data_message();

        // reserve space in our local data buffer
        m_data.reserve(m_test_sizes[m_subtest-1]);

        // Reset message to appropriate opcode and fill local buffer with
        // appropriate types of random data.
        if (m_minor == 7) {
            fill_utf8(m_data,m_test_sizes[m_subtest-1],true);
            msg->reset(websocketpp::frame::opcode::TEXT);
        } else if (m_minor == 8) {
            fill_binary(m_data,m_test_sizes[m_subtest-1],true);
            msg->reset(websocketpp::frame::opcode::BINARY);
        } else {
            std::cout << " has unknown definition." << std::endl;
            return;
        }

        msg->set_payload(m_data);

        // start test timer with 60-480 second timeout based on test length
        start(con,m_test_timeouts[m_subtest-1]);

        // send 1000 copies of prepared message
        for (int i = 0; i < m_iterations; i++) {
            con->send(msg);
        }
    }

    void on_message(connection_ptr con, message_ptr msg) {
        if (msg->get_payload() == m_data) {
            m_acks++;
        }

        if (m_acks == m_iterations) {
            m_pass = PASS;
            m_end_time = boost::posix_time::microsec_clock::local_time();
            m_timer->cancel();
            this->end(con);
        }
    }
private:
    int         m_minor;
    int         m_subtest;
    int         m_test_timeouts[6];
    size_t      m_test_sizes[6];
    std::string m_data;
    int         m_iterations;
    int         m_acks;

};

int main(int argc, char* argv[]) {
    std::string uri;

    srand(time(NULL));

    if (argc != 2) {
        uri = "ws://localhost:9002/";
    } else {
        uri = argv[1];
    }

    std::vector<client::handler_ptr>  tests;

    // 9.1.x and 9.2.x tests
    for (int i = 1; i <= 2; i++) {
        for (int j = 1; j <= 6; j++) {
            tests.push_back(client::handler_ptr(new test_9_1_X(i,j)));
        }
    }

    // 9.7.x and 9.8.x tests
    for (int i = 7; i <= 8; i++) {
        for (int j = 1; j <= 6; j++) {
            tests.push_back(client::handler_ptr(new test_9_7_X(i,j)));
        }
    }

    try {
        client endpoint(tests[0]);

        endpoint.alog().unset_level(websocketpp::log::alevel::ALL);
        endpoint.elog().unset_level(websocketpp::log::elevel::ALL);

        for (int i = 0; i < tests.size(); i++) {
            if (i > 0) {
                endpoint.reset();
                endpoint.set_handler(tests[i]);
            }

            endpoint.connect(uri);
            endpoint.run();
        }
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}
<|endoftext|>