<commit_before>/*
   webpresenceplugin.cpp

   Kopete Web Presence plugin

   Copyright (c) 2002,2003 by Will Stephenson <will@stevello.free-online.co.uk>

   Kopete    (c) 2002,2003 by the Kopete developers  <kopete-devel@kde.org>

 *************************************************************************
 *                                                                    	*
 * This program is free software; you can redistribute it and/or modify  *
 * it under the terms of the GNU General Public License as published by  *
 * the Free Software Foundation; either version 2 of the License, or     *
 * (at your option) any later version.                                   *
 *                                                                       *
 *************************************************************************
 */

#include "config.h"

#include <qdom.h>
#include <qtimer.h>
#include <qfile.h>

#include <kdebug.h>
#include <kconfig.h>
#include <kgenericfactory.h>
#include <ktempfile.h>
#include <kstandarddirs.h>

#ifdef HAVE_XSLT
#include <libxml/parser.h>
#include <libxml/tree.h>

#include <libxslt/xsltconfig.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#endif

#include "kopetepluginmanager.h"
#include "kopeteprotocol.h"
#include "kopeteaccountmanager.h"
#include "kopeteaccount.h"

#include "webpresenceplugin.h"

typedef KGenericFactory<WebPresencePlugin> WebPresencePluginFactory;
K_EXPORT_COMPONENT_FACTORY( kopete_webpresence, WebPresencePluginFactory( "kopete_webpresence" )  )

WebPresencePlugin::WebPresencePlugin( QObject *parent, const char *name, const QStringList& /*args*/ )
: KopetePlugin( WebPresencePluginFactory::instance(), parent, name )
{
	m_writeScheduler = new QTimer( this );
	connect ( m_writeScheduler, SIGNAL( timeout() ), this, SLOT( slotWriteFile() ) );
	connect( KopeteAccountManager::manager(), SIGNAL(accountReady(KopeteAccount*)),
				this, SLOT( listenToAllAccounts() ) );
	connect( KopeteAccountManager::manager(), SIGNAL(accountUnregistered(KopeteAccount*)),
				this, SLOT( listenToAllAccounts() ) );

	connect(this, SIGNAL(settingsChanged()), this, SLOT( loadSettings() ) );
	loadSettings();
	listenToAllAccounts();
}

WebPresencePlugin::~WebPresencePlugin()
{
}

void WebPresencePlugin::loadSettings()
{
	KConfig *kconfig = KGlobal::config();
	kconfig->setGroup( "Web Presence Plugin" );

	frequency = kconfig->readNumEntry("UploadFrequency" , 15);

	url = kconfig->readPathEntry("uploadURL");
	useDefaultStyleSheet = kconfig->readBoolEntry("formatDefault", true);
	justXml = kconfig->readBoolEntry("formatXML", false);
	userStyleSheet = kconfig->readEntry("formatStylesheetURL");

	useImName = kconfig->readBoolEntry("showName", true);
	userName = kconfig->readEntry("showThisName");
	showAddresses = kconfig->readBoolEntry("includeIMAddress", false);
}

void WebPresencePlugin::listenToAllAccounts()
{
	// connect to signals notifying of all accounts' status changes
	QPtrList<KopeteProtocol> protocols = allProtocols();
	for ( KopeteProtocol *p = protocols.first();
			p; p = protocols.next() )
	{
		QDict<KopeteAccount> dict=KopeteAccountManager::manager()->accounts( p );
		QDictIterator<KopeteAccount> it( dict );
		for( ; KopeteAccount *account=it.current(); ++it )
		{
			 listenToAccount( account );
		}
	}
	slotWaitMoreStatusChanges();
}

void WebPresencePlugin::listenToAccount( KopeteAccount* account )
{
	if(account && account->myself())
	{
		// Connect to the account's status changed signal
		// because we can't know if the account has already connected
		QObject::disconnect( account->myself(),
						SIGNAL(onlineStatusChanged( KopeteContact *,
								const KopeteOnlineStatus &,
								const KopeteOnlineStatus & ) ),
						this,
						SLOT( slotWaitMoreStatusChanges() ) ) ;
		QObject::connect( account->myself(),
						SIGNAL(onlineStatusChanged( KopeteContact *,
								const KopeteOnlineStatus &,
								const KopeteOnlineStatus & ) ),
						this,
						SLOT( slotWaitMoreStatusChanges() ) );
	}
}

void WebPresencePlugin::slotWaitMoreStatusChanges()
{
	if ( !m_writeScheduler->isActive() )
		 m_writeScheduler->start( frequency * 1000);

}

void WebPresencePlugin::slotWriteFile()
{
	bool error = false;
	// generate the (temporary) XML file representing the current contactlist
	KTempFile* xml = generateFile();
	xml->setAutoDelete( true );

	if ( url.isEmpty() )
	{
		kdDebug(14309) << "url is empty. NOT UPDATING!" << endl;
		error = true;
	}

	kdDebug(14309) << k_funcinfo << " " << xml->name() << endl;

	if ( justXml )
	{
		m_output = xml;
		xml = 0L;
	}
	else
	{
		// transform XML to the final format
		m_output = new KTempFile();
		m_output->setAutoDelete( true );
		if ( !transform( xml, m_output ) )
		{
			error = true;
			delete m_output;
		}
		delete xml; // might make debugging harder!
	}
	if ( !error )
	{
		// upload it to the specified URL
		KURL src( m_output->name() );
		KURL dest( url );
		KIO::FileCopyJob *job = KIO::file_copy( src, dest, -1, true, false, false );
		connect( job, SIGNAL( result( KIO::Job * ) ),
				SLOT(  slotUploadJobResult( KIO::Job * ) ) );
	}
	m_writeScheduler->stop();
}

void WebPresencePlugin::slotUploadJobResult( KIO::Job *job )
{
	if (  job->error() ) {
		kdDebug(14309) << "Error uploading presence info." << endl;
		job->showErrorDialog( 0 );
	}
	delete m_output;
	return;
}

