<commit_before>/*
 *  caosVM_debug.cpp
 *  openc2e
 *
 *  Created by Alyssa Milburn on Sun Oct 24 2004.
 *  Copyright (c) 2004 Alyssa Milburn. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */

#include "caosVM.h"
#include "openc2e.h"
#include "Agent.h"
#include "World.h"
#include <iostream>
#include "cmddata.h"
#include <cctype>
#include "dialect.h"
#include <algorithm>
#include "caosScript.h"

// #include "malloc.h" <- unportable horror!
#include <sstream>
#include <boost/format.hpp>

using std::cerr;
using std::cout;

/**
 DBG: OUTS (command) val (string)
 %status maybe
 %pragma variants all

 Outputs a string to the debug log.
*/
void caosVM::c_DBG_OUTS() {
	VM_PARAM_STRING(val)

	cout << val << std::endl;
}

/**
 DBGM (command) val (bareword)
 %status maybe
 %pragma variants c1 c2
 %pragma implementation caosVM::c_DBG_OUTS
*/

/**
 DBG: OUTV (command) val (decimal)
 %status maybe
 %pragma variants all

 Outputs a decimal value to the debug log.
*/
void caosVM::c_DBG_OUTV() {
	VM_VERIFY_SIZE(1)
	VM_PARAM_VALUE(val)

	if (val.hasFloat()) {
		cout << boost::format("%0.06f") % val.getFloat();
	} else if (val.hasInt()) {
		cout << val.getInt();
	} else if (val.hasVector()) {
		const Vector<float> &v = val.getVector();
		cout << boost::format("(%0.6f, %0.6f)") % v.x % v.y;
	} else throw badParamException();

	cout << std::endl;
}

/**
 DBGV (command) val (integer)
 %status maybe
 %pragma variants c1 c2
 %pragma implementation caosVM::c_DBG_OUTV
*/

/**
 DBUG (command) val (integer)
 %status maybe
 %pragma variants c1 c2
*/
void caosVM::c_DBUG() {
	inst = true;
	c_DBG_OUTV();
}

/**
 UNID (integer)
 %status maybe
 %pragma variants c3 cv sm

 Returns the unique ID of the target agent.
 This is currently no good for persisting.

XXX: when serialization support works, this might well become good for
	 persisting :)
*/
void caosVM::v_UNID() {
	VM_VERIFY_SIZE(0)
	valid_agent(targ);
	result.setInt(targ->getUNID());
}

/**
 UNID (agent)
 %status maybe
 %pragma variants c2
 %pragma implementation caosVM::v_UNID_c2

 Returns the unique ID of the target agent.
 This is currently no good for persisting.
*/
void caosVM::v_UNID_c2() {
	VM_VERIFY_SIZE(0)
	valid_agent(targ);
	result.setAgent(targ);
}

/**
 AGNT (agent) id (integer)
 %status maybe

 Returns the agent with the given UNID, or NULL if agent has been deleted.
*/
void caosVM::v_AGNT() {
	VM_VERIFY_SIZE(1)
	VM_PARAM_INTEGER(id)

	result.setAgent(world.lookupUNID(id));
}

/**
 DBG: MALLOC (command)
 %status stub

 Dumps some random memory stats to stderr.
*/
void caosVM::c_DBG_MALLOC() {
	VM_VERIFY_SIZE(0)

	// more unportable horror!
/*	struct mallinfo mi = mallinfo();
#define MPRINT(name) \
	fprintf(stderr, "%10s = %d\n", #name, mi. name)
	MPRINT(arena);
	MPRINT(ordblks);
	MPRINT(smblks);
	MPRINT(hblks);
	MPRINT(hblkhd);
	MPRINT(usmblks);
	MPRINT(fsmblks);
	MPRINT(uordblks);
	MPRINT(fordblks);
	MPRINT(keepcost);
	malloc_stats(); */

	/*std::cerr << "caosSlab free=" << caosVarSlab.free_elements() <<
				 " used=" << caosVarSlab.used_elements() <<
				 " total=" << caosVarSlab.total_elements() <<
				 std::endl;*/
}

/**
 DBG: DUMP (command)
 %status ok
 %pragma variants all

 Dumps the current script's bytecode to stderr.
*/
void caosVM::c_DBG_DUMP() {
	std::cerr << vm->currentscript->dump();
}

/**
 DBG: TRACE (command) level (integer)
 %status ok
 %pragma variants all

 Sets opcode trace level. Zero disables.
*/
void caosVM::c_DBG_TRACE() {
	VM_PARAM_INTEGER(en)

	std::cerr << "trace: " << en << std::endl;
	vm->trace = en;
	if (vm->trace < 0)
		vm->trace = 0;
}

/**
 MANN (command) cmd (string)
 %status stub

 Looks up documentation on the given command and spits it on the current output stream.
*/
void caosVM::c_MANN() {
	VM_PARAM_STRING(cmd)

	caos_assert(outputstream);

	std::transform(cmd.begin(), cmd.end(), cmd.begin(), toupper);
	const cmdinfo *i = currentscript->dialect->cmdbase();

	bool found = false;
	while (i->lookup_key) {
		// TODO: this doesn't work for FACE at the moment due to hack elsewhere
		if (cmd == i->fullname) {
			found = true;
			std::string d = i->docs;
			// TODO: docs should always include name/parameters/etc, so should never be empty
			if (d.size())
				*outputstream << std::string(i->docs) << std::endl;
			else
				*outputstream << "no documentation for " << cmd << std::endl << std::endl;
		}

		i++;
	}

	if (!found) {
		*outputstream << "didn't find " << cmd << std::endl;
		return;
	}
}

/**
 DBG: DISA (command) family (integer) genus (integer) species (integer) event (integer)
 %pragma variants all
 %status ok

 Dumps the "bytecode" of the indicated script to the current output channel.
 Note that this isn't really bytecode yet (though that's a possible future
 improvement).

 If the script is not found no output will be generated.
 */
void caosVM::c_DBG_DISA() {
	VM_PARAM_INTEGER(event)
	VM_PARAM_INTEGER(species)
	VM_PARAM_INTEGER(genus)
	VM_PARAM_INTEGER(family)

	caos_assert(outputstream);

	shared_ptr<script> s = world.scriptorium.getScript(family, genus, species, event);
	if (s) {
		if (s->fmly != family || s->gnus != genus || s->spcs != species) {
			*outputstream << "warning: search resulted in script from " << s->fmly << ", " << s->gnus << ", " << s->spcs << " script" << std::endl;
		}
		*outputstream << s->dump();
	} else
		*outputstream << "no such script" << std::endl;
}

/**
 DBG: ASRT (command) condition (condition)
 %pragma parser new AssertParser()
 %pragma variants all
 %status maybe

 Blows up unless the given condition is true.
*/
void caosVM::c_DBG_ASRT() {
	throw caosException("DBG: ASRT condition failed");
}

/**
 DBG: IDNT (string) agent (agent)
 %status ok
 %pragma variants all

 (openc2e-only)
 Return a nicely-formatted string identifying the classifier of the agent,
 using the catalogue to find the name if possible.
*/
void caosVM::v_DBG_IDNT() {
	VM_PARAM_AGENT(a)

	if (!a)
		result.setString("(null)");
	else
		result.setString(a->identify());
}

/**
 DBG: PROF (command)
 %status stub

 Dumps the current agent profiling information to the output stream, in CSV format.
*/
void caosVM::c_DBG_PROF() {
	// TODO
}

/**
 DBG: CPRO (command)
 %status stub

 Clears the current agent profiling information.
*/
void caosVM::c_DBG_CPRO() {
	// TODO
}

/**
 DBG: STOK (string) bareword (bareword)
 %status ok
 %pragma variants all

 Returns the bare token in 'bareword' as a string.
*/
void caosVM::v_DBG_STOK() {
	VM_PARAM_STRING(bareword)

	result.setString(bareword);
}

/**
 DBG: TSLC (command) timeslice (integer)
 %status ok
 %pragma variants all
 %cost 0

 Sets the currently executing script's remaining timeslice value. This command
 affects only the current timeslice; future slices use the normal amount for
 the dialect in question.
*/
void caosVM::c_DBG_TSLC() {
	VM_PARAM_INTEGER(tslc);
	timeslice = tslc;
}

/**
 DBG: TSLC (integer)
 %status ok
 %pragma variants all

 Returns the number of ticks left in the current script's remaining timeslice.
*/
void caosVM::v_DBG_TSLC() {
	result.setInt(timeslice);
}

/**
DBG: SIZO (string)
 %status ok
 %pragma variants all

 Returns a human-readable description of the sizes of various internal data structures
 */
void caosVM::v_DBG_SIZO() {
	std::ostringstream oss;
#define SIZEOF_OUT(t) do { oss << "sizeof(" #t ") = " << sizeof(t) << std::endl; } while(0)
	SIZEOF_OUT(caosVM);
	SIZEOF_OUT(caosVar);
	SIZEOF_OUT(Agent);
#undef SIZEOF_OUT
#ifdef PROFILE_ALLOCATION_COUNT
	AllocationCounter::walk(oss);
#else
	oss << "This build of openc2e does not have allocation profiling enabled." << std::endl;
#endif
	oss << "Free caosVMs: " << world.vmpool_size() << std::endl;

	result.setString(oss.str());
}

/* vim: set noet: */
<commit_msg>Make the DBG: SIZO helpstring slightly more accurate.<commit_after>/*
 *  caosVM_debug.cpp
 *  openc2e
 *
 *  Created by Alyssa Milburn on Sun Oct 24 2004.
 *  Copyright (c) 2004 Alyssa Milburn. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */

#include "caosVM.h"
#include "openc2e.h"
#include "Agent.h"
#include "World.h"
#include <iostream>
#include "cmddata.h"
#include <cctype>
#include "dialect.h"
#include <algorithm>
#include "caosScript.h"

// #include "malloc.h" <- unportable horror!
#include <sstream>
#include <boost/format.hpp>

using std::cerr;
using std::cout;

/**
 DBG: OUTS (command) val (string)
 %status maybe
 %pragma variants all

 Outputs a string to the debug log.
*/
void caosVM::c_DBG_OUTS() {
	VM_PARAM_STRING(val)

	cout << val << std::endl;
}

/**
 DBGM (command) val (bareword)
 %status maybe
 %pragma variants c1 c2
 %pragma implementation caosVM::c_DBG_OUTS
*/

/**
 DBG: OUTV (command) val (decimal)
 %status maybe
 %pragma variants all

 Outputs a decimal value to the debug log.
*/
void caosVM::c_DBG_OUTV() {
	VM_VERIFY_SIZE(1)
	VM_PARAM_VALUE(val)

	if (val.hasFloat()) {
		cout << boost::format("%0.06f") % val.getFloat();
	} else if (val.hasInt()) {
		cout << val.getInt();
	} else if (val.hasVector()) {
		const Vector<float> &v = val.getVector();
		cout << boost::format("(%0.6f, %0.6f)") % v.x % v.y;
	} else throw badParamException();

	cout << std::endl;
}

/**
 DBGV (command) val (integer)
 %status maybe
 %pragma variants c1 c2
 %pragma implementation caosVM::c_DBG_OUTV
*/

/**
 DBUG (command) val (integer)
 %status maybe
 %pragma variants c1 c2
*/
void caosVM::c_DBUG() {
	inst = true;
	c_DBG_OUTV();
}

/**
 UNID (integer)
 %status maybe
 %pragma variants c3 cv sm

 Returns the unique ID of the target agent.
 This is currently no good for persisting.

XXX: when serialization support works, this might well become good for
	 persisting :)
*/
void caosVM::v_UNID() {
	VM_VERIFY_SIZE(0)
	valid_agent(targ);
	result.setInt(targ->getUNID());
}

