~ubuntu-branches/ubuntu/precise/gwenview/precise-proposed

« back to all changes in this revision

Viewing changes to tests/auto/documenttest.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-12-15 14:17:54 UTC
  • mto: This revision was merged to the branch mainline in revision 12.
  • Revision ID: package-import@ubuntu.com-20111215141754-z043hyx69dulbggf
Tags: upstream-4.7.90
ImportĀ upstreamĀ versionĀ 4.7.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Gwenview: an image viewer
 
3
Copyright 2007 AurĆ©lien GĆ¢teau <agateau@kde.org>
 
4
 
 
5
This program is free software; you can redistribute it and/or
 
6
modify it under the terms of the GNU General Public License
 
7
as published by the Free Software Foundation; either version 2
 
8
of the License, or (at your option) any later version.
 
9
 
 
10
This program is distributed in the hope that it will be useful,
 
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
GNU General Public License for more details.
 
14
 
 
15
You should have received a copy of the GNU General Public License
 
16
along with this program; if not, write to the Free Software
 
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
18
 
 
19
*/
 
20
// Qt
 
21
#include <QConicalGradient>
 
22
#include <QImage>
 
23
#include <QPainter>
 
24
 
 
25
// KDE
 
26
#include <kdebug.h>
 
27
#include <kjobuidelegate.h>
 
28
#include <kio/netaccess.h>
 
29
#include <qtest_kde.h>
 
30
 
 
31
// Local
 
32
#include "../lib/abstractimageoperation.h"
 
33
#include "../lib/document/abstractdocumenteditor.h"
 
34
#include "../lib/document/documentjob.h"
 
35
#include "../lib/document/documentfactory.h"
 
36
#include "../lib/imagemetainfomodel.h"
 
37
#include "../lib/imageutils.h"
 
38
#include "../lib/transformimageoperation.h"
 
39
#include "testutils.h"
 
40
 
 
41
#include <exiv2/exif.hpp>
 
42
 
 
43
#include "documenttest.moc"
 
44
 
 
45
QTEST_KDEMAIN(DocumentTest, GUI)
 
46
 
 
47
using namespace Gwenview;
 
48
 
 
49
static void waitUntilMetaInfoLoaded(Document::Ptr doc)
 
50
{
 
51
    while (doc->loadingState() < Document::MetaInfoLoaded) {
 
52
        QTest::qWait(100);
 
53
    }
 
54
}
 
55
 
 
56
static bool waitUntilJobIsDone(DocumentJob* job)
 
57
{
 
58
    JobWatcher watcher(job);
 
59
    watcher.wait();
 
60
    return watcher.error() == KJob::NoError;
 
61
}
 
62
 
 
63
void DocumentTest::initTestCase()
 
64
{
 
65
    qRegisterMetaType<KUrl>("KUrl");
 
66
}
 
67
 
 
68
void DocumentTest::init()
 
69
{
 
70
    DocumentFactory::instance()->clearCache();
 
71
}
 
72
 
 
73
void DocumentTest::testLoad()
 
74
{
 
75
    QFETCH(QString, fileName);
 
76
    QFETCH(QByteArray, expectedFormat);
 
77
    QFETCH(int, expectedKindInt);
 
78
    QFETCH(bool, expectedIsAnimated);
 
79
    QFETCH(QImage, expectedImage);
 
80
    MimeTypeUtils::Kind expectedKind = MimeTypeUtils::Kind(expectedKindInt);
 
81
 
 
82
    KUrl url = urlForTestFile(fileName);
 
83
    if (expectedKind != MimeTypeUtils::KIND_SVG_IMAGE) {
 
84
        QVERIFY2(!expectedImage.isNull(), "Could not load test image");
 
85
    }
 
86
 
 
87
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
88
    QSignalSpy spy(doc.data(), SIGNAL(isAnimatedUpdated()));
 
89
    doc->startLoadingFullImage();
 
90
    doc->waitUntilLoaded();
 
91
    QCOMPARE(doc->loadingState(), Document::Loaded);
 
92
 
 
93
    QCOMPARE(expectedKind, doc->kind());
 
94
    QCOMPARE(expectedIsAnimated, doc->isAnimated());
 
95
    QCOMPARE(spy.count(), doc->isAnimated() ? 1 : 0);
 
96
    if (doc->kind() == MimeTypeUtils::KIND_RASTER_IMAGE) {
 
97
        QCOMPARE(expectedImage, doc->image());
 
98
        QCOMPARE(expectedFormat, doc->format());
 
99
    }
 
100
}
 