KTempFile* WebPresencePlugin::generateFile()
{
	// generate the (temporary) XML file representing the current contactlist
	kdDebug( 14309 ) << k_funcinfo << endl;
	QString notKnown = i18n( "Not yet known" );

	QDomDocument doc( "webpresence" );
	QDomElement root = doc.createElement( "webpresence" );
	doc.appendChild( root );

	// insert the current date/time
	QDomElement date = doc.createElement( "listdate" );
	QDomText t = doc.createTextNode(
			KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
	date.appendChild( t );
	root.appendChild( date );

	// insert the user's name
	QDomElement name = doc.createElement( "name" );
	QDomText nameText;
	if ( !useImName && !userName.isEmpty() )
		nameText = doc.createTextNode( userName );
	else
		nameText = doc.createTextNode( notKnown );
	name.appendChild( nameText );
	root.appendChild( name );

	// insert the list of the user's accounts
	QDomElement accounts = doc.createElement( "accounts" );
	root.appendChild( accounts );

	QPtrList<KopeteAccount> list = KopeteAccountManager::manager()->accounts();
	// If no accounts, stop here
	if ( !list.isEmpty() )
	{
		for( QPtrListIterator<KopeteAccount> it( list );
			 KopeteAccount *account=it.current();
			 ++it )
		{
			QDomElement acc = doc.createElement( "account" );
			//output += h.openTag( "account" );

		QDomElement protoName = doc.createElement( "protocol" );
		QDomText protoNameText = doc.createTextNode(
				account->protocol()->pluginId() );
		protoName.appendChild( protoNameText );
		acc.appendChild( protoName );

			KopeteContact* me = account->myself();
			QDomElement accName = doc.createElement( "accountname" );
			QDomText accNameText = doc.createTextNode( ( me )
					? me->displayName().latin1()
					: notKnown.latin1() );
			accName.appendChild( accNameText );
			acc.appendChild( accName );

			QDomElement accStatus = doc.createElement( "accountstatus" );
			QDomText statusText = doc.createTextNode( ( me )
					? statusAsString( me->onlineStatus() ).latin1()
					: notKnown.latin1() ) ;
			accStatus.appendChild( statusText );
			acc.appendChild( accStatus );

			if ( showAddresses )
			{
				QDomElement accAddress = doc.createElement( "accountaddress" );
				QDomText addressText = doc.createTextNode( ( me )
						? me->contactId().latin1()
						: notKnown.latin1() );
				accAddress.appendChild( addressText );
				acc.appendChild( accAddress );
			}

			accounts.appendChild( acc );
		}
	}

	// write the XML to a temporary file
	KTempFile* file = new KTempFile();
	QTextStream *stream = file->textStream();
	stream->setEncoding( QTextStream::UnicodeUTF8 );
	doc.save( *stream, 0 );
	file->close();
	return file;
}

bool WebPresencePlugin::transform( KTempFile * src, KTempFile * dest )
{
#ifdef HAVE_XSLT
	QString error = "";
	xmlSubstituteEntitiesDefault( 1 );
	xmlLoadExtDtdDefaultValue = 1;
	// test if the stylesheet exists
	QFile sheet;
	if ( useDefaultStyleSheet )
		sheet.setName( locate( "appdata", "webpresence/webpresencedefault.xsl" ) );
	else
		sheet.setName( userStyleSheet );

	if ( sheet.exists() )
	{
		// and if it is a valid stylesheet
		xsltStylesheetPtr cur = NULL;
		if ( ( cur = xsltParseStylesheetFile(
					( const xmlChar *) sheet.name().latin1() ) ) )
		{
			// and if we can parse the input XML
			xmlDocPtr doc = NULL;
			if ( ( doc = xmlParseFile( QFile::encodeName( src->name() ) ) ) != 0L )
			{
				// and if we can apply the stylesheet
				xmlDocPtr res = NULL;
				if ( ( res = xsltApplyStylesheet( cur, doc, 0 ) ) )
				{
					// and if we can save the result
					if ( xsltSaveResultToFile(dest->fstream() , res, cur)
							!= -1 )
					{
						// then it all worked!
						dest->close();
					}
					else
						error = "write result!";
				}
				else
				{
					error = "apply stylesheet!";
					error += " Check the stylesheet works using xsltproc";
				}
				xmlFreeDoc(res);
			}
			else
				error = "parse input XML!";
			xmlFreeDoc(doc);
		}
		else
			error = "parse stylesheet!";
		xsltFreeStylesheet(cur);
	}
	else
		error = "find stylesheet" + sheet.name() + "!";

	xsltCleanupGlobals();
	xmlCleanupParser();

	if ( error.isEmpty() )
		return true;
	else
	{
		kdDebug(14309) << k_funcinfo << " - couldn't "
			<< error << endl;
		return false;
	}
#else
	Q_UNUSED( src );
	Q_UNUSED( dest );

	return false;
#endif
}

QPtrList<KopeteProtocol> WebPresencePlugin::allProtocols()
{
	kdDebug( 14309 ) << k_funcinfo << endl;

	QMap<KPluginInfo *, KopetePlugin *> plugins = KopetePluginManager::self()->loadedPlugins( "Protocols" );
	QMap<KPluginInfo *, KopetePlugin *>::ConstIterator it;
	QPtrList<KopeteProtocol> result;
	for ( it = plugins.begin(); it != plugins.end(); ++it )
		result.append( static_cast<KopeteProtocol *>( it.data() ) );

	return result;
}

QString WebPresencePlugin::statusAsString( const KopeteOnlineStatus &newStatus )
{
	QString status;
	switch ( newStatus.status() )
	{
	case KopeteOnlineStatus::Online:
		status = "ONLINE";
		break;
	case KopeteOnlineStatus::Away:
		status = "AWAY";
		break;
	case KopeteOnlineStatus::Offline:
		status = "OFFLINE";
		break;
	default:
		status = "UNKNOWN";
	}

	return status;
}

// vim: set noet ts=4 sts=4 sw=4:
#include "webpresenceplugin.moc"
<commit_msg>Fix bug 64051. Make sure the that url specified is actually valid before trying to upload the file. Also moved some stuff around so that if there's an error, no unnecessary processing occurs.<commit_after>/*
   webpresenceplugin.cpp

   Kopete Web Presence plugin

   Copyright (c) 2002,2003 by Will Stephenson <will@stevello.free-online.co.uk>

   Kopete    (c) 2002,2003 by the Kopete developers  <kopete-devel@kde.org>

 *************************************************************************
 *                                                                    	*
 * This program is free software; you can redistribute it and/or modify  *
 * it under the terms of the GNU General Public License as published by  *
 * the Free Software Foundation; either version 2 of the License, or     *
 * (at your option) any later version.                                   *
 *                                                                       *
 *************************************************************************
 */

#include "config.h"

#include <qdom.h>
#include <qtimer.h>
#include <qfile.h>

#include <kdebug.h>
#include <kconfig.h>
#include <kgenericfactory.h>
#include <ktempfile.h>
#include <kstandarddirs.h>

#ifdef HAVE_XSLT
#include <libxml/parser.h>
#include <libxml/tree.h>

#include <libxslt/xsltconfig.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#endif

#include "kopetepluginmanager.h"
#include "kopeteprotocol.h"
#include "kopeteaccountmanager.h"
#include "kopeteaccount.h"

#include "webpresenceplugin.h"

typedef KGenericFactory<WebPresencePlugin> WebPresencePluginFactory;
K_EXPORT_COMPONENT_FACTORY( kopete_webpresence, WebPresencePluginFactory( "kopete_webpresence" )  )

WebPresencePlugin::WebPresencePlugin( QObject *parent, const char *name, const QStringList& /*args*/ )
: KopetePlugin( WebPresencePluginFactory::instance(), parent, name )
{
	m_writeScheduler = new QTimer( this );
	connect ( m_writeScheduler, SIGNAL( timeout() ), this, SLOT( slotWriteFile() ) );
	connect( KopeteAccountManager::manager(), SIGNAL(accountReady(KopeteAccount*)),
				this, SLOT( listenToAllAccounts() ) );
	connect( KopeteAccountManager::manager(), SIGNAL(accountUnregistered(KopeteAccount*)),
				this, SLOT( listenToAllAccounts() ) );

	connect(this, SIGNAL(settingsChanged()), this, SLOT( loadSettings() ) );
	loadSettings();
	listenToAllAccounts();
}

WebPresencePlugin::~WebPresencePlugin()
{
}

void WebPresencePlugin::loadSettings()
{
	KConfig *kconfig = KGlobal::config();
	kconfig->setGroup( "Web Presence Plugin" );

	frequency = kconfig->readNumEntry("UploadFrequency" , 15);

	url = kconfig->readPathEntry("uploadURL");
	useDefaultStyleSheet = kconfig->readBoolEntry("formatDefault", true);
	justXml = kconfig->readBoolEntry("formatXML", false);
	userStyleSheet = kconfig->readEntry("formatStylesheetURL");

	useImName = kconfig->readBoolEntry("showName", true);
	userName = kconfig->readEntry("showThisName");
	showAddresses = kconfig->readBoolEntry("includeIMAddress", false);
}

void WebPresencePlugin::listenToAllAccounts()
{
	// connect to signals notifying of all accounts' status changes
	QPtrList<KopeteProtocol> protocols = allProtocols();
	for ( KopeteProtocol *p = protocols.first();
			p; p = protocols.next() )
	{
		QDict<KopeteAccount> dict=KopeteAccountManager::manager()->accounts( p );
		QDictIterator<KopeteAccount> it( dict );
		for( ; KopeteAccount *account=it.current(); ++it )
		{
			 listenToAccount( account );
		}
	}
	slotWaitMoreStatusChanges();
}

void WebPresencePlugin::listenToAccount( KopeteAccount* account )
{
	if(account && account->myself())
	{
		// Connect to the account's status changed signal
		// because we can't know if the account has already connected
		QObject::disconnect( account->myself(),
						SIGNAL(onlineStatusChanged( KopeteContact *,
								const KopeteOnlineStatus &,
								const KopeteOnlineStatus & ) ),
						this,
						SLOT( slotWaitMoreStatusChanges() ) ) ;
		QObject::connect( account->myself(),
						SIGNAL(onlineStatusChanged( KopeteContact *,
								const KopeteOnlineStatus &,
								const KopeteOnlineStatus & ) ),
						this,
						SLOT( slotWaitMoreStatusChanges() ) );
	}
}

void WebPresencePlugin::slotWaitMoreStatusChanges()
{
	if ( !m_writeScheduler->isActive() )
		 m_writeScheduler->start( frequency * 1000);

}

void WebPresencePlugin::slotWriteFile()
{
	bool error = false;
	// generate the (temporary) XML file representing the current contactlist
	KURL dest( url );
	if ( url.isEmpty() || !dest.isValid() )
	{
		kdDebug(14309) << "url is empty or not valid. NOT UPDATING!" << endl;
		error = true;
	}

	if ( !error )
	{
		KTempFile* xml = generateFile();
		xml->setAutoDelete( true );
	kdDebug(14309) << k_funcinfo << " " << xml->name() << endl;

	if ( justXml )
	{
		m_output = xml;
		xml = 0L;
	}
	else
	{
		// transform XML to the final format
		m_output = new KTempFile();
		m_output->setAutoDelete( true );
		if ( !transform( xml, m_output ) )
		{
			error = true;
			delete m_output;
		}
		delete xml; // might make debugging harder!
	}

		// upload it to the specified URL
		KURL src( m_output->name() );
		KIO::FileCopyJob *job = KIO::file_copy( src, dest, -1, true, false, false );
		connect( job, SIGNAL( result( KIO::Job * ) ),
				SLOT(  slotUploadJobResult( KIO::Job * ) ) );
	}
	m_writeScheduler->stop();
}

void WebPresencePlugin::slotUploadJobResult( KIO::Job *job )
{
	if (  job->error() ) {
		kdDebug(14309) << "Error uploading presence info." << endl;
		job->showErrorDialog( 0 );
	}
	delete m_output;
	return;
}

KTempFile* WebPresencePlugin::generateFile()
{
	// generate the (temporary) XML file representing the current contactlist
	kdDebug( 14309 ) << k_funcinfo << endl;
	QString notKnown = i18n( "Not yet known" );

	QDomDocument doc( "webpresence" );
	QDomElement root = doc.createElement( "webpresence" );
	doc.appendChild( root );

	// insert the current date/time
	QDomElement date = doc.createElement( "listdate" );
	QDomText t = doc.createTextNode(
			KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
	date.appendChild( t );
	root.appendChild( date );

	// insert the user's name
	QDomElement name = doc.createElement( "name" );
	QDomText nameText;
	if ( !useImName && !userName.isEmpty() )
		nameText = doc.createTextNode( userName );
	else
		nameText = doc.createTextNode( notKnown );
	name.appendChild( nameText );
	root.appendChild( name );

	// insert the list of the user's accounts
	QDomElement accounts = doc.createElement( "accounts" );
	root.appendChild( accounts );

	QPtrList<KopeteAccount> list = KopeteAccountManager::manager()->accounts();
	// If no accounts, stop here
	if ( !list.isEmpty() )
	{
		for( QPtrListIterator<KopeteAccount> it( list );
			 KopeteAccount *account=it.current();
			 ++it )
		{
			QDomElement acc = doc.createElement( "account" );
			//output += h.openTag( "account" );

		QDomElement protoName = doc.createElement( "protocol" );
		QDomText protoNameText = doc.createTextNode(
				account->protocol()->pluginId() );
		protoName.appendChild( protoNameText );
		acc.appendChild( protoName );

			KopeteContact* me = account->myself();
			QDomElement accName = doc.createElement( "accountname" );
			QDomText accNameText = doc.createTextNode( ( me )
					? me->displayName().latin1()
					: notKnown.latin1() );
			accName.appendChild( accNameText );
			acc.appendChild( accName );

			QDomElement accStatus = doc.createElement( "accountstatus" );
			QDomText statusText = doc.createTextNode( ( me )
					? statusAsString( me->onlineStatus() ).latin1()
					: notKnown.latin1() ) ;
			accStatus.appendChild( statusText );
			acc.appendChild( accStatus );

			if ( showAddresses )
			{
				QDomElement accAddress = doc.createElement( "accountaddress" );
				QDomText addressText = doc.createTextNode( ( me )
						? me->contactId().latin1()
						: notKnown.latin1() );
				accAddress.appendChild( addressText );
				acc.appendChild( accAddress );
			}

			accounts.appendChild( acc );
		}
	}

	// write the XML to a temporary file
	KTempFile* file = new KTempFile();
	QTextStream *stream = file->textStream();
	stream->setEncoding( QTextStream::UnicodeUTF8 );
	doc.save( *stream, 0 );
	file->close();
	return file;
}

bool WebPresencePlugin::transform( KTempFile * src, KTempFile * dest )
{
#ifdef HAVE_XSLT
	QString error = "";
	xmlSubstituteEntitiesDefault( 1 );
	xmlLoadExtDtdDefaultValue = 1;
	// test if the stylesheet exists
	QFile sheet;
	if ( useDefaultStyleSheet )
		sheet.setName( locate( "appdata", "webpresence/webpresencedefault.xsl" ) );
	else
		sheet.setName( userStyleSheet );

	if ( sheet.exists() )
	{
		// and if it is a valid stylesheet
		xsltStylesheetPtr cur = NULL;
		if ( ( cur = xsltParseStylesheetFile(
					( const xmlChar *) sheet.name().latin1() ) ) )
		{
			// and if we can parse the input XML
			xmlDocPtr doc = NULL;
			if ( ( doc = xmlParseFile( QFile::encodeName( src->name() ) ) ) != 0L )
			{
				// and if we can apply the stylesheet
				xmlDocPtr res = NULL;
				if ( ( res = xsltApplyStylesheet( cur, doc, 0 ) ) )
				{
					// and if we can save the result
					if ( xsltSaveResultToFile(dest->fstream() , res, cur)
							!= -1 )
					{
						// then it all worked!
						dest->close();
					}
					else
						error = "write result!";
				}
				else
				{
					error = "apply stylesheet!";
					error += " Check the stylesheet works using xsltproc";
				}
				xmlFreeDoc(res);
			}
			else
				error = "parse input XML!";
			xmlFreeDoc(doc);
		}
		else
			error = "parse stylesheet!";
		xsltFreeStylesheet(cur);
	}
	else
		error = "find stylesheet" + sheet.name() + "!";

	xsltCleanupGlobals();
	xmlCleanupParser();

	if ( error.isEmpty() )
		return true;
	else
	{
		kdDebug(14309) << k_funcinfo << " - couldn't "
			<< error << endl;
		return false;
	}
#else
	Q_UNUSED( src );
	Q_UNUSED( dest );

	return false;
#endif
}

QPtrList<KopeteProtocol> WebPresencePlugin::allProtocols()
{
	kdDebug( 14309 ) << k_funcinfo << endl;

	QMap<KPluginInfo *, KopetePlugin *> plugins = KopetePluginManager::self()->loadedPlugins( "Protocols" );
	QMap<KPluginInfo *, KopetePlugin *>::ConstIterator it;
	QPtrList<KopeteProtocol> result;
	for ( it = plugins.begin(); it != plugins.end(); ++it )
		result.append( static_cast<KopeteProtocol *>( it.data() ) );

	return result;
}

QString WebPresencePlugin::statusAsString( const KopeteOnlineStatus &newStatus )
{
	QString status;
	switch ( newStatus.status() )
	{
	case KopeteOnlineStatus::Online:
		status = "ONLINE";
		break;
	case KopeteOnlineStatus::Away:
		status = "AWAY";
		break;
	case KopeteOnlineStatus::Offline:
		status = "OFFLINE";
		break;
	default:
		status = "UNKNOWN";
	}

	return status;
}

// vim: set noet ts=4 sts=4 sw=4:
#include "webpresenceplugin.moc"
<|endoftext|>


<commit_before>#include <sirius.h>

using namespace sirius;

void allreduce_func(mdarray<double_complex, 2>& buf)
{
    mpi_comm_world().allreduce(buf.at<CPU>(), static_cast<int>(buf.size()));
}

void test1(int BS, int n, int nt)
{
    int N = BS * n;

    if (mpi_comm_world().rank() == 0) {
        printf("BS=%i, N=%i, n=%i\n", BS, N, n);
    }

    mdarray<double_complex, 3> buf(BS, BS, nt);
    buf.zero();

    //omp_set_nested(1);

    sddk::timer t1("allreduce");

    #pragma omp parallel num_threads(nt)
    {
        int tid = omp_get_thread_num();
        int s{0};
        for (int ibc = 0; ibc < n; ibc++) {
            for (int ibr = 0; ibr < n; ibr++) {
                if (s % nt == tid) {
                    mpi_comm_world().allreduce(buf.at<CPU>(0, 0, tid), BS * BS);
                }
                s++;
            }
        }
    }
    t1.stop();

        //double time = t1.stop();

        //double perf = 8e-9 * N * N * (N * 1000) / time / mpi_comm_world().size();
        //if (mpi_comm_world().rank() == 0) {
        //    printf("predicted performance : %12.6f (GFlops / rank)\n", perf);
        //}

    //omp_set_nested(0);

    //int N = 1234;
    //int num_gkvec = 300000;

    //splindex<block> spl_ngk(num_gkvec, Platform::comm_world().size(), Platform::comm_world().rank());

    //matrix<double_complex> A(spl_ngk.local_size(), N);
    //for (int i = 0; i < N; i++)
    //{
    //    for (int j = 0; j < (int)spl_ngk.local_size(); j++) A(j, i) = 1.0 / (i + j + 1);
    //}
    //matrix<double_complex> C(N, N);
    //C.zero();

    //Timer t("allreduce");
    //for (int i = 0; i < 10; i++)
    //{
    //    blas<cpu>::gemm(2, 0, N, N, (int)spl_ngk.local_size(), &A(0, 0), A.ld(), &A(0, 0), A.ld(), &C(0, 0), C.ld());
    //    Platform::comm_world().allreduce(&C(0, 0), N * N);
    //}
    //double tval = t.stop();

    //if (Platform::comm_world().rank() == 0)
    //{
    //    printf("average time: %12.6f\n", tval / 10);
    //    printf("performance (GFlops) : %12.6f\n", 10 * 8e-9 * N * N * (int)spl_ngk.local_size() / tval);
    //}
}

int main(int argn, char** argv)
{
    cmd_args args;
    args.register_key("--BS=", "{int} block size of the panel");
    args.register_key("--n=", "{int} matrix size multiplier");
    args.register_key("--nt=", "{int} number of threads for independent MPI_Allreduce");

    args.parse_args(argn, argv);
    if (args.exist("help")) {
        printf("Usage: %s [options]\n", argv[0]);
        args.print_help();
        return 0;
    }

    int BS = args.value<int>("BS", 256);
    int n = args.value<int>("n", 4);
    int nt = args.value<int>("nt", 1);

    sirius::initialize(1);
    for (int repeat = 0; repeat < 10; repeat++) {
        test1(BS, n, nt);
    }
    sddk::timer::print();
    sirius::finalize();
}
<commit_msg>update<commit_after>#include <sirius.h>

using namespace sirius;

void allreduce_func(mdarray<double_complex, 2>& buf)
{
    mpi_comm_world().allreduce(buf.at<CPU>(), static_cast<int>(buf.size()));
}

void test1(int BS, int n, int nt)
{
    int N = BS * n;

    if (mpi_comm_world().rank() == 0) {
        printf("BS=%i, N=%i, n=%i\n", BS, N, n);
    }

    mdarray<double_complex, 3> buf(BS, BS, nt);
    buf.zero();

    //omp_set_nested(1);

    mpi_comm_world().barrier();
    sddk::timer t1("allreduce");

    if (mpi_comm_world().rank() == 0) {
        printf("spawning %i thread\n", nt);
    }

    #pragma omp parallel num_threads(nt)
    {
        int tid = omp_get_thread_num();
        int s{0};
        for (int ibc = 0; ibc < n; ibc++) {
            for (int ibr = 0; ibr < n; ibr++) {
                if (s % nt == tid) {
                    mpi_comm_world().allreduce(buf.at<CPU>(0, 0, tid), BS * BS);
                }
                s++;
            }
        }
    }
    mpi_comm_world().barrier();
    double tval = t1.stop();
    if (mpi_comm_world().rank() == 0) {
        printf("time: %12.6f sec.\n", tval);
    }

        //double time = t1.stop();

        //double perf = 8e-9 * N * N * (N * 1000) / time / mpi_comm_world().size();
        //if (mpi_comm_world().rank() == 0) {
        //    printf("predicted performance : %12.6f (GFlops / rank)\n", perf);
        //}

    //omp_set_nested(0);

    //int N = 1234;
    //int num_gkvec = 300000;

    //splindex<block> spl_ngk(num_gkvec, Platform::comm_world().size(), Platform::comm_world().rank());

    //matrix<double_complex> A(spl_ngk.local_size(), N);
    //for (int i = 0; i < N; i++)
    //{
    //    for (int j = 0; j < (int)spl_ngk.local_size(); j++) A(j, i) = 1.0 / (i + j + 1);
    //}
    //matrix<double_complex> C(N, N);
    //C.zero();

    //Timer t("allreduce");
    //for (int i = 0; i < 10; i++)
    //{
    //    blas<cpu>::gemm(2, 0, N, N, (int)spl_ngk.local_size(), &A(0, 0), A.ld(), &A(0, 0), A.ld(), &C(0, 0), C.ld());
    //    Platform::comm_world().allreduce(&C(0, 0), N * N);
    //}
    //double tval = t.stop();

    //if (Platform::comm_world().rank() == 0)
    //{
    //    printf("average time: %12.6f\n", tval / 10);
    //    printf("performance (GFlops) : %12.6f\n", 10 * 8e-9 * N * N * (int)spl_ngk.local_size() / tval);
    //}
}

int main(int argn, char** argv)
{
    cmd_args args;
    args.register_key("--BS=", "{int} block size of the panel");
    args.register_key("--n=", "{int} matrix size multiplier");
    args.register_key("--nt=", "{int} number of threads for independent MPI_Allreduce");

    args.parse_args(argn, argv);
    if (args.exist("help")) {
        printf("Usage: %s [options]\n", argv[0]);
        args.print_help();
        return 0;
    }

    int BS = args.value<int>("BS", 256);
    int n = args.value<int>("n", 4);
    int nt = args.value<int>("nt", 1);

    sirius::initialize(1);
    for (int repeat = 0; repeat < 10; repeat++) {
        test1(BS, n, nt);
    }
    sddk::timer::print();
    sirius::finalize();
}
<|endoftext|>


<commit_before>#include "TemplateSQL.h"
#include "SqliteDB.h"
#include "SqlDB.h"
#include "Log.h"

#include <string>
#include <iostream>
#include <unistd.h>

#include <TestFixture.h>
#include <TestAssert.h>
#include <TestSuite.h>
#include <TestCaller.h>
#include <ui/text/TestRunner.h>

using namespace std;

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

/* --------------------------------------- */
/*   WRAPPER TO ACCESS TEMPLATESQL CLASS   */
/* --------------------------------------- */

class TSQL : public TemplateSQL
{
public:
    TSQL(const char * _table,
         int          template_id = -1,
         bool         replace     = false,
         const char   separator   = '=',
         const char * xml_root    = "TEMPLATE"):
        TemplateSQL(_table,template_id,replace,separator,xml_root){}

    ~TSQL(){}

    /* --------------------------------------------------------------------- */
    int insert(SqlDB * db){return TemplateSQL::insert(db);}
    int update(SqlDB * db){return TemplateSQL::update(db);}
    int select(SqlDB * db){return TemplateSQL::select(db);}
    int drop  (SqlDB * db){return TemplateSQL::drop(db);}

    /* --------------------------------------------------------------------- */
    int replace_attribute(SqlDB * db, Attribute * attr)
        {return TemplateSQL::replace_attribute(db,attr);}
    int insert_attribute(SqlDB * db, Attribute * attr)
        {return TemplateSQL::insert_attribute(db,attr);}
    /* --------------------------------------------------------------------- */
    int id(){return TemplateSQL::id;};
};

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

class TemplateSQLTest : public CppUnit::TestFixture
{
private:
    SqlDB *db;

    string filename;

    string template_ok;
    string template_xml;

    static void log(
        const char *            module,
        const Log::MessageType  type,
        const ostringstream&    message,
        const char *            filename = 0,
        Log::MessageType        clevel   = Log::ERROR)
    {
        cerr << message.str() << endl;
    };

public:
    /* --------------------------------------------------------------------- */
    /* --------------------------------------------------------------------- */

    TemplateSQLTest()
    {
        filename = "template_sql.db";

        template_ok =
            "#This line is a comment\n"
            "  # Other comment\n"
            "MEMORY=345 # more comments behind an attribute\n"
            "   CPU     =         4\n"
            "#------------------------------\n"
            "#Comments in the middle\n"
            "#------------------------------\n"
            " empty_var = \n"
            "REQUIREMENTS = \"HOSTNAME = \\\"host*.com\\\"\"\n"
            " DISK= [file = path1, extra = \"disk attribute \"]\n"
            "DISK =[ FILE = \"path2\", EXTRA = str, TYPE=disk]\n"
            " graphics = [\n"
            "   VNC = \"127.0.0.1\",\n"
            "   PORT = 12\n"
            " ]\n";

        template_xml =
            "<TEMPLATE><CPU><![CDATA[4]]></CPU><DISK><EXTRA><![CDATA[disk attribute ]]></EXTRA><FILE><![CDATA[path1]]></FILE></DISK><DISK><EXTRA><![CDATA[str]]></EXTRA><FILE><![CDATA[path2]]></FILE><TYPE><![CDATA[disk]]></TYPE></DISK><EMPTY_VAR><![CDATA[]]></EMPTY_VAR><GRAPHICS><PORT><![CDATA[12]]></PORT><VNC><![CDATA[127.0.0.1]]></VNC></GRAPHICS><MEMORY><![CDATA[345]]></MEMORY><REQUIREMENTS><![CDATA[HOSTNAME = \"host*.com\"]]></REQUIREMENTS></TEMPLATE>";
    }

    ~TemplateSQLTest(){};

    /* --------------------------------------------------------------------- */
    /* --------------------------------------------------------------------- */

    void setUp()
    {
        ostringstream db_bs("CREATE TABLE template (id INTEGER, name TEXT,"
                            " type INTEGER, value TEXT)");

        db = new SqliteDB(filename);
        CPPUNIT_ASSERT(db->exec(db_bs)== 0);
    }

    void tearDown()
    {
        delete db;
        unlink(filename.c_str());
    }

    /* ********************************************************************* */
    /* ********************************************************************* */

    void test_insert()
    {
        char * error = 0;
        int    rc;
        string tmp;
        TSQL   t("template");

        rc = t.parse(template_ok,&error);

        if ( error != 0 )
        {
            cerr << error << endl;
            free(error);
        }

        CPPUNIT_ASSERT( rc == 0);

        CPPUNIT_ASSERT( t.insert(db) == 0 );
        CPPUNIT_ASSERT( t.id() == 0 );

        CPPUNIT_ASSERT( t.insert(db) == 0 );
        CPPUNIT_ASSERT( t.id() == 1 );

        CPPUNIT_ASSERT( t.insert(db) == 0 );
        CPPUNIT_ASSERT( t.id() == 2 );
    }

    /* --------------------------------------------------------------------- */

    void test_select()
    {
        char * error = 0;

        TSQL t("template");
        TSQL t2("template",0);

        string t2_xml;

        t.parse(template_ok,&error);
        t.insert(db);

        if ( error != 0 )
        {
            free(error);
        }

        CPPUNIT_ASSERT( t2.select(db) == 0 );

        t2.to_xml(t2_xml);

        CPPUNIT_ASSERT( t2_xml == template_xml );
    }

    /* ********************************************************************* */
    /* ********************************************************************* */

    static CppUnit::TestSuite * suite()
    {
        CppUnit::TestSuite *ts=new CppUnit::TestSuite("TemplateSQL Tests");

        ts->addTest(new CppUnit::TestCaller<TemplateSQLTest>(
                    "bootstrap() Test",
                    &TemplateSQLTest::test_insert));

        ts->addTest(new CppUnit::TestCaller<TemplateSQLTest>(
                    "insert() Test",
                    &TemplateSQLTest::test_insert));

        ts->addTest(new CppUnit::TestCaller<TemplateSQLTest>(
                    "select() Test",
                    &TemplateSQLTest::test_select));

        return ts;
    }
};

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

int main(int argc, char ** argv)
{
    CppUnit::TextUi::TestRunner tr;

    tr.addTest(TemplateSQLTest::suite());
    tr.run();

    return 0;
}
<commit_msg>feature #192: More tests for TemplateSQL, added also support for testing MySQL.<commit_after>#include "TemplateSQL.h"
#include "SqliteDB.h"
#include "SqlDB.h"
#include "MySqlDB.h"
#include "Log.h"

#include <string>
#include <iostream>
#include <unistd.h>
#include <getopt.h>

#include <TestFixture.h>
#include <TestAssert.h>
#include <TestSuite.h>
#include <TestCaller.h>
#include <ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>

using namespace std;

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

/* --------------------------------------- */
/*   WRAPPER TO ACCESS TEMPLATESQL CLASS   */
/* --------------------------------------- */

class TSQL : public TemplateSQL
{
public:
    TSQL(const char * _table,
         int          template_id = -1,
         bool         replace     = false,
         const char   separator   = '=',
         const char * xml_root    = "TEMPLATE"):
        TemplateSQL(_table,template_id,replace,separator,xml_root){}

    ~TSQL(){}

    /* --------------------------------------------------------------------- */
    int insert(SqlDB * db){return TemplateSQL::insert(db);}
    int update(SqlDB * db){return TemplateSQL::update(db);}
    int select(SqlDB * db){return TemplateSQL::select(db);}
    int drop  (SqlDB * db){return TemplateSQL::drop(db);}

    /* --------------------------------------------------------------------- */
    int replace_attribute(SqlDB * db, Attribute * attr)
        {return TemplateSQL::replace_attribute(db,attr);}
    int insert_attribute(SqlDB * db, Attribute * attr)
        {return TemplateSQL::insert_attribute(db,attr);}
    int remove_attribute(SqlDB * db, const string& name)
        {return TemplateSQL::remove_attribute(db,name);};
    /* --------------------------------------------------------------------- */
    int id(){return TemplateSQL::id;};
};

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

class TemplateSQLTest : public CppUnit::TestFixture
{

    CPPUNIT_TEST_SUITE (TemplateSQLTest);

    CPPUNIT_TEST (test_insert);
    CPPUNIT_TEST (test_update);
    CPPUNIT_TEST (test_select);
    CPPUNIT_TEST (test_drop);
    CPPUNIT_TEST (test_replace_attribute);
    CPPUNIT_TEST (test_insert_attribute);
    CPPUNIT_TEST (test_remove_attribute);

    CPPUNIT_TEST_SUITE_END ();

private:
    SqlDB *db;

    string db_name;

    string template_ok;
    string template_xml;

public:
    // Global flag to use either Sqlite or MySQL
    static bool mysql;

    /* --------------------------------------------------------------------- */
    /* --------------------------------------------------------------------- */

    TemplateSQLTest()
    {
        db_name  = "ONE_test_database";

        template_ok =
            "#This line is a comment\n"
            "  # Other comment\n"
            "MEMORY=345 # more comments behind an attribute\n"
            "   CPU     =         4\n"
            "#------------------------------\n"
            "#Comments in the middle\n"
            "#------------------------------\n"
            " empty_var = \n"
            "REQUIREMENTS = \"HOSTNAME = \\\"host*.com\\\"\"\n"
            " DISK= [file = path1, extra = \"disk attribute \"]\n"
            "DISK =[ FILE = \"path2\", EXTRA = str, TYPE=disk]\n"
            " graphics = [\n"
            "   VNC = \"127.0.0.1\",\n"
            "   PORT = 12\n"
            " ]\n";

        template_xml =
            "<TEMPLATE><CPU><![CDATA[4]]></CPU><DISK><EXTRA><![CDATA[disk attribute ]]></EXTRA><FILE><![CDATA[path1]]></FILE></DISK><DISK><EXTRA><![CDATA[str]]></EXTRA><FILE><![CDATA[path2]]></FILE><TYPE><![CDATA[disk]]></TYPE></DISK><EMPTY_VAR><![CDATA[]]></EMPTY_VAR><GRAPHICS><PORT><![CDATA[12]]></PORT><VNC><![CDATA[127.0.0.1]]></VNC></GRAPHICS><MEMORY><![CDATA[345]]></MEMORY><REQUIREMENTS><![CDATA[HOSTNAME = \"host*.com\"]]></REQUIREMENTS></TEMPLATE>";
    }

    ~TemplateSQLTest(){};

    /* --------------------------------------------------------------------- */
    /* --------------------------------------------------------------------- */

    void setUp()
    {
        if (mysql)
        {
            db = new MySqlDB("localhost","oneadmin","onepass",NULL);

            ostringstream   oss1;
            oss1 << "DROP DATABASE IF EXISTS " << db_name;
            db->exec(oss1);

            ostringstream   oss;
            oss << "CREATE DATABASE " << db_name;
            db->exec(oss);

            ostringstream   oss2;
            oss2 << "use " << db_name;
            db->exec(oss2);
        }
        else
        {
            unlink(db_name.c_str());

            db = new SqliteDB(db_name);
        }

        ostringstream db_bs(
                "CREATE TABLE IF NOT EXISTS template (id INTEGER, "
                "name TEXT, type INTEGER, value TEXT)");

        CPPUNIT_ASSERT(db->exec(db_bs) == 0);
    }

    void tearDown()
    {
        if (mysql)
        {
            ostringstream   oss;
            oss << "DROP DATABASE IF EXISTS " << db_name;
            db->exec(oss);
        }
        else
        {
            unlink(db_name.c_str());
        }

        if( db != 0 )
            delete db;
    }

    /* ********************************************************************* */
    /* ********************************************************************* */

    void test_insert()
    {
        char * error = 0;
        int    rc;
        string tmp;
        TSQL   t("template");

        rc = t.parse(template_ok,&error);

        if ( error != 0 )
        {
            cerr << error << endl;
            free(error);
        }

        CPPUNIT_ASSERT( rc == 0);

        CPPUNIT_ASSERT( t.insert(db) == 0 );
        CPPUNIT_ASSERT( t.id() == 0 );

        CPPUNIT_ASSERT( t.insert(db) == 0 );
        CPPUNIT_ASSERT( t.id() == 1 );

        CPPUNIT_ASSERT( t.insert(db) == 0 );
        CPPUNIT_ASSERT( t.id() == 2 );
    }

    /* --------------------------------------------------------------------- */

    void test_update()
    {

        // Update only makes sense with a table with primary keys
        //----------------------------------------------- //
        tearDown();

        if (mysql)
        {
            db = new MySqlDB("localhost","oneadmin","onepass",NULL);

            ostringstream   oss1;
            oss1 << "DROP DATABASE IF EXISTS " << db_name;
            db->exec(oss1);

            ostringstream   oss;
            oss << "CREATE DATABASE " << db_name;
            db->exec(oss);

            ostringstream   oss2;
            oss2 << "use " << db_name;
            db->exec(oss2);
        }
        else
        {
            unlink(db_name.c_str());

            db = new SqliteDB(db_name);
        }

        ostringstream db_bs("CREATE TABLE IF NOT EXISTS template (id INTEGER, "
                            "name VARCHAR(256), type INTEGER, value TEXT, "
                            "PRIMARY KEY(id,name))");

        CPPUNIT_ASSERT(db->exec(db_bs) == 0);

        //----------------------------------------------- //

        int         rc;
        string      att_name = "NEW_ATT";
        string      value = "";

        TSQL t ("template",0);
        TSQL t2("template",0);

        // Insert template t into the DB
        t.insert(db);

        // Add a new vector attribute to t, and insert it into the DB
        SingleAttribute * s_att = new SingleAttribute(att_name, "some value");
        t.insert_attribute(db, ((Attribute*)s_att));

        // Now replace the attribute's value
        s_att->replace("a new value");

        // And update the DB
        t.update(db);

        // Read from the DB to t2
        rc = t2.select(db);
        CPPUNIT_ASSERT( rc == 0 );

        t2.get(att_name, value);
        CPPUNIT_ASSERT( value == "a new value" );
    }

    /* --------------------------------------------------------------------- */

    void test_select()
    {
        char * error = 0;

        TSQL t("template");
        TSQL t2("template",0);

        string t2_xml;

        t.parse(template_ok,&error);
        t.insert(db);

        if ( error != 0 )
        {
            free(error);
        }

        CPPUNIT_ASSERT( t2.select(db) == 0 );

        t2.to_xml(t2_xml);

        CPPUNIT_ASSERT( t2_xml == template_xml );
    }

    /* --------------------------------------------------------------------- */

    void test_drop()
    {
        char *  error = 0;
        string  str   = "";

        TSQL t ("template",0);
        TSQL t2("template",0);

        t.parse(template_ok,&error);
        t.insert(db);

        if ( error != 0 )
        {
            free(error);
        }

        // Drop the template from the DB
        t.drop(db);

        // Try to read it from the DB
        CPPUNIT_ASSERT( t2.select(db) == 0 );
        // It should be empty
        CPPUNIT_ASSERT( t2.to_str(str) == "" );
    }

    /* --------------------------------------------------------------------- */

    void test_replace_attribute()
    {
        int         rc;
        string      att_name = "NEW_ATT";
        string      value = "";

        TSQL t ("template",0);
        TSQL t2("template",0);

        // Add a new vector attribute to t, and insert it into the DB
        VectorAttribute * v_att = new VectorAttribute(att_name);
        v_att->replace("A", "A value");
        v_att->replace("B", "B value");
        v_att->replace("C", "C value");

        t.insert(db);
        t.insert_attribute(db, ((Attribute*)v_att));

        // Now replace its value, with a single value attribute
        SingleAttribute * s_att = new SingleAttribute(att_name, "some value");

        t.replace_attribute(db, ((Attribute*)s_att) );

        // Read from the DB to t2
        rc = t2.select(db);
        CPPUNIT_ASSERT( rc == 0 );

        t2.get(att_name, value);
        CPPUNIT_ASSERT( value == "some value" );
    }

    /* --------------------------------------------------------------------- */

    void test_insert_attribute()
    {
        Attribute * att;
        int         rc;
        string      att_name = "NEW_ATT";
        string      value = "";

        TSQL t ("template",0);
        TSQL t2("template",0);

        // Add a new attribute to t, and insert it into the DB
        att = new SingleAttribute(att_name, "some value");

        t.insert(db);
        t.insert_attribute(db, att);

        // Read from the DB to t2
        rc = t2.select(db);
        CPPUNIT_ASSERT( rc == 0 );

        t2.get(att_name, value);
        CPPUNIT_ASSERT( value == "some value" );
    }

    /* --------------------------------------------------------------------- */

    void test_remove_attribute()
    {
        Attribute * att;
        int         rc;
        string      att_name = "NEW_ATT";
        string      value = "";

        TSQL t ("template",0);
        TSQL t2("template",0);

        // Add a new attribute to t, and insert it into the DB
        att = new SingleAttribute(att_name, "some value");

        t.insert(db);
        t.insert_attribute(db, att);

        t.remove_attribute(db, att_name);

        // Read from the DB to t2
        rc = t2.select(db);
        CPPUNIT_ASSERT( rc == 0 );

        t2.get(att_name, value);
        CPPUNIT_ASSERT( value == "" );
    }
};

/* ************************************************************************* */

bool TemplateSQLTest::mysql;

/* ************************************************************************* */
/* ************************************************************************* */
/* ************************************************************************* */

static void show_options ()
{
    cout << "Options:\n";
    cout << "    -h  --help         Show this help\n"
            "    -s  --sqlite       Run Sqlite tests (default)\n"
            "    -m  --mysql        Run MySQL tests\n"
            "    -l  --log          Keep the log file, test.log\n";
}

int main(int argc, char ** argv)
{

    // Option flags
    bool sqlite_flag = true;
    bool log_flag    = false;

    // Long options
    const struct option long_opt[] =
    {
        { "sqlite", 0,  NULL,   's'},
        { "mysql",  0,  NULL,   'm'},
        { "log",    0,  NULL,   'l'},
        { "help",   0,  NULL,   'h'}
    };

    int c;
    while ((c = getopt_long (argc, argv, "smlh", long_opt, NULL)) != -1)
        switch (c)
        {
            case 'm':
                sqlite_flag = false;
                break;
            case 'l':
                log_flag = true;
                break;
            case 'h':
                show_options();
                return 0;
        }

    CppUnit::TextUi::TestRunner tr;

    tr.addTest(TemplateSQLTest::suite());

    NebulaLog::init_log_system(NebulaLog::FILE, Log::DEBUG, "test.log");
    NebulaLog::log("Test", Log::INFO, "Test started");

    if (sqlite_flag)
    {
        TemplateSQLTest::mysql = false;
        NebulaLog::log("Test", Log::INFO, "Running Sqlite tests...");
        cout << "\nRunning Sqlite tests...\n";
    }
    else
    {
        TemplateSQLTest::mysql = true;
        NebulaLog::log("Test", Log::INFO, "Running MySQL tests...");
        cout << "\nRunning MySQL tests...\n";
    }

    tr.run();

    if (!log_flag)
        remove("test.log");

    NebulaLog::finalize_log_system();

    return 0;
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief timed V8 task
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "V8TimerTask.h"

#include "Basics/json.h"
#include "Dispatcher/Dispatcher.h"
#include "Scheduler/Scheduler.h"
#include "V8/v8-conv.h"
#include "V8Server/V8Job.h"
#include "VocBase/server.h"

using namespace std;
using namespace triagens::rest;
using namespace triagens::arango;

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////

V8TimerTask::V8TimerTask (string const& id,
                          string const& name,
                          TRI_vocbase_t* vocbase,
                          ApplicationV8* v8Dealer,
                          Scheduler* scheduler,
                          Dispatcher* dispatcher,
                          double offset,
                          string const& command,
                          TRI_json_t* parameters,
                          bool _allowUseDatabase)
  : Task(id, name),
    TimerTask(id, (offset <= 0.0 ? 0.00001 : offset)), // offset must be (at least slightly) greater than zero, otherwise
                                                       // the timertask will not execute the task at all
    _vocbase(vocbase),
    _v8Dealer(v8Dealer),
    _dispatcher(dispatcher),
    _command(command),
    _parameters(parameters),
    _created(TRI_microtime()) {

  TRI_ASSERT(vocbase != nullptr);

  // increase reference counter for the database used
  TRI_UseVocBase(_vocbase);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////

V8TimerTask::~V8TimerTask () {
  // decrease reference counter for the database used
  TRI_ReleaseVocBase(_vocbase);

  if (_parameters != nullptr) {
    TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _parameters);
  }
}

// -----------------------------------------------------------------------------
// --SECTION--                                                 TimerTask methods
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief get a task specific description in JSON format
////////////////////////////////////////////////////////////////////////////////

void V8TimerTask::getDescription (TRI_json_t* json) const {
  TimerTask::getDescription(json);

  TRI_json_t* created = TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, _created);
  TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "created", created);

  TRI_json_t* cmd = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, _command.c_str(), _command.size());
  TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "command", cmd);

  TRI_json_t* db = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, _vocbase->_name, strlen(_vocbase->_name));
  TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "database", db);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief handles the timer event
