1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "devicemanager.h"
27
28 #include "idevicefactory.h"
29
30 #include <coreplugin/icore.h>
31 #include <coreplugin/messagemanager.h>
32
33 #include <projectexplorer/projectexplorerconstants.h>
34 #include <utils/algorithm.h>
35 #include <utils/fileutils.h>
36 #include <utils/persistentsettings.h>
37 #include <utils/portlist.h>
38 #include <utils/qtcassert.h>
39 #include <utils/qtcprocess.h>
40 #include <utils/stringutils.h>
41
42 #include <QDateTime>
43 #include <QFileInfo>
44 #include <QHash>
45 #include <QList>
46 #include <QString>
47 #include <QVariantList>
48
49 #include <limits>
50 #include <memory>
51
52 using namespace Utils;
53
54 namespace ProjectExplorer {
55 namespace Internal {
56
57 const char DeviceManagerKey[] = "DeviceManager";
58 const char DeviceListKey[] = "DeviceList";
59 const char DefaultDevicesKey[] = "DefaultDevices";
60
61 class DeviceManagerPrivate
62 {
63 public:
64 DeviceManagerPrivate() = default;
65
indexForId(Utils::Id id) const66 int indexForId(Utils::Id id) const
67 {
68 for (int i = 0; i < devices.count(); ++i) {
69 if (devices.at(i)->id() == id)
70 return i;
71 }
72 return -1;
73 }
74
75 static DeviceManager *clonedInstance;
76 QList<IDevice::Ptr> devices;
77 QHash<Utils::Id, Utils::Id> defaultDevices;
78
79 Utils::PersistentSettingsWriter *writer = nullptr;
80 };
81 DeviceManager *DeviceManagerPrivate::clonedInstance = nullptr;
82
83 } // namespace Internal
84
85 using namespace Internal;
86
87 DeviceManager *DeviceManager::m_instance = nullptr;
88
instance()89 DeviceManager *DeviceManager::instance()
90 {
91 return m_instance;
92 }
93
deviceCount() const94 int DeviceManager::deviceCount() const
95 {
96 return d->devices.count();
97 }
98
replaceInstance()99 void DeviceManager::replaceInstance()
100 {
101 const QList<Id> newIds =
102 Utils::transform(DeviceManagerPrivate::clonedInstance->d->devices, &IDevice::id);
103
104 for (IDevice::Ptr dev : m_instance->d->devices) {
105 if (!newIds.contains(dev->id()))
106 dev->aboutToBeRemoved();
107 }
108
109 copy(DeviceManagerPrivate::clonedInstance, instance(), false);
110
111 emit instance()->deviceListReplaced();
112 emit instance()->updated();
113 }
114
removeClonedInstance()115 void DeviceManager::removeClonedInstance()
116 {
117 delete DeviceManagerPrivate::clonedInstance;
118 DeviceManagerPrivate::clonedInstance = nullptr;
119 }
120
cloneInstance()121 DeviceManager *DeviceManager::cloneInstance()
122 {
123 QTC_ASSERT(!DeviceManagerPrivate::clonedInstance, return nullptr);
124
125 DeviceManagerPrivate::clonedInstance = new DeviceManager(false);
126 copy(instance(), DeviceManagerPrivate::clonedInstance, true);
127 return DeviceManagerPrivate::clonedInstance;
128 }
129
copy(const DeviceManager * source,DeviceManager * target,bool deep)130 void DeviceManager::copy(const DeviceManager *source, DeviceManager *target, bool deep)
131 {
132 if (deep) {
133 foreach (const IDevice::ConstPtr &device, source->d->devices)
134 target->d->devices << device->clone();
135 } else {
136 target->d->devices = source->d->devices;
137 }
138 target->d->defaultDevices = source->d->defaultDevices;
139 }
140
save()141 void DeviceManager::save()
142 {
143 if (d->clonedInstance == this || !d->writer)
144 return;
145 QVariantMap data;
146 data.insert(QLatin1String(DeviceManagerKey), toMap());
147 d->writer->save(data, Core::ICore::dialogParent());
148 }
149
load()150 void DeviceManager::load()
151 {
152 QTC_ASSERT(!d->writer, return);
153
154 // Only create writer now: We do not want to save before the settings were read!
155 d->writer = new PersistentSettingsWriter(settingsFilePath("devices.xml"), "QtCreatorDevices");
156
157 Utils::PersistentSettingsReader reader;
158 // read devices file from global settings path
159 QHash<Utils::Id, Utils::Id> defaultDevices;
160 QList<IDevice::Ptr> sdkDevices;
161 if (reader.load(systemSettingsFilePath("devices.xml")))
162 sdkDevices = fromMap(reader.restoreValues().value(DeviceManagerKey).toMap(), &defaultDevices);
163 // read devices file from user settings path
164 QList<IDevice::Ptr> userDevices;
165 if (reader.load(settingsFilePath("devices.xml")))
166 userDevices = fromMap(reader.restoreValues().value(DeviceManagerKey).toMap(), &defaultDevices);
167 // Insert devices into the model. Prefer the higher device version when there are multiple
168 // devices with the same id.
169 foreach (IDevice::Ptr device, userDevices) {
170 foreach (const IDevice::Ptr &sdkDevice, sdkDevices) {
171 if (device->id() == sdkDevice->id()) {
172 if (device->version() < sdkDevice->version())
173 device = sdkDevice;
174 sdkDevices.removeOne(sdkDevice);
175 break;
176 }
177 }
178 addDevice(device);
179 }
180 // Append the new SDK devices to the model.
181 foreach (const IDevice::Ptr &sdkDevice, sdkDevices)
182 addDevice(sdkDevice);
183
184 // Overwrite with the saved default devices.
185 for (auto itr = defaultDevices.constBegin(); itr != defaultDevices.constEnd(); ++itr) {
186 IDevice::ConstPtr device = find(itr.value());
187 if (device)
188 d->defaultDevices[device->type()] = device->id();
189 }
190
191 emit devicesLoaded();
192 }
193
fromMap(const QVariantMap & map,QHash<Utils::Id,Utils::Id> * defaultDevices)194 QList<IDevice::Ptr> DeviceManager::fromMap(const QVariantMap &map,
195 QHash<Utils::Id, Utils::Id> *defaultDevices)
196 {
197 QList<IDevice::Ptr> devices;
198
199 if (defaultDevices) {
200 const QVariantMap defaultDevsMap = map.value(DefaultDevicesKey).toMap();
201 for (auto it = defaultDevsMap.constBegin(); it != defaultDevsMap.constEnd(); ++it)
202 defaultDevices->insert(Utils::Id::fromString(it.key()), Utils::Id::fromSetting(it.value()));
203 }
204 const QVariantList deviceList = map.value(QLatin1String(DeviceListKey)).toList();
205 foreach (const QVariant &v, deviceList) {
206 const QVariantMap map = v.toMap();
207 const IDeviceFactory * const factory = restoreFactory(map);
208 if (!factory)
209 continue;
210 const IDevice::Ptr device = factory->construct();
211 QTC_ASSERT(device, continue);
212 device->fromMap(map);
213 devices << device;
214 }
215 return devices;
216 }
217
toMap() const218 QVariantMap DeviceManager::toMap() const
219 {
220 QVariantMap map;
221 QVariantMap defaultDeviceMap;
222 using TypeIdHash = QHash<Utils::Id, Utils::Id>;
223 for (TypeIdHash::ConstIterator it = d->defaultDevices.constBegin();
224 it != d->defaultDevices.constEnd(); ++it) {
225 defaultDeviceMap.insert(it.key().toString(), it.value().toSetting());
226 }
227 map.insert(QLatin1String(DefaultDevicesKey), defaultDeviceMap);
228 QVariantList deviceList;
229 foreach (const IDevice::ConstPtr &device, d->devices)
230 deviceList << device->toMap();
231 map.insert(QLatin1String(DeviceListKey), deviceList);
232 return map;
233 }
234
settingsFilePath(const QString & extension)235 FilePath DeviceManager::settingsFilePath(const QString &extension)
236 {
237 return Core::ICore::userResourcePath(extension);
238 }
239
systemSettingsFilePath(const QString & deviceFileRelativePath)240 FilePath DeviceManager::systemSettingsFilePath(const QString &deviceFileRelativePath)
241 {
242 return Core::ICore::installerResourcePath(deviceFileRelativePath);
243 }
244
addDevice(const IDevice::ConstPtr & _device)245 void DeviceManager::addDevice(const IDevice::ConstPtr &_device)
246 {
247 const IDevice::Ptr device = _device->clone();
248
249 QStringList names;
250 foreach (const IDevice::ConstPtr &tmp, d->devices) {
251 if (tmp->id() != device->id())
252 names << tmp->displayName();
253 }
254
255 device->setDisplayName(Utils::makeUniquelyNumbered(device->displayName(), names));
256
257 const int pos = d->indexForId(device->id());
258
259 if (!defaultDevice(device->type()))
260 d->defaultDevices.insert(device->type(), device->id());
261 if (this == DeviceManager::instance() && d->clonedInstance)
262 d->clonedInstance->addDevice(device->clone());
263
264 if (pos >= 0) {
265 d->devices[pos] = device;
266 emit deviceUpdated(device->id());
267 } else {
268 d->devices << device;
269 emit deviceAdded(device->id());
270 }
271
272 emit updated();
273 }
274
removeDevice(Utils::Id id)275 void DeviceManager::removeDevice(Utils::Id id)
276 {
277 const IDevice::Ptr device = mutableDevice(id);
278 QTC_ASSERT(device, return);
279 QTC_ASSERT(this != instance() || device->isAutoDetected(), return);
280
281 const bool wasDefault = d->defaultDevices.value(device->type()) == device->id();
282 const Utils::Id deviceType = device->type();
283 d->devices.removeAt(d->indexForId(id));
284 emit deviceRemoved(device->id());
285
286 if (wasDefault) {
287 for (int i = 0; i < d->devices.count(); ++i) {
288 if (deviceAt(i)->type() == deviceType) {
289 d->defaultDevices.insert(deviceAt(i)->type(), deviceAt(i)->id());
290 emit deviceUpdated(deviceAt(i)->id());
291 break;
292 }
293 }
294 }
295 if (this == instance() && d->clonedInstance)
296 d->clonedInstance->removeDevice(id);
297
298 emit updated();
299 }
300
setDeviceState(Utils::Id deviceId,IDevice::DeviceState deviceState)301 void DeviceManager::setDeviceState(Utils::Id deviceId, IDevice::DeviceState deviceState)
302 {
303 // To see the state change in the DeviceSettingsWidget. This has to happen before
304 // the pos check below, in case the device is only present in the cloned instance.
305 if (this == instance() && d->clonedInstance)
306 d->clonedInstance->setDeviceState(deviceId, deviceState);
307
308 const int pos = d->indexForId(deviceId);
309 if (pos < 0)
310 return;
311 IDevice::Ptr &device = d->devices[pos];
312 if (device->deviceState() == deviceState)
313 return;
314
315 device->setDeviceState(deviceState);
316 emit deviceUpdated(deviceId);
317 emit updated();
318 }
319
isLoaded() const320 bool DeviceManager::isLoaded() const
321 {
322 return d->writer;
323 }
324
deviceForPath(const FilePath & path)325 IDevice::ConstPtr DeviceManager::deviceForPath(const FilePath &path)
326 {
327 for (IDevice::Ptr &dev : instance()->d->devices) {
328 if (dev->handlesFile(path))
329 return dev;
330 }
331 return {};
332 }
333
defaultDesktopDevice()334 IDevice::ConstPtr DeviceManager::defaultDesktopDevice()
335 {
336 return m_instance->defaultDevice(Constants::DESKTOP_DEVICE_TYPE);
337 }
338
setDefaultDevice(Utils::Id id)339 void DeviceManager::setDefaultDevice(Utils::Id id)
340 {
341 QTC_ASSERT(this != instance(), return);
342
343 const IDevice::ConstPtr &device = find(id);
344 QTC_ASSERT(device, return);
345 const IDevice::ConstPtr &oldDefaultDevice = defaultDevice(device->type());
346 if (device == oldDefaultDevice)
347 return;
348 d->defaultDevices.insert(device->type(), device->id());
349 emit deviceUpdated(device->id());
350 emit deviceUpdated(oldDefaultDevice->id());
351
352 emit updated();
353 }
354
restoreFactory(const QVariantMap & map)355 const IDeviceFactory *DeviceManager::restoreFactory(const QVariantMap &map)
356 {
357 const Utils::Id deviceType = IDevice::typeFromMap(map);
358 IDeviceFactory *factory = Utils::findOrDefault(IDeviceFactory::allDeviceFactories(),
359 [&map, deviceType](IDeviceFactory *factory) {
360 return factory->canRestore(map) && factory->deviceType() == deviceType;
361 });
362
363 if (!factory)
364 qWarning("Warning: No factory found for device '%s' of type '%s'.",
365 qPrintable(IDevice::idFromMap(map).toString()),
366 qPrintable(IDevice::typeFromMap(map).toString()));
367 return factory;
368 }
369
DeviceManager(bool isInstance)370 DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManagerPrivate>())
371 {
372 QTC_ASSERT(isInstance == !m_instance, return);
373
374 if (!isInstance)
375 return;
376
377 m_instance = this;
378 connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
379 this, &DeviceManager::save);
380
381 DeviceFileHooks deviceHooks;
382
383 deviceHooks.isExecutableFile = [](const FilePath &filePath) {
384 auto device = DeviceManager::deviceForPath(filePath);
385 QTC_ASSERT(device, return false);
386 return device->isExecutableFile(filePath);
387 };
388
389 deviceHooks.isReadableFile = [](const FilePath &filePath) {
390 auto device = DeviceManager::deviceForPath(filePath);
391 QTC_ASSERT(device, return false);
392 return device->isReadableFile(filePath);
393 };
394
395 deviceHooks.isReadableDir = [](const FilePath &filePath) {
396 auto device = DeviceManager::deviceForPath(filePath);
397 QTC_ASSERT(device, return false);
398 return device->isReadableDirectory(filePath);
399 };
400
401 deviceHooks.isWritableDir = [](const FilePath &filePath) {
402 auto device = DeviceManager::deviceForPath(filePath);
403 QTC_ASSERT(device, return false);
404 return device->isWritableDirectory(filePath);
405 };
406
407 deviceHooks.isWritableFile = [](const FilePath &filePath) {
408 auto device = DeviceManager::deviceForPath(filePath);
409 QTC_ASSERT(device, return false);
410 return device->isWritableFile(filePath);
411 };
412
413 deviceHooks.isFile = [](const FilePath &filePath) {
414 auto device = DeviceManager::deviceForPath(filePath);
415 QTC_ASSERT(device, return false);
416 return device->isFile(filePath);
417 };
418
419 deviceHooks.isDir = [](const FilePath &filePath) {
420 auto device = DeviceManager::deviceForPath(filePath);
421 QTC_ASSERT(device, return false);
422 return device->isDirectory(filePath);
423 };
424
425 deviceHooks.ensureWritableDir = [](const FilePath &filePath) {
426 auto device = DeviceManager::deviceForPath(filePath);
427 QTC_ASSERT(device, return false);
428 return device->ensureWritableDirectory(filePath);
429 };
430
431 deviceHooks.ensureExistingFile = [](const FilePath &filePath) {
432 auto device = DeviceManager::deviceForPath(filePath);
433 QTC_ASSERT(device, return false);
434 return device->ensureExistingFile(filePath);
435 };
436
437 deviceHooks.createDir = [](const FilePath &filePath) {
438 auto device = DeviceManager::deviceForPath(filePath);
439 QTC_ASSERT(device, return false);
440 return device->createDirectory(filePath);
441 };
442
443 deviceHooks.exists = [](const FilePath &filePath) {
444 auto device = DeviceManager::deviceForPath(filePath);
445 QTC_ASSERT(device, return false);
446 return device->exists(filePath);
447 };
448
449 deviceHooks.removeFile = [](const FilePath &filePath) {
450 auto device = DeviceManager::deviceForPath(filePath);
451 QTC_ASSERT(device, return false);
452 return device->removeFile(filePath);
453 };
454
455 deviceHooks.removeRecursively = [](const FilePath &filePath) {
456 auto device = DeviceManager::deviceForPath(filePath);
457 QTC_ASSERT(device, return false);
458 return device->removeRecursively(filePath);
459 };
460
461 deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) {
462 auto device = DeviceManager::deviceForPath(filePath);
463 QTC_ASSERT(device, return false);
464 return device->copyFile(filePath, target);
465 };
466
467 deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) {
468 auto device = DeviceManager::deviceForPath(filePath);
469 QTC_ASSERT(device, return false);
470 return device->renameFile(filePath, target);
471 };
472
473 deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) {
474 auto device = DeviceManager::deviceForPath(filePath);
475 QTC_ASSERT(device, return FilePath{});
476 return device->searchExecutable(filePath.path(), dirs);
477 };
478
479 deviceHooks.symLinkTarget = [](const FilePath &filePath) {
480 auto device = DeviceManager::deviceForPath(filePath);
481 QTC_ASSERT(device, return FilePath{});
482 return device->symLinkTarget(filePath);
483 };
484
485 deviceHooks.dirEntries = [](const FilePath &filePath, const QStringList &nameFilters,
486 QDir::Filters filters, QDir::SortFlags sort) {
487 auto device = DeviceManager::deviceForPath(filePath);
488 QTC_ASSERT(device, return FilePaths());
489 return device->directoryEntries(filePath, nameFilters, filters, sort);
490 };
491
492 deviceHooks.fileContents = [](const FilePath &filePath, qint64 maxSize, qint64 offset) {
493 auto device = DeviceManager::deviceForPath(filePath);
494 QTC_ASSERT(device, return QByteArray());
495 return device->fileContents(filePath, maxSize, offset);
496 };
497
498 deviceHooks.writeFileContents = [](const FilePath &filePath, const QByteArray &data) {
499 auto device = DeviceManager::deviceForPath(filePath);
500 QTC_ASSERT(device, return false);
501 return device->writeFileContents(filePath, data);
502 };
503
504 deviceHooks.lastModified = [](const FilePath &filePath) {
505 auto device = DeviceManager::deviceForPath(filePath);
506 QTC_ASSERT(device, return QDateTime());
507 return device->lastModified(filePath);
508 };
509
510 deviceHooks.permissions = [](const FilePath &filePath) {
511 auto device = DeviceManager::deviceForPath(filePath);
512 QTC_ASSERT(device, return QFile::Permissions());
513 return device->permissions(filePath);
514 };
515
516 deviceHooks.osType = [](const FilePath &filePath) {
517 auto device = DeviceManager::deviceForPath(filePath);
518 QTC_ASSERT(device, return OsTypeOther);
519 return device->osType();
520 };
521
522 deviceHooks.environment = [](const FilePath &filePath) {
523 auto device = DeviceManager::deviceForPath(filePath);
524 QTC_ASSERT(device, return Environment{});
525 return device->systemEnvironment();
526 };
527
528 FileUtils::setDeviceFileHooks(deviceHooks);
529
530 DeviceProcessHooks processHooks;
531
532 processHooks.startProcessHook = [](QtcProcess &process) {
533 FilePath filePath = process.commandLine().executable();
534 auto device = DeviceManager::deviceForPath(filePath);
535 QTC_ASSERT(device, return);
536 device->runProcess(process);
537 };
538
539 processHooks.systemEnvironmentForBinary = [](const FilePath &filePath) {
540 auto device = DeviceManager::deviceForPath(filePath);
541 QTC_ASSERT(device, return Environment());
542 return device->systemEnvironment();
543 };
544
545 QtcProcess::setRemoteProcessHooks(processHooks);
546 }
547
~DeviceManager()548 DeviceManager::~DeviceManager()
549 {
550 if (d->clonedInstance != this)
551 delete d->writer;
552 if (m_instance == this)
553 m_instance = nullptr;
554 }
555
deviceAt(int idx) const556 IDevice::ConstPtr DeviceManager::deviceAt(int idx) const
557 {
558 QTC_ASSERT(idx >= 0 && idx < deviceCount(), return IDevice::ConstPtr());
559 return d->devices.at(idx);
560 }
561
mutableDevice(Utils::Id id) const562 IDevice::Ptr DeviceManager::mutableDevice(Utils::Id id) const
563 {
564 const int index = d->indexForId(id);
565 return index == -1 ? IDevice::Ptr() : d->devices.at(index);
566 }
567
hasDevice(const QString & name) const568 bool DeviceManager::hasDevice(const QString &name) const
569 {
570 return Utils::anyOf(d->devices, [&name](const IDevice::Ptr &device) {
571 return device->displayName() == name;
572 });
573 }
574
find(Utils::Id id) const575 IDevice::ConstPtr DeviceManager::find(Utils::Id id) const
576 {
577 const int index = d->indexForId(id);
578 return index == -1 ? IDevice::ConstPtr() : deviceAt(index);
579 }
580
defaultDevice(Utils::Id deviceType) const581 IDevice::ConstPtr DeviceManager::defaultDevice(Utils::Id deviceType) const
582 {
583 const Utils::Id id = d->defaultDevices.value(deviceType);
584 return id.isValid() ? find(id) : IDevice::ConstPtr();
585 }
586
587 } // namespace ProjectExplorer
588
589
590 #ifdef WITH_TESTS
591 #include <projectexplorer/projectexplorer.h>
592 #include <QSignalSpy>
593 #include <QTest>
594 #include <QUuid>
595
596 namespace ProjectExplorer {
597
598 class TestDevice : public IDevice
599 {
600 public:
TestDevice()601 TestDevice()
602 {
603 setupId(AutoDetected, Utils::Id::fromString(QUuid::createUuid().toString()));
604 setType(testTypeId());
605 setMachineType(Hardware);
606 setOsType(HostOsInfo::hostOs());
607 setDisplayType("blubb");
608 }
609
testTypeId()610 static Utils::Id testTypeId() { return "TestType"; }
611 private:
createWidget()612 IDeviceWidget *createWidget() override { return nullptr; }
signalOperation() const613 DeviceProcessSignalOperation::Ptr signalOperation() const override
614 {
615 return DeviceProcessSignalOperation::Ptr();
616 }
617 };
618
619 class TestDeviceFactory final : public IDeviceFactory
620 {
621 public:
TestDeviceFactory()622 TestDeviceFactory() : IDeviceFactory(TestDevice::testTypeId())
623 {
624 setConstructionFunction([] { return IDevice::Ptr(new TestDevice); });
625 }
626 };
627
testDeviceManager()628 void ProjectExplorerPlugin::testDeviceManager()
629 {
630 TestDeviceFactory factory;
631
632 TestDevice::Ptr dev = IDevice::Ptr(new TestDevice);
633 dev->setDisplayName(QLatin1String("blubbdiblubbfurz!"));
634 QVERIFY(dev->isAutoDetected());
635 QCOMPARE(dev->deviceState(), IDevice::DeviceStateUnknown);
636 QCOMPARE(dev->type(), TestDevice::testTypeId());
637
638 TestDevice::Ptr dev2 = dev->clone();
639 QCOMPARE(dev->id(), dev2->id());
640
641 DeviceManager * const mgr = DeviceManager::instance();
642 QVERIFY(!mgr->find(dev->id()));
643 const int oldDeviceCount = mgr->deviceCount();
644
645 QSignalSpy deviceAddedSpy(mgr, &DeviceManager::deviceAdded);
646 QSignalSpy deviceRemovedSpy(mgr, &DeviceManager::deviceRemoved);
647 QSignalSpy deviceUpdatedSpy(mgr, &DeviceManager::deviceUpdated);
648 QSignalSpy deviceListReplacedSpy(mgr, &DeviceManager::deviceListReplaced);
649 QSignalSpy updatedSpy(mgr, &DeviceManager::updated);
650
651 mgr->addDevice(dev);
652 QCOMPARE(mgr->deviceCount(), oldDeviceCount + 1);
653 QVERIFY(mgr->find(dev->id()));
654 QVERIFY(mgr->hasDevice(dev->displayName()));
655 QCOMPARE(deviceAddedSpy.count(), 1);
656 QCOMPARE(deviceRemovedSpy.count(), 0);
657 QCOMPARE(deviceUpdatedSpy.count(), 0);
658 QCOMPARE(deviceListReplacedSpy.count(), 0);
659 QCOMPARE(updatedSpy.count(), 1);
660 deviceAddedSpy.clear();
661 updatedSpy.clear();
662
663 mgr->setDeviceState(dev->id(), IDevice::DeviceStateUnknown);
664 QCOMPARE(deviceAddedSpy.count(), 0);
665 QCOMPARE(deviceRemovedSpy.count(), 0);
666 QCOMPARE(deviceUpdatedSpy.count(), 0);
667 QCOMPARE(deviceListReplacedSpy.count(), 0);
668 QCOMPARE(updatedSpy.count(), 0);
669
670 mgr->setDeviceState(dev->id(), IDevice::DeviceReadyToUse);
671 QCOMPARE(mgr->find(dev->id())->deviceState(), IDevice::DeviceReadyToUse);
672 QCOMPARE(deviceAddedSpy.count(), 0);
673 QCOMPARE(deviceRemovedSpy.count(), 0);
674 QCOMPARE(deviceUpdatedSpy.count(), 1);
675 QCOMPARE(deviceListReplacedSpy.count(), 0);
676 QCOMPARE(updatedSpy.count(), 1);
677 deviceUpdatedSpy.clear();
678 updatedSpy.clear();
679
680 mgr->addDevice(dev2);
681 QCOMPARE(mgr->deviceCount(), oldDeviceCount + 1);
682 QVERIFY(mgr->find(dev->id()));
683 QCOMPARE(deviceAddedSpy.count(), 0);
684 QCOMPARE(deviceRemovedSpy.count(), 0);
685 QCOMPARE(deviceUpdatedSpy.count(), 1);
686 QCOMPARE(deviceListReplacedSpy.count(), 0);
687 QCOMPARE(updatedSpy.count(), 1);
688 deviceUpdatedSpy.clear();
689 updatedSpy.clear();
690
691 TestDevice::Ptr dev3 = IDevice::Ptr(new TestDevice);
692 QVERIFY(dev->id() != dev3->id());
693
694 dev3->setDisplayName(dev->displayName());
695 mgr->addDevice(dev3);
696 QCOMPARE(mgr->deviceAt(mgr->deviceCount() - 1)->displayName(),
697 QString(dev3->displayName() + QLatin1Char('2')));
698 QCOMPARE(deviceAddedSpy.count(), 1);
699 QCOMPARE(deviceRemovedSpy.count(), 0);
700 QCOMPARE(deviceUpdatedSpy.count(), 0);
701 QCOMPARE(deviceListReplacedSpy.count(), 0);
702 QCOMPARE(updatedSpy.count(), 1);
703 deviceAddedSpy.clear();
704 updatedSpy.clear();
705
706 mgr->removeDevice(dev->id());
707 mgr->removeDevice(dev3->id());
708 QCOMPARE(mgr->deviceCount(), oldDeviceCount);
709 QVERIFY(!mgr->find(dev->id()));
710 QVERIFY(!mgr->find(dev3->id()));
711 QCOMPARE(deviceAddedSpy.count(), 0);
712 QCOMPARE(deviceRemovedSpy.count(), 2);
713 // QCOMPARE(deviceUpdatedSpy.count(), 0); Uncomment once the "default" stuff is gone.
714 QCOMPARE(deviceListReplacedSpy.count(), 0);
715 QCOMPARE(updatedSpy.count(), 2);
716 }
717
718 } // namespace ProjectExplorer
719
720 #endif // WITH_TESTS
721