101
 
 
102
#define NEW_ROW(fileName, format, kind, isAnimated) QTest::newRow(fileName) << fileName << QByteArray(format) << int(kind) << isAnimated << QImage(pathForTestFile(fileName))
 
103
void DocumentTest::testLoad_data()
 
104
{
 
105
    QTest::addColumn<QString>("fileName");
 
106
    QTest::addColumn<QByteArray>("expectedFormat");
 
107
    QTest::addColumn<int>("expectedKindInt");
 
108
    QTest::addColumn<bool>("expectedIsAnimated");
 
109
    QTest::addColumn<QImage>("expectedImage");
 
110
 
 
111
    NEW_ROW("test.png",
 
112
            "png", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
113
    NEW_ROW("160216_no_size_before_decoding.eps",
 
114
            "eps", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
115
    NEW_ROW("160382_corrupted.jpeg",
 
116
            "jpeg", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
117
    NEW_ROW("test.svg",
 
118
            "", MimeTypeUtils::KIND_SVG_IMAGE, false);
 
119
    // FIXME: Test svgz
 
120
    NEW_ROW("1x10k.png",
 
121
            "png", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
122
    NEW_ROW("1x10k.jpg",
 
123
            "jpeg", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
124
    NEW_ROW("4frames.gif",
 
125
            "gif", MimeTypeUtils::KIND_RASTER_IMAGE, true);
 
126
    NEW_ROW("1frame.gif",
 
127
            "gif", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
128
    NEW_ROW("185523_1frame_with_graphic_control_extension.gif",
 
129
            "gif", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
130
    NEW_ROW("test.xcf",
 
131
            "xcf", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
132
    NEW_ROW("188191_does_not_load.tga",
 
133
            "tga", MimeTypeUtils::KIND_RASTER_IMAGE, false);
 
134
}
 
135
#undef NEW_ROW
 
136
 
 
137
void DocumentTest::testLoadTwoPasses()
 
138
{
 
139
    KUrl url = urlForTestFile("test.png");
 
140
    QImage image;
 
141
    bool ok = image.load(url.toLocalFile());
 
142
    QVERIFY2(ok, "Could not load 'test.png'");
 
143
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
144
    waitUntilMetaInfoLoaded(doc);
 
145
    QVERIFY2(doc->image().isNull(), "Image shouldn't have been loaded at this time");
 
146
    QCOMPARE(doc->format().data(), "png");
 
147
    doc->startLoadingFullImage();
 
148
    doc->waitUntilLoaded();
 
149
    QCOMPARE(image, doc->image());
 
150
}
 
151
 
 
152
void DocumentTest::testLoadEmpty()
 
153
{
 
154
    KUrl url = urlForTestFile("empty.png");
 
155
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
156
    while (doc->loadingState() <= Document::KindDetermined) {
 
157
        QTest::qWait(100);
 
158
    }
 
159
    QCOMPARE(doc->loadingState(), Document::LoadingFailed);
 
160
}
 
161
 
 
162
#define NEW_ROW(fileName) QTest::newRow(fileName) << fileName
 
163
void DocumentTest::testLoadDownSampled_data()
 
164
{
 
165
    QTest::addColumn<QString>("fileName");
 
166
 
 
167
    NEW_ROW("orient6.jpg");
 
168
    NEW_ROW("1x10k.jpg");
 
169
}
 
170
#undef NEW_ROW
 
171
 
 
172
void DocumentTest::testLoadDownSampled()
 
173
{
 
174
    // Note: for now we only support down sampling on jpeg, do not use test.png
 
175
    // here
 
176
    QFETCH(QString, fileName);
 
177
    KUrl url = urlForTestFile(fileName);
 
178
    QImage image;
 
179
    bool ok = image.load(url.toLocalFile());
 
180
    QVERIFY2(ok, "Could not load test image");
 
181
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
182
 
 
183
    QSignalSpy downSampledImageReadySpy(doc.data(), SIGNAL(downSampledImageReady()));
 
184
    QSignalSpy loadingFailedSpy(doc.data(), SIGNAL(loadingFailed(KUrl)));
 
185
    QSignalSpy loadedSpy(doc.data(), SIGNAL(loaded(KUrl)));
 
186
    bool ready = doc->prepareDownSampledImageForZoom(0.2);
 
187
    QVERIFY2(!ready, "There should not be a down sampled image at this point");
 
188
 
 
189
    while (downSampledImageReadySpy.count() == 0 && loadingFailedSpy.count() == 0 && loadedSpy.count() == 0) {
 
190
        QTest::qWait(100);
 
191
    }
 
192
    QImage downSampledImage = doc->downSampledImageForZoom(0.2);
 
193
    QVERIFY2(!downSampledImage.isNull(), "Down sampled image should not be null");
 
194
 
 
195
    QSize expectedSize = doc->size() / 4;
 
196
    if (expectedSize.isEmpty()) {
 
197
        expectedSize = image.size();
 
198
    }
 
199
    QCOMPARE(downSampledImage.size(), expectedSize);
 
200
}
 
201
 
 
202
/**
 
203
 * Down sampling is not supported on png. We should get a complete image
 
204
 * instead.
 
205
 */
 
206
void DocumentTest::testLoadDownSampledPng()
 
207
{
 
208
    KUrl url = urlForTestFile("test.png");
 
209
    QImage image;
 
210
    bool ok = image.load(url.toLocalFile());
 
211
    QVERIFY2(ok, "Could not load test image");
 
212
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
213
 
 
214
    LoadingStateSpy stateSpy(doc);
 
215
    connect(doc.data(), SIGNAL(loaded(KUrl)), &stateSpy, SLOT(readState()));
 
216
 
 
217
    bool ready = doc->prepareDownSampledImageForZoom(0.2);
 
218
    QVERIFY2(!ready, "There should not be a down sampled image at this point");
 
219
 
 
220
    doc->waitUntilLoaded();
 
221
 
 
222
    QCOMPARE(stateSpy.mCallCount, 1);
 
223
    QCOMPARE(stateSpy.mState, Document::Loaded);
 
224
}
 
225
 
 
226
void DocumentTest::testLoadRemote()
 
227
{
 
228
    KUrl url = setUpRemoteTestDir("test.png");
 
229
    if (!url.isValid()) {
 
230
        return;
 
231
    }
 
232
    url.addPath("test.png");
 
233
 
 
234
    QVERIFY2(KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, 0), "test url not found");
 
235
 
 
236
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
237
    doc->startLoadingFullImage();
 
238
    doc->waitUntilLoaded();
 
239
    QImage image = doc->image();
 
240
    QCOMPARE(image.width(), 150);
 
241
    QCOMPARE(image.height(), 100);
 
242
}
 
243
 
 
244
void DocumentTest::testLoadAnimated()
 
245
{
 
246
    KUrl srcUrl = urlForTestFile("40frames.gif");
 
247
    Document::Ptr doc = DocumentFactory::instance()->load(srcUrl);
 
248
    QSignalSpy spy(doc.data(), SIGNAL(imageRectUpdated(QRect)));
 
249
    doc->startLoadingFullImage();
 
250
    doc->waitUntilLoaded();
 
251
    QVERIFY(doc->isAnimated());
 
252
 
 
253
    // Test we receive only one imageRectUpdated() until animation is started
 
254
    // (the imageRectUpdated() is triggered by the loading of the first image)
 
255
    QTest::qWait(1000);
 
256
    QCOMPARE(spy.count(), 1);
 
257
 
 
258
    // Test we now receive some imageRectUpdated()
 
259
    doc->startAnimation();
 
260
    QTest::qWait(1000);
 
261
    int count = spy.count();
 
262
    doc->stopAnimation();
 
263
    QVERIFY2(count > 0, "No imageRectUpdated() signal received");
 
264
 
 
265
    // Test we do not receive imageRectUpdated() anymore
 
266
    QTest::qWait(1000);
 
267
    QCOMPARE(count, spy.count());
 
268
 
 
269
    // Start again, we should receive imageRectUpdated() again
 
270
    doc->startAnimation();
 
271
    QTest::qWait(1000);
 
272
    QVERIFY2(spy.count() > count, "No imageRectUpdated() signal received after restarting");
 
273
}
 
274
 
 
275
void DocumentTest::testSaveRemote()
 
276
{
 
277
    KUrl dstUrl = setUpRemoteTestDir();
 
278
    if (!dstUrl.isValid()) {
 
279
        return;
 
280
    }
 
281
 
 
282
    KUrl srcUrl = urlForTestFile("test.png");
 
283
    Document::Ptr doc = DocumentFactory::instance()->load(srcUrl);
 
284
    doc->startLoadingFullImage();
 
285
    doc->waitUntilLoaded();
 
286
 
 
287
    dstUrl.addPath("testSaveRemote.png");
 
288
    QVERIFY(waitUntilJobIsDone(doc->save(dstUrl, "png")));
 
289
}
 
290
 
 
291
/**
 
292
 * Check that deleting a document while it is loading does not crash
 
293
 */
 
294
void DocumentTest::testDeleteWhileLoading()
 
295
{
 
296
    {
 
297
        KUrl url = urlForTestFile("test.png");
 
298
        QImage image;
 
299
        bool ok = image.load(url.toLocalFile());
 
300
        QVERIFY2(ok, "Could not load 'test.png'");
 
301
        Document::Ptr doc = DocumentFactory::instance()->load(url);
 
302
    }
 
303
    DocumentFactory::instance()->clearCache();
 
304
    // Wait two seconds. If the test fails we will get a segfault while waiting
 
305
    QTest::qWait(2000);
 
306
}
 
307
 
 
308
void DocumentTest::testLoadRotated()
 
309
{
 
310
    KUrl url = urlForTestFile("orient6.jpg");
 
311
    QImage image;
 
312
    bool ok = image.load(url.toLocalFile());
 
313
    QVERIFY2(ok, "Could not load 'orient6.jpg'");
 
314
    QMatrix matrix = ImageUtils::transformMatrix(ROT_90);
 
315
    image = image.transformed(matrix);
 
316
 
 
317
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
318
    doc->startLoadingFullImage();
 
319
    doc->waitUntilLoaded();
 
320
    QCOMPARE(image, doc->image());
 
321
}
 
322
 
 
323
/**
 
324
 * Checks that asking the DocumentFactory the same document twice in a row does
 
325
 * not load it twice
 
326
 */
 
327
void DocumentTest::testMultipleLoads()
 
328
{
 
329
    KUrl url = urlForTestFile("orient6.jpg");
 
330
    Document::Ptr doc1 = DocumentFactory::instance()->load(url);
 
331
    Document::Ptr doc2 = DocumentFactory::instance()->load(url);
 
332
 
 
333
    QCOMPARE(doc1.data(), doc2.data());
 
334
}
 
335
 
 
336
void DocumentTest::testSaveAs()
 
337
{
 
338
    KUrl url = urlForTestFile("orient6.jpg");
 
339
    DocumentFactory* factory = DocumentFactory::instance();
 
340
    Document::Ptr doc = factory->load(url);
 
341
    QSignalSpy savedSpy(doc.data(), SIGNAL(saved(KUrl, KUrl)));
 
342
    QSignalSpy modifiedDocumentListChangedSpy(factory, SIGNAL(modifiedDocumentListChanged()));
 
343
    QSignalSpy documentChangedSpy(factory, SIGNAL(documentChanged(KUrl)));
 
344
    doc->startLoadingFullImage();
 
345
 
 
346
    KUrl destUrl = urlForTestOutputFile("result.png");
 
347
    QVERIFY(waitUntilJobIsDone(doc->save(destUrl, "png")));
 
348
    QCOMPARE(doc->format().data(), "png");
 
349
    QCOMPARE(doc->url(), destUrl);
 
350
    QCOMPARE(doc->metaInfo()->getValueForKey("General.Name"), destUrl.fileName());
 
351
 
 
352
    QVERIFY2(doc->loadingState() == Document::Loaded,
 
353
             "Document is supposed to finish loading before saving"
 
354
            );
 
355
 
 
356
    QTest::qWait(100); // saved() is emitted asynchronously
 
357
    QCOMPARE(savedSpy.count(), 1);
 
358
    QVariantList args = savedSpy.takeFirst();
 
359
    QCOMPARE(args.at(0).value<KUrl>(), url);
 
360
    QCOMPARE(args.at(1).value<KUrl>(), destUrl);
 
361
 
 
362
    QImage image("result.png", "png");
 
363
    QCOMPARE(doc->image(), image);
 
364
 
 
365
    QVERIFY(!DocumentFactory::instance()->hasUrl(url));
 
366
    QVERIFY(DocumentFactory::instance()->hasUrl(destUrl));
 
367
 
 
368
    QCOMPARE(modifiedDocumentListChangedSpy.count(), 0); // No changes were made
 
369
 
 
370
    QCOMPARE(documentChangedSpy.count(), 1);
 
371
    args = documentChangedSpy.takeFirst();
 
372
    QCOMPARE(args.at(0).value<KUrl>(), destUrl);
 
373
}
 
374
 
 
375
void DocumentTest::testLosslessSave()
 
376
{
 
377
    KUrl url1 = urlForTestFile("orient6.jpg");
 
378
    Document::Ptr doc = DocumentFactory::instance()->load(url1);
 
379
    doc->startLoadingFullImage();
 
380
 
 
381
    KUrl url2 = urlForTestOutputFile("orient1.jpg");
 
382
    QVERIFY(waitUntilJobIsDone(doc->save(url2, "jpeg")));
 
383
 
 
384
    QImage image1;
 
385
    QVERIFY(image1.load(url1.toLocalFile()));
 
386
 
 
387
    QImage image2;
 
388
    QVERIFY(image2.load(url2.toLocalFile()));
 
389
 
 
390
    QCOMPARE(image1, image2);
 
391
}
 
392
 
 
393
void DocumentTest::testLosslessRotate()
 
394
{
 
395
    // Generate test image
 
396
    QImage image1(200, 96, QImage::Format_RGB32);
 
397
    {
 
398
        QPainter painter(&image1);
 
399
        QConicalGradient gradient(QPointF(100, 48), 100);
 
400
        gradient.setColorAt(0, Qt::white);
 
401
        gradient.setColorAt(1, Qt::blue);
 
402
        painter.fillRect(image1.rect(), gradient);
 
403
    }
 
404
 
 
405
    KUrl url1 = urlForTestOutputFile("lossless1.jpg");
 
406
    QVERIFY(image1.save(url1.toLocalFile(), "jpeg"));
 
407
 
 
408
    // Load it as a Gwenview document
 
409
    Document::Ptr doc = DocumentFactory::instance()->load(url1);
 
410
    doc->startLoadingFullImage();
 
411
    doc->waitUntilLoaded();
 
412
 
 
413
    // Rotate one time
 
414
    QVERIFY(doc->editor());
 
415
    doc->editor()->applyTransformation(ROT_90);
 
416
 
 
417
    // Save it
 
418
    KUrl url2 = urlForTestOutputFile("lossless2.jpg");
 
419
    waitUntilJobIsDone(doc->save(url2, "jpeg"));
 
420
 
 
421
    // Load the saved image
 
422
    doc = DocumentFactory::instance()->load(url2);
 
423
    doc->startLoadingFullImage();
 
424
    doc->waitUntilLoaded();
 
425
 
 
426
    // Rotate the other way
 
427
    QVERIFY(doc->editor());
 
428
    doc->editor()->applyTransformation(ROT_270);
 
429
    waitUntilJobIsDone(doc->save(url2, "jpeg"));
 
430
 
 
431
    // Compare the saved images
 
432
    QVERIFY(image1.load(url1.toLocalFile()));
 
433
    QImage image2;
 
434
    QVERIFY(image2.load(url2.toLocalFile()));
 
435
 
 
436
    QCOMPARE(image1, image2);
 
437
}
 
438
 
 
439
void DocumentTest::testModifyAndSaveAs()
 
440
{
 
441
    QVariantList args;
 
442
    class TestOperation : public AbstractImageOperation
 
443
    {
 
444
    public:
 
445
        void redo()
 
446
    {
 
447
            QImage image(10, 10, QImage::Format_ARGB32);
 
448
            image.fill(QColor(Qt::white).rgb());
 
449
            document()->editor()->setImage(image);
 
450
            finish(true);
 
451
        }
 
452
    };
 
453
    KUrl url = urlForTestFile("orient6.jpg");
 
454
    DocumentFactory* factory = DocumentFactory::instance();
 
455
    Document::Ptr doc = factory->load(url);
 
456
 
 
457
    QSignalSpy savedSpy(doc.data(), SIGNAL(saved(KUrl, KUrl)));
 
458
    QSignalSpy modifiedDocumentListChangedSpy(factory, SIGNAL(modifiedDocumentListChanged()));
 
459
    QSignalSpy documentChangedSpy(factory, SIGNAL(documentChanged(KUrl)));
 
460
 
 
461
    doc->startLoadingFullImage();
 
462
    doc->waitUntilLoaded();
 
463
    QVERIFY(!doc->isModified());
 
464
    QCOMPARE(modifiedDocumentListChangedSpy.count(), 0);
 
465
 
 
466
    // Modify image
 
467
    QVERIFY(doc->editor());
 
468
    TestOperation* op = new TestOperation;
 
469
    op->applyToDocument(doc);
 
470
    QVERIFY(doc->isModified());
 
471
    QCOMPARE(modifiedDocumentListChangedSpy.count(), 1);
 
472
    modifiedDocumentListChangedSpy.clear();
 
473
    QList<KUrl> lst = factory->modifiedDocumentList();
 
474
    QCOMPARE(lst.count(), 1);
 
475
    QCOMPARE(lst.first(), url);
 
476
    QCOMPARE(documentChangedSpy.count(), 1);
 
477
    args = documentChangedSpy.takeFirst();
 
478
    QCOMPARE(args.at(0).value<KUrl>(), url);
 
479
 
 
480
    // Save it under a new name
 
481
    KUrl destUrl = urlForTestOutputFile("modify.png");
 
482
    QVERIFY(waitUntilJobIsDone(doc->save(destUrl, "png")));
 
483
 
 
484
    // Wait a bit because save() will clear the undo stack when back to the
 
485
    // event loop
 
486
    QTest::qWait(100);
 
487
    QVERIFY(!doc->isModified());
 
488
 
 
489
    QVERIFY(!factory->hasUrl(url));
 
490
    QVERIFY(factory->hasUrl(destUrl));
 
491
    QCOMPARE(modifiedDocumentListChangedSpy.count(), 1);
 
492
    QVERIFY(DocumentFactory::instance()->modifiedDocumentList().isEmpty());
 
493
 
 
494
    QCOMPARE(documentChangedSpy.count(), 2);
 
495
    KUrl::List modifiedUrls = KUrl::List() << url << destUrl;
 
496
    QVERIFY(modifiedUrls.contains(url));
 
497
    QVERIFY(modifiedUrls.contains(destUrl));
 
498
}
 
499
 
 
500
void DocumentTest::testMetaInfoJpeg()
 
501
{
 
502
    KUrl url = urlForTestFile("orient6.jpg");
 
503
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
504
 
 
505
    // We cleared the cache, so the document should not be loaded
 
506
    Q_ASSERT(doc->loadingState() <= Document::KindDetermined);
 
507
 
 
508
    // Wait until we receive the metaInfoUpdated() signal
 
509
    QSignalSpy metaInfoUpdatedSpy(doc.data(), SIGNAL(metaInfoUpdated()));
 
510
    while (metaInfoUpdatedSpy.count() == 0) {
 
511
        QTest::qWait(100);
 
512
    }
 
513
 
 
514
    // Extract an exif key
 
515
    QString value = doc->metaInfo()->getValueForKey("Exif.Image.Make");
 
516
    QCOMPARE(value, QString::fromUtf8("Canon"));
 
517
}
 
518
 
 
519
void DocumentTest::testMetaInfoBmp()
 
520
{
 
521
    KUrl url = urlForTestOutputFile("metadata.bmp");
 
522
    const int width = 200;
 
523
    const int height = 100;
 
524
    QImage image(width, height, QImage::Format_ARGB32);
 
525
    image.fill(Qt::black);
 
526
    image.save(url.toLocalFile(), "BMP");
 
527
 
 
528
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
529
    QSignalSpy metaInfoUpdatedSpy(doc.data(), SIGNAL(metaInfoUpdated()));
 
530
    waitUntilMetaInfoLoaded(doc);
 
531
 
 
532
    Q_ASSERT(metaInfoUpdatedSpy.count() >= 1);
 
533
 
 
534
    QString value = doc->metaInfo()->getValueForKey("General.ImageSize");
 
535
    QString expectedValue = QString("%1x%2").arg(width).arg(height);
 
536
    QCOMPARE(value, expectedValue);
 
537
}
 
538
 
 
539
void DocumentTest::testForgetModifiedDocument()
 
540
{
 
541
    QSignalSpy spy(DocumentFactory::instance(), SIGNAL(modifiedDocumentListChanged()));
 
542
    DocumentFactory::instance()->forget(KUrl("file://does/not/exist.png"));
 
543
    QCOMPARE(spy.count(), 0);
 
544
 
 
545
    // Generate test image
 
546
    QImage image1(200, 96, QImage::Format_RGB32);
 
547
    {
 
548
        QPainter painter(&image1);
 
549
        QConicalGradient gradient(QPointF(100, 48), 100);
 
550
        gradient.setColorAt(0, Qt::white);
 
551
        gradient.setColorAt(1, Qt::blue);
 
552
        painter.fillRect(image1.rect(), gradient);
 
553
    }
 
554
 
 
555
    KUrl url = urlForTestOutputFile("testForgetModifiedDocument.png");
 
556
    QVERIFY(image1.save(url.toLocalFile(), "png"));
 
557
 
 
558
    // Load it as a Gwenview document
 
559
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
560
    doc->startLoadingFullImage();
 
561
    doc->waitUntilLoaded();
 
562
 
 
563
    // Modify it
 
564
    TransformImageOperation* op = new TransformImageOperation(ROT_90);
 
565
    op->applyToDocument(doc);
 
566
    QTest::qWait(100);
 
567
 
 
568
    QCOMPARE(spy.count(), 1);
 
569
 
 
570
    QList<KUrl> lst = DocumentFactory::instance()->modifiedDocumentList();
 
571
    QCOMPARE(lst.length(), 1);
 
572
    QCOMPARE(lst.first(), url);
 
573
 
 
574
    // Forget it
 
575
    DocumentFactory::instance()->forget(url);
 
576
 
 
577
    QCOMPARE(spy.count(), 2);
 
578
    lst = DocumentFactory::instance()->modifiedDocumentList();
 
579
    QVERIFY(lst.isEmpty());
 
580
}
 
581
 
 
582
void DocumentTest::testModifiedAndSavedSignals()
 
583
{
 
584
    TransformImageOperation* op;
 
585
 
 
586
    KUrl url = urlForTestFile("orient6.jpg");
 
587
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
588
    QSignalSpy modifiedSpy(doc.data(), SIGNAL(modified(KUrl)));
 
589
    QSignalSpy savedSpy(doc.data(), SIGNAL(saved(KUrl, KUrl)));
 
590
    doc->startLoadingFullImage();
 
591
    doc->waitUntilLoaded();
 
592
 
 
593
    QCOMPARE(modifiedSpy.count(), 0);
 
594
    QCOMPARE(savedSpy.count(), 0);
 
595
 
 
596
    op = new TransformImageOperation(ROT_90);
 
597
    op->applyToDocument(doc);
 
598
    QTest::qWait(100);
 
599
    QCOMPARE(modifiedSpy.count(), 1);
 
600
 
 
601
    op = new TransformImageOperation(ROT_90);
 
602
    op->applyToDocument(doc);
 
603
    QTest::qWait(100);
 
604
    QCOMPARE(modifiedSpy.count(), 2);
 
605
 
 
606
    doc->undoStack()->undo();
 
607
    QCOMPARE(modifiedSpy.count(), 3);
 
608
 
 
609
    doc->undoStack()->undo();
 
610
    QCOMPARE(savedSpy.count(), 1);
 
611
}
 
612
 
 
613
class TestJob : public DocumentJob
 
614
{
 
615
public:
 
616
    TestJob(QString* str, char ch)
 
617
        : mStr(str)
 
618
        , mCh(ch)
 
619
    {}
 
620
 
 
621
protected:
 
622
    virtual void doStart()
 
623
    {
 
624
        *mStr += mCh;
 
625
        emitResult();
 
626
    }
 
627
 
 
628
private:
 
629
    QString* mStr;
 
630
    char mCh;
 
631
};
 
632
 
 
633
void DocumentTest::testJobQueue()
 
634
{
 
635
    KUrl url = urlForTestFile("orient6.jpg");
 
636
    Document::Ptr doc = DocumentFactory::instance()->load(url);
 
637
    QSignalSpy spy(doc.data(), SIGNAL(busyChanged(KUrl, bool)));
 
638
 
 
639
    QString str;
 
640
    doc->enqueueJob(new TestJob(&str, 'a'));
 
641
    doc->enqueueJob(new TestJob(&str, 'b'));
 
642
    doc->enqueueJob(new TestJob(&str, 'c'));
 
643
    QVERIFY(doc->isBusy());
 
644
    QEventLoop loop;
 
645
    connect(doc.data(), SIGNAL(allTasksDone()),
 
646
            &loop, SLOT(quit()));
 
647
    loop.exec();
 
648
    QVERIFY(!doc->isBusy());
 
649
    QCOMPARE(spy.count(), 2);
 
650
    QVariantList row = spy.takeFirst();
 
651
    QCOMPARE(row.at(0).value<KUrl>(), url);
 
652
    QVERIFY(row.at(1).toBool());
 
653
    row = spy.takeFirst();
 
654
    QCOMPARE(row.at(0).value<KUrl>(), url);
 
655
    QVERIFY(!row.at(1).toBool());
 
656
    QCOMPARE(str, QString("abc"));
 
657
}
 
658
 
 
659
class TestCheckDocumentEditorJob : public DocumentJob
 
660
{
 
661
public:
 
662
    TestCheckDocumentEditorJob(int* hasEditor)
 
663
        : mHasEditor(hasEditor) {
 
664
        *mHasEditor = -1;
 
665
    }
 
666
 
 
667
protected:
 
668
    virtual void doStart()
 
669
    {
 
670
        document()->waitUntilLoaded();
 
671
        *mHasEditor = checkDocumentEditor() ? 1 : 0;
 
672
        emitResult();
 
673
    }
 
674
 
 
675
private:
 
676
    int* mHasEditor;
 
677
};
 
678
 
 
679
class TestUiDelegate : public KJobUiDelegate
 
680
{
 
681
public:
 
682
    TestUiDelegate(bool* showErrorMessageCalled)
 
683
        : mShowErrorMessageCalled(showErrorMessageCalled) {
 
684
        setAutoErrorHandlingEnabled(true);
 
685
        *mShowErrorMessageCalled = false;
 
686
    }
 
687
 
 
688
    virtual void showErrorMessage()
 
689
    {
 
690
        kDebug();
 
691
        *mShowErrorMessageCalled = true;
 
692
    }
 
693
 
 
694
private:
 
695
    bool* mShowErrorMessageCalled;
 
696
};
 
697
 
 
698
/**
 
699
 * Test that an error is reported when a DocumentJob fails because there is no
 
700
 * document editor available
 
701
 */
 
702
void DocumentTest::testCheckDocumentEditor()
 
703
{
 
704
    int hasEditor;
 
705
    bool showErrorMessageCalled;
 
706
    QEventLoop loop;
 
707
    Document::Ptr doc;
 
708
    TestCheckDocumentEditorJob* job;
 
709
 
 
710
    doc = DocumentFactory::instance()->load(urlForTestFile("orient6.jpg"));
 
711
 
 
712
    job = new TestCheckDocumentEditorJob(&hasEditor);
 
713
    job->setUiDelegate(new TestUiDelegate(&showErrorMessageCalled));
 
714
    doc->enqueueJob(job);
 
715
    connect(doc.data(), SIGNAL(allTasksDone()), &loop, SLOT(quit()));
 
716
    loop.exec();
 
717
    QVERIFY(!showErrorMessageCalled);
 
718
    QCOMPARE(hasEditor, 1);
 
719
 
 
720
    doc = DocumentFactory::instance()->load(urlForTestFile("test.svg"));
 
721
 
 
722
    job = new TestCheckDocumentEditorJob(&hasEditor);
 
723
    job->setUiDelegate(new TestUiDelegate(&showErrorMessageCalled));
 
724
    doc->enqueueJob(job);
 
725
    connect(doc.data(), SIGNAL(allTasksDone()), &loop, SLOT(quit()));
 
726
    loop.exec();
 
727
    QVERIFY(showErrorMessageCalled);
 
728
    QCOMPARE(hasEditor, 0);
 
729
}
 
730
 
 
731
/**
 
732
 * An operation should only pushed to the document undo stack if it succeed
 
733
 */
 
734
void DocumentTest::testUndoStackPush()
 
735
{
 
736
    class SuccessOperation : public AbstractImageOperation
 
737
    {
 
738
    protected:
 
739
        virtual void redo()
 
740
    {
 
741
            QMetaObject::invokeMethod(this, "finish", Qt::QueuedConnection, Q_ARG(bool, true));
 
742
        }
 
743
    };
 
744
 
 
745
    class FailureOperation : public AbstractImageOperation
 
746
    {
 
747
    protected:
 
748
        virtual void redo()
 
749
    {
 
750
            QMetaObject::invokeMethod(this, "finish", Qt::QueuedConnection, Q_ARG(bool, false));
 
751
        }
 
752
    };
 
753
 
 
754
    AbstractImageOperation* op;
 
755
    Document::Ptr doc = DocumentFactory::instance()->load(urlForTestFile("orient6.jpg"));
 
756
 
 
757
    // A successful operation should be added to the undo stack
 
758
    op = new SuccessOperation;
 
759
    op->applyToDocument(doc);
 
760
    QTest::qWait(100);
 
761
    QVERIFY(!doc->undoStack()->isClean());
 
762
 
 
763
    // Reset
 
764
    doc->undoStack()->undo();
 
765
    QVERIFY(doc->undoStack()->isClean());
 
766
 
 
767
    // A failed operation should not be added to the undo stack
 
768
    op = new FailureOperation;
 
769
    op->applyToDocument(doc);
 
770
    QTest::qWait(100);
 
771
    QVERIFY(doc->undoStack()->isClean());
 
772
}