////////////////////////////////////////////////////////////////////////////////

bool V8TimerTask::handleTimeout () {
  V8Job* job = new V8Job(
    _vocbase,
    _v8Dealer,
    "(function (params) { " + _command + " } )(params);",
    _parameters,
    _allowUseDatabase);

  if (_dispatcher->addJob(job) != TRI_ERROR_NO_ERROR) {
    // just in case the dispatcher cannot accept the job (e.g. when shutting down)
    delete job;
  }

  // note: this will destroy the task (i.e. ourselves!!)
  _scheduler->destroyTask(this);

  return true;
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<commit_msg>fixed isSystem for timer tasks<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief timed V8 task
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

#include "V8TimerTask.h"

#include "Basics/json.h"
#include "Dispatcher/Dispatcher.h"
#include "Scheduler/Scheduler.h"
#include "V8/v8-conv.h"
#include "V8Server/V8Job.h"
#include "VocBase/server.h"

using namespace std;
using namespace triagens::rest;
using namespace triagens::arango;

// -----------------------------------------------------------------------------
// --SECTION--                                      constructors and destructors
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////

V8TimerTask::V8TimerTask (string const& id,
                          string const& name,
                          TRI_vocbase_t* vocbase,
                          ApplicationV8* v8Dealer,
                          Scheduler* scheduler,
                          Dispatcher* dispatcher,
                          double offset,
                          string const& command,
                          TRI_json_t* parameters,
                          bool allowUseDatabase)
  : Task(id, name),
    TimerTask(id, (offset <= 0.0 ? 0.00001 : offset)), // offset must be (at least slightly) greater than zero, otherwise
                                                       // the timertask will not execute the task at all
    _vocbase(vocbase),
    _v8Dealer(v8Dealer),
    _dispatcher(dispatcher),
    _command(command),
    _parameters(parameters),
    _created(TRI_microtime()),
    _allowUseDatabase(allowUseDatabase) {

  TRI_ASSERT(vocbase != nullptr);

  // increase reference counter for the database used
  TRI_UseVocBase(_vocbase);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////

V8TimerTask::~V8TimerTask () {
  // decrease reference counter for the database used
  TRI_ReleaseVocBase(_vocbase);

  if (_parameters != nullptr) {
    TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, _parameters);
  }
}

// -----------------------------------------------------------------------------
// --SECTION--                                                 TimerTask methods
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief get a task specific description in JSON format
////////////////////////////////////////////////////////////////////////////////

void V8TimerTask::getDescription (TRI_json_t* json) const {
  TimerTask::getDescription(json);

  TRI_json_t* created = TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, _created);
  TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "created", created);

  TRI_json_t* cmd = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, _command.c_str(), _command.size());
  TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "command", cmd);

  TRI_json_t* db = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, _vocbase->_name, strlen(_vocbase->_name));
  TRI_Insert3ObjectJson(TRI_UNKNOWN_MEM_ZONE, json, "database", db);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief handles the timer event