/**
 UNID (agent)
 %status maybe
 %pragma variants c2
 %pragma implementation caosVM::v_UNID_c2

 Returns the unique ID of the target agent.
 This is currently no good for persisting.
*/
void caosVM::v_UNID_c2() {
	VM_VERIFY_SIZE(0)
	valid_agent(targ);
	result.setAgent(targ);
}

/**
 AGNT (agent) id (integer)
 %status maybe

 Returns the agent with the given UNID, or NULL if agent has been deleted.
*/
void caosVM::v_AGNT() {
	VM_VERIFY_SIZE(1)
	VM_PARAM_INTEGER(id)

	result.setAgent(world.lookupUNID(id));
}

/**
 DBG: MALLOC (command)
 %status stub

 Dumps some random memory stats to stderr.
*/
void caosVM::c_DBG_MALLOC() {
	VM_VERIFY_SIZE(0)

	// more unportable horror!
/*	struct mallinfo mi = mallinfo();
#define MPRINT(name) \
	fprintf(stderr, "%10s = %d\n", #name, mi. name)
	MPRINT(arena);
	MPRINT(ordblks);
	MPRINT(smblks);
	MPRINT(hblks);
	MPRINT(hblkhd);
	MPRINT(usmblks);
	MPRINT(fsmblks);
	MPRINT(uordblks);
	MPRINT(fordblks);
	MPRINT(keepcost);
	malloc_stats(); */

	/*std::cerr << "caosSlab free=" << caosVarSlab.free_elements() <<
				 " used=" << caosVarSlab.used_elements() <<
				 " total=" << caosVarSlab.total_elements() <<
				 std::endl;*/
}

/**
 DBG: DUMP (command)
 %status ok
 %pragma variants all

 Dumps the current script's bytecode to stderr.
*/
void caosVM::c_DBG_DUMP() {
	std::cerr << vm->currentscript->dump();
}

/**
 DBG: TRACE (command) level (integer)
 %status ok
 %pragma variants all

 Sets opcode trace level. Zero disables.
*/
void caosVM::c_DBG_TRACE() {
	VM_PARAM_INTEGER(en)

	std::cerr << "trace: " << en << std::endl;
	vm->trace = en;
	if (vm->trace < 0)
		vm->trace = 0;
}

/**
 MANN (command) cmd (string)
 %status stub

 Looks up documentation on the given command and spits it on the current output stream.
*/
void caosVM::c_MANN() {
	VM_PARAM_STRING(cmd)

	caos_assert(outputstream);

	std::transform(cmd.begin(), cmd.end(), cmd.begin(), toupper);
	const cmdinfo *i = currentscript->dialect->cmdbase();

	bool found = false;
	while (i->lookup_key) {
		// TODO: this doesn't work for FACE at the moment due to hack elsewhere
		if (cmd == i->fullname) {
			found = true;
			std::string d = i->docs;
			// TODO: docs should always include name/parameters/etc, so should never be empty
			if (d.size())
				*outputstream << std::string(i->docs) << std::endl;
			else
				*outputstream << "no documentation for " << cmd << std::endl << std::endl;
		}

		i++;
	}

	if (!found) {
		*outputstream << "didn't find " << cmd << std::endl;
		return;
	}
}

/**
 DBG: DISA (command) family (integer) genus (integer) species (integer) event (integer)
 %pragma variants all
 %status ok

 Dumps the "bytecode" of the indicated script to the current output channel.
 Note that this isn't really bytecode yet (though that's a possible future
 improvement).

 If the script is not found no output will be generated.
 */
void caosVM::c_DBG_DISA() {
	VM_PARAM_INTEGER(event)
	VM_PARAM_INTEGER(species)
	VM_PARAM_INTEGER(genus)
	VM_PARAM_INTEGER(family)

	caos_assert(outputstream);

	shared_ptr<script> s = world.scriptorium.getScript(family, genus, species, event);
	if (s) {
		if (s->fmly != family || s->gnus != genus || s->spcs != species) {
			*outputstream << "warning: search resulted in script from " << s->fmly << ", " << s->gnus << ", " << s->spcs << " script" << std::endl;
		}
		*outputstream << s->dump();
	} else
		*outputstream << "no such script" << std::endl;
}

/**
 DBG: ASRT (command) condition (condition)
 %pragma parser new AssertParser()
 %pragma variants all
 %status maybe

 Blows up unless the given condition is true.
*/
void caosVM::c_DBG_ASRT() {
	throw caosException("DBG: ASRT condition failed");
}

/**
 DBG: IDNT (string) agent (agent)
 %status ok
 %pragma variants all

 (openc2e-only)
 Return a nicely-formatted string identifying the classifier of the agent,
 using the catalogue to find the name if possible.
*/
void caosVM::v_DBG_IDNT() {
	VM_PARAM_AGENT(a)

	if (!a)
		result.setString("(null)");
	else
		result.setString(a->identify());
}

/**
 DBG: PROF (command)
 %status stub

 Dumps the current agent profiling information to the output stream, in CSV format.
*/
void caosVM::c_DBG_PROF() {
	// TODO
}

/**
 DBG: CPRO (command)
 %status stub

 Clears the current agent profiling information.
*/
void caosVM::c_DBG_CPRO() {
	// TODO
}

/**
 DBG: STOK (string) bareword (bareword)
 %status ok
 %pragma variants all

 Returns the bare token in 'bareword' as a string.
*/
void caosVM::v_DBG_STOK() {
	VM_PARAM_STRING(bareword)

	result.setString(bareword);
}

/**
 DBG: TSLC (command) timeslice (integer)
 %status ok
 %pragma variants all
 %cost 0

 Sets the currently executing script's remaining timeslice value. This command
 affects only the current timeslice; future slices use the normal amount for
 the dialect in question.
*/
void caosVM::c_DBG_TSLC() {
	VM_PARAM_INTEGER(tslc);
	timeslice = tslc;
}

/**
 DBG: TSLC (integer)
 %status ok
 %pragma variants all

 Returns the number of ticks left in the current script's remaining timeslice.
*/
void caosVM::v_DBG_TSLC() {
	result.setInt(timeslice);
}

/**
DBG: SIZO (string)
 %status ok
 %pragma variants all

 Returns a human-readable profile of the sizes and allocation counts of
 various internal data structures
 */
void caosVM::v_DBG_SIZO() {
	std::ostringstream oss;
#define SIZEOF_OUT(t) do { oss << "sizeof(" #t ") = " << sizeof(t) << std::endl; } while(0)
	SIZEOF_OUT(caosVM);
	SIZEOF_OUT(caosVar);
	SIZEOF_OUT(Agent);
#undef SIZEOF_OUT
#ifdef PROFILE_ALLOCATION_COUNT
	AllocationCounter::walk(oss);
#else
	oss << "This build of openc2e does not have allocation profiling enabled." << std::endl;
#endif
	oss << "Free caosVMs: " << world.vmpool_size() << std::endl;

	result.setString(oss.str());
}

/* vim: set noet: */
<|endoftext|>


