2
* This file is part of system-settings
4
* Copyright (C) 2016 Canonical Ltd.
6
* This program is free software: you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License version 3, as published
8
* by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful, but
11
* WITHOUT ANY WARRANTY; without even the implied warranties of
12
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13
* PURPOSE. See the GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License along
16
* with this program. If not, see <http://www.gnu.org/licenses/>.
27
#include "updatemodel.h"
29
using namespace UpdatePlugin;
31
Q_DECLARE_METATYPE(QList<QSharedPointer<Update> >)
32
Q_DECLARE_METATYPE(QSharedPointer<Update>)
33
Q_DECLARE_METATYPE(Update::Kind)
34
Q_DECLARE_METATYPE(UpdateModel::Roles)
36
class TstUpdateModel : public QObject
42
m_model = new UpdateModel(":memory:");
44
m_filter = new UpdateModelFilter(m_model);
45
m_filter->setSourceModel(m_model);
49
QSignalSpy destroyedSpy(m_model, SIGNAL(destroyed(QObject*)));
50
m_model->deleteLater();
51
QTRY_COMPARE(destroyedSpy.count(), 1);
53
QSharedPointer<Update> createUpdate()
55
return QSharedPointer<Update>(new Update);
57
QSharedPointer<Update> createUpdate(QString id, int rev)
59
auto update = createUpdate();
60
update->setIdentifier(id);
61
update->setRevision(rev);
64
QSharedPointer<Update> createClickUpdate(QString id, int rev)
66
auto update = createUpdate(id, rev);
67
update->setKind(Update::Kind::KindClick);
70
QSharedPointer<Update> createImageUpdate(QString id, int rev)
72
auto update = createUpdate(id, rev);
73
update->setKind(Update::Kind::KindImage);
78
QCOMPARE(m_model->rowCount(), 0);
82
auto update = createUpdate();
83
update->setIdentifier("test.app");
84
update->setRevision(1);
86
QSignalSpy insertedSpy(m_model, SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)));
88
QTRY_COMPARE(insertedSpy.count(), 1);
89
QCOMPARE(m_model->rowCount(), 1);
91
void testAddMultiple()
93
auto a = createUpdate();
94
a->setIdentifier("a.app");
96
auto b = createUpdate();
97
b->setIdentifier("b.app");
99
auto c = createUpdate();
100
c->setIdentifier("c.app");
107
QCOMPARE(m_model->rowCount(), 3);
111
auto update = createUpdate();
112
update->setIdentifier("a.app");
113
update->setRevision(1);
116
QSignalSpy removeSpy(m_model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)));
117
m_model->remove(update);
118
QTRY_COMPARE(removeSpy.count(), 1);
119
QCOMPARE(m_model->rowCount(), 0);
122
m_model->remove(update->identifier(), update->revision());
123
QTRY_COMPARE(removeSpy.count(), 2);
124
QCOMPARE(m_model->rowCount(), 0);
128
auto update = createUpdate();
129
update->setIdentifier("test.app");
130
update->setRevision(1);
131
update->setTitle("old");
134
update->setTitle("updated");
135
QSignalSpy dataChangedSpy(m_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
137
QTRY_COMPARE(dataChangedSpy.count(), 1);
138
QList<QVariant> args = dataChangedSpy.takeFirst();
139
QCOMPARE(args.at(0).toInt(), 0);
141
void testMultipleChanges()
143
auto a = createUpdate();
144
a->setIdentifier("a.app");
148
auto b = createUpdate();
149
b->setIdentifier("b.app");
153
auto c = createUpdate();
154
c->setIdentifier("c.app");
162
QVector<QSharedPointer<Update>> list;
165
// Change three titles.
166
Q_FOREACH(auto update, list) {
167
QSqlQuery q(m_db->db());
168
q.prepare("UPDATE updates SET title=:title WHERE id=:id AND revision=:revision");
169
q.bindValue(":title", update->title() + "-new");
170
q.bindValue(":id", update->identifier());
171
q.bindValue(":revision", update->revision());
176
QSignalSpy dataChangedSpy(m_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
178
QTRY_COMPARE(dataChangedSpy.count(), 3);
179
QList<QVariant> args = dataChangedSpy.takeFirst();
181
void testRoles_data()
183
QTest::addColumn<UpdateModel::Roles>("role");
184
QTest::addColumn<QVariant>("value");
185
QTest::addColumn<QSharedPointer<Update>>("update");
188
auto update = createUpdate("test", 1);
189
UpdateModel::Roles role(UpdateModel::KindRole);
190
QVariant value((uint) Update::Kind::KindClick);
191
update->setKind(Update::Kind::KindClick);
192
QTest::newRow("KindRole") << role << value << update;
195
auto update = createUpdate("test", 1);
196
UpdateModel::Roles role(UpdateModel::IdRole);
197
QVariant value("test");
198
QTest::newRow("IdRole") << role << value << update;
201
auto update = createUpdate("test", 1);
202
UpdateModel::Roles role(UpdateModel::LocalVersionRole);
203
QVariant value("v1");
204
update->setLocalVersion("v1");
205
QTest::newRow("IdRole") << role << value << update;
208
auto update = createUpdate("test", 1);
209
UpdateModel::Roles role(UpdateModel::RemoteVersionRole);
210
QVariant value("0.2");
211
update->setRemoteVersion("0.2");
212
QTest::newRow("RemoteVersionRole") << role << value << update;
215
auto update = createUpdate("test", 1);
216
UpdateModel::Roles role(UpdateModel::RevisionRole);
218
update->setRevision(42);
219
QTest::newRow("RevisionRole") << role << value << update;
222
auto update = createUpdate("test", 1);
223
UpdateModel::Roles role(UpdateModel::TitleRole);
224
QVariant value("Test App");
225
update->setTitle("Test App");
226
QTest::newRow("TitleRole") << role << value << update;
229
auto update = createUpdate("test", 1);
230
UpdateModel::Roles role(UpdateModel::DownloadHashRole);
231
QVariant value("987654323456789");
232
update->setDownloadHash("987654323456789");
233
QTest::newRow("DownloadHashRole") << role << value << update;
236
auto update = createUpdate("test", 1);
237
UpdateModel::Roles role(UpdateModel::DownloadIdRole);
238
QVariant value("someuuid");
239
update->setDownloadId("someuuid");
240
QTest::newRow("DownloadIdRole") << role << value << update;
243
auto update = createUpdate("test", 1);
244
UpdateModel::Roles role(UpdateModel::SizeRole);
245
QVariant value(1000);
246
update->setBinaryFilesize(1000);
247
QTest::newRow("SizeRole") << role << value << update;
250
auto update = createUpdate("test", 1);
251
UpdateModel::Roles role(UpdateModel::IconUrlRole);
252
QVariant value("http://example.org/testapp.png");
253
update->setIconUrl("http://example.org/testapp.png");
254
QTest::newRow("IconUrlRole") << role << value << update;
257
auto update = createUpdate("test", 1);
258
UpdateModel::Roles role(UpdateModel::DownloadUrlRole);
259
QVariant value("http://example.org/testapp.click");
260
update->setDownloadUrl("http://example.org/testapp.click");
261
QTest::newRow("DownloadUrlRole") << role << value << update;
264
auto update = createUpdate("test", 1);
265
UpdateModel::Roles role(UpdateModel::CommandRole);
269
update->setCommand(mc);
270
QTest::newRow("CommandRole") << role << value << update;
273
auto update = createUpdate("test", 1);
274
UpdateModel::Roles role(UpdateModel::ChangelogRole);
275
QVariant value("* Fixed all bugs * Introduced new bugs");
276
update->setChangelog("* Fixed all bugs * Introduced new bugs");
277
QTest::newRow("ChangelogRole") << role << value << update;
280
auto update = createUpdate("test", 1);
281
UpdateModel::Roles role(UpdateModel::TokenRole);
282
QVariant value("token");
283
update->setToken("token");
284
QTest::newRow("TokenRole") << role << value << update;
287
auto update = createUpdate("test", 1);
288
UpdateModel::Roles role(UpdateModel::ProgressRole);
290
update->setProgress(50);
291
QTest::newRow("ProgressRole") << role << value << update;
294
auto update = createUpdate("test", 1);
295
UpdateModel::Roles role(UpdateModel::UpdateStateRole);
296
QVariant value((uint) Update::State::StateInstallPaused);
297
update->setState(Update::State::StateInstallPaused);
298
QTest::newRow("UpdateStateRole") << role << value << update;
301
auto update = createUpdate("test", 1);
302
UpdateModel::Roles role(UpdateModel::ErrorRole);
303
QVariant value("Failure");
304
update->setError("Failure");
305
QTest::newRow("ErrorRole") << role << value << update;
308
auto update = createUpdate("test", 1);
309
UpdateModel::Roles role(UpdateModel::PackageNameRole);
310
QVariant value("testapp");
311
update->setPackageName("testapp");
312
QTest::newRow("PackageNameRole") << role << value << update;
315
auto update = createUpdate("test", 1);
316
UpdateModel::Roles role(UpdateModel::SignedDownloadUrlRole);
317
QVariant value("signedUrl");
318
update->setSignedDownloadUrl("signedUrl");
319
QTest::newRow("SignedDownloadUrlRole") << role << value << update;
322
auto update = createUpdate("test", 1);
323
auto target = QDateTime::currentDateTimeUtc();
324
UpdateModel::Roles role(UpdateModel::UpdatedAtRole);
325
QVariant value(target);
326
update->setUpdatedAt(target);
327
QTest::newRow("UpdatedAtRole") << role << value << update;
332
QFETCH(UpdateModel::Roles, role);
333
QFETCH(QVariant, value);
334
QFETCH(QSharedPointer<Update>, update);
336
m_model->add(update);
337
QCOMPARE(m_model->data(m_model->index(0), role), value);
339
void testCreatedAtRole()
341
m_model->add(createUpdate("test", 1));
342
// Just make sure the createdAt role isn't empty.
343
QVERIFY(m_model->data(
345
UpdateModel::CreatedAtRole).toDateTime().isValid()
350
auto notFound = m_model->get("notfound", 0);
351
QVERIFY(notFound.isNull());
353
void testImageUpdate()
355
m_model->setImageUpdate("ubuntu", 350, 400000);
356
QCOMPARE(m_model->rowCount(), 1);
357
QCOMPARE(m_model->data(
358
m_model->index(0), UpdateModel::IdRole
359
).toString(), QString("ubuntu"));
360
QCOMPARE(m_model->data(
361
m_model->index(0), UpdateModel::RevisionRole
362
).toUInt(), (uint) 350);
363
QCOMPARE(m_model->data(
364
m_model->index(0), UpdateModel::AutomaticRole
367
void testSetAvailable()
369
auto update = createUpdate("id", 42);
370
update->setError("Some error");
371
update->setProgress(95);
372
update->setToken("foobar");
374
m_model->setAvailable(update->identifier(), update->revision());
376
auto update1 = m_model->get(update->identifier(), update->revision());
378
QVERIFY(!update1.isNull());
380
QCOMPARE(update1->error(), QString(""));
381
QCOMPARE(update1->progress(), 0);
382
QCOMPARE(update1->token(), QString(""));
383
QCOMPARE(update1->downloadId(), QString(""));
384
QCOMPARE(update1->state(), Update::State::StateAvailable);
386
void testSetInstalled()
388
auto update = createUpdate("id", 42);
390
m_model->setInstalled(update->identifier(), update->revision());
391
auto update1 = m_db->get(update->identifier(), update->revision());
392
QCOMPARE(update1->state(), Update::State::StateInstallFinished);
393
QCOMPARE(update1->downloadId(), QString(""));
397
auto update = createUpdate("id", 42);
399
m_model->setError(update->identifier(), update->revision(), "fail");
401
auto update1 = m_db->get(update->identifier(), update->revision());
402
QCOMPARE(update1->state(), Update::State::StateFailed);
403
QCOMPARE(update1->downloadId(), QString(""));
405
void testSetProgress()
407
auto update = createUpdate("id", 42);
409
m_model->setProgress(update->identifier(), update->revision(), 5);
410
auto update1 = m_db->get(update->identifier(), update->revision());
411
QCOMPARE(update1->state(), Update::State::StateDownloading);
412
QCOMPARE(update1->progress(), 5);
414
void testSetInstalling()
416
auto update = createUpdate("id", 42);
418
m_model->setInstalling(update->identifier(), update->revision(), 5);
419
auto update1 = m_db->get(update->identifier(), update->revision());
420
QCOMPARE(update1->state(), Update::State::StateInstalling);
421
QCOMPARE(update1->progress(), 5);
423
void testSetDownloaded()
425
auto update = createUpdate("id", 42);
427
m_model->setDownloaded(update->identifier(), update->revision());
428
QCOMPARE(m_db->get(update->identifier(), update->revision())->state(),
429
Update::State::StateDownloaded);
431
void testStartUpdate()
433
auto update = createUpdate("id", 42);
435
m_model->startUpdate(update->identifier(), update->revision());
436
QCOMPARE(m_db->get(update->identifier(), update->revision())->state(),
437
Update::State::StateDownloading);
439
void testQueueUpdate()
441
auto update = createUpdate("id", 42);
442
QString targetDid("someId");
444
m_model->queueUpdate(update->identifier(), update->revision(), targetDid);
445
auto update1 = m_db->get(update->identifier(), update->revision());
446
QCOMPARE(update1->state(), Update::State::StateQueuedForDownload);
447
QCOMPARE(update1->downloadId(), targetDid);
449
void testProcessUpdate()
451
auto update = createUpdate("id", 42);
453
m_model->processUpdate(update->identifier(), update->revision());
454
QCOMPARE(m_db->get(update->identifier(), update->revision())->state(),
455
Update::State::StateInstalling);
457
void testPauseUpdate()
459
auto update = createUpdate("id", 42);
461
m_model->pauseUpdate(update->identifier(), update->revision());
462
QCOMPARE(m_db->get(update->identifier(), update->revision())->state(),
463
Update::State::StateDownloadPaused);
465
void testResumeUpdate()
467
auto update = createUpdate("id", 42);
469
m_model->resumeUpdate(update->identifier(), update->revision());
470
QCOMPARE(m_db->get(update->identifier(), update->revision())->state(),
471
Update::State::StateDownloading);
473
void testCancelUpdate()
475
auto update = createUpdate("id", 42);
477
m_model->cancelUpdate(update->identifier(), update->revision());
479
auto update1 = m_model->get(update->identifier(), update->revision());
480
QCOMPARE(update1->error(), QString(""));
481
QCOMPARE(update1->progress(), 0);
482
QCOMPARE(update1->token(), QString(""));
483
QCOMPARE(update1->downloadId(), QString(""));
484
QCOMPARE(update1->state(), Update::State::StateAvailable);
488
QHash<int, QByteArray> names = m_model->roleNames();
489
QVERIFY(names[Qt::DisplayRole] == "displayName");
490
QVERIFY(names[UpdateModel::Roles::KindRole] == "kind");
491
QVERIFY(names[UpdateModel::Roles::IconUrlRole] == "iconUrl");
492
QVERIFY(names[UpdateModel::Roles::IdRole] == "identifier");
493
QVERIFY(names[UpdateModel::Roles::LocalVersionRole] == "localVersion");
494
QVERIFY(names[UpdateModel::Roles::RemoteVersionRole] == "remoteVersion");
495
QVERIFY(names[UpdateModel::Roles::RevisionRole] == "revision");
496
QVERIFY(names[UpdateModel::Roles::InstalledRole] == "installed");
497
QVERIFY(names[UpdateModel::Roles::CreatedAtRole] == "createdAt");
498
QVERIFY(names[UpdateModel::Roles::UpdatedAtRole] == "updatedAt");
499
QVERIFY(names[UpdateModel::Roles::TitleRole] == "title");
500
QVERIFY(names[UpdateModel::Roles::DownloadHashRole] == "downloadHash");
501
QVERIFY(names[UpdateModel::Roles::SizeRole] == "size");
502
QVERIFY(names[UpdateModel::Roles::DownloadUrlRole] == "downloadUrl");
503
QVERIFY(names[UpdateModel::Roles::ChangelogRole] == "changelog");
504
QVERIFY(names[UpdateModel::Roles::CommandRole] == "command");
505
QVERIFY(names[UpdateModel::Roles::TokenRole] == "token");
506
QVERIFY(names[UpdateModel::Roles::UpdateStateRole] == "updateState");
507
QVERIFY(names[UpdateModel::Roles::ProgressRole] == "progress");
508
QVERIFY(names[UpdateModel::Roles::AutomaticRole] == "automatic");
509
QVERIFY(names[UpdateModel::Roles::ErrorRole] == "error");
510
QVERIFY(names[UpdateModel::Roles::PackageNameRole] == "packageName");
511
QVERIFY(names[UpdateModel::Roles::SignedDownloadUrlRole] == "signedDownloadUrl");
513
void testFilterKinds_data()
515
QTest::addColumn<Update::Kind>("kind");
516
QTest::newRow("Unknown") << Update::Kind::KindUnknown;
517
QTest::newRow("Click") << Update::Kind::KindClick;
518
QTest::newRow("Images") << Update::Kind::KindImage;
520
void testFilterKinds()
522
QFETCH(Update::Kind, kind);
524
m_filter->filterOnKind((uint) kind);
525
QCOMPARE((uint) m_filter->kindFilter(), (uint) kind);
527
void testFilterInstalled_data()
529
QTest::addColumn<bool>("installed");
531
QTest::newRow("Installed") << true;
532
QTest::newRow("Not installed") << false;
534
void testFilterInstalled()
536
QFETCH(bool, installed);
538
m_filter->filterOnInstalled(installed);
539
QCOMPARE(m_filter->installed(), installed);
541
void testFilterKindsIntegration_data()
543
QTest::addColumn<QList<QSharedPointer<Update>> >("updates");
544
QTest::addColumn<Update::Kind>("kindFilter");
545
QTest::addColumn<int>("targetCount");
547
QList<QSharedPointer<Update>> sample;
548
sample << createClickUpdate("a", 1) << createClickUpdate("b", 2)
549
<< createImageUpdate("u", 1);
551
QTest::newRow("Filter on clicks") << sample << Update::Kind::KindClick << 2;
552
QTest::newRow("Filter on images") << sample << Update::Kind::KindImage << 1;
555
void testFilterKindsIntegration()
557
QFETCH(QList<QSharedPointer<Update> >, updates);
558
QFETCH(Update::Kind, kindFilter);
559
QFETCH(int, targetCount);
561
Q_FOREACH(auto update, updates) {
562
m_model->add(update);
565
m_filter->filterOnKind((uint) kindFilter);
566
QCOMPARE(m_filter->rowCount(), targetCount);
568
void testFilteringInstalledIntegration_data()
570
QTest::addColumn<QList<QSharedPointer<Update>> >("updates");
571
QTest::addColumn<bool>("installedFilter");
572
QTest::addColumn<int>("targetCount");
574
auto installed = createUpdate("a", 1);
575
installed->setInstalled(true);
577
QList<QSharedPointer<Update>> sample;
578
sample << createUpdate("b", 1) << createUpdate("c", 1) << installed;
580
QTest::newRow("Filter not installed") << sample << false << 2;
581
QTest::newRow("Filter installed") << sample << true << 1;
583
void testFilteringInstalledIntegration()
585
QFETCH(QList<QSharedPointer<Update> >, updates);
586
QFETCH(bool, installedFilter);
587
QFETCH(int, targetCount);
589
Q_FOREACH(auto update, updates) {
590
m_model->add(update);
593
m_filter->filterOnInstalled(installedFilter);
594
QCOMPARE(m_filter->rowCount(), targetCount);
596
void testPendingSort_data()
598
QTest::addColumn<QList<QSharedPointer<Update>> >("updates");
599
QTest::addColumn<QStringList>("titleOrder");
601
auto a = createUpdate("a", 1);
603
a->setInstalled(false);
604
auto b = createUpdate("b", 1);
606
b->setInstalled(false);
607
auto c = createUpdate("c", 1);
609
c->setInstalled(false);
610
QList<QSharedPointer<Update> > updates1; updates1 << a << b << c;
611
QStringList order1; order1 << "A" << "B" << "C";
612
QTest::newRow("abc") << updates1 << order1;
614
QList<QSharedPointer<Update> > updates2; updates2 << c << b << a;
615
QTest::newRow("cba") << updates2 << order1;
617
void testPendingSort()
619
QFETCH(QList<QSharedPointer<Update>>, updates);
620
QFETCH(QStringList, titleOrder);
622
m_filter->filterOnInstalled(false);
623
Q_FOREACH(auto update, updates) {
624
m_model->add(update);
627
QCOMPARE(m_filter->rowCount(), 3);
628
for (int i = 0; i < m_filter->rowCount(); i++) {
630
m_filter->data(m_filter->index(i, 0), UpdateModel::TitleRole).toString(),
635
void testInstalledSort_data()
637
QTest::addColumn<QList<QSharedPointer<Update>> >("updates");
638
QTest::addColumn<QStringList>("idOrder");
640
auto old = createUpdate("old", 1);
641
old->setUpdatedAt(QDateTime(QDate(2012, 1, 1)));
642
old->setInstalled(true);
643
auto older = createUpdate("older", 1);
644
older->setUpdatedAt(QDateTime(QDate(2011, 1, 1)));
645
older->setInstalled(true);
646
auto oldest = createUpdate("oldest", 1);
647
oldest->setUpdatedAt(QDateTime(QDate(2010, 1, 1)));
648
oldest->setInstalled(true);
649
QList<QSharedPointer<Update> > updates1; updates1 << old << older << oldest;
650
QStringList order1; order1 << "old" << "older" << "oldest";
651
QTest::newRow("old, older, oldest") << updates1 << order1;
653
QList<QSharedPointer<Update> > updates2; updates2 << oldest << older << old;
654
QTest::newRow("oldest, older, old") << updates2 << order1;
656
void testInstalledSort()
658
QFETCH(QList<QSharedPointer<Update>>, updates);
659
QFETCH(QStringList, idOrder);
661
m_filter->filterOnInstalled(true);
662
Q_FOREACH(auto update, updates) {
663
m_model->add(update);
666
QCOMPARE(m_filter->rowCount(), 3);
667
for (int i = 0; i < m_filter->rowCount(); i++) {
669
m_filter->data(m_filter->index(i, 0), UpdateModel::IdRole).toString(),
675
UpdateDb *m_db = nullptr;
676
UpdateModel *m_model = nullptr;
677
UpdateModelFilter *m_filter = nullptr;
680
QTEST_GUILESS_MAIN(TstUpdateModel)
681
#include "tst_updatemodel.moc"