////////////////////////////////////////////////////////////////////////////////

bool V8TimerTask::handleTimeout () {
  V8Job* job = new V8Job(
    _vocbase,
    _v8Dealer,
    "(function (params) { " + _command + " } )(params);",
    _parameters,
    _allowUseDatabase);

  if (_dispatcher->addJob(job) != TRI_ERROR_NO_ERROR) {
    // just in case the dispatcher cannot accept the job (e.g. when shutting down)
    delete job;
  }

  // note: this will destroy the task (i.e. ourselves!!)
  _scheduler->destroyTask(this);

  return true;
}

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:
<|endoftext|>


<commit_before>// Copyright (c) 2011-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <blockencodings.h>
#include <consensus/merkle.h>
#include <chainparams.h>
#include <random.h>

#include <test/test_bitcoin.h>

#include <boost/test/unit_test.hpp>

std::vector<std::pair<uint256, CTransactionRef>> extra_txn;

struct RegtestingSetup : public TestingSetup {
    RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};

BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegtestingSetup)

static CBlock BuildBlockTestCase() {
    CBlock block;
    CMutableTransaction tx;
    tx.vin.resize(1);
    tx.vin[0].scriptSig.resize(10);
    tx.vout.resize(1);
    tx.vout[0].nValue = 42000;
    tx.nTime = 1585412782;

    block.vtx.resize(3);
    block.vtx[0] = MakeTransactionRef(tx);
    block.nTime = tx.nTime;
    block.nVersion = 42;
    block.hashPrevBlock = InsecureRand256();
    block.nBits = 0x207fffff;

    tx.vin[0].prevout.hash = InsecureRand256();
    tx.vin[0].prevout.n = 0;
    block.vtx[1] = MakeTransactionRef(tx);

    tx.vin.resize(10);
    for (size_t i = 0; i < tx.vin.size(); i++) {
        tx.vin[i].prevout.hash = InsecureRand256();
        tx.vin[i].prevout.n = 0;
    }
    block.vtx[2] = MakeTransactionRef(tx);

    bool mutated;
    block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
    assert(!mutated);
    while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
    return block;
}

