kde-playground/kdepim-runtime/resourcetester/xmloperations.cpp
2015-04-14 22:08:21 +00:00

428 lines
13 KiB
C++

/*
Copyright (c) 2009 Volker Krause <vkrause@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "xmloperations.h"
#include "global.h"
#include "test.h"
#include <akonadi/collectionfetchjob.h>
#include <akonadi/collectionfetchscope.h>
#include <akonadi/itemfetchjob.h>
#include <akonadi/itemfetchscope.h>
#include <akonadi/xml/xmlwritejob.h>
#include <KDebug>
#include <QDir>
#include <QFileInfo>
#include <QStringList>
using namespace Akonadi;
template <typename T> QTextStream& operator<<( QTextStream &s, const QSet<T> &set )
{
s << '{';
foreach ( const T &element, set )
s << element << ", ";
s << '}';
return s;
}
QTextStream& operator<<( QTextStream &s, const QStringList &list )
{
s << '(' << list.join( ", " ) << ')';
return s;
}
XmlOperations::XmlOperations(QObject* parent) :
QObject( parent ),
mCollectionFields( 0xFF ),
mCollectionKey( RemoteId ),
mItemFields( 0xFF ),
mItemKey( ItemRemoteId ),
mNormalizeRemoteIds( false )
{
}
XmlOperations::~XmlOperations()
{
}
Item XmlOperations::getItemByRemoteId(const QString& rid)
{
return mDocument.itemByRemoteId( rid, true);
}
Collection XmlOperations::getCollectionByRemoteId(const QString& rid)
{
return mDocument.collectionByRemoteId(rid);
}
void XmlOperations::setRootCollections(const QString& resourceId)
{
CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel, this );
job->fetchScope().setAncestorRetrieval( CollectionFetchScope::All );
job->fetchScope().setResource( resourceId );
if ( job->exec() )
setRootCollections( job->collections() );
else
mErrorMsg = job->errorText();
}
void XmlOperations::setRootCollections(const Collection::List& roots)
{
mRoots = roots;
}
void XmlOperations::setXmlFile(const QString& fileName)
{
mFileName = fileName;
if ( QFileInfo( fileName ).isAbsolute() )
mDocument.loadFile( fileName );
else
mDocument.loadFile( Global::basePath() + QDir::separator() + fileName );
}
QString XmlOperations::lastError() const
{
return mErrorMsg;
}
void XmlOperations::setCollectionKey(XmlOperations::CollectionField field)
{
mCollectionKey = field;
}
void XmlOperations::setCollectionKey(const QString& fieldName)
{
const QMetaEnum me = metaObject()->enumerator( metaObject()->indexOfEnumerator( "CollectionField" ) );
setCollectionKey( static_cast<CollectionField>( me.keyToValue( fieldName.toLatin1() ) ) );
}
void XmlOperations::ignoreCollectionField(XmlOperations::CollectionField field)
{
mCollectionFields = mCollectionFields & ~field;
}
void XmlOperations::ignoreCollectionField(const QString& fieldName)
{
const QMetaEnum me = metaObject()->enumerator( metaObject()->indexOfEnumerator( "CollectionField" ) );
ignoreCollectionField( static_cast<CollectionField>( me.keyToValue( fieldName.toLatin1() ) ) );
}
void XmlOperations::setItemKey(XmlOperations::ItemField field)
{
mItemKey = field;
}
void XmlOperations::setItemKey(const QString& _fieldName)
{
QString fieldName = _fieldName;
if ( !fieldName.startsWith( QLatin1String ( "Item" ) ) )
fieldName.prepend( "Item" );
const QMetaEnum me = metaObject()->enumerator( metaObject()->indexOfEnumerator( "ItemField" ) );
setItemKey( static_cast<ItemField>( me.keyToValue( fieldName.toLatin1() ) ) );
}
void XmlOperations::ignoreItemField(XmlOperations::ItemField field)
{
mItemFields = mItemFields & ~field;
}
void XmlOperations::ignoreItemField(const QString& _fieldName)
{
QString fieldName = _fieldName;
if ( !fieldName.startsWith( "Item" ) )
fieldName.prepend( "Item" );
const QMetaEnum me = metaObject()->enumerator( metaObject()->indexOfEnumerator( "ItemField" ) );
ignoreItemField( static_cast<ItemField>( me.keyToValue( fieldName.toLatin1() ) ) );
}
void XmlOperations::setNormalizeRemoteIds(bool enable)
{
mNormalizeRemoteIds = enable;
}
bool XmlOperations::compare()
{
if ( !mDocument.isValid() ) {
mErrorMsg = mDocument.lastError();
return false;
}
if ( mRoots.isEmpty() ) {
if ( !mErrorMsg.isEmpty() )
mErrorMsg = QLatin1String( "No root collections specified." );
return false;
}
const Collection::List docRoots = mDocument.childCollections( Collection::root() );
if ( compareCollections( mRoots, docRoots ) )
return true;
XmlWriteJob *xmlJob = new XmlWriteJob( mRoots, mFileName + ".actual", this );
if ( !xmlJob->exec() )
kError() << xmlJob->errorText();
return false;
}
void XmlOperations::assertEqual()
{
if ( !compare() )
Test::instance()->fail( lastError() );
}
static QString normalizeRemoteId( const QString &in )
{
QString out( in );
if ( in.startsWith( Global:: basePath() ) ) {
out = in.mid( Global::basePath().length() );
if ( out.startsWith( QDir::separator() ) )
out = out.mid( 1 );
}
return out;
}
Collection XmlOperations::normalizeCollection( const Collection &in ) const
{
Collection out( in );
if ( mNormalizeRemoteIds )
out.setRemoteId( normalizeRemoteId( in.remoteId() ) );
QStringList l = in.contentMimeTypes();
std::sort( l.begin(), l.end() );
out.setContentMimeTypes( l );
return out;
}
Item XmlOperations::normalizeItem( const Item& in ) const
{
Item out( in );
if ( mNormalizeRemoteIds )
out.setRemoteId( normalizeRemoteId( in.remoteId() ) );
return out;
}
bool XmlOperations::compareCollections(const Collection::List& _cols, const Collection::List& _refCols)
{
Collection::List cols;
foreach ( const Collection &c, _cols )
cols.append( normalizeCollection( c ) );
Collection::List refCols;
foreach ( const Collection &c, _refCols )
refCols.append( normalizeCollection( c ) );
switch ( mCollectionKey ) {
case RemoteId:
sortEntityList( cols, &Collection::remoteId );
sortEntityList( refCols, &Collection::remoteId );
break;
case Name:
sortEntityList( cols, &Collection::name );
sortEntityList( refCols, &Collection::name );
break;
case None:
break;
default:
Q_ASSERT( false );
}
for ( int i = 0; i < cols.count(); ++i ) {
const Collection col = cols.at( i );
if ( refCols.count() <= i ) {
mErrorMsg = QString::fromLatin1( "Additional collection with remote id '%1' was found." ).arg( col.remoteId() );
return false;
}
const Collection refCol = refCols.at( i );
if ( !compareCollection( col, refCol ) )
return false;
}
if ( refCols.count() != cols.count() ) {
const Collection refCol = refCols.at( cols.count() );
mErrorMsg = QString::fromLatin1( "Collection with remote id '%1' is missing." ).arg( refCol.remoteId() );
return false;
}
return true;
}
bool XmlOperations::compareCollection(const Collection& col, const Collection& refCol)
{
// compare the two collections
if ( !compareValue( col, refCol, &Collection::remoteId, RemoteId ) ||
!compareValue( col, refCol, &Collection::contentMimeTypes, ContentMimeType ) ||
!compareValue( col, refCol, &Collection::name, Name ) )
return false;
if ( (mCollectionFields & Attributes) && !compareAttributes( col, refCol ) )
return false;
// compare child items
ItemFetchJob *ijob = new ItemFetchJob( col, this );
ijob->fetchScope().fetchAllAttributes( true );
ijob->fetchScope().fetchFullPayload( true );
if ( !ijob->exec() ) {
mErrorMsg = ijob->errorText();
return false;
}
const Item::List items = ijob->items();
const Item::List refItems = mDocument.items( refCol, true );
if ( !compareItems( items, refItems ) )
return false;
// compare child collections
CollectionFetchJob *cjob = new CollectionFetchJob( col, CollectionFetchJob::FirstLevel, this );
cjob->fetchScope().setAncestorRetrieval( CollectionFetchScope::All );
if ( !cjob->exec() ) {
mErrorMsg = cjob->errorText();
return false;
}
const Collection::List cols = cjob->collections();
const Collection::List refCols = mDocument.childCollections( refCol );
return compareCollections( cols, refCols );
}
bool XmlOperations::hasItem(const Item& _item, const Collection& _col)
{
ItemFetchJob *ijob = new ItemFetchJob( _col, this );
ijob->fetchScope().fetchAllAttributes( true );
ijob->fetchScope().fetchFullPayload( true );
if ( !ijob->exec() ) {
mErrorMsg = ijob->errorText();
return false;
}
const Item::List items = ijob->items();
for( int i = 0; i < items.count(); ++i) {
if( _item == items.at(i) ) {
return true;
}
}
return false;
}
bool XmlOperations::hasItem(const Item& _item, const QString& rid)
{
CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel, this );
Collection::List colist;
if ( job->exec() )
colist = job->collections() ;
foreach( const Collection &collection, colist ) {
if(rid == collection.remoteId()) {
return hasItem(_item, collection);
}
}
return false;
}
bool XmlOperations::compareItems(const Item::List& _items, const Item::List& _refItems)
{
Item::List items;
foreach ( const Item &i, _items )
items.append( normalizeItem( i ) );
Item::List refItems;
foreach ( const Item &i, _refItems )
refItems.append( normalizeItem( i ) );
switch ( mItemKey ) {
case ItemRemoteId:
sortEntityList( items, &Item::remoteId );
sortEntityList( refItems, &Item::remoteId );
break;
case ItemNone:
break;
default:
Q_ASSERT( false );
}
for ( int i = 0; i < items.count(); ++i ) {
const Item item = items.at( i );
if ( refItems.count() <= i ) {
mErrorMsg = QString::fromLatin1( "Additional item with remote id '%1' was found." ).arg( item.remoteId() );
return false;
}
const Item refItem = refItems.at( i );
if ( !compareItem( item, refItem ) )
return false;
}
if ( refItems.count() != items.count() ) {
const Item refItem = refItems.at( items.count() );
mErrorMsg = QString::fromLatin1( "Item with remote id '%1' is missing." ).arg( refItem.remoteId() );
return false;
}
return true;
}
bool XmlOperations::compareItem(const Item& item, const Item& refItem)
{
if ( !compareValue( item, refItem, &Item::remoteId, ItemRemoteId ) ||
!compareValue( item, refItem, &Item::mimeType, ItemMimeType ) ||
!compareValue( item, refItem, &Item::flags, ItemFlags ) ||
!compareValue( item, refItem, &Item::payloadData, ItemPayload ) )
return false;
return compareAttributes( item, refItem );
}
bool XmlOperations::compareAttributes(const Entity& entity, const Entity& refEntity)
{
Attribute::List attrs = entity.attributes();
Attribute::List refAttrs = refEntity.attributes();
std::sort( attrs.begin(), attrs.end(), boost::bind( &Attribute::type, _1 ) < boost::bind( &Attribute::type, _2 ) );
std::sort( refAttrs.begin(), refAttrs.end(), boost::bind( &Attribute::type, _1 ) < boost::bind( &Attribute::type, _2 ) );
for ( int i = 0; i < attrs.count(); ++i ) {
Attribute* attr = attrs.at( i );
if ( refAttrs.count() <= i ) {
mErrorMsg = QString::fromLatin1( "Additional attribute of type '%1' for object with remote id '%2' was found." )
.arg( QString::fromLatin1( attr->type() ) ).arg( entity.remoteId() );
return false;
}
Attribute* refAttr = refAttrs.at( i );
if ( attr->type() != refAttr->type() ) {
mErrorMsg = QString::fromLatin1( "Object with remote id '%1' misses attribute of type '%2'." )
.arg( entity.remoteId() ).arg( QString::fromLatin1( refAttr->type() ) );
return false;
}
bool result = compareValue( attr->serialized(), refAttr->serialized() );
if ( !result ) {
mErrorMsg.prepend( QString::fromLatin1( "Object with remote id '%1' differs in attribute '%2':\n" )
.arg( entity.remoteId() ).arg( QString::fromLatin1( attr->type() ) ) );
return false;
}
}
if ( refAttrs.count() != attrs.count() ) {
Attribute* refAttr = refAttrs.at( attrs.count() );
mErrorMsg = QString::fromLatin1( "Object with remote id '%1' misses attribute of type '%2'." )
.arg( entity.remoteId() ).arg( QString::fromLatin1( refAttr->type() ) );;
return false;
}
return true;
}