diff --git a/akonadi/item.cpp b/akonadi/item.cpp index 8650e5ad3..0cdb49114 100644 --- a/akonadi/item.cpp +++ b/akonadi/item.cpp @@ -1,210 +1,212 @@ /* Copyright (c) 2006 Volker Krause 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 "item.h" #include "item_p.h" #include "itemserializer_p.h" #include "protocol_p.h" #include #include using namespace Akonadi; // Change to something != RFC822 as soon as the server supports it const char* Item::FullPayload = "RFC822"; Item::Item() : Entity( new ItemPrivate ) { } Item::Item( Id id ) : Entity( new ItemPrivate( id ) ) { } Item::Item( const QString & mimeType ) : Entity( new ItemPrivate ) { d_func()->mMimeType = mimeType; } Item::Item( const Item &other ) : Entity( other ) { } Item::~Item() { } Item::Flags Item::flags() const { return d_func()->mFlags; } void Item::setFlag( const QByteArray & name ) { Q_D( Item ); d->mFlags.insert( name ); if ( !d->mFlagsOverwritten ) d->mAddedFlags.insert( name ); } void Item::clearFlag( const QByteArray & name ) { Q_D( Item ); d->mFlags.remove( name ); if ( !d->mFlagsOverwritten ) d->mDeletedFlags.insert( name ); } void Item::setFlags( const Flags &flags ) { Q_D( Item ); d->mFlags = flags; d->mFlagsOverwritten = true; } void Item::clearFlags() { Q_D( Item ); d->mFlags.clear(); d->mFlagsOverwritten = true; } QDateTime Item::modificationTime() const { return d_func()->mModificationTime; } void Item::setModificationTime( const QDateTime &datetime ) { d_func()->mModificationTime = datetime; } bool Item::hasFlag( const QByteArray & name ) const { return d_func()->mFlags.contains( name ); } QSet Item::loadedPayloadParts() const { return ItemSerializer::parts( *this ); } QByteArray Item::payloadData() const { int version = 0; QByteArray data; ItemSerializer::serialize( *this, FullPayload, data, version ); return data; } void Item::setPayloadFromData( const QByteArray &data ) { ItemSerializer::deserialize( *this, FullPayload, data, 0, false ); } int Item::revision() const { return d_func()->mRevision; } void Item::setRevision( int rev ) { d_func()->mRevision = rev; } Entity::Id Item::collectionId() const { return d_func()->mCollectionId; } void Item::setCollectionId( Entity::Id collectionId ) { d_func()->mCollectionId = collectionId; } QString Item::mimeType() const { return d_func()->mMimeType; } void Item::setSize( qint64 size ) { - d_func()->mSize = size; + Q_D( Item ); + d->mSize = size; + d->mSizeChanged = true; } qint64 Item::size() const { return d_func()->mSize; } void Item::setMimeType( const QString & mimeType ) { d_func()->mMimeType = mimeType; } bool Item::hasPayload() const { return d_func()->mPayload != 0; } KUrl Item::url( UrlType type ) const { KUrl url; url.setProtocol( QString::fromLatin1("akonadi") ); url.addQueryItem( QLatin1String( "item" ), QString::number( id() ) ); if ( type == UrlWithMimeType ) url.addQueryItem( QLatin1String( "type" ), mimeType() ); return url; } Item Item::fromUrl( const KUrl &url ) { if ( url.protocol() != QLatin1String( "akonadi" ) ) return Item(); const QString itemStr = url.queryItem( QLatin1String( "item" ) ); bool ok = false; Item::Id itemId = itemStr.toLongLong( &ok ); if ( !ok ) return Item(); return Item( itemId ); } PayloadBase* Item::payloadBase() const { return d_func()->mPayload; } void Item::setPayloadBase( PayloadBase* p ) { Q_D( Item ); delete d->mPayload; d->mPayload = p; } AKONADI_DEFINE_PRIVATE( Item ) diff --git a/akonadi/item_p.h b/akonadi/item_p.h index 768ead1f5..75bdf8226 100644 --- a/akonadi/item_p.h +++ b/akonadi/item_p.h @@ -1,97 +1,101 @@ /* Copyright (c) 2008 Tobias Koenig 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. */ #ifndef AKONADI_ITEM_P_H #define AKONADI_ITEM_P_H #include #include #include "entity_p.h" #include "itempayloadinternals_p.h" namespace Akonadi { /** * @internal */ class ItemPrivate : public EntityPrivate { public: ItemPrivate( Item::Id id = -1 ) : EntityPrivate( id ), mPayload( 0 ), mRevision( -1 ), mCollectionId( -1 ), mSize( 0 ), mModificationTime(), - mFlagsOverwritten( false ) + mFlagsOverwritten( false ), + mSizeChanged( false ) { } ItemPrivate( const ItemPrivate &other ) : EntityPrivate( other ) { mFlags = other.mFlags; mRevision = other.mRevision; mSize = other.mSize; mModificationTime = other.mModificationTime; mMimeType = other.mMimeType; if ( other.mPayload ) mPayload = other.mPayload->clone(); else mPayload = 0; mAddedFlags = other.mAddedFlags; mDeletedFlags = other.mDeletedFlags; mFlagsOverwritten = other.mFlagsOverwritten; + mSizeChanged = other.mSizeChanged; } ~ItemPrivate() { delete mPayload; } void resetChangeLog() { mFlagsOverwritten = false; mAddedFlags.clear(); mDeletedFlags.clear(); + mSizeChanged = false; } EntityPrivate *clone() const { return new ItemPrivate( *this ); } PayloadBase* mPayload; Item::Flags mFlags; int mRevision; Entity::Id mCollectionId; qint64 mSize; QDateTime mModificationTime; QString mMimeType; Item::Flags mAddedFlags; Item::Flags mDeletedFlags; bool mFlagsOverwritten; + bool mSizeChanged; }; } #endif diff --git a/akonadi/itemmodifyjob.cpp b/akonadi/itemmodifyjob.cpp index 157838f8f..c274684bc 100644 --- a/akonadi/itemmodifyjob.cpp +++ b/akonadi/itemmodifyjob.cpp @@ -1,230 +1,231 @@ /* Copyright (c) 2006 - 2007 Volker Krause 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 "itemmodifyjob.h" #include "itemmodifyjob_p.h" #include "collection.h" #include "entity_p.h" #include "imapparser_p.h" #include "itemserializer_p.h" #include "job_p.h" #include "item_p.h" #include "protocolhelper_p.h" #include using namespace Akonadi; ItemModifyJobPrivate::ItemModifyJobPrivate( ItemModifyJob *parent, const Item &item ) : JobPrivate( parent ), mItem( item ), mRevCheck( true ), mIgnorePayload( false ) { mParts = mItem.loadedPayloadParts(); } void ItemModifyJobPrivate::setClean() { mOperations.insert( Dirty ); } QByteArray ItemModifyJobPrivate::nextPartHeader() { QByteArray command; if ( !mParts.isEmpty() ) { QSetIterator it( mParts ); const QByteArray label = it.next(); mParts.remove( label ); mPendingData.clear(); int version = 0; ItemSerializer::serialize( mItem, label, mPendingData, version ); command += ' ' + ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartPayload, label, version ); command += ".SILENT"; if ( mPendingData.size() > 0 ) { command += " {" + QByteArray::number( mPendingData.size() ) + "}\n"; } else { if ( mPendingData.isNull() ) command += " NIL"; else command += " \"\""; command += nextPartHeader(); } } else { command += ")\n"; } return command; } ItemModifyJob::ItemModifyJob( const Item &item, QObject * parent ) : Job( new ItemModifyJobPrivate( this, item ), parent ) { Q_D( ItemModifyJob ); d->mOperations.insert( ItemModifyJobPrivate::RemoteId ); } ItemModifyJob::~ItemModifyJob() { } void ItemModifyJob::doStart() { Q_D( ItemModifyJob ); QList changes; foreach ( int op, d->mOperations ) { switch ( op ) { case ItemModifyJobPrivate::RemoteId: if ( !d->mItem.remoteId().isNull() ) { changes << "REMOTEID.SILENT"; changes << ImapParser::quote( d->mItem.remoteId().toUtf8() ); } break; case ItemModifyJobPrivate::Dirty: changes << "DIRTY.SILENT"; changes << "false"; break; } } if ( d->mItem.d_func()->mFlagsOverwritten ) { changes << "FLAGS.SILENT"; changes << '(' + ImapParser::join( d->mItem.flags(), " " ) + ')'; } else { if ( !d->mItem.d_func()->mAddedFlags.isEmpty() ) { changes << "+FLAGS.SILENT"; changes << '(' + ImapParser::join( d->mItem.d_func()->mAddedFlags, " " ) + ')'; } if ( !d->mItem.d_func()->mDeletedFlags.isEmpty() ) { changes << "-FLAGS.SILENT"; changes << '(' + ImapParser::join( d->mItem.d_func()->mDeletedFlags, " " ) + ')'; } } if ( !d->mItem.d_func()->mDeletedAttributes.isEmpty() ) { changes << "-PARTS.SILENT"; QList attrs; foreach ( const QByteArray &attr, d->mItem.d_func()->mDeletedAttributes ) attrs << ProtocolHelper::encodePartIdentifier( ProtocolHelper::PartAttribute, attr ); changes << '(' + ImapParser::join( attrs, " " ) + ')'; } // nothing to do if ( changes.isEmpty() && d->mParts.isEmpty() && d->mItem.attributes().isEmpty() ) { emitResult(); return; } d->mTag = d->newTag(); QByteArray command = d->mTag; command += " UID STORE " + QByteArray::number( d->mItem.id() ) + ' '; if ( !d->mRevCheck ) { command += "NOREV "; } else { command += "REV " + QByteArray::number( d->mItem.revision() ) + ' '; } - command += "SIZE " + QByteArray::number( d->mItem.size() ); + if ( d->mItem.d_func()->mSizeChanged ) + command += "SIZE " + QByteArray::number( d->mItem.size() ); command += " (" + ImapParser::join( changes, " " ); const QByteArray attrs = ProtocolHelper::attributesToByteArray( d->mItem, true ); if ( !attrs.isEmpty() ) command += ' ' + attrs; command += d->nextPartHeader(); d->writeData( command ); d->newTag(); // hack to circumvent automatic response handling } void ItemModifyJob::doHandleResponse(const QByteArray &_tag, const QByteArray & data) { Q_D( ItemModifyJob ); if ( _tag == "+" ) { // ready for literal data d->writeData( d->mPendingData ); d->writeData( d->nextPartHeader() ); return; } if ( _tag == d->mTag ) { if ( data.startsWith( "OK" ) ) { //krazy:exclude=strings QDateTime modificationDateTime; int dateTimePos = data.indexOf( "DATETIME" ); if ( dateTimePos != -1 ) { int resultPos = ImapParser::parseDateTime( data, modificationDateTime, dateTimePos + 8 ); if ( resultPos == (dateTimePos + 8) ) { kDebug( 5250 ) << "Invalid DATETIME response to STORE command: " << _tag << data; } } // increase item revision of own copy of item d->mItem.setRevision( d->mItem.revision() + 1 ); d->mItem.setModificationTime( modificationDateTime ); d->mItem.d_ptr->resetChangeLog(); } else { setError( Unknown ); setErrorText( QString::fromUtf8( data ) ); } emitResult(); return; } kDebug( 5250 ) << "Unhandled response: " << _tag << data; } void ItemModifyJob::setIgnorePayload( bool ignore ) { Q_D( ItemModifyJob ); if ( d->mIgnorePayload == ignore ) return; d->mIgnorePayload = ignore; if ( d->mIgnorePayload ) d->mParts = QSet(); else { Q_ASSERT( !d->mItem.mimeType().isEmpty() ); d->mParts = d->mItem.loadedPayloadParts(); } } bool ItemModifyJob::ignorePayload() const { Q_D( const ItemModifyJob ); return d->mIgnorePayload; } void ItemModifyJob::disableRevisionCheck() { Q_D( ItemModifyJob ); d->mRevCheck = false; } Item ItemModifyJob::item() const { Q_D( const ItemModifyJob ); return d->mItem; } #include "itemmodifyjob.moc" diff --git a/akonadi/tests/itemappendtest.cpp b/akonadi/tests/itemappendtest.cpp index bb6126f1b..d2b8b3ef2 100644 --- a/akonadi/tests/itemappendtest.cpp +++ b/akonadi/tests/itemappendtest.cpp @@ -1,204 +1,238 @@ /* Copyright (c) 2006 Volker Krause 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 "control.h" #include "itemappendtest.h" #include "testattribute.h" #include "test_utils.h" #include #include #include #include #include #include #include using namespace Akonadi; QTEST_AKONADIMAIN( ItemAppendTest, NoGUI ) void ItemAppendTest::initTestCase() { Control::start(); } void ItemAppendTest::testItemAppend_data() { QTest::addColumn( "remoteId" ); QTest::newRow( "empty" ) << QString(); QTest::newRow( "non empty" ) << QString( "remote-id" ); QTest::newRow( "whitespace" ) << QString( "remote id" ); QTest::newRow( "quotes" ) << QString ( "\"remote\" id" ); } void ItemAppendTest::testItemAppend() { const Collection testFolder1( collectionIdFromPath( "res2/space folder" ) ); QVERIFY( testFolder1.isValid() ); QFETCH( QString, remoteId ); Item ref; // for cleanup Item item( -1 ); item.setRemoteId( remoteId ); item.setMimeType( "application/octet-stream" ); item.setFlag( "TestFlag" ); item.setSize( 3456 ); ItemCreateJob *job = new ItemCreateJob( item, Collection( testFolder1 ), this ); - QVERIFY( job->exec() ); + AKVERIFYEXEC( job ); ref = job->item(); ItemFetchJob *fjob = new ItemFetchJob( testFolder1, this ); - QVERIFY( fjob->exec() ); + AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); QCOMPARE( fjob->items()[0], ref ); QCOMPARE( fjob->items()[0].remoteId(), remoteId ); QVERIFY( fjob->items()[0].flags().contains( "TestFlag" ) ); qint64 size = 3456; QCOMPARE( fjob->items()[0].size(), size ); ItemDeleteJob *djob = new ItemDeleteJob( ref, this ); - QVERIFY( djob->exec() ); + AKVERIFYEXEC( djob ); fjob = new ItemFetchJob( testFolder1, this ); - QVERIFY( fjob->exec() ); + AKVERIFYEXEC( fjob ); QVERIFY( fjob->items().isEmpty() ); } void ItemAppendTest::testContent_data() { QTest::addColumn( "data" ); QTest::newRow( "null" ) << QByteArray(); QTest::newRow( "empty" ) << QByteArray( "" ); QTest::newRow( "nullbyte" ) << QByteArray( "\0", 1 ); QTest::newRow( "nullbyte2" ) << QByteArray( "\0X", 2 ); QString utf8string = QString::fromUtf8("äöüß@€µøđ¢©®"); QTest::newRow( "utf8" ) << utf8string.toUtf8(); QTest::newRow( "newlines" ) << QByteArray("\nsome\n\nbreaked\ncontent\n\n"); QByteArray b; QTest::newRow( "big" ) << b.fill( 'a', 1 << 20 ); QTest::newRow( "bignull" ) << b.fill( '\0', 1 << 20 ); QTest::newRow( "bigcr" ) << b.fill( '\r', 1 << 20 ); QTest::newRow( "biglf" ) << b.fill( '\n', 1 << 20 ); } void ItemAppendTest::testContent() { const Collection testFolder1( collectionIdFromPath( "res2/space folder" ) ); QVERIFY( testFolder1.isValid() ); QFETCH( QByteArray, data ); Item item; item.setMimeType( "application/octet-stream" ); item.setPayload( data ); ItemCreateJob* job = new ItemCreateJob( item, testFolder1, this ); - QVERIFY( job->exec() ); + AKVERIFYEXEC( job ); Item ref = job->item(); ItemFetchJob *fjob = new ItemFetchJob( testFolder1, this ); fjob->fetchScope().fetchFullPayload(); - QVERIFY( fjob->exec() ); + AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); Item item2 = fjob->items().first(); QCOMPARE( item2.payload(), data ); QEXPECT_FAIL( "null", "Serializer cannot distinguish null vs. empty", Continue ); QCOMPARE( item2.payload().isNull(), data.isNull() ); ItemDeleteJob *djob = new ItemDeleteJob( ref, this ); - QVERIFY( djob->exec() ); + AKVERIFYEXEC( djob ); } void ItemAppendTest::testNewMimetype() { const Collection col( collectionIdFromPath( "res2/space folder" ) ); QVERIFY( col.isValid() ); Item item; item.setMimeType( "application/new-type" ); ItemCreateJob *job = new ItemCreateJob( item, col, this ); - QVERIFY( job->exec() ); + AKVERIFYEXEC( job ); item = job->item(); QVERIFY( item.isValid() ); ItemFetchJob *fetch = new ItemFetchJob( item, this ); - QVERIFY( fetch->exec() ); + AKVERIFYEXEC( fetch ); QCOMPARE( fetch->items().count(), 1 ); QCOMPARE( fetch->items().first().mimeType(), item.mimeType() ); } void ItemAppendTest::testIllegalAppend() { const Collection testFolder1( collectionIdFromPath( "res2/space folder" ) ); QVERIFY( testFolder1.isValid() ); Item item; item.setMimeType( "application/octet-stream" ); // adding item to non-existing collection ItemCreateJob *job = new ItemCreateJob( item, Collection( INT_MAX ), this ); QVERIFY( !job->exec() ); // adding item into a collection which can't handle items of this type const Collection col( collectionIdFromPath( "res1/foo/bla" ) ); QVERIFY( col.isValid() ); job = new ItemCreateJob( item, col, this ); QEXPECT_FAIL( "", "Test not yet implemented in the server.", Continue ); QVERIFY( !job->exec() ); } void ItemAppendTest::testMultipartAppend() { AttributeFactory::registerAttribute(); const Collection testFolder1( collectionIdFromPath( "res2/space folder" ) ); QVERIFY( testFolder1.isValid() ); Item item; item.setMimeType( "application/octet-stream" ); item.setPayload( "body data" ); item.attribute( Item::AddIfMissing )->data = "extra data"; item.setFlag( "TestFlag" ); ItemCreateJob *job = new ItemCreateJob( item, testFolder1, this ); - QVERIFY( job->exec() ); + AKVERIFYEXEC( job ); Item ref = job->item(); ItemFetchJob *fjob = new ItemFetchJob( ref, this ); fjob->fetchScope().fetchFullPayload(); fjob->fetchScope().fetchAttribute(); - QVERIFY( fjob->exec() ); + AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); item = fjob->items().first(); QCOMPARE( item.payload(), QByteArray( "body data" ) ); QVERIFY( item.hasAttribute() ); QCOMPARE( item.attribute()->data, QByteArray( "extra data" ) ); QVERIFY( item.flags().contains( "TestFlag" ) ); ItemDeleteJob *djob = new ItemDeleteJob( ref, this ); - QVERIFY( djob->exec() ); + AKVERIFYEXEC( djob ); +} + +void ItemAppendTest::testItemSize_data() +{ + QTest::addColumn( "item" ); + QTest::addColumn( "size" ); + + Item i( "application/octet-stream" ); + i.setPayload( QByteArray( "ABCD" ) ); + + QTest::newRow( "auto size" ) << i << 4ll; + i.setSize( 3 ); + QTest::newRow( "too small" ) << i << 4ll; + i.setSize( 10 ); + QTest::newRow( "too large" ) << i << 10ll; +} + +void ItemAppendTest::testItemSize() +{ + QFETCH( Akonadi::Item, item ); + QFETCH( qint64, size ); + + const Collection col( collectionIdFromPath( "res2/space folder" ) ); + QVERIFY( col.isValid() ); + + ItemCreateJob *create = new ItemCreateJob( item, col, this ); + AKVERIFYEXEC( create ); + Item newItem = create->item(); + + ItemFetchJob *fetch = new ItemFetchJob( newItem, this ); + AKVERIFYEXEC( fetch ); + QCOMPARE( fetch->items().count(), 1 ); + + QCOMPARE( fetch->items().first().size(), size ); } #include "itemappendtest.moc" diff --git a/akonadi/tests/itemappendtest.h b/akonadi/tests/itemappendtest.h index 995572214..317b12092 100644 --- a/akonadi/tests/itemappendtest.h +++ b/akonadi/tests/itemappendtest.h @@ -1,40 +1,42 @@ /* Copyright (c) 2006 Volker Krause 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. */ #ifndef ITEMAPPENDTEST_H #define ITEMAPPENDTEST_H #include class ItemAppendTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testItemAppend_data(); void testItemAppend(); void testContent_data(); void testContent(); void testNewMimetype(); void testIllegalAppend(); void testMultipartAppend(); + void testItemSize_data(); + void testItemSize(); }; #endif diff --git a/akonadi/tests/itemcopytest.cpp b/akonadi/tests/itemcopytest.cpp index 385c71389..fa6ecce96 100644 --- a/akonadi/tests/itemcopytest.cpp +++ b/akonadi/tests/itemcopytest.cpp @@ -1,100 +1,97 @@ /* Copyright (c) 2008 Volker Krause 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 #include #include #include #include #include #include #include #include "test_utils.h" #include using namespace Akonadi; class ItemCopyTest : public QObject { Q_OBJECT private slots: void initTestCase() { Control::start(); // switch target resources offline to reduce interference from them foreach ( Akonadi::AgentInstance agent, Akonadi::AgentManager::self()->instances() ) { if ( agent.identifier() == "akonadi_knut_resource_2" ) agent.setIsOnline( false ); } } void testCopy() { const Collection target( collectionIdFromPath( "res3" ) ); QVERIFY( target.isValid() ); ItemCopyJob *copy = new ItemCopyJob( Item( 1 ), target ); - QVERIFY( copy->exec() ); + AKVERIFYEXEC( copy ); Item source( 1 ); ItemFetchJob *sourceFetch = new ItemFetchJob( source ); - QVERIFY( sourceFetch->exec() ); + AKVERIFYEXEC( sourceFetch ); + source = sourceFetch->items().first(); ItemFetchJob *fetch = new ItemFetchJob( target ); fetch->fetchScope().fetchFullPayload(); fetch->fetchScope().fetchAllAttributes(); fetch->fetchScope().setCacheOnly( true ); - QVERIFY( fetch->exec() ); + AKVERIFYEXEC( fetch ); QCOMPARE( fetch->items().count(), 1 ); Item item = fetch->items().first(); QVERIFY( item.hasPayload() ); - QEXPECT_FAIL( "", "For some reason the size is not set correctly after " - "the fetch", Continue); QVERIFY( source.size() > 0 ); - QEXPECT_FAIL( "", "For some reason the size is not set correctly after " - "the fetch", Continue); QVERIFY( item.size() > 0 ); QCOMPARE( item.size(), source.size() ); QCOMPARE( item.attributes().count(), 1 ); QVERIFY( item.remoteId().isEmpty() ); } void testIlleagalCopy() { // empty item list ItemCopyJob *copy = new ItemCopyJob( Item::List(), Collection::root() ); QVERIFY( !copy->exec() ); // non-existing target copy = new ItemCopyJob( Item( 1 ), Collection( INT_MAX ) ); QVERIFY( !copy->exec() ); // non-existing source copy = new ItemCopyJob( Item( INT_MAX ), Collection::root() ); QVERIFY( !copy->exec() ); } }; QTEST_AKONADIMAIN( ItemCopyTest, NoGUI ) #include "itemcopytest.moc" diff --git a/akonadi/tests/itemstoretest.cpp b/akonadi/tests/itemstoretest.cpp index 8d9a4b2e2..5d2fdb0d2 100644 --- a/akonadi/tests/itemstoretest.cpp +++ b/akonadi/tests/itemstoretest.cpp @@ -1,379 +1,382 @@ /* Copyright (c) 2006 Volker Krause Copyright (c) 2007 Robert Zwerus 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 "control.h" #include "itemstoretest.h" #include "testattribute.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "test_utils.h" using namespace Akonadi; QTEST_AKONADIMAIN( ItemStoreTest, NoGUI ) static Collection res1_foo; static Collection res2; static Collection res3; void ItemStoreTest::initTestCase() { Control::start(); AttributeFactory::registerAttribute(); // get the collections we run the tests on res1_foo = Collection( collectionIdFromPath( "res1/foo" ) ); QVERIFY( res1_foo.isValid() ); res2 = Collection( collectionIdFromPath( "res2" ) ); QVERIFY( res2.isValid() ); res3 = Collection( collectionIdFromPath( "res3" ) ); QVERIFY( res3.isValid() ); // switch all resources offline to reduce interference from them foreach ( Akonadi::AgentInstance agent, Akonadi::AgentManager::self()->instances() ) agent.setIsOnline( false ); } void ItemStoreTest::testFlagChange() { ItemFetchJob *fjob = new ItemFetchJob( Item( 1 ) ); AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); Item item = fjob->items()[0]; // add a flag Item::Flags origFlags = item.flags(); Item::Flags expectedFlags = origFlags; expectedFlags.insert( "added_test_flag_1" ); item.setFlag( "added_test_flag_1" ); ItemModifyJob *sjob = new ItemModifyJob( item, this ); AKVERIFYEXEC( sjob ); fjob = new ItemFetchJob( Item( 1 ) ); AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); item = fjob->items()[0]; QCOMPARE( item.flags().count(), expectedFlags.count() ); Item::Flags diff = expectedFlags - item.flags(); QVERIFY( diff.isEmpty() ); // set flags expectedFlags.insert( "added_test_flag_2" ); item.setFlags( expectedFlags ); sjob = new ItemModifyJob( item, this ); AKVERIFYEXEC( sjob ); fjob = new ItemFetchJob( Item( 1 ) ); AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); item = fjob->items()[0]; QCOMPARE( item.flags().count(), expectedFlags.count() ); diff = expectedFlags - item.flags(); QVERIFY( diff.isEmpty() ); // remove a flag item.clearFlag( "added_test_flag_1" ); item.clearFlag( "added_test_flag_2" ); sjob = new ItemModifyJob( item, this ); AKVERIFYEXEC( sjob ); fjob = new ItemFetchJob( Item( 1 ) ); AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); item = fjob->items()[0]; QCOMPARE( item.flags().count(), origFlags.count() ); diff = origFlags - item.flags(); QVERIFY( diff.isEmpty() ); } void ItemStoreTest::testDataChange_data() { QTest::addColumn( "data" ); QTest::newRow( "simple" ) << QByteArray( "testbody" ); QTest::newRow( "null" ) << QByteArray(); QTest::newRow( "empty" ) << QByteArray( "" ); QTest::newRow( "nullbyte" ) << QByteArray( "\0", 1 ); QTest::newRow( "nullbyte2" ) << QByteArray( "\0X", 2 ); QTest::newRow( "linebreaks" ) << QByteArray( "line1\nline2\n\rline3\rline4\r\n" ); QTest::newRow( "linebreaks2" ) << QByteArray( "line1\r\nline2\r\n\r\n" ); QTest::newRow( "linebreaks3" ) << QByteArray( "line1\nline2" ); QByteArray b; QTest::newRow( "big" ) << b.fill( 'a', 1 << 20 ); QTest::newRow( "bignull" ) << b.fill( '\0', 1 << 20 ); QTest::newRow( "bigcr" ) << b.fill( '\r', 1 << 20 ); QTest::newRow( "biglf" ) << b.fill( '\n', 1 << 20 ); } void ItemStoreTest::testDataChange() { QFETCH( QByteArray, data ); Item item; ItemFetchJob *prefetchjob = new ItemFetchJob( Item( 1 ) ); - prefetchjob->exec(); + AKVERIFYEXEC( prefetchjob ); item = prefetchjob->items()[0]; item.setMimeType( "application/octet-stream" ); item.setPayload( data ); QCOMPARE( item.payload(), data ); // modify data ItemModifyJob *sjob = new ItemModifyJob( item ); AKVERIFYEXEC( sjob ); ItemFetchJob *fjob = new ItemFetchJob( Item( 1 ) ); fjob->fetchScope().fetchFullPayload(); AKVERIFYEXEC( fjob ); QCOMPARE( fjob->items().count(), 1 ); item = fjob->items()[0]; QVERIFY( item.hasPayload() ); QCOMPARE( item.payload(), data ); + QEXPECT_FAIL( "null", "STORE will not update item size on 0 sizes", Continue ); + QEXPECT_FAIL( "empty", "STORE will not update item size on 0 sizes", Continue ); + QCOMPARE( item.size(), static_cast( data.size() ) ); QEXPECT_FAIL( "null", "Serializer cannot distinguish null vs. empty", Continue ); QCOMPARE( item.payload().isNull(), data.isNull() ); } void ItemStoreTest::testRemoteId_data() { QTest::addColumn( "rid" ); QTest::addColumn( "exprid" ); QTest::newRow( "set" ) << QString( "A" ) << QString( "A" ); QTest::newRow( "no-change" ) << QString() << QString( "A" ); QTest::newRow( "clear" ) << QString( "" ) << QString( "" ); QTest::newRow( "reset" ) << QString( "A" ) << QString( "A" ); QTest::newRow( "utf8" ) << QString( "ä ö ü @" ) << QString( "ä ö ü @" ); } void ItemStoreTest::testRemoteId() { QFETCH( QString, rid ); QFETCH( QString, exprid ); // pretend to be a resource, we cannot change remote identifiers otherwise ResourceSelectJob *rsel = new ResourceSelectJob( "akonadi_knut_resource_0", this ); AKVERIFYEXEC( rsel ); ItemFetchJob *prefetchjob = new ItemFetchJob( Item( 1 ) ); AKVERIFYEXEC( prefetchjob ); Item item = prefetchjob->items()[0]; item.setRemoteId( rid ); ItemModifyJob *store = new ItemModifyJob( item, this ); AKVERIFYEXEC( store ); ItemFetchJob *fetch = new ItemFetchJob( item, this ); AKVERIFYEXEC( fetch ); QCOMPARE( fetch->items().count(), 1 ); item = fetch->items().at( 0 ); QCOMPARE( item.remoteId().toUtf8(), exprid.toUtf8() ); // no longer pretend to be a resource rsel = new ResourceSelectJob( QString(), this ); AKVERIFYEXEC( rsel ); } void ItemStoreTest::testMultiPart() { ItemFetchJob *prefetchjob = new ItemFetchJob( Item( 1 ) ); QVERIFY( prefetchjob->exec() ); QCOMPARE( prefetchjob->items().count(), 1 ); Item item = prefetchjob->items()[0]; item.setMimeType( "application/octet-stream" ); item.setPayload( "testmailbody" ); item.attribute( Item::AddIfMissing )->data = "extra"; // store item ItemModifyJob *sjob = new ItemModifyJob( item ); QVERIFY( sjob->exec() ); ItemFetchJob *fjob = new ItemFetchJob( Item( 1 ) ); fjob->fetchScope().fetchAttribute(); fjob->fetchScope().fetchFullPayload(); QVERIFY( fjob->exec() ); QCOMPARE( fjob->items().count(), 1 ); item = fjob->items()[0]; QVERIFY( item.hasPayload() ); QCOMPARE( item.payload(), QByteArray("testmailbody") ); QVERIFY( item.hasAttribute() ); QCOMPARE( item.attribute()->data, QByteArray("extra") ); // clean up item.removeAttribute( "EXTRA" ); sjob = new ItemModifyJob( item ); QVERIFY( sjob->exec() ); } void ItemStoreTest::testPartRemove() { ItemFetchJob *prefetchjob = new ItemFetchJob( Item( 2 ) ); prefetchjob->exec(); Item item = prefetchjob->items()[0]; item.setMimeType( "application/octet-stream" ); item.attribute( Item::AddIfMissing )->data = "extra"; // store item ItemModifyJob *sjob = new ItemModifyJob( item ); QVERIFY( sjob->exec() ); // fetch item and its parts (should be RFC822, HEAD and EXTRA) ItemFetchJob *fjob = new ItemFetchJob( Item( 2 ) ); fjob->fetchScope().fetchFullPayload(); fjob->fetchScope().fetchAllAttributes(); QVERIFY( fjob->exec() ); QCOMPARE( fjob->items().count(), 1 ); item = fjob->items()[0]; QCOMPARE( item.attributes().count(), 2 ); QVERIFY( item.hasAttribute() ); // remove a part item.removeAttribute(); sjob = new ItemModifyJob( item ); QVERIFY( sjob->exec() ); // fetch item again (should only have RFC822 and HEAD left) ItemFetchJob *fjob2 = new ItemFetchJob( Item( 2 ) ); fjob2->fetchScope().fetchFullPayload(); fjob2->fetchScope().fetchAllAttributes(); QVERIFY( fjob2->exec() ); QCOMPARE( fjob2->items().count(), 1 ); item = fjob2->items()[0]; QCOMPARE( item.attributes().count(), 1 ); QVERIFY( !item.hasAttribute() ); } void ItemStoreTest::testRevisionCheck() { // make sure we don't have any other collection selected // otherwise EXPUNGE doesn't work and will be triggered by // the following tests and mess up the monitor testing CollectionSelectJob *sel = new CollectionSelectJob( Collection::root(), this ); QVERIFY( sel->exec() ); // fetch same item twice Item ref( 2 ); ItemFetchJob *prefetchjob = new ItemFetchJob( ref ); QVERIFY( prefetchjob->exec() ); QCOMPARE( prefetchjob->items().count(), 1 ); Item item1 = prefetchjob->items()[0]; Item item2 = prefetchjob->items()[0]; // store first item unmodified ItemModifyJob *sjob = new ItemModifyJob( item1 ); QVERIFY( sjob->exec() ); // try to store second item ItemModifyJob *sjob2 = new ItemModifyJob( item2 ); item2.attribute( Item::AddIfMissing )->data = "extra"; QVERIFY( !sjob2->exec() ); // fetch same again prefetchjob = new ItemFetchJob( ref ); prefetchjob->exec(); item1 = prefetchjob->items()[0]; // delete item ItemDeleteJob *djob = new ItemDeleteJob( ref, this ); djob->exec(); // try to store it sjob = new ItemModifyJob( item1 ); QVERIFY( !sjob->exec() ); } void ItemStoreTest::testModificationTime() { Item item; item.setMimeType( "text/directory" ); QVERIFY( item.modificationTime().isNull() ); ItemCreateJob *job = new ItemCreateJob( item, res1_foo ); QVERIFY( job->exec() ); // The item should have a datetime set now. item = job->item(); QVERIFY( !item.modificationTime().isNull() ); QDateTime initialDateTime = item.modificationTime(); // Fetch the same item again. Item item2( item.id() ); ItemFetchJob *fjob = new ItemFetchJob( item2, this ); QVERIFY( fjob->exec() ); item2 = fjob->items().first(); QCOMPARE( initialDateTime, item2.modificationTime() ); // Lets wait 5 secs. QTest::qWait( 5000 ); // Modify the item item.attribute( Item::AddIfMissing )->data = "extra"; ItemModifyJob *mjob = new ItemModifyJob( item ); QVERIFY( mjob->exec() ); // The item should still have a datetime set and that date should be somewhere // after the initialDateTime. item = mjob->item(); QVERIFY( !item.modificationTime().isNull() ); QVERIFY( initialDateTime < item.modificationTime() ); // Fetch the item after modification. Item item3( item.id() ); ItemFetchJob *fjob2 = new ItemFetchJob( item3, this ); QVERIFY( fjob2->exec() ); // item3 should have the same modification time as item. item3 = fjob2->items().first(); QCOMPARE( item3.modificationTime(), item.modificationTime() ); // Clean up ItemDeleteJob *idjob = new ItemDeleteJob( item, this ); QVERIFY( idjob->exec() ); } void ItemStoreTest::testRemoteIdRace() { // Create an item and store it Item item; item.setMimeType( "text/directory" ); ItemCreateJob *job = new ItemCreateJob( item, res1_foo ); QVERIFY( job->exec() ); // Fetch the same item again. It should not have a remote Id yet, as the resource // is offline. // The remote id should be null, not only empty, so that item modify jobs with this // item don't overwrite the remote id. Item item2( job->item().id() ); ItemFetchJob *fetchJob = new ItemFetchJob( item2 ); QVERIFY( fetchJob->exec() ); QCOMPARE( fetchJob->items().size(), 1 ); QVERIFY( fetchJob->items().first().remoteId().isNull() ); } #include "itemstoretest.moc"