// Number of shared use_counts we expect for a tx we haven't touched
// == 2 (mempool + our copy from the GetSharedTx call)
#define SHARED_TX_OFFSET 2

BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
{
    CTxMemPool pool;
    TestMemPoolEntryHelper entry;
    CBlock block(BuildBlockTestCase());

    pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
    LOCK(pool.cs);
    BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);

    // Do a simple ShortTxIDs RT
    {
        CBlockHeaderAndShortTxIDs shortIDs(block, true);

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK( partialBlock.IsTxAvailable(0));
        BOOST_CHECK(!partialBlock.IsTxAvailable(1));
        BOOST_CHECK( partialBlock.IsTxAvailable(2));

        BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);

        size_t poolSize = pool.size();
        pool.removeRecursive(*block.vtx[2]);
        BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);

        CBlock block2;
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
            partialBlock = tmp;
        }

        // Wrong transaction
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            partialBlock.FillBlock(block2, {block.vtx[2]}); // Current implementation doesn't check txn here, but don't require that
            partialBlock = tmp;
        }
        bool mutated;
        BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));

        CBlock block3;
        BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
        BOOST_CHECK(!mutated);
    }
}

class TestHeaderAndShortIDs {
    // Utility to encode custom CBlockHeaderAndShortTxIDs
public:
    CBlockHeader header;
    uint64_t nonce;
    std::vector<uint64_t> shorttxids;
    std::vector<PrefilledTransaction> prefilledtxn;