<commit_before>/*!
  \copyright (c) RDO-Team, 2009-2012
  \file      main.cpp
  \author      (rdo@rk9.bmstu.ru)
  \date      01.05.2012
  \brief
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- INCLUDES
#define BOOST_TEST_MODULE RDOCalc_Test
#include <boost/test/included/unit_test.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/chrono.hpp>
// ----------------------------------------------------------------------- SYNOPSIS
#include "simulator/runtime/rdo_runtime.h"
#include "simulator/runtime/calc/procedural/calc_const.h"
#include "simulator/runtime/calc/procedural/calc_statement.h"
#include "simulator/runtime/calc/operation/calc_arithm.h"
#include "simulator/runtime/calc/operation/calc_logic.h"
#include "simulator/runtime/calc/function/calc_function.h"
// --------------------------------------------------------------------------------

OPEN_RDO_RUNTIME_NAMESPACE

BOOST_AUTO_TEST_SUITE(RDOCalc_Test)

static const double const1 = 10.5;
static const double const2 = 11.4;

typedef  boost::tuple<LPRDORuntime, LPRDOCalc, LPRDOCalc>  CalcTriple;

template <class T>
RDOValue calc(CREF(CalcTriple) calcTriple)
{
	BOOST_CHECK(calcTriple.get<0>());
	BOOST_CHECK(calcTriple.get<1>());
	BOOST_CHECK(calcTriple.get<2>());

	rdo::intrusive_ptr<T> pCalc = rdo::Factory<T>::create(calcTriple.get<1>(), calcTriple.get<2>());
	ASSERT(pCalc);

	return pCalc->calcValue(calcTriple.get<0>());
}

CalcTriple prepair()
{
	static Error error;
	LPRDORuntime pRuntime = rdo::Factory<RDORuntime>::create(&error);
	BOOST_CHECK(pRuntime);

	LPRDOCalc pLeft = rdo::Factory<RDOCalcConst>::create(RDOValue(const1));
	BOOST_CHECK(pLeft);

	LPRDOCalc pRight = rdo::Factory<RDOCalcConst>::create(RDOValue(const2));
	BOOST_CHECK(pRight);

	return CalcTriple(pRuntime, pLeft, pRight);
}

BOOST_AUTO_TEST_CASE(RDOCalc_MultDouble)
{
	RDOValue result = calc<RDOCalcMult>(prepair());
	BOOST_CHECK(result.getDouble() == const1 * const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_DivDouble)
{
	RDOValue result = calc<RDOCalcDiv>(prepair());
	BOOST_CHECK(result.getDouble() == const1 / const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_PlusDouble)
{
	RDOValue result = calc<RDOCalcPlus>(prepair());
	BOOST_CHECK(result.getDouble() == const1 + const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_MinusDouble)
{
	RDOValue result = calc<RDOCalcMinus>(prepair());
	BOOST_CHECK(result.getDouble() == const1 - const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_RecursSimulator)
{
	struct RecursCalcSimulator
	{
		struct Param
		{
			Param(int value)
				: m_value(value)
			{}

			//! ,    int,   int&
			//!       1
			//!   1  120
			int value()
			{
				return m_value;
			}

			Param& dec()
			{
				--m_value;
				return *this;
			}

		private:
			int m_value;
		};

		int funError(Param& param)
		{
			if (param.value() == 1)
			{
				return 1;
			}
			else
			{
				return funError(param.dec()) * param.value();
			}
		}

		int funOk(Param& param)
		{
			if (param.value() == 1)
			{
				return 1;
			}
			else
			{
				return param.value() * funOk(param.dec());
			}
		}
	};

	RecursCalcSimulator calc1;
	RecursCalcSimulator calc2;
	RecursCalcSimulator::Param param1(5);
	RecursCalcSimulator::Param param2(5);

	int resultError = calc1.funError(param1);
	int resultOk    = calc2.funOk   (param2);
	BOOST_CHECK(resultError == 1);
	BOOST_CHECK(resultOk    == 120);
}

BOOST_AUTO_TEST_CASE(RDOCalc_Recurs)
{
	struct generator
	{
		enum MultOrder
		{
			MO_FUN_PARAM,
			MO_PARAM_FUN
		};

		static LPRDOCalc create(MultOrder order)
		{
			//!
			//!
			//! int fun(int param)
			//! {
			//!     if (param == 1)
			//!     {
			//!        return 1;
			//!     }
			//!     else
			//!     {
			//!        return fun(param - 1) * param;
			//!     }
			//! }

			LPRDOCalcReturnCatch pReturnCatch = rdo::Factory<RDOCalcReturnCatch>::create();
			BOOST_CHECK(pReturnCatch);

			LPRDOCalc pGetFunParam = rdo::Factory<RDOCalcFuncParam>::create(0, RDOSrcInfo());

			//! if (param == 1)
			LPRDOCalc pIFCondition;
			{
				LPRDOCalc pParamLeft = pGetFunParam;
				BOOST_CHECK(pParamLeft);

				LPRDOCalc pParamRight = rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(1)));
				BOOST_CHECK(pParamRight);

				pIFCondition = rdo::Factory<RDOCalcIsEqual>::create(pParamLeft, pParamRight);
			}
			BOOST_CHECK(pIFCondition);

			LPRDOCalcIf pIf = rdo::Factory<RDOCalcIf>::create(pIFCondition);
			BOOST_CHECK(pIf);

			//! return 1
			LPRDOCalc pThen = rdo::Factory<RDOCalcFunReturn>::create(
				rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(1)))
			);
			BOOST_CHECK(pThen);

			//! return fun(param - 1) * param
			LPRDOCalc pElse;
			{
				//! param - 1
				LPRDOCalc pParamValue;
				{
					LPRDOCalc pParamLeft = pGetFunParam;
					BOOST_CHECK(pParamLeft);

					LPRDOCalc pParamRight = rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(1)));
					BOOST_CHECK(pParamRight);

					pParamValue = rdo::Factory<RDOCalcMinus>::create(pParamLeft, pParamRight);
				}
				BOOST_CHECK(pParamValue);

				//!  fun(param - 1)
				LPRDOCalc pFunctionCaller = caller(pReturnCatch, pParamValue);
				BOOST_CHECK(pFunctionCaller);

				//! fun(param - 1) * param
				//!
				//! param * fun(param - 1)
				LPRDOCalc pMult;
				switch (order)
				{
				case MO_FUN_PARAM: pMult = rdo::Factory<RDOCalcMult>::create(pFunctionCaller, pGetFunParam); break;
				case MO_PARAM_FUN: pMult = rdo::Factory<RDOCalcMult>::create(pGetFunParam, pFunctionCaller); break;
				}
				BOOST_CHECK(pMult);

				pElse = rdo::Factory<RDOCalcFunReturn>::create(pMult);
			}
			BOOST_CHECK(pElse);

			pIf->setThenStatement(pThen);
			pIf->setElseStatement(pElse);

			pReturnCatch->setTryCalc(pIf);

			return externalCaller(pReturnCatch);
		}

	private:
		//!     int fun(5)
		static LPRDOCalc externalCaller(CREF(LPRDOCalc) pBody)
		{
			LPRDOCalc pParam = rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(5)));
			BOOST_CHECK(pParam);

			return caller(pBody, pParam);
		}

		static LPRDOCalc caller(CREF(LPRDOCalc) pBody, CREF(LPRDOCalc) pParam)
		{
			BOOST_CHECK(pBody );
			BOOST_CHECK(pParam);

			LPRDOCalcFunctionCaller pFunctionCaller = rdo::Factory<RDOCalcFunctionCaller>::create(pBody);
			BOOST_CHECK(pFunctionCaller);

			pFunctionCaller->addParameter(pParam);

			return pFunctionCaller;
		}
	};

	Error error;

	RDOValue resultFunParam = generator::create(generator::MO_FUN_PARAM)->calcValue(rdo::Factory<RDORuntime>::create(&error));
	tstring resultFunParamStr = resultFunParam.getAsString();
	BOOST_CHECK(resultFunParam.getInt() == 120);

	RDOValue resultParamFun = generator::create(generator::MO_PARAM_FUN)->calcValue(rdo::Factory<RDORuntime>::create(&error));
	tstring resultParamFunStr = resultParamFun.getAsString();
	BOOST_CHECK(resultParamFun.getInt() == 120);
}

BOOST_AUTO_TEST_CASE(RDOCalc_SpeedTest)
{
	CalcTriple triple = prepair();
	LPRDOCalc pPlus = rdo::Factory<RDOCalcPlus>::create(triple.get<1>(), triple.get<2>());
	BOOST_CHECK(pPlus);

	LPRDORuntime pRuntime = triple.get<0>();
	BOOST_CHECK(pRuntime);

	typedef  boost::chrono::process_user_cpu_clock  clock;

	static const ruint RUN_TEST_COUNT = 1000000;

	clock::time_point timeStart = clock::now();
	for (ruint i = 0; i < RUN_TEST_COUNT; ++i)
	{
		pPlus->calcValue(pRuntime);
	}
	clock::duration duration(clock::now() - timeStart);
	boost::chrono::duration<double> seconds(duration);

	{
		using namespace boost::unit_test;
		log_level logLevelBackup = runtime_config::log_level();
		unit_test_log.set_threshold_level(log_messages);

		BOOST_TEST_MESSAGE(_T("RDOCalc_SpeedTest: ") << seconds);

		unit_test_log.set_threshold_level(logLevelBackup);
	}
}

BOOST_AUTO_TEST_SUITE_END() // RDOCalc_Test

CLOSE_RDO_RUNTIME_NAMESPACE
<commit_msg> - логирование<commit_after>/*!
  \copyright (c) RDO-Team, 2009-2012
  \file      main.cpp
  \author      (rdo@rk9.bmstu.ru)
  \date      01.05.2012
  \brief
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- INCLUDES
#define BOOST_TEST_MODULE RDOCalc_Test
#include <boost/test/included/unit_test.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/chrono.hpp>
// ----------------------------------------------------------------------- SYNOPSIS
#include "simulator/runtime/rdo_runtime.h"
#include "simulator/runtime/calc/procedural/calc_const.h"
#include "simulator/runtime/calc/procedural/calc_statement.h"
#include "simulator/runtime/calc/operation/calc_arithm.h"
#include "simulator/runtime/calc/operation/calc_logic.h"
#include "simulator/runtime/calc/function/calc_function.h"
// --------------------------------------------------------------------------------

OPEN_RDO_RUNTIME_NAMESPACE

BOOST_AUTO_TEST_SUITE(RDOCalc_Test)

static const double const1 = 10.5;
static const double const2 = 11.4;

typedef  boost::tuple<LPRDORuntime, LPRDOCalc, LPRDOCalc>  CalcTriple;

template <class T>
RDOValue calc(CREF(CalcTriple) calcTriple)
{
	BOOST_CHECK(calcTriple.get<0>());
	BOOST_CHECK(calcTriple.get<1>());
	BOOST_CHECK(calcTriple.get<2>());

	rdo::intrusive_ptr<T> pCalc = rdo::Factory<T>::create(calcTriple.get<1>(), calcTriple.get<2>());
	ASSERT(pCalc);

	return pCalc->calcValue(calcTriple.get<0>());
}

CalcTriple prepair()
{
	static Error error;
	LPRDORuntime pRuntime = rdo::Factory<RDORuntime>::create(&error);
	BOOST_CHECK(pRuntime);

	LPRDOCalc pLeft = rdo::Factory<RDOCalcConst>::create(RDOValue(const1));
	BOOST_CHECK(pLeft);

	LPRDOCalc pRight = rdo::Factory<RDOCalcConst>::create(RDOValue(const2));
	BOOST_CHECK(pRight);

	return CalcTriple(pRuntime, pLeft, pRight);
}

BOOST_AUTO_TEST_CASE(RDOCalc_MultDouble)
{
	RDOValue result = calc<RDOCalcMult>(prepair());
	BOOST_CHECK(result.getDouble() == const1 * const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_DivDouble)
{
	RDOValue result = calc<RDOCalcDiv>(prepair());
	BOOST_CHECK(result.getDouble() == const1 / const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_PlusDouble)
{
	RDOValue result = calc<RDOCalcPlus>(prepair());
	BOOST_CHECK(result.getDouble() == const1 + const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_MinusDouble)
{
	RDOValue result = calc<RDOCalcMinus>(prepair());
	BOOST_CHECK(result.getDouble() == const1 - const2);
}

BOOST_AUTO_TEST_CASE(RDOCalc_RecursSimulator)
{
	struct RecursCalcSimulator
	{
		struct Param
		{
			Param(int value)
				: m_value(value)
			{}

			//! ,    int,   int&
			//!       1
			//!   1  120
			int value()
			{
				return m_value;
			}

			Param& dec()
			{
				--m_value;
				return *this;
			}

		private:
			int m_value;
		};

		int funError(Param& param)
		{
			if (param.value() == 1)
			{
				return 1;
			}
			else
			{
				return funError(param.dec()) * param.value();
			}
		}

		int funOk(Param& param)
		{
			if (param.value() == 1)
			{
				return 1;
			}
			else
			{
				return param.value() * funOk(param.dec());
			}
		}
	};

	RecursCalcSimulator calc1;
	RecursCalcSimulator calc2;
	RecursCalcSimulator::Param param1(5);
	RecursCalcSimulator::Param param2(5);

	int resultError = calc1.funError(param1);
	int resultOk    = calc2.funOk   (param2);

	BOOST_CHECK_MESSAGE(resultError == 1,   "resultError " << resultError << " != 1"  );
	BOOST_CHECK_MESSAGE(resultOk    == 120, "resultOk "    << resultOk    << " != 120");
}

BOOST_AUTO_TEST_CASE(RDOCalc_Recurs)
{
	struct generator
	{
		enum MultOrder
		{
			MO_FUN_PARAM,
			MO_PARAM_FUN
		};

		static LPRDOCalc create(MultOrder order)
		{
			//!
			//!
			//! int fun(int param)
			//! {
			//!     if (param == 1)
			//!     {
			//!        return 1;
			//!     }
			//!     else
			//!     {
			//!        return fun(param - 1) * param;
			//!     }
			//! }

			LPRDOCalcReturnCatch pReturnCatch = rdo::Factory<RDOCalcReturnCatch>::create();
			BOOST_CHECK(pReturnCatch);

			LPRDOCalc pGetFunParam = rdo::Factory<RDOCalcFuncParam>::create(0, RDOSrcInfo());

			//! if (param == 1)
			LPRDOCalc pIFCondition;
			{
				LPRDOCalc pParamLeft = pGetFunParam;
				BOOST_CHECK(pParamLeft);

				LPRDOCalc pParamRight = rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(1)));
				BOOST_CHECK(pParamRight);

				pIFCondition = rdo::Factory<RDOCalcIsEqual>::create(pParamLeft, pParamRight);
			}
			BOOST_CHECK(pIFCondition);

			LPRDOCalcIf pIf = rdo::Factory<RDOCalcIf>::create(pIFCondition);
			BOOST_CHECK(pIf);

			//! return 1
			LPRDOCalc pThen = rdo::Factory<RDOCalcFunReturn>::create(
				rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(1)))
			);
			BOOST_CHECK(pThen);

			//! return fun(param - 1) * param
			LPRDOCalc pElse;
			{
				//! param - 1
				LPRDOCalc pParamValue;
				{
					LPRDOCalc pParamLeft = pGetFunParam;
					BOOST_CHECK(pParamLeft);

					LPRDOCalc pParamRight = rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(1)));
					BOOST_CHECK(pParamRight);

					pParamValue = rdo::Factory<RDOCalcMinus>::create(pParamLeft, pParamRight);
				}
				BOOST_CHECK(pParamValue);

				//!  fun(param - 1)
				LPRDOCalc pFunctionCaller = caller(pReturnCatch, pParamValue);
				BOOST_CHECK(pFunctionCaller);

				//! fun(param - 1) * param
				//!
				//! param * fun(param - 1)
				LPRDOCalc pMult;
				switch (order)
				{
				case MO_FUN_PARAM: pMult = rdo::Factory<RDOCalcMult>::create(pFunctionCaller, pGetFunParam); break;
				case MO_PARAM_FUN: pMult = rdo::Factory<RDOCalcMult>::create(pGetFunParam, pFunctionCaller); break;
				}
				BOOST_CHECK(pMult);

				pElse = rdo::Factory<RDOCalcFunReturn>::create(pMult);
			}
			BOOST_CHECK(pElse);

			pIf->setThenStatement(pThen);
			pIf->setElseStatement(pElse);

			pReturnCatch->setTryCalc(pIf);

			return externalCaller(pReturnCatch);
		}

	private:
		//!     int fun(5)
		static LPRDOCalc externalCaller(CREF(LPRDOCalc) pBody)
		{
			LPRDOCalc pParam = rdo::Factory<RDOCalcConst>::create(RDOValue(rsint(5)));
			BOOST_CHECK(pParam);

			return caller(pBody, pParam);
		}

		static LPRDOCalc caller(CREF(LPRDOCalc) pBody, CREF(LPRDOCalc) pParam)
		{
			BOOST_CHECK(pBody );
			BOOST_CHECK(pParam);

			LPRDOCalcFunctionCaller pFunctionCaller = rdo::Factory<RDOCalcFunctionCaller>::create(pBody);
			BOOST_CHECK(pFunctionCaller);

			pFunctionCaller->addParameter(pParam);

			return pFunctionCaller;
		}
	};

	Error error;

	RDOValue resultFunParam = generator::create(generator::MO_FUN_PARAM)->calcValue(rdo::Factory<RDORuntime>::create(&error));
	tstring resultFunParamStr = resultFunParam.getAsString();
	BOOST_CHECK(resultFunParam.getInt() == 120);

	RDOValue resultParamFun = generator::create(generator::MO_PARAM_FUN)->calcValue(rdo::Factory<RDORuntime>::create(&error));
	tstring resultParamFunStr = resultParamFun.getAsString();
	BOOST_CHECK(resultParamFun.getInt() == 120);
}

BOOST_AUTO_TEST_CASE(RDOCalc_SpeedTest)
{
	CalcTriple triple = prepair();
	LPRDOCalc pPlus = rdo::Factory<RDOCalcPlus>::create(triple.get<1>(), triple.get<2>());
	BOOST_CHECK(pPlus);

	LPRDORuntime pRuntime = triple.get<0>();
	BOOST_CHECK(pRuntime);

	typedef  boost::chrono::process_user_cpu_clock  clock;

	static const ruint RUN_TEST_COUNT = 1000000;

	clock::time_point timeStart = clock::now();
	for (ruint i = 0; i < RUN_TEST_COUNT; ++i)
	{
		pPlus->calcValue(pRuntime);
	}
	clock::duration duration(clock::now() - timeStart);
	boost::chrono::duration<double> seconds(duration);

	{
		using namespace boost::unit_test;
		log_level logLevelBackup = runtime_config::log_level();
		unit_test_log.set_threshold_level(log_messages);

		BOOST_TEST_MESSAGE(_T("RDOCalc_SpeedTest: ") << seconds);

		unit_test_log.set_threshold_level(logLevelBackup);
	}
}

BOOST_AUTO_TEST_SUITE_END() // RDOCalc_Test

CLOSE_RDO_RUNTIME_NAMESPACE
<|endoftext|>


<commit_before>/*!
  \copyright (c) RDO-Team, 2011
  \file      main.cpp
  \author    Урусов Андрей (rdo@rk9.bmstu.ru)
  \authors   Пройдаков Евгений (lord.tiran@gmail.com)
  \date      10.05.2009
  \brief     Тест common-библиотеки
  \indent    4T
*/

