1 /* 2 qtivipropertymodeltest.cpp 3 4 This file is part of GammaRay, the Qt application inspection and 5 manipulation tool. 6 7 Copyright (C) 2016-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com 8 Author: Filipe Azevedo <filipe.azevedo@kdab.com> 9 10 Licensees holding valid commercial KDAB GammaRay licenses may use this file in 11 accordance with GammaRay Commercial License Agreement provided with the Software. 12 13 Contact info@kdab.com if any conditions of this licensing are not clear to you. 14 15 This program is free software; you can redistribute it and/or modify 16 it under the terms of the GNU General Public License as published by 17 the Free Software Foundation, either version 2 of the License, or 18 (at your option) any later version. 19 20 This program is distributed in the hope that it will be useful, 21 but WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 GNU General Public License for more details. 24 25 You should have received a copy of the GNU General Public License 26 along with this program. If not, see <http://www.gnu.org/licenses/>. 27 */ 28 29 #include "baseprobetest.h" 30 #include "testhelpers.h" 31 32 #include <plugins/qtivi/qtivipropertymodel.h> 33 34 #include <common/objectbroker.h> 35 #include <common/objectid.h> 36 37 #include <3rdparty/qt/modeltest.h> 38 39 #include <QDebug> 40 #include <QIviServiceObject> 41 #include <QIviClimateControl> 42 #if defined(QT_IVIMEDIA_LIB) 43 #include <QIviAmFmTuner> 44 #endif 45 46 #include <algorithm> 47 48 //#define ENABLE_LOG 49 50 using namespace GammaRay; 51 using namespace TestHelpers; 52 53 class QtIviPropertyModelTest : public BaseProbeTest 54 { 55 Q_OBJECT 56 QIviClimateControl *m_climate; 57 #if defined(QT_IVIMEDIA_LIB) 58 QIviAmFmTuner *m_amfm; 59 #endif 60 QAbstractItemModel *m_model; 61 62 typedef QSharedPointer<QtIviPropertyModel::IviCarrierProperty> IviCarrierProperty; 63 64 public: QtIviPropertyModelTest(QObject * parent=nullptr)65 explicit QtIviPropertyModelTest(QObject *parent = nullptr) 66 : BaseProbeTest(parent) 67 , m_climate(nullptr) 68 #if defined(QT_IVIMEDIA_LIB) 69 , m_amfm(nullptr) 70 #endif 71 , m_model(nullptr) 72 { 73 } 74 75 private: createIviProperties()76 void createIviProperties() 77 { 78 m_climate = new QIviClimateControl(QString(), this); 79 QVERIFY(m_climate); 80 m_climate->setDiscoveryMode(QIviAbstractZonedFeature::LoadOnlySimulationBackends); 81 QVERIFY(m_climate->startAutoDiscovery() == QIviAbstractFeature::SimulationBackendLoaded); 82 QVERIFY(m_climate->isValid()); 83 #if defined(QT_IVIMEDIA_LIB) 84 m_amfm = new QIviAmFmTuner(this); 85 QVERIFY(m_amfm); 86 m_amfm->setDiscoveryMode(QIviAbstractZonedFeature::LoadOnlySimulationBackends); 87 QVERIFY(m_amfm->startAutoDiscovery() == QIviAbstractFeature::SimulationBackendLoaded); 88 QVERIFY(m_amfm->isValid()); 89 #endif 90 } 91 carrierIndex(QObject * carrier,int column) const92 QModelIndex carrierIndex(QObject *carrier, int column) const 93 { 94 const auto index(m_model->match(m_model->index(0, 0), ObjectModel::ObjectIdRole, 95 QVariant::fromValue(ObjectId(carrier)), 1, 96 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)).value(0)); 97 return index.sibling(index.row(), column); 98 } 99 carrierPropertyIndex(QObject * carrier,const QString & property,int column) const100 QModelIndex carrierPropertyIndex(QObject *carrier, const QString &property, int column) const 101 { 102 const QModelIndex parent(carrierIndex(carrier, 0)); 103 const auto index(parent.isValid() 104 ? m_model->match(m_model->index(0, 0, parent), Qt::DisplayRole, property, 1, 105 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap)).value(0) 106 : QModelIndex()); 107 return index.sibling(index.row(), column); 108 } 109 carrierPropertyData(QObject * carrier,const QString & property,int column,int role) const110 QVariant carrierPropertyData(QObject *carrier, const QString &property, int column, int role) const 111 { 112 return carrierPropertyIndex(carrier, property, column).data(role); 113 } 114 carrierPropertyValue(QObject * carrier,const QString & property) const115 QVariant carrierPropertyValue(QObject *carrier, const QString &property) const 116 { 117 return carrierPropertyData(carrier, property, QtIviPropertyModel::ValueColumn, 118 QtIviPropertyModel::RawValue); 119 } 120 carrierPropertyIndex(QIviProperty * property,int column) const121 QModelIndex carrierPropertyIndex(QIviProperty *property, int column) const 122 { 123 const auto index(m_model->match(m_model->index(0, 0), ObjectModel::ObjectIdRole, 124 QVariant::fromValue(ObjectId(property)), 1, 125 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap | Qt::MatchRecursive)) 126 .value(0)); 127 return index.sibling(index.row(), column); 128 } 129 carrierPropertyData(QIviProperty * property,int column,int role) const130 QVariant carrierPropertyData(QIviProperty *property, int column, int role) const 131 { 132 return carrierPropertyIndex(property, column).data(role); 133 } 134 carrierPropertyValue(QIviProperty * property) const135 QVariant carrierPropertyValue(QIviProperty *property) const 136 { 137 return carrierPropertyData(property, QtIviPropertyModel::ValueColumn, 138 QtIviPropertyModel::RawValue); 139 } 140 iviCarrierProperty(QObject * carrier,const QString & property) const141 IviCarrierProperty iviCarrierProperty(QObject *carrier, const QString &property) const { 142 if (!carrier) 143 return IviCarrierProperty::create(); 144 145 const QMetaObject *mo(carrier->metaObject()); 146 const int index(mo->indexOfProperty(qPrintable(property))); 147 148 if (index == -1) 149 return IviCarrierProperty::create(); 150 151 const QMetaProperty metaProperty(mo->property(index)); 152 153 if (metaProperty.userType() == qMetaTypeId<QIviProperty *>()) { 154 return IviCarrierProperty::create(metaProperty.read(carrier).value<QIviProperty *>(), metaProperty); 155 } 156 157 return IviCarrierProperty::create(metaProperty); 158 } 159 160 // Check ivi object properties vs model properties testIviObject(QObject * iviObject) const161 bool testIviObject(QObject *iviObject) const 162 { 163 #if defined(ENABLE_LOG) 164 qWarning() << "Testing ivi object: " << iviObject; 165 #endif 166 QVERIFY_RETURN_FALSE(iviObject); 167 168 QVector<QString> iviPropertyNames; 169 QMap<QString, QVariant> iviProperties; 170 const QMetaObject *iviMo(iviObject->metaObject()); 171 int propertyOffset = -1; 172 173 if (qobject_cast<QIviServiceObject *>(iviObject)) 174 propertyOffset = QIviServiceObject::staticMetaObject.propertyOffset(); 175 else if (qobject_cast<QIviAbstractFeature *>(iviObject)) 176 propertyOffset = QIviAbstractFeature::staticMetaObject.propertyOffset(); 177 178 QVERIFY_RETURN_FALSE(propertyOffset != -1); 179 180 for (int i = propertyOffset; i < iviMo->propertyCount(); ++i) { 181 const QMetaProperty property(iviMo->property(i)); 182 183 iviPropertyNames << QLatin1String(property.name()); 184 QVERIFY_RETURN_FALSE(!iviPropertyNames.last().isEmpty()); 185 186 if (property.userType() == qMetaTypeId<QIviProperty *>()) { 187 QIviProperty *ivi = property.read(iviObject).value<QIviProperty *>(); 188 QVERIFY_RETURN_FALSE(ivi); 189 iviProperties[iviPropertyNames.last()] = ivi->value(); 190 } else { 191 iviProperties[iviPropertyNames.last()] = property.read(iviObject); 192 } 193 } 194 195 std::stable_sort(iviPropertyNames.begin(), iviPropertyNames.end()); 196 QVERIFY_RETURN_FALSE(!iviPropertyNames.isEmpty()); 197 QVERIFY_RETURN_FALSE(!iviProperties.isEmpty()); 198 199 const QModelIndex carrierIndex(this->carrierIndex(iviObject, 0)); 200 const int carrierPropertyCount(m_model->rowCount(carrierIndex)); 201 QVERIFY_RETURN_FALSE(carrierIndex.isValid()); 202 QCOMPARE_RETURN_FALSE(iviProperties.count(), carrierPropertyCount); 203 204 QVector<QString> carrierModelPropertyNames; 205 206 for (int i = 0; i < carrierPropertyCount; ++i) { 207 const QModelIndex nameIndex(m_model->index(i, QtIviPropertyModel::NameColumn, carrierIndex)); 208 carrierModelPropertyNames << nameIndex.data(Qt::DisplayRole).toString(); 209 QVERIFY_RETURN_FALSE(!carrierModelPropertyNames.last().isEmpty()); 210 QCOMPARE_RETURN_FALSE(iviProperties[carrierModelPropertyNames.last()], 211 carrierPropertyValue(iviObject, carrierModelPropertyNames.last())); 212 } 213 214 std::stable_sort(carrierModelPropertyNames.begin(), carrierModelPropertyNames.end()); 215 QCOMPARE_RETURN_FALSE(carrierModelPropertyNames.count(), iviPropertyNames.count()); 216 QVERIFY_RETURN_FALSE(carrierModelPropertyNames == iviPropertyNames); 217 218 if (QIviAbstractZonedFeature *zonedFeature = qobject_cast<QIviAbstractZonedFeature *>(iviObject)) { 219 foreach (QIviAbstractZonedFeature *subZone, zonedFeature->zones()) { 220 if (!testIviObject(subZone)) { 221 return false; 222 } 223 } 224 } 225 226 return true; 227 } 228 229 private slots: initTestCase()230 void initTestCase() 231 { 232 createIviProperties(); 233 createProbe(); 234 m_model = ObjectBroker::model("com.kdab.GammaRay.PropertyModel"); 235 QVERIFY(m_model); 236 } 237 testModelContent()238 void testModelContent() 239 { 240 QVERIFY(new ModelTest(m_model, this)); 241 QVERIFY(testIviObject(m_climate)); 242 #if defined(QT_IVIMEDIA_LIB) 243 QVERIFY(testIviObject(m_amfm)); 244 #endif 245 } 246 testModelContentChanges_data()247 void testModelContentChanges_data() 248 { 249 QTest::addColumn<QObject *>("carrier"); 250 QTest::addColumn<QString>("property"); 251 QTest::addColumn<bool>("override"); 252 QTest::addColumn<QVariant>("value"); 253 QTest::addColumn<QVariant>("expect"); 254 QTest::addColumn<bool>("invalidProperty"); 255 256 // Invalid value mean nothing to set 257 // Invalid expect mean use value 258 259 QTest::newRow("climate-default-climateMode") 260 << (QObject *)m_climate 261 << "climateMode" 262 << true 263 << QVariant() 264 << QVariant::fromValue(QIviClimateControl::ClimateOn) 265 << false 266 ; 267 268 QTest::newRow("climate-climateMode-Off") 269 << (QObject *)m_climate 270 << "climateMode" 271 << false 272 << QVariant::fromValue(QIviClimateControl::ClimateOff) 273 << QVariant() 274 << false 275 ; 276 277 QTest::newRow("climate-airflowDirections-Floor") 278 << (QObject *)m_climate 279 << "airflowDirections" 280 << false 281 << QVariant::fromValue(QIviClimateControl::AirflowDirections(QIviClimateControl::Floor)) 282 << QVariant() 283 << false 284 ; 285 286 QTest::newRow("climate-airflowDirections-Floor|Dashboard") 287 << (QObject *)m_climate 288 << "airflowDirections" 289 << false 290 << QVariant::fromValue(QIviClimateControl::AirflowDirections(QIviClimateControl::Floor | 291 QIviClimateControl::Dashboard)) 292 << QVariant() 293 << false 294 ; 295 296 QTest::newRow("climate-FrontLeft-targetTemperature-25") 297 << (QObject *)m_climate->zoneAt("FrontLeft") 298 << "targetTemperature" 299 << false 300 << QVariant(25) 301 << QVariant() 302 << false 303 ; 304 305 QTest::newRow("climate-FrontRight-seatHeater-8") 306 << (QObject *)m_climate->zoneAt("FrontRight") 307 << "seatHeater" 308 << false 309 << QVariant(8) 310 << QVariant() 311 << false 312 ; 313 314 QTest::newRow("unknown-property") 315 << (QObject *)nullptr 316 << "hey" 317 << false 318 << QVariant(42) 319 << QVariant() 320 << true 321 ; 322 323 QTest::newRow("climate-unknown-property-override") 324 << (QObject *)m_climate->zoneAt("FrontLeft") 325 << "hey" 326 << true 327 << QVariant(42) 328 << QVariant() 329 << true 330 ; 331 332 QTest::newRow("climate-FrontLeft-heater-true(readonly)") 333 << (QObject *)m_climate->zoneAt("FrontLeft") 334 << "heater" 335 << false 336 << QVariant(true) 337 << QVariant(false) 338 << false 339 ; 340 341 #if defined(QT_IVIMEDIA_LIB) 342 QTest::newRow("amfm-default-discoveryMode") 343 << (QObject *)m_amfm 344 << "discoveryMode" 345 << false 346 << QVariant() 347 << QVariant::fromValue(QIviAbstractFeature::LoadOnlySimulationBackends) 348 << false 349 ; 350 351 QTest::newRow("amfm-discoveryMode-AutoDiscovery") 352 << (QObject *)m_amfm 353 << "discoveryMode" 354 << false 355 << QVariant::fromValue(QIviAbstractFeature::AutoDiscovery) 356 << QVariant() 357 << false 358 ; 359 360 QTest::newRow("amfm-band-AMBand") 361 << (QObject *)m_amfm 362 << "band" 363 << false 364 << QVariant::fromValue(QIviAmFmTuner::AMBand) 365 << QVariant() 366 << false 367 ; 368 369 QTest::newRow("amfm-unknown-property-override") 370 << (QObject *)m_amfm 371 << "hey" 372 << true 373 << QVariant(42) 374 << QVariant() 375 << true 376 ; 377 378 QTest::newRow("amfm-scanRunning-true(readonly)") 379 << (QObject *)m_amfm 380 << "scanRunning" 381 << false 382 << QVariant(true) 383 << QVariant(false) 384 << false 385 ; 386 #endif 387 } 388 testModelContentChanges()389 void testModelContentChanges() 390 { 391 QFETCH(QObject *, carrier); 392 QFETCH(QString, property); 393 QFETCH(bool, override); 394 QFETCH(QVariant, value); 395 QFETCH(QVariant, expect); 396 QFETCH(bool, invalidProperty); 397 const QVariant expectValue(expect.isValid() ? expect : value); 398 const IviCarrierProperty iviProperty(iviCarrierProperty(carrier, property)); 399 400 if (invalidProperty) { 401 QEXPECT_FAIL("", "Expected not found property", Continue); 402 } 403 QVERIFY(iviProperty->isValid()); 404 405 const QModelIndex valueIndex(carrierPropertyIndex(carrier, property, QtIviPropertyModel::ValueColumn)); 406 if (iviProperty->m_iviProperty) { 407 QCOMPARE(valueIndex, carrierPropertyIndex(iviProperty->m_iviProperty, valueIndex.column())); 408 } 409 QCOMPARE(valueIndex.isValid(), iviProperty->isValid()); 410 QCOMPARE(!invalidProperty, iviProperty->isValid()); 411 412 413 #if defined(ENABLE_LOG) 414 qWarning("Original value: %s", invalidProperty ? "" : qPrintable(iviProperty->value().toString())); 415 #endif 416 const QVariant originalValue = iviProperty->cppValue(carrier); 417 const QModelIndex overrideIndex(valueIndex.sibling(valueIndex.row(), QtIviPropertyModel::OverrideColumn)); 418 419 #if defined(ENABLE_LOG) 420 qWarning("Setting override: %i", (int)override); 421 #endif 422 const bool willOverride(iviProperty->isAvailable() && iviProperty->isOverridable() && 423 (overrideIndex.data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked) != override); 424 QCOMPARE(m_model->setData(overrideIndex, override ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole), willOverride); 425 QCOMPARE((overrideIndex.data(Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked), 426 (!iviProperty->isAvailable() ? false : override)); 427 428 if (value.isValid()) { 429 #if defined(ENABLE_LOG) 430 qWarning("Setting value: %s", qPrintable(value.toString())); 431 #endif 432 const bool willWrite(iviProperty->isAvailable() && iviProperty->isWritable() && 433 valueIndex.data(QtIviPropertyModel::RawValue) != value); 434 QCOMPARE(m_model->setData(valueIndex, value, Qt::EditRole), willWrite); 435 if (willWrite) { 436 QCOMPARE(valueIndex.data(QtIviPropertyModel::RawValue), value); 437 } 438 } 439 440 if (iviProperty->isValid()) { 441 QCOMPARE(iviProperty->cppValue(carrier), iviProperty->isAvailable() && !override ? expectValue : originalValue); 442 } 443 QCOMPARE(iviProperty->isAvailable(), 444 valueIndex.flags().testFlag(Qt::ItemIsEnabled)); 445 QCOMPARE(iviProperty->isAvailable() && iviProperty->isWritable(), 446 valueIndex.flags().testFlag(Qt::ItemIsEditable)); 447 QCOMPARE(valueIndex.data(QtIviPropertyModel::RawValue), 448 invalidProperty ? QVariant() : expectValue); 449 } 450 }; 451 452 QTEST_MAIN(QtIviPropertyModelTest) 453 454 #include "qtivipropertymodeltest.moc" 455