    explicit TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) {
        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << orig;
        stream >> *this;
    }
    explicit TestHeaderAndShortIDs(const CBlock& block) :
        TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {}

    uint64_t GetShortID(const uint256& txhash) const {
        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << *this;
        CBlockHeaderAndShortTxIDs base;
        stream >> base;
        return base.GetShortID(txhash);
    }

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(header);
        READWRITE(nonce);
        size_t shorttxids_size = shorttxids.size();
        READWRITE(VARINT(shorttxids_size));
        shorttxids.resize(shorttxids_size);
        for (size_t i = 0; i < shorttxids.size(); i++) {
            uint32_t lsb = shorttxids[i] & 0xffffffff;
            uint16_t msb = (shorttxids[i] >> 32) & 0xffff;
            READWRITE(lsb);
            READWRITE(msb);
            shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb);
        }
        READWRITE(prefilledtxn);
    }
};

BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
{
    CTxMemPool pool;
    TestMemPoolEntryHelper entry;
    CBlock block(BuildBlockTestCase());

    pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
    LOCK(pool.cs);
    BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);

    uint256 txhash;

    // Test with pre-forwarding tx 1, but not coinbase
    {
        TestHeaderAndShortIDs shortIDs(block);
        shortIDs.prefilledtxn.resize(1);
        shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
        shortIDs.shorttxids.resize(2);
        shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
        shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK(!partialBlock.IsTxAvailable(0));
        BOOST_CHECK( partialBlock.IsTxAvailable(1));
        BOOST_CHECK( partialBlock.IsTxAvailable(2));

        BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);

        CBlock block2;
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
            partialBlock = tmp;
        }

        // Wrong transaction
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
            partialBlock = tmp;
        }
        bool mutated;
        BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));

        CBlock block3;
        PartiallyDownloadedBlock partialBlockCopy = partialBlock;
        BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
        BOOST_CHECK(!mutated);

        txhash = block.vtx[2]->GetHash();
        block.vtx.clear();
        block2.vtx.clear();
        block3.vtx.clear();
        BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy.
    }
    BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
}

BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
{
    CTxMemPool pool;
    TestMemPoolEntryHelper entry;
    CBlock block(BuildBlockTestCase());

    pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1]));
    LOCK(pool.cs);
    BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);

    uint256 txhash;

    // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
    {
        TestHeaderAndShortIDs shortIDs(block);
        shortIDs.prefilledtxn.resize(2);
        shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
        shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
        shortIDs.shorttxids.resize(1);
        shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK( partialBlock.IsTxAvailable(0));
        BOOST_CHECK( partialBlock.IsTxAvailable(1));
        BOOST_CHECK( partialBlock.IsTxAvailable(2));

        BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);

        CBlock block2;
        PartiallyDownloadedBlock partialBlockCopy = partialBlock;
        BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
        bool mutated;
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
        BOOST_CHECK(!mutated);

        txhash = block.vtx[1]->GetHash();
        block.vtx.clear();
        block2.vtx.clear();
        BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy.
    }
    BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
}

BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
{
    CTxMemPool pool;
    CMutableTransaction coinbase;
    coinbase.vin.resize(1);
    coinbase.vin[0].scriptSig.resize(10);
    coinbase.vout.resize(1);
    coinbase.vout[0].nValue = 42000;
    coinbase.nTime = 1585412782;

    CBlock block;
    block.vtx.resize(1);
    block.vtx[0] = MakeTransactionRef(std::move(coinbase));
    block.nVersion = 42;
    block.hashPrevBlock = InsecureRand256();
    block.nBits = 0x207fffff;
    block.nTime = coinbase.nTime;

    bool mutated;
    block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
    assert(!mutated);
    while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;

    // Test simple header round-trip with only coinbase
    {
        CBlockHeaderAndShortTxIDs shortIDs(block, false);

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK(partialBlock.IsTxAvailable(0));

        CBlock block2;
        std::vector<CTransactionRef> vtx_missing;
        BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
        BOOST_CHECK(!mutated);
    }
}

BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
    BlockTransactionsRequest req1;
    req1.blockhash = InsecureRand256();
    req1.indexes.resize(4);
    req1.indexes[0] = 0;
    req1.indexes[1] = 1;
    req1.indexes[2] = 3;
    req1.indexes[3] = 4;

    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << req1;

    BlockTransactionsRequest req2;
    stream >> req2;

    BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
    BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
    BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
    BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
    BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
    BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>add block signature<commit_after>// Copyright (c) 2011-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <blockencodings.h>
#include <consensus/merkle.h>
#include <chainparams.h>
#include <random.h>

#include <test/test_bitcoin.h>

#include <boost/test/unit_test.hpp>

std::vector<std::pair<uint256, CTransactionRef>> extra_txn;

struct RegtestingSetup : public TestingSetup {
    RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};

BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegtestingSetup)

static CBlock BuildBlockTestCase() {
    CBlock block;
    CMutableTransaction tx;
    tx.vin.resize(1);
    tx.vin[0].scriptSig.resize(10);
    tx.vout.resize(1);
    tx.vout[0].nValue = 42000;
    tx.nTime = 1585412782;

    block.vtx.resize(3);
    block.vtx[0] = MakeTransactionRef(tx);
    block.nTime = tx.nTime;
    block.nVersion = 42;
    block.hashPrevBlock = InsecureRand256();
    block.nBits = 0x207fffff;

    tx.vin[0].prevout.hash = InsecureRand256();
    tx.vin[0].prevout.n = 0;
    block.vtx[1] = MakeTransactionRef(tx);

    tx.vin.resize(10);
    for (size_t i = 0; i < tx.vin.size(); i++) {
        tx.vin[i].prevout.hash = InsecureRand256();
        tx.vin[i].prevout.n = 0;
    }
    block.vtx[2] = MakeTransactionRef(tx);

    bool mutated;
    block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
    assert(!mutated);
    while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
    return block;
}

// Number of shared use_counts we expect for a tx we haven't touched
// == 2 (mempool + our copy from the GetSharedTx call)
#define SHARED_TX_OFFSET 2

BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
{
    CTxMemPool pool;
    TestMemPoolEntryHelper entry;
    CBlock block(BuildBlockTestCase());

    pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
    LOCK(pool.cs);
    BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);

    // Do a simple ShortTxIDs RT
    {
        CBlockHeaderAndShortTxIDs shortIDs(block, true);

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK( partialBlock.IsTxAvailable(0));
        BOOST_CHECK(!partialBlock.IsTxAvailable(1));
        BOOST_CHECK( partialBlock.IsTxAvailable(2));

        BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);

        size_t poolSize = pool.size();
        pool.removeRecursive(*block.vtx[2]);
        BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);

        CBlock block2;
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
            partialBlock = tmp;
        }

        // Wrong transaction
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            partialBlock.FillBlock(block2, {block.vtx[2]}); // Current implementation doesn't check txn here, but don't require that
            partialBlock = tmp;
        }
        bool mutated;
        BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));

        CBlock block3;
        BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
        BOOST_CHECK(!mutated);
    }
}

class TestHeaderAndShortIDs {
    // Utility to encode custom CBlockHeaderAndShortTxIDs
public:
    CBlockHeader header;
    uint64_t nonce;
    std::vector<unsigned char> vchBlockSig;
    std::vector<uint64_t> shorttxids;
    std::vector<PrefilledTransaction> prefilledtxn;

    explicit TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) {
        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << orig;
        stream >> *this;
    }
    explicit TestHeaderAndShortIDs(const CBlock& block) :
        TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {}

    uint64_t GetShortID(const uint256& txhash) const {
        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << *this;
        CBlockHeaderAndShortTxIDs base;
        stream >> base;
        return base.GetShortID(txhash);
    }

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(header);
        READWRITE(nonce);
        READWRITE(vchBlockSig);
        size_t shorttxids_size = shorttxids.size();
        READWRITE(VARINT(shorttxids_size));
        shorttxids.resize(shorttxids_size);
        for (size_t i = 0; i < shorttxids.size(); i++) {
            uint32_t lsb = shorttxids[i] & 0xffffffff;
            uint16_t msb = (shorttxids[i] >> 32) & 0xffff;
            READWRITE(lsb);
            READWRITE(msb);
            shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb);
        }
        READWRITE(prefilledtxn);
    }
};

BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
{
    CTxMemPool pool;
    TestMemPoolEntryHelper entry;
    CBlock block(BuildBlockTestCase());

    pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
    LOCK(pool.cs);
    BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);

    uint256 txhash;

    // Test with pre-forwarding tx 1, but not coinbase
    {
        TestHeaderAndShortIDs shortIDs(block);
        shortIDs.prefilledtxn.resize(1);
        shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
        shortIDs.shorttxids.resize(2);
        shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
        shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK(!partialBlock.IsTxAvailable(0));
        BOOST_CHECK( partialBlock.IsTxAvailable(1));
        BOOST_CHECK( partialBlock.IsTxAvailable(2));

        BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);

        CBlock block2;
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
            partialBlock = tmp;
        }

        // Wrong transaction
        {
            PartiallyDownloadedBlock tmp = partialBlock;
            partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
            partialBlock = tmp;
        }
        bool mutated;
        BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));

        CBlock block3;
        PartiallyDownloadedBlock partialBlockCopy = partialBlock;
        BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
        BOOST_CHECK(!mutated);

        txhash = block.vtx[2]->GetHash();
        block.vtx.clear();
        block2.vtx.clear();
        block3.vtx.clear();
        BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy.
    }
    BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
}

BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
{
    CTxMemPool pool;
    TestMemPoolEntryHelper entry;
    CBlock block(BuildBlockTestCase());

    pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1]));
    LOCK(pool.cs);
    BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);

    uint256 txhash;

    // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
    {
        TestHeaderAndShortIDs shortIDs(block);
        shortIDs.prefilledtxn.resize(2);
        shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
        shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
        shortIDs.shorttxids.resize(1);
        shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK( partialBlock.IsTxAvailable(0));
        BOOST_CHECK( partialBlock.IsTxAvailable(1));
        BOOST_CHECK( partialBlock.IsTxAvailable(2));

        BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);

        CBlock block2;
        PartiallyDownloadedBlock partialBlockCopy = partialBlock;
        BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
        bool mutated;
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
        BOOST_CHECK(!mutated);

        txhash = block.vtx[1]->GetHash();
        block.vtx.clear();
        block2.vtx.clear();
        BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy.
    }
    BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
}

BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
{
    CTxMemPool pool;
    CMutableTransaction coinbase;
    coinbase.vin.resize(1);
    coinbase.vin[0].scriptSig.resize(10);
    coinbase.vout.resize(1);
    coinbase.vout[0].nValue = 42000;
    coinbase.nTime = 1585412782;

    CBlock block;
    block.vtx.resize(1);
    block.vtx[0] = MakeTransactionRef(std::move(coinbase));
    block.nVersion = 42;
    block.hashPrevBlock = InsecureRand256();
    block.nBits = 0x207fffff;
    block.nTime = coinbase.nTime;

    bool mutated;
    block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
    assert(!mutated);
    while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;

    // Test simple header round-trip with only coinbase
    {
        CBlockHeaderAndShortTxIDs shortIDs(block, false);

        CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
        stream << shortIDs;

        CBlockHeaderAndShortTxIDs shortIDs2;
        stream >> shortIDs2;

        PartiallyDownloadedBlock partialBlock(&pool);
        BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
        BOOST_CHECK(partialBlock.IsTxAvailable(0));

        CBlock block2;
        std::vector<CTransactionRef> vtx_missing;
        BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
        BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
        BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
        BOOST_CHECK(!mutated);
    }
}

BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
    BlockTransactionsRequest req1;
    req1.blockhash = InsecureRand256();
    req1.indexes.resize(4);
    req1.indexes[0] = 0;
    req1.indexes[1] = 1;
    req1.indexes[2] = 3;
    req1.indexes[3] = 4;

    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << req1;

    BlockTransactionsRequest req2;
    stream >> req2;

    BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
    BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
    BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
    BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
    BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
    BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// 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 "src/main/cpp/util/strings.h"
#include "gtest/gtest.h"

namespace blaze_util {

TEST(BlazeUtil, JoinStrings) {
  vector<string> pieces;
  string output;
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("", output);

  pieces.push_back("abc");
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("abc", output);

  pieces.push_back("");
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("abc ", output);

  pieces.push_back("def");
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("abc  def", output);
}

TEST(BlazeUtil, Split) {
  string lines = "";
  vector<string> pieces = Split(lines, '\n');
  ASSERT_EQ(0, pieces.size());

  lines = "foo";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "\nfoo";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "\n\n\nfoo";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo\n";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo\n\n\n";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo\nbar";
  pieces = Split(lines, '\n');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);

  lines = "foo\n\nbar";
  pieces = Split(lines, '\n');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);
}