// ----------------------------------------------------------------------- PLATFORM
#include "utils/platform.h"
// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- INCLUDES
#include <boost/regex.hpp>
#define BOOST_TEST_MODULE RDOCommon_Test
#include <boost/test/included/unit_test.hpp>
// ----------------------------------------------------------------------- SYNOPSIS
#include "utils/rdocommon.h"
#include "utils/rdofile.h"
#include "utils/rdotime.h"
// --------------------------------------------------------------------------------

const tstring s_testFileName("test_file");
const ruint64 s_createTestLocalTime = 129557633912040000;

BOOST_AUTO_TEST_SUITE(RDOCommon_Test)

BOOST_AUTO_TEST_CASE(RDOCommon_FileCreate)
{
	BOOST_CHECK(rdo::File::create(s_testFileName));
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileExist)
{
	BOOST_CHECK(rdo::File::exist(s_testFileName));
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileReadOnly)
{
	BOOST_CHECK(!rdo::File::read_only(s_testFileName));
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileRemove)
{
	BOOST_CHECK(rdo::File::unlink(s_testFileName));
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitPathInLinux)
{
	tstring file("/rdo/run and space/files/project.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "/rdo/run and space/files/");
	BOOST_CHECK(name == "project");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitByRootPathInLinux)
{
	tstring file("/project.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "/");
	BOOST_CHECK(name == "project");
	BOOST_CHECK(ext  == ".smr");
}

#ifdef OST_WINDOWS
BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitPathInWindows)
{
	tstring file("С:/rdo/русский пробел/files/проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "С:/rdo/русский пробел/files/");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitByRootPathInWindows)
{
	tstring file("С:/проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "С:/");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitPathInWindows_BackSlash)
{
	tstring file("С:\\rdo\\русский пробел\\files\\проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "С:\\rdo\\русский пробел\\files\\");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitByRootPathInWindows_BackSlash)
{
	tstring file("С:\\проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "С:\\");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}
#endif // #endif

BOOST_AUTO_TEST_CASE(RDOCommon_GetTempFile)
{
	std::set<tstring> name_set;
	for(int i = 0; i < 15; ++i)
	{
		tstring file_name = rdo::File::getTempFileName();
		BOOST_CHECK(name_set.find(file_name) == name_set.end());
		name_set.insert(file_name);
	}
}

BOOST_AUTO_TEST_CASE(RDOCommon_Time)
{
	rdo::Time timeValue = rdo::Time::local();
	BOOST_CHECK(timeValue > s_createTestLocalTime);
	std::cout << "Today:  " << timeValue.asString().c_str() << "  is not it?";
}

BOOST_AUTO_TEST_SUITE_END() // RDOCommon_Test
<commit_msg> - исправлены тесты<commit_after>/*!
  \copyright (c) RDO-Team, 2011
  \file      main.cpp
  \author    Урусов Андрей (rdo@rk9.bmstu.ru)
  \authors   Пройдаков Евгений (lord.tiran@gmail.com)
  \date      10.05.2009
  \brief     Тест common-библиотеки
  \indent    4T
*/

// ----------------------------------------------------------------------- PLATFORM
#include "utils/platform.h"
// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- INCLUDES
#include <boost/regex.hpp>
#define BOOST_TEST_MODULE RDOCommon_Test
#include <boost/test/included/unit_test.hpp>
// ----------------------------------------------------------------------- SYNOPSIS
#include "utils/rdocommon.h"
#include "utils/rdofile.h"
#include "utils/rdotime.h"
#include "utils/rdolocale.h"
// --------------------------------------------------------------------------------

const tstring s_testFileName("test_file");
const ruint64 s_createTestLocalTime = 129557633912040000;

BOOST_AUTO_TEST_SUITE(RDOCommon_Test)

BOOST_AUTO_TEST_CASE(RDOCommon_FileCreate)
{
	rdo::locale::init();
	BOOST_CHECK(rdo::File::create(s_testFileName));
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileExist)
{
	BOOST_CHECK(rdo::File::exist(s_testFileName));
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileReadOnly)
{
	BOOST_CHECK(!rdo::File::read_only(s_testFileName));
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileRemove)
{
	BOOST_CHECK(rdo::File::unlink(s_testFileName));
}

#ifdef OST_LINUX
BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitPathInLinux)
{
	tstring file("/rdo/run and space/files/project.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "/rdo/run and space/files/");
	BOOST_CHECK(name == "project");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitByRootPathInLinux)
{
	tstring file("/project.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "/");
	BOOST_CHECK(name == "project");
	BOOST_CHECK(ext  == ".smr");
}
#endif

#ifdef OST_WINDOWS
BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitPathInWindows)
{
	tstring file("C:/rdo/русский пробел/files/проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "C:\\rdo\\русский пробел\\files\\");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitByRootPathInWindows)
{
	tstring file("C:/проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "C:\\");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitPathInWindows_BackSlash)
{
	tstring file("C:\\rdo\\русский пробел\\files\\проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "C:\\rdo\\русский пробел\\files\\");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}

BOOST_AUTO_TEST_CASE(RDOCommon_FileSplitByRootPathInWindows_BackSlash)
{
	tstring file("C:\\проект.smr");
	tstring dir;
	tstring name;
	tstring ext;

	BOOST_CHECK(rdo::File::splitpath(file, dir, name, ext));
	BOOST_CHECK(dir  == "C:\\");
	BOOST_CHECK(name == "проект");
	BOOST_CHECK(ext  == ".smr");
}
#endif // #endif

BOOST_AUTO_TEST_CASE(RDOCommon_GetTempFile)
{
	std::set<tstring> name_set;
	for(int i = 0; i < 15; ++i)
	{
		tstring file_name = rdo::File::getTempFileName();
		BOOST_CHECK(name_set.find(file_name) == name_set.end());
		name_set.insert(file_name);
	}
}

BOOST_AUTO_TEST_CASE(RDOCommon_Time)
{
	rdo::Time timeValue = rdo::Time::local();
	BOOST_CHECK(timeValue > s_createTestLocalTime);
	std::cout << "Today:  " << timeValue.asString().c_str() << "  is not it?";
}

BOOST_AUTO_TEST_SUITE_END() // RDOCommon_Test
<|endoftext|>


<commit_before>// RUN: not %clang_cc1 %s -fno-rtti -triple=i686-pc-win32 -emit-llvm -o /dev/null 2>&1 | FileCheck --check-prefix=CHECK32 %s
// RUN: %clang_cc1 %s -fno-rtti -triple=x86_64-pc-win32 -emit-llvm -o - | FileCheck --check-prefix=CHECK64 %s

namespace byval_thunk {
struct Agg {
  Agg();
  Agg(const Agg &);
  ~Agg();
  int x;
};

struct A { virtual void foo(Agg x); };
struct B { virtual void foo(Agg x); };
struct C : A, B { virtual void foo(Agg x); };
C c;

// CHECK32: cannot compile this non-trivial argument copy for thunk yet

// CHECK64-LABEL: define linkonce_odr void @"\01?foo@C@byval_thunk@@W7EAAXUAgg@2@@Z"
// CHECK64:             (%"struct.byval_thunk::C"* %this, %"struct.byval_thunk::Agg"* %x)
// CHECK64:   getelementptr i8* %{{.*}}, i32 -8
// CHECK64:   call void @"\01?foo@C@byval_thunk@@UEAAXUAgg@2@@Z"(%"struct.byval_thunk::C"* %2, %"struct.byval_thunk::Agg"* %x)
// CHECK64-NOT: call
// CHECK64:   ret void
}
<commit_msg>Fix my broken test case in NDEBUG  :(<commit_after>// RUN: not %clang_cc1 %s -fno-rtti -triple=i686-pc-win32 -emit-llvm -o /dev/null 2>&1 | FileCheck --check-prefix=CHECK32 %s
// RUN: %clang_cc1 %s -fno-rtti -triple=x86_64-pc-win32 -emit-llvm -o - | FileCheck --check-prefix=CHECK64 %s

namespace byval_thunk {
struct Agg {
  Agg();
  Agg(const Agg &);
  ~Agg();
  int x;
};

struct A { virtual void foo(Agg x); };
struct B { virtual void foo(Agg x); };
struct C : A, B { virtual void foo(Agg x); };
C c;

// CHECK32: cannot compile this non-trivial argument copy for thunk yet

// CHECK64-LABEL: define linkonce_odr void @"\01?foo@C@byval_thunk@@W7EAAXUAgg@2@@Z"
// CHECK64:             (%"struct.byval_thunk::C"* %this, %"struct.byval_thunk::Agg"* %x)
// CHECK64:   getelementptr i8* %{{.*}}, i32 -8
// CHECK64:   call void @"\01?foo@C@byval_thunk@@UEAAXUAgg@2@@Z"(%"struct.byval_thunk::C"* %{{.*}}, %"struct.byval_thunk::Agg"* %x)
// CHECK64-NOT: call
// CHECK64:   ret void
}
<|endoftext|>


<commit_before>/*
 * kmajority_index.cpp
 *
 *  Created on: Aug 28, 2013
 *      Author: andresf
 */

#include <KMajorityIndex.h>
#include <CentersChooser.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/flann/random.h>
#include <opencv2/flann/dist.h>

#include <iostream>
#include <functional>
#include <bitset>

typedef cvflann::Hamming<uchar> Distance;
typedef typename Distance::ResultType DistanceType;

KMajorityIndex::KMajorityIndex(uint _k, uint _max_iterations,
		const cv::Mat& _data, cv::Ptr<int>& _indices,
		const int& _indices_length, uint* _belongs_to,
		cvflann::flann_centers_init_t centers_init) :
		k(_k), max_iterations(_max_iterations), m_centers_init(centers_init), data(
				_data), indices(_indices), indices_length(_indices_length), dim(
				_data.cols), n(_indices_length), distance_to(
				new uint[_indices_length]), cluster_counts(new uint[_k]) {

	belongs_to = _belongs_to != NULL ? _belongs_to : new uint[_indices_length];
	// Set the pointer belongs_to to be deleted
	delete_belongs_to = _belongs_to == NULL;

	// Initially all transactions belong to any cluster
	std::fill_n(this->belongs_to, _indices_length, _k);

	// Initially all transactions are at the farthest possible distance
	// i.e. dim*8 the max hamming distance
	std::fill_n(this->distance_to, _indices_length, _data.cols * 8);

	// Initially no transaction is assigned to any cluster
	std::fill_n(this->cluster_counts, this->k, 0);
}

KMajorityIndex::~KMajorityIndex() {
	if (delete_belongs_to == true) {
		delete[] belongs_to;
	}
	delete[] distance_to;
	delete[] cluster_counts;
}

void KMajorityIndex::cluster() {
	if (data.type() != CV_8U) {
		throw std::runtime_error(
				"KMajorityIndex::cluster: error, descriptors matrix is not binary");
	}

	if (data.empty()) {
		throw std::runtime_error(
				"KMajorityIndex::cluster: error, descriptors is empty");
	}

	// Trivial case: less data than clusters, assign one data point per cluster
	if (this->n <= this->k) {
		centroids.create(this->k, dim, data.type());
		for (uint i = 0; i < this->n; ++i) {
			data.row(i).copyTo(
					centroids(cv::Range(i, i + 1), cv::Range(0, this->dim)));
			belongs_to[i] = i;
		}
		return;
	}

	// Randomly generate clusters
	this->initCentroids();

	// Assign data to clusters
	this->quantize();

	bool converged = false;
	uint iteration = 0;

	while (converged == false && iteration < max_iterations) {

		iteration++;

		// Compute the new cluster centers
		this->computeCentroids();

		// Reassign data to clusters
		converged = this->quantize();

		// TODO handle empty clusters case
		// Find empty clusters
//		for (unsigned int j = 0; j < k; j++) {
//			if (cluster_counts[j] == 0) {
//				printf("Cluster %u is empty\n", j);
//				// Find farthest element to its assigned cluster
//				unsigned int farthest_element_idx = 0;
//				for (unsigned int i = 1; i < n; i++) {
//					if (distance_to[i] > distance_to[farthest_element_idx]) {
//						farthest_element_idx = i;
//					}
//				}
//				// Re-assign farthest_element to the found empty cluster
//				cluster_counts[belongs_to[farthest_element_idx]]--;
//				belongs_to[farthest_element_idx] = j;
//				cluster_counts[j]++;
//				keypoints[farthest_element_idx].class_id = j;
//				distance_to[farthest_element_idx] = hammingDistance(
//						descriptors.row(farthest_element_idx),
//						centroids.row(j));
//			}
//		}
	}

}

void KMajorityIndex::initCentroids() {

	// Initializing variables useful for obtaining indexes of random chosen center
	cv::Ptr<int> centers_idx = new int[k];
	int centers_length;

	// Randomly chose centers
	CentersChooser<cv::flann::Hamming<uchar> >::create(m_centers_init)->chooseCenters(
			k, indices, n, centers_idx, centers_length, this->data);

	// Assign centers based on the chosen indexes
	centroids.create(centers_length, dim, data.type());
	for (int i = 0; i < centers_length; i++) {
		data.row(centers_idx[i]).copyTo(
				centroids(cv::Range(i, i + 1), cv::Range(0, this->dim)));
	}

}

bool KMajorityIndex::quantize() {

	bool converged = true;

	// Comparison of all descriptors vs. all centroids
	for (size_t i = 0; i < this->n; i++) {
		// Set minimum distance as the distance to its assigned cluster
		int min_hd = distance_to[i];

		for (size_t j = 0; j < this->k; j++) {
			// Compute hamming distance between ith descriptor and jth cluster
			cv::Hamming distance;
			int hd = distance(data.row(indices[i]).data, centroids.row(j).data,
					data.cols);

			// Update cluster assignment to the nearest cluster
			if (hd < min_hd) {
				min_hd = hd;
				// If cluster assignment changed that means the algorithm hasn't converged yet
				if (belongs_to[i] != j) {
					converged = false;
				}
				// Decrease cluster count in case it was assigned to some valid cluster before.
				// Recall that initially all transaction are assigned to kth cluster which
				// is not valid since valid clusters run from 0 to k-1 both inclusive.
				if (belongs_to[i] != k) {
					cluster_counts[belongs_to[i]]--;
				}
				belongs_to[i] = j;
				cluster_counts[j]++;
				distance_to[i] = hd;
			}
		}
	}

	return converged;
}

void KMajorityIndex::computeCentroids() {

	// Warning: using matrix of integers, there might be an overflow when summing too much descriptors
	cv::Mat bitwiseCount(this->k, this->dim * 8, cv::DataType<int>::type);
	// Zeroing matrix of cumulative bits
	bitwiseCount = cv::Scalar::all(0);
	// Zeroing all the centroids dimensions
	centroids = cv::Scalar::all(0);

	// Bitwise summing the data into each centroid
	for (size_t i = 0; i < this->n; i++) {
		uint j = belongs_to[i];
		cv::Mat b = bitwiseCount.row(j);
		KMajorityIndex::cumBitSum(data.row(i), b);
	}

	// Bitwise majority voting
	for (size_t j = 0; j < k; j++) {
		cv::Mat centroid = centroids.row(j);
		KMajorityIndex::majorityVoting(bitwiseCount.row(j), centroid,
				cluster_counts[j]);
	}
}

void KMajorityIndex::cumBitSum(const cv::Mat& data, cv::Mat& accVector) {

	// cumResult and data must be a row vectors
	if (data.rows != 1 || accVector.rows != 1) {
		throw std::runtime_error(
				"KMajorityIndex::cumBitSum: data and cumResult parameters must be row vectors\n");
	}
	// cumResult and data must be same length
	if (data.cols * 8 != accVector.cols) {
		throw std::runtime_error(
				"KMajorityIndex::cumBitSum: number of columns in cumResult must be that of data times 8\n");
	}

	uchar byte = 0;
	for (int l = 0; l < accVector.cols; l++) {
		// bit: 7-(l%8) col: (int)l/8 descriptor: i
		// Load byte every 8 bits
		if ((l % 8) == 0) {
			byte = *(data.col((int) l / 8).data);
		}
		// Warning: ignore maybe-uninitialized warning because loop starts with l=0 that means byte gets a value as soon as the loop start
		// bit at ith position is mod(bitleftshift(byte,i),2) where ith position is 7-mod(l,8) i.e 7, 6, 5, 4, 3, 2, 1, 0
		accVector.at<int>(0, l) += ((int) ((byte >> (7 - (l % 8))) % 2));
	}

}

void KMajorityIndex::majorityVoting(const cv::Mat& accVector, cv::Mat& result,
		const uint& threshold) {

	// cumResult and data must be a row vectors
	if (accVector.rows != 1 || result.rows != 1) {
		throw std::runtime_error(
				"KMajorityIndex::majorityVoting: `bitwiseCount` and `centroid` parameters must be row vectors\n");
	}

	// cumResult and data must be same length
	if (result.cols * 8 != accVector.cols) {
		throw std::runtime_error(
				"KMajorityIndex::majorityVoting: number of columns in `bitwiseCount` must be that of `data` times 8\n");
	}

	// In this point I already have stored in bitwiseCount the bitwise sum of all data assigned to jth cluster
	for (size_t l = 0; (int) l < accVector.cols; l++) {
		// If the bitcount for jth cluster at dimension l is greater than half of the data assigned to it
		// then set lth centroid bit to 1 otherwise set it to 0 (break ties randomly)
		bool bit;
		// There is a tie if the number of data assigned to jth cluster is even
		// AND the number of bits set to 1 in lth dimension is the half of the data assigned to jth cluster
		if (threshold % 2 == 1
				&& 2 * accVector.at<int>(0, l) == (int) threshold) {
			bit = rand() % 2;
		} else {
			bit = 2 * accVector.at<int>(0, l) > (int) (threshold);
		}
		// Stores the majority voting result from the LSB to the MSB
		result.at<unsigned char>(0, (int) (accVector.cols - 1 - l) / 8) += (bit)
				<< ((accVector.cols - 1 - l) % 8);
	}
}

const cv::Mat& KMajorityIndex::getCentroids() const {
	return centroids;
}

uint* KMajorityIndex::getClusterCounts() const {
	return cluster_counts;
}

uint* KMajorityIndex::getClusterAssignments() const {
	return belongs_to;
}

int KMajorityIndex::getNumberOfClusters() const {
	return centroids.rows;
}
<commit_msg>KMajorityLib/src/KMajorityIndex.cpp<commit_after>/*
 * kmajority_index.cpp
 *
 *  Created on: Aug 28, 2013
 *      Author: andresf
 */

#include <KMajorityIndex.h>
#include <CentersChooser.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/flann/random.h>
#include <opencv2/flann/dist.h>

#include <iostream>
#include <functional>
#include <bitset>

typedef cvflann::Hamming<uchar> Distance;
typedef typename Distance::ResultType DistanceType;

KMajorityIndex::KMajorityIndex(uint _k, uint _max_iterations,
		const cv::Mat& _data, cv::Ptr<int>& _indices,
		const int& _indices_length, uint* _belongs_to,
		cvflann::flann_centers_init_t centers_init) :
		k(_k), max_iterations(_max_iterations), m_centers_init(centers_init), data(
				_data), indices(_indices), indices_length(_indices_length), dim(
				_data.cols), n(_indices_length), distance_to(
				new uint[_indices_length]), cluster_counts(new uint[_k]) {

	belongs_to = _belongs_to != NULL ? _belongs_to : new uint[_indices_length];
	// Set the pointer belongs_to to be deleted
	delete_belongs_to = _belongs_to == NULL;

	// Initially all transactions belong to any cluster
	std::fill_n(this->belongs_to, _indices_length, _k);

	// Initially all transactions are at the farthest possible distance
	// i.e. dim*8 the max hamming distance
	std::fill_n(this->distance_to, _indices_length, _data.cols * 8);

	// Initially no transaction is assigned to any cluster
	std::fill_n(this->cluster_counts, this->k, 0);
}

KMajorityIndex::~KMajorityIndex() {
	if (delete_belongs_to == true) {
		delete[] belongs_to;
	}
	delete[] distance_to;
	delete[] cluster_counts;
}

void KMajorityIndex::cluster() {
	if (data.type() != CV_8U) {
		throw std::runtime_error(
				"KMajorityIndex::cluster: error, descriptors matrix is not binary");
	}

	if (data.empty()) {
		throw std::runtime_error(
				"KMajorityIndex::cluster: error, descriptors is empty");
	}

	// Trivial case: less data than clusters, assign one data point per cluster
	if (this->n <= this->k) {
		centroids.create(this->k, dim, data.type());
		for (uint i = 0; i < this->n; ++i) {
			data.row(i).copyTo(
					centroids(cv::Range(i, i + 1), cv::Range(0, this->dim)));
			belongs_to[i] = i;
		}
		return;
	}

	// Randomly generate clusters
	this->initCentroids();

	// Assign data to clusters
	this->quantize();

	bool converged = false;
	uint iteration = 0;

	while (converged == false && iteration < max_iterations) {

		iteration++;

		// Compute the new cluster centers
		this->computeCentroids();

		// Reassign data to clusters
		converged = this->quantize();

		// TODO handle empty clusters case
		// Find empty clusters
//		for (unsigned int j = 0; j < k; j++) {
//			if (cluster_counts[j] == 0) {
//				printf("Cluster %u is empty\n", j);
//				// Find farthest element to its assigned cluster
//				unsigned int farthest_element_idx = 0;
//				for (unsigned int i = 1; i < n; i++) {
//					if (distance_to[i] > distance_to[farthest_element_idx]) {
//						farthest_element_idx = i;
//					}
//				}
//				// Re-assign farthest_element to the found empty cluster
//				cluster_counts[belongs_to[farthest_element_idx]]--;
//				belongs_to[farthest_element_idx] = j;
//				cluster_counts[j]++;
//				keypoints[farthest_element_idx].class_id = j;
//				distance_to[farthest_element_idx] = hammingDistance(
//						descriptors.row(farthest_element_idx),
//						centroids.row(j));
//			}
//		}
	}

}

void KMajorityIndex::initCentroids() {

	// Initializing variables useful for obtaining indexes of random chosen center
	cv::Ptr<int> centers_idx = new int[k];
	int centers_length;

	// Randomly chose centers
	CentersChooser<cvflann::Hamming<uchar> >::create(m_centers_init)->chooseCenters(
			k, indices, n, centers_idx, centers_length, this->data);

	// Assign centers based on the chosen indexes
	centroids.create(centers_length, dim, data.type());
	for (int i = 0; i < centers_length; i++) {
		data.row(centers_idx[i]).copyTo(
				centroids(cv::Range(i, i + 1), cv::Range(0, this->dim)));
	}

}

bool KMajorityIndex::quantize() {

	bool converged = true;

	// Comparison of all descriptors vs. all centroids
	for (size_t i = 0; i < this->n; i++) {
		// Set minimum distance as the distance to its assigned cluster
		int min_hd = distance_to[i];

		for (size_t j = 0; j < this->k; j++) {
			// Compute hamming distance between ith descriptor and jth cluster
			cv::Hamming distance;
			int hd = distance(data.row(indices[i]).data, centroids.row(j).data,
					data.cols);

			// Update cluster assignment to the nearest cluster
			if (hd < min_hd) {
				min_hd = hd;
				// If cluster assignment changed that means the algorithm hasn't converged yet
				if (belongs_to[i] != j) {
					converged = false;
				}
				// Decrease cluster count in case it was assigned to some valid cluster before.
				// Recall that initially all transaction are assigned to kth cluster which
				// is not valid since valid clusters run from 0 to k-1 both inclusive.
				if (belongs_to[i] != k) {
					cluster_counts[belongs_to[i]]--;
				}
				belongs_to[i] = j;
				cluster_counts[j]++;
				distance_to[i] = hd;
			}
		}
	}

	return converged;
}

void KMajorityIndex::computeCentroids() {

	// Warning: using matrix of integers, there might be an overflow when summing too much descriptors
	cv::Mat bitwiseCount(this->k, this->dim * 8, cv::DataType<int>::type);
	// Zeroing matrix of cumulative bits
	bitwiseCount = cv::Scalar::all(0);
	// Zeroing all the centroids dimensions
	centroids = cv::Scalar::all(0);

	// Bitwise summing the data into each centroid
	for (size_t i = 0; i < this->n; i++) {
		uint j = belongs_to[i];
		cv::Mat b = bitwiseCount.row(j);
		KMajorityIndex::cumBitSum(data.row(i), b);
	}

	// Bitwise majority voting
	for (size_t j = 0; j < k; j++) {
		cv::Mat centroid = centroids.row(j);
		KMajorityIndex::majorityVoting(bitwiseCount.row(j), centroid,
				cluster_counts[j]);
	}
}

void KMajorityIndex::cumBitSum(const cv::Mat& data, cv::Mat& accVector) {

	// cumResult and data must be a row vectors
	if (data.rows != 1 || accVector.rows != 1) {
		throw std::runtime_error(
				"KMajorityIndex::cumBitSum: data and cumResult parameters must be row vectors\n");
	}
	// cumResult and data must be same length
	if (data.cols * 8 != accVector.cols) {
		throw std::runtime_error(
				"KMajorityIndex::cumBitSum: number of columns in cumResult must be that of data times 8\n");
	}

	uchar byte = 0;
	for (int l = 0; l < accVector.cols; l++) {
		// bit: 7-(l%8) col: (int)l/8 descriptor: i
		// Load byte every 8 bits
		if ((l % 8) == 0) {
			byte = *(data.col((int) l / 8).data);
		}
		// Warning: ignore maybe-uninitialized warning because loop starts with l=0 that means byte gets a value as soon as the loop start
		// bit at ith position is mod(bitleftshift(byte,i),2) where ith position is 7-mod(l,8) i.e 7, 6, 5, 4, 3, 2, 1, 0
		accVector.at<int>(0, l) += ((int) ((byte >> (7 - (l % 8))) % 2));
	}

}

void KMajorityIndex::majorityVoting(const cv::Mat& accVector, cv::Mat& result,
		const uint& threshold) {

	// cumResult and data must be a row vectors
	if (accVector.rows != 1 || result.rows != 1) {
		throw std::runtime_error(
				"KMajorityIndex::majorityVoting: `bitwiseCount` and `centroid` parameters must be row vectors\n");
	}

	// cumResult and data must be same length
	if (result.cols * 8 != accVector.cols) {
		throw std::runtime_error(
				"KMajorityIndex::majorityVoting: number of columns in `bitwiseCount` must be that of `data` times 8\n");
	}

	// In this point I already have stored in bitwiseCount the bitwise sum of all data assigned to jth cluster
	for (size_t l = 0; (int) l < accVector.cols; l++) {
		// If the bitcount for jth cluster at dimension l is greater than half of the data assigned to it
		// then set lth centroid bit to 1 otherwise set it to 0 (break ties randomly)
		bool bit;
		// There is a tie if the number of data assigned to jth cluster is even
		// AND the number of bits set to 1 in lth dimension is the half of the data assigned to jth cluster
		if (threshold % 2 == 1
				&& 2 * accVector.at<int>(0, l) == (int) threshold) {
			bit = rand() % 2;
		} else {
			bit = 2 * accVector.at<int>(0, l) > (int) (threshold);
		}
		// Stores the majority voting result from the LSB to the MSB
		result.at<unsigned char>(0, (int) (accVector.cols - 1 - l) / 8) += (bit)
				<< ((accVector.cols - 1 - l) % 8);
	}
}

const cv::Mat& KMajorityIndex::getCentroids() const {
	return centroids;
}

uint* KMajorityIndex::getClusterCounts() const {
	return cluster_counts;
}

uint* KMajorityIndex::getClusterAssignments() const {
	return belongs_to;
}

int KMajorityIndex::getNumberOfClusters() const {
	return centroids.rows;
}
<|endoftext|>


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

#include <stdint.h>
#include <string.h>

#include <memory>

#include <grpc/grpc.h>
#include <grpc/impl/codegen/propagation_bits.h>
#include <grpc/slice.h>
#include <grpc/status.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>

#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/useful.h"
#include "test/core/end2end/cq_verifier.h"
#include "test/core/end2end/end2end_tests.h"
#include "test/core/util/test_config.h"

#define MAX_CONNECTION_IDLE_MS 500
#define MAX_CONNECTION_AGE_MS 9999

static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); }

static void drain_cq(grpc_completion_queue* cq) {
  grpc_event ev;
  do {
    ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5),
                                    nullptr);
  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
}

static void simple_request_body(grpc_end2end_test_config /*config*/,
                                grpc_end2end_test_fixture* f) {
  grpc_call* c;
  grpc_call* s;
  grpc_core::CqVerifier cqv(f->cq);
  grpc_op ops[6];
  grpc_op* op;
  grpc_metadata_array initial_metadata_recv;
  grpc_metadata_array trailing_metadata_recv;
  grpc_metadata_array request_metadata_recv;
  grpc_call_details call_details;
  grpc_status_code status;
  grpc_call_error error;
  grpc_slice details;
  int was_cancelled = 2;
  char* peer;

  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(30);
  c = grpc_channel_create_call(f->client, nullptr, GRPC_PROPAGATE_DEFAULTS,
                               f->cq, grpc_slice_from_static_string("/foo"),
                               nullptr, deadline, nullptr);
  GPR_ASSERT(c);

  peer = grpc_call_get_peer(c);
  GPR_ASSERT(peer != nullptr);
  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
  gpr_free(peer);

  grpc_metadata_array_init(&initial_metadata_recv);
  grpc_metadata_array_init(&trailing_metadata_recv);
  grpc_metadata_array_init(&request_metadata_recv);
  grpc_call_details_init(&call_details);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_INITIAL_METADATA;
  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
  op->data.recv_status_on_client.status = &status;
  op->data.recv_status_on_client.status_details = &details;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  error =
      grpc_server_request_call(f->server, &s, &call_details,
                               &request_metadata_recv, f->cq, f->cq, tag(101));
  GPR_ASSERT(GRPC_CALL_OK == error);
  cqv.Expect(tag(101), true);
  cqv.Verify();

  peer = grpc_call_get_peer(s);
  GPR_ASSERT(peer != nullptr);
  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
  gpr_free(peer);
  peer = grpc_call_get_peer(c);
  GPR_ASSERT(peer != nullptr);
  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
  gpr_free(peer);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
  op->data.send_status_from_server.trailing_metadata_count = 0;
  op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
  grpc_slice status_details = grpc_slice_from_static_string("xyz");
  op->data.send_status_from_server.status_details = &status_details;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
  op->data.recv_close_on_server.cancelled = &was_cancelled;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(102),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  cqv.Expect(tag(102), true);
  cqv.Expect(tag(1), true);
  cqv.Verify();

  GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
  GPR_ASSERT(was_cancelled == 0);

  grpc_slice_unref(details);
  grpc_metadata_array_destroy(&initial_metadata_recv);
  grpc_metadata_array_destroy(&trailing_metadata_recv);
  grpc_metadata_array_destroy(&request_metadata_recv);
  grpc_call_details_destroy(&call_details);

  grpc_call_unref(c);
  grpc_call_unref(s);
}

static void test_max_connection_idle(grpc_end2end_test_config config) {
  grpc_end2end_test_fixture f = config.create_fixture(nullptr, nullptr);
  grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
  grpc_core::CqVerifier cqv(f.cq);

  auto client_args = grpc_core::ChannelArgs()
                         .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000)
                         .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000)
                         .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000)
                         .ToC();
  grpc_arg server_a[2];
  server_a[0].type = GRPC_ARG_INTEGER;
  server_a[0].key = const_cast<char*>(GRPC_ARG_MAX_CONNECTION_IDLE_MS);
  server_a[0].value.integer = MAX_CONNECTION_IDLE_MS;
  server_a[1].type = GRPC_ARG_INTEGER;
  server_a[1].key = const_cast<char*>(GRPC_ARG_MAX_CONNECTION_AGE_MS);
  server_a[1].value.integer = MAX_CONNECTION_AGE_MS;
  grpc_channel_args server_args = {GPR_ARRAY_SIZE(server_a), server_a};

  config.init_client(&f, client_args.get());
  config.init_server(&f, &server_args);

  /* check that we're still in idle, and start connecting */
  GPR_ASSERT(grpc_channel_check_connectivity_state(f.client, 1) ==
             GRPC_CHANNEL_IDLE);
  /* we'll go through some set of transitions (some might be missed), until
     READY is reached */
  while (state != GRPC_CHANNEL_READY) {
    grpc_channel_watch_connectivity_state(
        f.client, state, grpc_timeout_seconds_to_deadline(10), f.cq, tag(99));
    cqv.Expect(tag(99), true);
    cqv.Verify();
    state = grpc_channel_check_connectivity_state(f.client, 0);
    GPR_ASSERT(state == GRPC_CHANNEL_READY ||
               state == GRPC_CHANNEL_CONNECTING ||
               state == GRPC_CHANNEL_TRANSIENT_FAILURE);
  }

  /* Use a simple request to cancel and reset the max idle timer */
  simple_request_body(config, &f);

  /* wait for the channel to reach its maximum idle time */
  grpc_channel_watch_connectivity_state(
      f.client, GRPC_CHANNEL_READY,
      grpc_timeout_milliseconds_to_deadline(MAX_CONNECTION_IDLE_MS + 3000),
      f.cq, tag(99));
  cqv.Expect(tag(99), true);
  cqv.Verify();
  state = grpc_channel_check_connectivity_state(f.client, 0);
  GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
             state == GRPC_CHANNEL_CONNECTING || state == GRPC_CHANNEL_IDLE);

  grpc_server_shutdown_and_notify(f.server, f.cq, tag(0xdead));
  cqv.Expect(tag(0xdead), true);
  cqv.Verify();

  grpc_server_destroy(f.server);
  grpc_channel_destroy(f.client);
  grpc_completion_queue_shutdown(f.cq);
  drain_cq(f.cq);
  grpc_completion_queue_destroy(f.cq);
  config.tear_down_data(&f);
}

void max_connection_idle(grpc_end2end_test_config config) {
  test_max_connection_idle(config);
}

void max_connection_idle_pre_init(void) {}
<commit_msg>[fixit] MaxConnectionIdle: Increase idleness period to 2sec (#30632)<commit_after>/*
 *
 * Copyright 2017 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <stdint.h>
#include <string.h>

#include <memory>

#include <grpc/grpc.h>
#include <grpc/impl/codegen/propagation_bits.h>
#include <grpc/slice.h>
#include <grpc/status.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>

#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/useful.h"
#include "test/core/end2end/cq_verifier.h"
#include "test/core/end2end/end2end_tests.h"
#include "test/core/util/test_config.h"

#define MAX_CONNECTION_IDLE_MS 2000
#define MAX_CONNECTION_AGE_MS 9999

static void* tag(intptr_t t) { return reinterpret_cast<void*>(t); }

static void drain_cq(grpc_completion_queue* cq) {
  grpc_event ev;
  do {
    ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5),
                                    nullptr);
  } while (ev.type != GRPC_QUEUE_SHUTDOWN);
}

static void simple_request_body(grpc_end2end_test_config /*config*/,
                                grpc_end2end_test_fixture* f) {
  grpc_call* c;
  grpc_call* s;
  grpc_core::CqVerifier cqv(f->cq);
  grpc_op ops[6];
  grpc_op* op;
  grpc_metadata_array initial_metadata_recv;
  grpc_metadata_array trailing_metadata_recv;
  grpc_metadata_array request_metadata_recv;
  grpc_call_details call_details;
  grpc_status_code status;
  grpc_call_error error;
  grpc_slice details;
  int was_cancelled = 2;
  char* peer;

  gpr_timespec deadline = grpc_timeout_seconds_to_deadline(30);
  c = grpc_channel_create_call(f->client, nullptr, GRPC_PROPAGATE_DEFAULTS,
                               f->cq, grpc_slice_from_static_string("/foo"),
                               nullptr, deadline, nullptr);
  GPR_ASSERT(c);

  peer = grpc_call_get_peer(c);
  GPR_ASSERT(peer != nullptr);
  gpr_log(GPR_DEBUG, "client_peer_before_call=%s", peer);
  gpr_free(peer);

  grpc_metadata_array_init(&initial_metadata_recv);
  grpc_metadata_array_init(&trailing_metadata_recv);
  grpc_metadata_array_init(&request_metadata_recv);
  grpc_call_details_init(&call_details);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_INITIAL_METADATA;
  op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
  op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
  op->data.recv_status_on_client.status = &status;
  op->data.recv_status_on_client.status_details = &details;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops), tag(1),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  error =
      grpc_server_request_call(f->server, &s, &call_details,
                               &request_metadata_recv, f->cq, f->cq, tag(101));
  GPR_ASSERT(GRPC_CALL_OK == error);
  cqv.Expect(tag(101), true);
  cqv.Verify();

  peer = grpc_call_get_peer(s);
  GPR_ASSERT(peer != nullptr);
  gpr_log(GPR_DEBUG, "server_peer=%s", peer);
  gpr_free(peer);
  peer = grpc_call_get_peer(c);
  GPR_ASSERT(peer != nullptr);
  gpr_log(GPR_DEBUG, "client_peer=%s", peer);
  gpr_free(peer);

  memset(ops, 0, sizeof(ops));
  op = ops;
  op->op = GRPC_OP_SEND_INITIAL_METADATA;
  op->data.send_initial_metadata.count = 0;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
  op->data.send_status_from_server.trailing_metadata_count = 0;
  op->data.send_status_from_server.status = GRPC_STATUS_UNIMPLEMENTED;
  grpc_slice status_details = grpc_slice_from_static_string("xyz");
  op->data.send_status_from_server.status_details = &status_details;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
  op->data.recv_close_on_server.cancelled = &was_cancelled;
  op->flags = 0;
  op->reserved = nullptr;
  op++;
  error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops), tag(102),
                                nullptr);
  GPR_ASSERT(GRPC_CALL_OK == error);

  cqv.Expect(tag(102), true);
  cqv.Expect(tag(1), true);
  cqv.Verify();

  GPR_ASSERT(status == GRPC_STATUS_UNIMPLEMENTED);
  GPR_ASSERT(0 == grpc_slice_str_cmp(details, "xyz"));
  GPR_ASSERT(0 == grpc_slice_str_cmp(call_details.method, "/foo"));
  GPR_ASSERT(was_cancelled == 0);

  grpc_slice_unref(details);
  grpc_metadata_array_destroy(&initial_metadata_recv);
  grpc_metadata_array_destroy(&trailing_metadata_recv);
  grpc_metadata_array_destroy(&request_metadata_recv);
  grpc_call_details_destroy(&call_details);

  grpc_call_unref(c);
  grpc_call_unref(s);
}

static void test_max_connection_idle(grpc_end2end_test_config config) {
  grpc_end2end_test_fixture f = config.create_fixture(nullptr, nullptr);
  grpc_connectivity_state state = GRPC_CHANNEL_IDLE;
  grpc_core::CqVerifier cqv(f.cq);

  auto client_args = grpc_core::ChannelArgs()
                         .Set(GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS, 1000)
                         .Set(GRPC_ARG_MAX_RECONNECT_BACKOFF_MS, 1000)
                         .Set(GRPC_ARG_MIN_RECONNECT_BACKOFF_MS, 5000)
                         .ToC();
  grpc_arg server_a[2];
  server_a[0].type = GRPC_ARG_INTEGER;
  server_a[0].key = const_cast<char*>(GRPC_ARG_MAX_CONNECTION_IDLE_MS);
  server_a[0].value.integer = MAX_CONNECTION_IDLE_MS;
  server_a[1].type = GRPC_ARG_INTEGER;
  server_a[1].key = const_cast<char*>(GRPC_ARG_MAX_CONNECTION_AGE_MS);
  server_a[1].value.integer = MAX_CONNECTION_AGE_MS;
  grpc_channel_args server_args = {GPR_ARRAY_SIZE(server_a), server_a};

  config.init_client(&f, client_args.get());
  config.init_server(&f, &server_args);

  /* check that we're still in idle, and start connecting */
  GPR_ASSERT(grpc_channel_check_connectivity_state(f.client, 1) ==
             GRPC_CHANNEL_IDLE);
  /* we'll go through some set of transitions (some might be missed), until
     READY is reached */
  while (state != GRPC_CHANNEL_READY) {
    grpc_channel_watch_connectivity_state(
        f.client, state, grpc_timeout_seconds_to_deadline(10), f.cq, tag(99));
    cqv.Expect(tag(99), true);
    cqv.Verify();
    state = grpc_channel_check_connectivity_state(f.client, 0);
    GPR_ASSERT(state == GRPC_CHANNEL_READY ||
               state == GRPC_CHANNEL_CONNECTING ||
               state == GRPC_CHANNEL_TRANSIENT_FAILURE);
  }

  /* Use a simple request to cancel and reset the max idle timer */
  simple_request_body(config, &f);

  /* wait for the channel to reach its maximum idle time */
  grpc_channel_watch_connectivity_state(
      f.client, GRPC_CHANNEL_READY,
      grpc_timeout_milliseconds_to_deadline(MAX_CONNECTION_IDLE_MS + 3000),
      f.cq, tag(99));
  cqv.Expect(tag(99), true);
  cqv.Verify();
  state = grpc_channel_check_connectivity_state(f.client, 0);
  GPR_ASSERT(state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
             state == GRPC_CHANNEL_CONNECTING || state == GRPC_CHANNEL_IDLE);

  grpc_server_shutdown_and_notify(f.server, f.cq, tag(0xdead));
  cqv.Expect(tag(0xdead), true);
  cqv.Verify();

  grpc_server_destroy(f.server);
  grpc_channel_destroy(f.client);
  grpc_completion_queue_shutdown(f.cq);
  drain_cq(f.cq);
  grpc_completion_queue_destroy(f.cq);
  config.tear_down_data(&f);
}

void max_connection_idle(grpc_end2end_test_config config) {
  test_max_connection_idle(config);
}

void max_connection_idle_pre_init(void) {}
<|endoftext|>


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

#include <chrono>
#include <condition_variable>
#include <map>
#include <mutex>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#include <gflags/gflags.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>

#include "src/proto/grpc/testing/empty.pb.h"
#include "src/proto/grpc/testing/messages.pb.h"
#include "src/proto/grpc/testing/test.grpc.pb.h"
#include "test/core/util/test_config.h"
#include "test/cpp/util/test_config.h"

DEFINE_int32(num_channels, 1, "Number of channels.");
DEFINE_bool(print_response, false, "Write RPC response to stdout.");
DEFINE_int32(qps, 1, "Qps per channel.");
DEFINE_int32(rpc_timeout_sec, 10, "Per RPC timeout seconds.");
DEFINE_string(server, "localhost:50051", "Address of server.");
DEFINE_int32(stats_port, 50052,
             "Port to expose peer distribution stats service.");

using grpc::Channel;
using grpc::ClientContext;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCredentials;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::Status;
using grpc::testing::LoadBalancerStatsRequest;
using grpc::testing::LoadBalancerStatsResponse;
using grpc::testing::LoadBalancerStatsService;
using grpc::testing::SimpleRequest;
using grpc::testing::SimpleResponse;
using grpc::testing::TestService;

class XdsStatsWatcher;

// Unique ID for each outgoing RPC
int global_request_id;
// Stores a set of watchers that should be notified upon outgoing RPC completion
std::set<XdsStatsWatcher*> watchers;
// Mutex for global_request_id and watchers
std::mutex mu;

/** Records the remote peer distribution for a given range of RPCs. */
class XdsStatsWatcher {
 public:
  XdsStatsWatcher(int start_id, int end_id)
      : start_id_(start_id), end_id_(end_id), rpcs_needed_(end_id - start_id) {}

  void RpcCompleted(int request_id, const std::string& peer) {
    if (start_id_ <= request_id && request_id < end_id_) {
      {
        std::lock_guard<std::mutex> lk(m_);
        if (peer.empty()) {
          no_remote_peer_++;
        } else {
          rpcs_by_peer_[peer]++;
        }
        rpcs_needed_--;
      }
      cv_.notify_one();
    }
  }

  void WaitForRpcStatsResponse(LoadBalancerStatsResponse* response,
                               int timeout_sec) {
    {
      std::unique_lock<std::mutex> lk(m_);
      cv_.wait_for(lk, std::chrono::seconds(timeout_sec),
                   [this] { return rpcs_needed_ == 0; });
      response->mutable_rpcs_by_peer()->insert(rpcs_by_peer_.begin(),
                                               rpcs_by_peer_.end());
      response->set_num_failures(no_remote_peer_ + rpcs_needed_);
    }
  }

 private:
  int start_id_;
  int end_id_;
  int rpcs_needed_;
  std::map<std::string, int> rpcs_by_peer_;
  int no_remote_peer_;
  std::mutex m_;
  std::condition_variable cv_;
};

class TestClient {
 public:
  TestClient(const std::shared_ptr<Channel>& channel)
      : stub_(TestService::NewStub(channel)) {}

  void UnaryCall() {
    SimpleResponse response;
    ClientContext context;

    int saved_request_id;
    {
      std::lock_guard<std::mutex> lk(mu);
      saved_request_id = ++global_request_id;
    }
    std::chrono::system_clock::time_point deadline =
        std::chrono::system_clock::now() +
        std::chrono::seconds(FLAGS_rpc_timeout_sec);
    context.set_deadline(deadline);
    Status status = stub_->UnaryCall(
        &context, SimpleRequest::default_instance(), &response);

    {
      std::lock_guard<std::mutex> lk(mu);
      for (auto watcher : watchers) {
        watcher->RpcCompleted(saved_request_id, response.hostname());
      }
    }

    if (FLAGS_print_response) {
      if (status.ok()) {
        std::cout << "Greeting: Hello world, this is " << response.hostname()
                  << ", from " << context.peer() << std::endl;
      } else {
        std::cout << "RPC failed: " << status.error_code() << ": "
                  << status.error_message() << std::endl;
      }
    }
  }

 private:
  std::unique_ptr<TestService::Stub> stub_;
};

class LoadBalancerStatsServiceImpl : public LoadBalancerStatsService::Service {
 public:
  Status GetClientStats(ServerContext* context,
                        const LoadBalancerStatsRequest* request,
                        LoadBalancerStatsResponse* response) {
    int start_id;
    int end_id;
    XdsStatsWatcher* watcher;
    {
      std::lock_guard<std::mutex> lk(mu);
      start_id = global_request_id + 1;
      end_id = start_id + request->num_rpcs();
      watcher = new XdsStatsWatcher(start_id, end_id);
      watchers.insert(watcher);
    }
    watcher->WaitForRpcStatsResponse(response, request->timeout_sec());
    {
      std::lock_guard<std::mutex> lk(mu);
      watchers.erase(watcher);
    }
    delete watcher;
    return Status::OK;
  }
};

void RunTestLoop(const std::string& server,
                 std::chrono::duration<double> duration_per_query) {
  TestClient client(
      grpc::CreateChannel(server, grpc::InsecureChannelCredentials()));
  std::chrono::time_point<std::chrono::system_clock> start =
      std::chrono::system_clock::now();
  std::chrono::duration<double> elapsed;

  while (true) {
    elapsed = std::chrono::system_clock::now() - start;
    if (elapsed > duration_per_query) {
      start = std::chrono::system_clock::now();
      client.UnaryCall();
    }
  }
}

void RunServer(const int port) {
  GPR_ASSERT(port != 0);
  std::ostringstream server_address;
  server_address << "0.0.0.0:" << port;

  LoadBalancerStatsServiceImpl service;

  ServerBuilder builder;
  builder.RegisterService(&service);
  builder.AddListeningPort(server_address.str(),
                           grpc::InsecureServerCredentials());
  std::unique_ptr<Server> server(builder.BuildAndStart());
  gpr_log(GPR_INFO, "Stats server listening on %s",
          server_address.str().c_str());

  server->Wait();
}

int main(int argc, char** argv) {
  grpc::testing::TestEnvironment env(argc, argv);
  grpc::testing::InitTest(&argc, &argv, true);

  std::chrono::duration<double> duration_per_query =
      std::chrono::nanoseconds(std::chrono::seconds(1)) / FLAGS_qps;

  std::vector<std::thread> test_threads;

  for (int i = 0; i < FLAGS_num_channels; i++) {
    test_threads.emplace_back(
        std::thread(&RunTestLoop, FLAGS_server, duration_per_query));
  }

  RunServer(FLAGS_stats_port);

  for (auto it = test_threads.begin(); it != test_threads.end(); it++) {
    it->join();
  }

  return 0;
}
<commit_msg>Clang tidy xds interop client<commit_after>/*
 *
 * Copyright 2020 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <chrono>
#include <condition_variable>
#include <map>
#include <mutex>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#include <gflags/gflags.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>

#include "src/proto/grpc/testing/empty.pb.h"
#include "src/proto/grpc/testing/messages.pb.h"
#include "src/proto/grpc/testing/test.grpc.pb.h"
#include "test/core/util/test_config.h"
#include "test/cpp/util/test_config.h"

DEFINE_int32(num_channels, 1, "Number of channels.");
DEFINE_bool(print_response, false, "Write RPC response to stdout.");
DEFINE_int32(qps, 1, "Qps per channel.");
DEFINE_int32(rpc_timeout_sec, 10, "Per RPC timeout seconds.");
DEFINE_string(server, "localhost:50051", "Address of server.");
DEFINE_int32(stats_port, 50052,
             "Port to expose peer distribution stats service.");

using grpc::Channel;
using grpc::ClientContext;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCredentials;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::Status;
using grpc::testing::LoadBalancerStatsRequest;
using grpc::testing::LoadBalancerStatsResponse;
using grpc::testing::LoadBalancerStatsService;
using grpc::testing::SimpleRequest;
using grpc::testing::SimpleResponse;
using grpc::testing::TestService;

class XdsStatsWatcher;

// Unique ID for each outgoing RPC
int global_request_id;
// Stores a set of watchers that should be notified upon outgoing RPC completion
std::set<XdsStatsWatcher*> watchers;
// Mutex for global_request_id and watchers
std::mutex mu;

/** Records the remote peer distribution for a given range of RPCs. */
class XdsStatsWatcher {
 public:
  XdsStatsWatcher(int start_id, int end_id)
      : start_id_(start_id), end_id_(end_id), rpcs_needed_(end_id - start_id) {}

  void RpcCompleted(int request_id, const std::string& peer) {
    if (start_id_ <= request_id && request_id < end_id_) {
      {
        std::lock_guard<std::mutex> lk(m_);
        if (peer.empty()) {
          no_remote_peer_++;
        } else {
          rpcs_by_peer_[peer]++;
        }
        rpcs_needed_--;
      }
      cv_.notify_one();
    }
  }

  void WaitForRpcStatsResponse(LoadBalancerStatsResponse* response,
                               int timeout_sec) {
    {
      std::unique_lock<std::mutex> lk(m_);
      cv_.wait_for(lk, std::chrono::seconds(timeout_sec),
                   [this] { return rpcs_needed_ == 0; });
      response->mutable_rpcs_by_peer()->insert(rpcs_by_peer_.begin(),
                                               rpcs_by_peer_.end());
      response->set_num_failures(no_remote_peer_ + rpcs_needed_);
    }
  }

 private:
  int start_id_;
  int end_id_;
  int rpcs_needed_;
  std::map<std::string, int> rpcs_by_peer_;
  int no_remote_peer_;
  std::mutex m_;
  std::condition_variable cv_;
};

class TestClient {
 public:
  TestClient(const std::shared_ptr<Channel>& channel)
      : stub_(TestService::NewStub(channel)) {}

  void UnaryCall() {
    SimpleResponse response;
    ClientContext context;

    int saved_request_id;
    {
      std::lock_guard<std::mutex> lk(mu);
      saved_request_id = ++global_request_id;
    }
    std::chrono::system_clock::time_point deadline =
        std::chrono::system_clock::now() +
        std::chrono::seconds(FLAGS_rpc_timeout_sec);
    context.set_deadline(deadline);
    Status status = stub_->UnaryCall(
        &context, SimpleRequest::default_instance(), &response);

    {
      std::lock_guard<std::mutex> lk(mu);
      for (auto watcher : watchers) {
        watcher->RpcCompleted(saved_request_id, response.hostname());
      }
    }

    if (FLAGS_print_response) {
      if (status.ok()) {
        std::cout << "Greeting: Hello world, this is " << response.hostname()
                  << ", from " << context.peer() << std::endl;
      } else {
        std::cout << "RPC failed: " << status.error_code() << ": "
                  << status.error_message() << std::endl;
      }
    }
  }

 private:
  std::unique_ptr<TestService::Stub> stub_;
};

class LoadBalancerStatsServiceImpl : public LoadBalancerStatsService::Service {
 public:
  Status GetClientStats(ServerContext* context,
                        const LoadBalancerStatsRequest* request,
                        LoadBalancerStatsResponse* response) {
    int start_id;
    int end_id;
    XdsStatsWatcher* watcher;
    {
      std::lock_guard<std::mutex> lk(mu);
      start_id = global_request_id + 1;
      end_id = start_id + request->num_rpcs();
      watcher = new XdsStatsWatcher(start_id, end_id);
      watchers.insert(watcher);
    }
    watcher->WaitForRpcStatsResponse(response, request->timeout_sec());
    {
      std::lock_guard<std::mutex> lk(mu);
      watchers.erase(watcher);
    }
    delete watcher;
    return Status::OK;
  }
};

void RunTestLoop(const std::string& server,
                 std::chrono::duration<double> duration_per_query) {
  TestClient client(
      grpc::CreateChannel(server, grpc::InsecureChannelCredentials()));
  std::chrono::time_point<std::chrono::system_clock> start =
      std::chrono::system_clock::now();
  std::chrono::duration<double> elapsed;

  while (true) {
    elapsed = std::chrono::system_clock::now() - start;
    if (elapsed > duration_per_query) {
      start = std::chrono::system_clock::now();
      client.UnaryCall();
    }
  }
}

void RunServer(const int port) {
  GPR_ASSERT(port != 0);
  std::ostringstream server_address;
  server_address << "0.0.0.0:" << port;

  LoadBalancerStatsServiceImpl service;

  ServerBuilder builder;
  builder.RegisterService(&service);
  builder.AddListeningPort(server_address.str(),
                           grpc::InsecureServerCredentials());
  std::unique_ptr<Server> server(builder.BuildAndStart());
  gpr_log(GPR_INFO, "Stats server listening on %s",
          server_address.str().c_str());

  server->Wait();
}

int main(int argc, char** argv) {
  grpc::testing::TestEnvironment env(argc, argv);
  grpc::testing::InitTest(&argc, &argv, true);

  std::chrono::duration<double> duration_per_query =
      std::chrono::nanoseconds(std::chrono::seconds(1)) / FLAGS_qps;

  std::vector<std::thread> test_threads;

  test_threads.reserve(FLAGS_num_channels);
  for (int i = 0; i < FLAGS_num_channels; i++) {
    test_threads.emplace_back(
        std::thread(&RunTestLoop, FLAGS_server, duration_per_query));
  }

  RunServer(FLAGS_stats_port);

  for (auto it = test_threads.begin(); it != test_threads.end(); it++) {
    it->join();
  }

  return 0;
}
<|endoftext|>