QGIS/tests/src/core/testqgscomposergroup.cpp
Nyall Dawson 885269ee78 Only create composer item config widgets on demand
Instead of creating them for all items when composer window
is opened, instead just create and destroy them when
required. None are too heavy to have a noticable delay
when selecting items, but in contrast keeping them around
forever is wasteful on memory.

Also clean up a lot of duplicate composer/composition
code and remove unnecessary signals from api (use a single
itemAdded signal instead of multiple signals for every
item type)
2017-03-21 10:32:53 +10:00

438 lines
15 KiB
C++

/***************************************************************************
testqgscomposergroup.cpp
-----------------------
begin : October 2014
copyright : (C) 2014 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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 "qgscomposeritemgroup.h"
#include "qgscomposerlabel.h"
#include "qgscomposerpolygon.h"
#include "qgscomposition.h"
#include "qgsmultirenderchecker.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsproject.h"
#include <QObject>
#include <QtTest/QSignalSpy>
#include "qgstest.h"
class TestQgsComposerGroup : public QObject
{
Q_OBJECT
public:
TestQgsComposerGroup()
: mComposition( 0 )
, mMapSettings( 0 )
, mItem1( 0 )
, mItem2( 0 )
, mGroup( 0 )
{}
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void createGroup(); //test grouping items
void ungroup(); //test ungrouping items
void deleteGroup(); //test deleting group works
void undoRedo(); //test that group/ungroup undo/redo commands don't crash
private:
void dumpUndoStack( const QUndoStack &, QString prefix = QLatin1String( QLatin1String( "" ) ) ) const;
QgsComposition *mComposition = nullptr;
QgsMapSettings *mMapSettings = nullptr;
QgsComposerLabel *mItem1 = nullptr;
QgsComposerLabel *mItem2 = nullptr;
QgsComposerItemGroup *mGroup = nullptr;
QString mReport;
};
// private
void TestQgsComposerGroup::dumpUndoStack( const QUndoStack &us, QString prefix ) const
{
if ( ! prefix.isEmpty() ) prefix += QLatin1String( ": " );
for ( int i = 0; i < us.count(); ++i )
{
QgsDebugMsg( QString( "%4US %1: %2%3" )
.arg( i ). arg( i >= us.index() ? "-" : "",
us.text( i ), prefix ) );
}
}
void TestQgsComposerGroup::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();
mMapSettings = new QgsMapSettings();
mComposition = new QgsComposition( QgsProject::instance() );
mComposition->setPaperSize( 297, 210 ); //A4 landscape
//create some items
mItem1 = new QgsComposerLabel( mComposition );
mComposition->addItem( mItem1 );
mItem2 = new QgsComposerLabel( mComposition );
mComposition->addItem( mItem2 );
mGroup = 0;
mReport = QStringLiteral( "<h1>Composer Grouped Item Tests</h1>\n" );
}
void TestQgsComposerGroup::cleanupTestCase()
{
delete mComposition;
delete mMapSettings;
QgsApplication::exitQgis();
QString myReportFile = QDir::tempPath() + "/qgistest.html";
QFile myFile( myReportFile );
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
{
QTextStream myQTextStream( &myFile );
myQTextStream << mReport;
myFile.close();
}
}
void TestQgsComposerGroup::init()
{
}
void TestQgsComposerGroup::cleanup()
{
}
void TestQgsComposerGroup::createGroup()
{
//group items
QList<QgsComposerItem *> items;
items << mItem1 << mItem2;
mGroup = mComposition->groupItems( items );
//check result
QVERIFY( mGroup );
QCOMPARE( mGroup->items().size(), 2 );
QVERIFY( mGroup->items().contains( mItem1 ) );
QVERIFY( mGroup->items().contains( mItem2 ) );
QVERIFY( mItem1->isGroupMember() );
QVERIFY( mItem2->isGroupMember() );
}
void TestQgsComposerGroup::ungroup()
{
//test ungrouping items
//simple tests - check that we don't crash
mComposition->ungroupItems( 0 ); //no item
//ungroup mGroup
QList<QgsComposerItem *> ungroupedItems;
ungroupedItems = mComposition->ungroupItems( mGroup );
QCOMPARE( ungroupedItems.size(), 2 );
QVERIFY( ungroupedItems.contains( mItem1 ) );
QVERIFY( ungroupedItems.contains( mItem2 ) );
QVERIFY( !mItem1->isGroupMember() );
QVERIFY( !mItem2->isGroupMember() );
//should also be no groups left in the composition
QList<QgsComposerItemGroup *> groups;
mComposition->composerItems( groups );
QCOMPARE( groups.size(), 0 );
}
void TestQgsComposerGroup::deleteGroup()
{
//group items
QList<QgsComposerItem *> groupItems;
groupItems << mItem1 << mItem2;
mGroup = mComposition->groupItems( groupItems );
QList<QgsComposerItem *> items;
mComposition->composerItems( items );
//expect initially 4 items, as paper counts as an item
QCOMPARE( items.size(), 4 );
//test that deleting group also removes all grouped items
mComposition->removeComposerItem( mGroup );
mComposition->composerItems( items );
//expect a single item (paper item)
QCOMPARE( items.size(), 1 );
QVERIFY( mItem1->isRemoved() );
QVERIFY( mItem2->isRemoved() );
QVERIFY( mGroup->isRemoved() );
}
Q_DECLARE_METATYPE( QgsComposerItemGroup * )
Q_DECLARE_METATYPE( QgsComposerPolygon * )
Q_DECLARE_METATYPE( QgsComposerItem * )
void TestQgsComposerGroup::undoRedo()
{
QgsComposerPolygon *item1, *item2;
int polygonsAdded = 0;
int groupsAdded = 0;
int itemsRemoved = 0;
qRegisterMetaType<QgsComposerPolygon *>();
QSignalSpy spyPolygonAdded( mComposition, &QgsComposition::itemAdded );
QCOMPARE( spyPolygonAdded.count(), 0 );
qRegisterMetaType<QgsComposerItemGroup *>();
QSignalSpy spyGroupAdded( mComposition, &QgsComposition::composerItemGroupAdded );
QCOMPARE( spyGroupAdded.count(), 0 );
qRegisterMetaType<QgsComposerItem *>();
QSignalSpy spyItemRemoved( mComposition, &QgsComposition::itemRemoved );
QCOMPARE( spyItemRemoved.count(), 0 );
//test for crash when undo/redoing with groups
// Set initial condition
QUndoStack *us = mComposition->undoStack();
QgsDebugMsg( QString( "clearing" ) );
us->clear();
QgsDebugMsg( QString( "clearing completed" ) );
QList<QgsComposerItem *> items;
mComposition->composerItems( items );
QCOMPARE( items.size(), 1 ); // paper only
QgsDebugMsg( QString( "clear stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
//create some items
item1 = new QgsComposerPolygon( QPolygonF( QRectF( 0, 0, 1, 1 ) ), mComposition ); //QgsComposerLabel( mComposition );
mComposition->addComposerPolygon( item1 );
QCOMPARE( spyPolygonAdded.count(), ++polygonsAdded );
item2 = new QgsComposerPolygon( QPolygonF( QRectF( -1, -2, 1, 1 ) ), mComposition ); //QgsComposerLabel( mComposition );
mComposition->addComposerPolygon( item2 );
QCOMPARE( spyPolygonAdded.count(), ++polygonsAdded );
mComposition->composerItems( items );
QCOMPARE( items.size(), 3 ); // paper, 2 shapes
QgsDebugMsg( QString( "addedItems stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
//dumpUndoStack(*us, "after initial items addition");
//group items
items.clear();
items << item1 << item2;
mGroup = mComposition->groupItems( items );
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
QCOMPARE( mGroup->items().size(), 2 );
mComposition->composerItems( items );
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
QVERIFY( ! item1->isRemoved() );
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
QVERIFY( ! item2->isRemoved() );
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
QVERIFY( ! mGroup->isRemoved() );
QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
//dumpUndoStack(*us, "after initial items addition");
//move group
QgsDebugMsg( QString( "moving group" ) );
mGroup->beginCommand( QStringLiteral( "move group" ) );
mGroup->move( 10.0, 20.0 );
mGroup->endCommand();
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), groupsAdded );
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
QgsDebugMsg( QString( "groupItems stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
QCOMPARE( mGroup->items().size(), 2 );
mComposition->composerItems( items );
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
QVERIFY( ! item1->isRemoved() );
QCOMPARE( item1->pos(), QPointF( 10, 20 ) );
QVERIFY( ! item2->isRemoved() );
QCOMPARE( item2->pos(), QPointF( 9, 18 ) );
QVERIFY( ! mGroup->isRemoved() );
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
//ungroup
QgsDebugMsg( QString( "ungrouping" ) );
mComposition->ungroupItems( mGroup );
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), groupsAdded );
QCOMPARE( spyItemRemoved.count(), ++itemsRemoved );
mComposition->composerItems( items );
QCOMPARE( items.size(), 3 ); // paper, 2 shapes
QVERIFY( ! item1->isRemoved() );
QVERIFY( ! item2->isRemoved() );
QVERIFY( mGroup->isRemoved() );
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) ); // should not rely on this
//dumpUndoStack(*us, "after ungroup");
// US 0: Items grouped
// US 1: move group
// US 2: Remove item group
//undo (groups again) -- crashed here before #11371 got fixed
QgsDebugMsg( QString( "undo ungrouping" ) );
us->undo();
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
QCOMPARE( mGroup->items().size(), 2 ); // WARNING: might not be alive anymore
mComposition->composerItems( items );
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
QVERIFY( ! item1->isRemoved() );
QCOMPARE( item1->pos(), QPointF( 10, 20 ) );
QVERIFY( ! item2->isRemoved() );
QCOMPARE( item2->pos(), QPointF( 9, 18 ) );
QVERIFY( ! mGroup->isRemoved() );
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
//dumpUndoStack(*us, "after undo ungroup");
// US 0: Items grouped
// US 1: move group
// US 2: -Remove item group
//remove group
QgsDebugMsg( QString( "remove group" ) );
mComposition->removeComposerItem( mGroup, true, true );
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), groupsAdded );
itemsRemoved += 3; // the group and the two items
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
mComposition->composerItems( items );
QCOMPARE( items.size(), 1 ); // paper only
QgsDebugMsg( QString( "remove stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
//dumpUndoStack(*us, "after remove group");
// US 0: Items grouped
// US 1: move group
// US 2: Remove item group
//undo remove group
QgsDebugMsg( QString( "undo remove group" ) );
us->undo();
polygonsAdded += 2;
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
mComposition->composerItems( items );
QCOMPARE( mGroup->items().size(), 2 );
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
QgsDebugMsg( QString( "undo stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
//dumpUndoStack(*us, "after undo remove group");
// US 0: Items grouped
// US 1: move group
// US 2: -Remove item group
//undo move group
QgsDebugMsg( QString( "undo move group" ) );
us->undo();
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), groupsAdded );
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
QCOMPARE( mGroup->items().size(), 2 );
mComposition->composerItems( items );
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
QCOMPARE( item1->isGroupMember(), true );
QCOMPARE( item2->isGroupMember(), true );
QVERIFY( ! item1->isRemoved() );
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
QVERIFY( ! item2->isRemoved() );
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
QVERIFY( ! mGroup->isRemoved() );
QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
//dumpUndoStack(*us, "after undo move group");
// US 0: Items grouped
// US 1: -move group
// US 2: -Remove item group
//undo group
QgsDebugMsg( QString( "undo group" ) );
us->undo();
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), groupsAdded );
QCOMPARE( spyItemRemoved.count(), ++itemsRemoved );
//QCOMPARE( mGroup->items().size(), 2 ); // not important
mComposition->composerItems( items );
QCOMPARE( items.size(), 3 ); // paper, 2 shapes
QCOMPARE( item1->isGroupMember(), false );
QCOMPARE( item2->isGroupMember(), false );
QVERIFY( ! item1->isRemoved() );
QCOMPARE( item1->pos(), QPointF( 0, 0 ) );
QVERIFY( ! item2->isRemoved() );
QCOMPARE( item2->pos(), QPointF( -1, -2 ) );
QVERIFY( mGroup->isRemoved() );
//QCOMPARE( mGroup->pos(), QPointF( -1, -2 ) );
//dumpUndoStack(*us, "after undo group");
// US 0: -Items grouped
// US 1: -move group
// US 2: -Remove item group
//redo group
QgsDebugMsg( QString( "redo group" ) );
us->redo();
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), ++groupsAdded );
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
mComposition->composerItems( items );
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
QCOMPARE( item1->isGroupMember(), true );
QCOMPARE( item2->isGroupMember(), true );
//// QCOMPARE( mGroup->pos(), QPointF( 0, 0 ) ); // getting nan,nan here
//dumpUndoStack(*us, "after redo group");
// US 0: Items grouped
// US 1: -move group
// US 2: -Remove item group
//redo move group
QgsDebugMsg( QString( "redo move group" ) );
us->redo();
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), groupsAdded );
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
mComposition->composerItems( items );
QCOMPARE( items.size(), 4 ); // paper, 2 shapes, 1 group
QCOMPARE( item1->isGroupMember(), true );
QCOMPARE( item2->isGroupMember(), true );
QCOMPARE( mGroup->pos(), QPointF( 9, 18 ) );
//dumpUndoStack(*us, "after redo move group");
// US 0: Items grouped
// US 1: move group
// US 2: -Remove item group
//redo remove group
QgsDebugMsg( QString( "redo remove group" ) );
us->redo();
QCOMPARE( spyPolygonAdded.count(), polygonsAdded );
QCOMPARE( spyGroupAdded.count(), groupsAdded );
itemsRemoved += 3; // 1 group, 2 contained items
QCOMPARE( spyItemRemoved.count(), itemsRemoved );
mComposition->composerItems( items );
QCOMPARE( items.size(), 1 ); // paper only
QgsDebugMsg( QString( "undo stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
//dumpUndoStack(*us, "after redo remove group");
// US 0: Items grouped
// US 1: move group
// US 2: Remove item group
//unwind the whole stack
us->clear();
QgsDebugMsg( QString( "clear stack count:%1 index:%2" ) .arg( us->count() ) .arg( us->index() ) );
}
QGSTEST_MAIN( TestQgsComposerGroup )
#include "testqgscomposergroup.moc"