TEST(BlazeUtil, Replace) {
  string line = "foo\\\nbar\nbaz";
  Replace("\\\n", "", &line);
  ASSERT_EQ("foobar\nbaz", line);

  line = "foo\\\n\\\nbar";
  Replace("\\\n", "", &line);
  ASSERT_EQ("foobar", line);

  line = "foo\\\r\nbar";
  Replace("\\\r\n", "", &line);
  ASSERT_EQ("foobar", line);

  line = "\\\n\\\r\n";
  Replace("\\\n", "", &line);
  Replace("\\\r\n", "", &line);
  ASSERT_EQ("", line);

  line = "x:y:z";
  Replace(":", "_C", &line);
  ASSERT_EQ("x_Cy_Cz", line);

  line = "x_::y_:__z";
  Replace("_", "_U", &line);
  Replace(":", "_C", &line);
  ASSERT_EQ("x_U_C_Cy_U_C_U_Uz", line);
}

TEST(BlazeUtil, StripWhitespace) {
  string str = "   ";
  StripWhitespace(&str);
  ASSERT_EQ("", str);

  str = "    abc  ";
  StripWhitespace(&str);
  ASSERT_EQ("abc", str);

  str = "abc";
  StripWhitespace(&str);
  ASSERT_EQ("abc", str);
}

TEST(BlazeUtil, Tokenize) {
  vector<string> result;
  string str = "a b c";
  Tokenize(str, '#', &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("a", result[0]);
  EXPECT_EQ("b", result[1]);
  EXPECT_EQ("c", result[2]);

  str = "a 'b c'";
  Tokenize(str, '#', &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("a", result[0]);
  EXPECT_EQ("b c", result[1]);

  str = "foo# bar baz";
  Tokenize(str, '#', &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ("foo", result[0]);

  str = "foo # bar baz";
  Tokenize(str, '#', &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ("foo", result[0]);

  str = "#bar baz";
  Tokenize(str, '#', &result);
  ASSERT_EQ(0, result.size());

  str = "#";
  Tokenize(str, '#', &result);
  ASSERT_EQ(0, result.size());

  str = " \tfirst second /    ";
  Tokenize(str, 0, &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second", result[1]);
  EXPECT_EQ("/", result[2]);

  str = " \tfirst second /    ";
  Tokenize(str, '/', &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second", result[1]);

  str = "first \"second' third\" fourth";
  Tokenize(str, '/', &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second' third", result[1]);
  EXPECT_EQ("fourth", result[2]);

  str = "first 'second\" third' fourth";
  Tokenize(str, '/', &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second\" third", result[1]);
  EXPECT_EQ("fourth", result[2]);

  str = "\\ this\\ is\\ one\\'\\ token";
  Tokenize(str, 0, &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ(" this is one' token", result[0]);

  str = "\\ this\\ is\\ one\\'\\ token\\";
  Tokenize(str, 0, &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ(" this is one' token", result[0]);

  str = "unterminated \" runs to end of line";
  Tokenize(str, 0, &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("unterminated", result[0]);
  EXPECT_EQ(" runs to end of line", result[1]);

  str = "";
  Tokenize(str, 0, &result);
  ASSERT_EQ(0, result.size());

  str = "one two\'s three";
  Tokenize(str, 0, &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("one", result[0]);
  EXPECT_EQ("twos three", result[1]);

  str = "one \'two three";
  Tokenize(str, 0, &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("one", result[0]);
  EXPECT_EQ("two three", result[1]);
}

static vector<string> SplitQuoted(const string &contents,
                                  const char delimeter) {
  vector<string> result;
  SplitQuotedStringUsing(contents, delimeter, &result);
  return result;
}

TEST(BlazeUtil, SplitQuoted) {
  string lines = "";
  vector<string> pieces = SplitQuoted(lines, '\n');
  ASSERT_EQ(0, pieces.size());

  // Same behaviour without quotes as Split
  lines = "foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = " foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "   foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo   ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo bar";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);

  lines = "foo  bar";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);

  // Test with quotes
  lines = "' 'foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("' 'foo", pieces[0]);

  lines = " ' ' foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("' '", pieces[0]);
  ASSERT_EQ("foo", pieces[1]);

  lines = "foo' \\' ' ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo' \\' '", pieces[0]);

  lines = "foo'\\'\" ' ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo'\\'\" '", pieces[0]);
}

TEST(BlazeUtil, StringPrintf) {
  string out;
  StringPrintf(&out, "%s %s", "a", "b");
  EXPECT_EQ("a b", out);
}

}  // namespace blaze_util
<commit_msg>Fixed Bazel test target //src/test/cpp/util:strings_test by adding a missing "using" statement.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// 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 "src/main/cpp/util/strings.h"
#include "gtest/gtest.h"

using std::vector;

namespace blaze_util {

TEST(BlazeUtil, JoinStrings) {
  vector<string> pieces;
  string output;
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("", output);

  pieces.push_back("abc");
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("abc", output);

  pieces.push_back("");
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("abc ", output);

  pieces.push_back("def");
  JoinStrings(pieces, ' ', &output);
  ASSERT_EQ("abc  def", output);
}

TEST(BlazeUtil, Split) {
  string lines = "";
  vector<string> pieces = Split(lines, '\n');
  ASSERT_EQ(0, pieces.size());

  lines = "foo";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "\nfoo";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "\n\n\nfoo";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo\n";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo\n\n\n";
  pieces = Split(lines, '\n');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo\nbar";
  pieces = Split(lines, '\n');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);

  lines = "foo\n\nbar";
  pieces = Split(lines, '\n');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);
}

TEST(BlazeUtil, Replace) {
  string line = "foo\\\nbar\nbaz";
  Replace("\\\n", "", &line);
  ASSERT_EQ("foobar\nbaz", line);

  line = "foo\\\n\\\nbar";
  Replace("\\\n", "", &line);
  ASSERT_EQ("foobar", line);

  line = "foo\\\r\nbar";
  Replace("\\\r\n", "", &line);
  ASSERT_EQ("foobar", line);

  line = "\\\n\\\r\n";
  Replace("\\\n", "", &line);
  Replace("\\\r\n", "", &line);
  ASSERT_EQ("", line);

  line = "x:y:z";
  Replace(":", "_C", &line);
  ASSERT_EQ("x_Cy_Cz", line);

  line = "x_::y_:__z";
  Replace("_", "_U", &line);
  Replace(":", "_C", &line);
  ASSERT_EQ("x_U_C_Cy_U_C_U_Uz", line);
}

TEST(BlazeUtil, StripWhitespace) {
  string str = "   ";
  StripWhitespace(&str);
  ASSERT_EQ("", str);

  str = "    abc  ";
  StripWhitespace(&str);
  ASSERT_EQ("abc", str);

  str = "abc";
  StripWhitespace(&str);
  ASSERT_EQ("abc", str);
}

TEST(BlazeUtil, Tokenize) {
  vector<string> result;
  string str = "a b c";
  Tokenize(str, '#', &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("a", result[0]);
  EXPECT_EQ("b", result[1]);
  EXPECT_EQ("c", result[2]);

  str = "a 'b c'";
  Tokenize(str, '#', &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("a", result[0]);
  EXPECT_EQ("b c", result[1]);

  str = "foo# bar baz";
  Tokenize(str, '#', &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ("foo", result[0]);

  str = "foo # bar baz";
  Tokenize(str, '#', &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ("foo", result[0]);

  str = "#bar baz";
  Tokenize(str, '#', &result);
  ASSERT_EQ(0, result.size());

  str = "#";
  Tokenize(str, '#', &result);
  ASSERT_EQ(0, result.size());

  str = " \tfirst second /    ";
  Tokenize(str, 0, &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second", result[1]);
  EXPECT_EQ("/", result[2]);

  str = " \tfirst second /    ";
  Tokenize(str, '/', &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second", result[1]);

  str = "first \"second' third\" fourth";
  Tokenize(str, '/', &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second' third", result[1]);
  EXPECT_EQ("fourth", result[2]);

  str = "first 'second\" third' fourth";
  Tokenize(str, '/', &result);
  ASSERT_EQ(3, result.size());
  EXPECT_EQ("first", result[0]);
  EXPECT_EQ("second\" third", result[1]);
  EXPECT_EQ("fourth", result[2]);

  str = "\\ this\\ is\\ one\\'\\ token";
  Tokenize(str, 0, &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ(" this is one' token", result[0]);

  str = "\\ this\\ is\\ one\\'\\ token\\";
  Tokenize(str, 0, &result);
  ASSERT_EQ(1, result.size());
  EXPECT_EQ(" this is one' token", result[0]);

  str = "unterminated \" runs to end of line";
  Tokenize(str, 0, &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("unterminated", result[0]);
  EXPECT_EQ(" runs to end of line", result[1]);

  str = "";
  Tokenize(str, 0, &result);
  ASSERT_EQ(0, result.size());

  str = "one two\'s three";
  Tokenize(str, 0, &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("one", result[0]);
  EXPECT_EQ("twos three", result[1]);

  str = "one \'two three";
  Tokenize(str, 0, &result);
  ASSERT_EQ(2, result.size());
  EXPECT_EQ("one", result[0]);
  EXPECT_EQ("two three", result[1]);
}

static vector<string> SplitQuoted(const string &contents,
                                  const char delimeter) {
  vector<string> result;
  SplitQuotedStringUsing(contents, delimeter, &result);
  return result;
}

TEST(BlazeUtil, SplitQuoted) {
  string lines = "";
  vector<string> pieces = SplitQuoted(lines, '\n');
  ASSERT_EQ(0, pieces.size());

  // Same behaviour without quotes as Split
  lines = "foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = " foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "   foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo   ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo", pieces[0]);

  lines = "foo bar";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);

  lines = "foo  bar";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("foo", pieces[0]);
  ASSERT_EQ("bar", pieces[1]);

  // Test with quotes
  lines = "' 'foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("' 'foo", pieces[0]);

  lines = " ' ' foo";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(2, pieces.size());
  ASSERT_EQ("' '", pieces[0]);
  ASSERT_EQ("foo", pieces[1]);

  lines = "foo' \\' ' ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo' \\' '", pieces[0]);

  lines = "foo'\\'\" ' ";
  pieces = SplitQuoted(lines, ' ');
  ASSERT_EQ(1, pieces.size());
  ASSERT_EQ("foo'\\'\" '", pieces[0]);
}

TEST(BlazeUtil, StringPrintf) {
  string out;
  StringPrintf(&out, "%s %s", "a", "b");
  EXPECT_EQ("a b", out);
}

}  // namespace blaze_util
<|endoftext|>