| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the test suite of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | 
| 21 | ** included in the packaging of this file. Please review the following | 
| 22 | ** information to ensure the GNU General Public License requirements will | 
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
| 24 | ** | 
| 25 | ** $QT_END_LICENSE$ | 
| 26 | ** | 
| 27 | ****************************************************************************/ | 
| 28 | #include <QtTest/QtTest> | 
| 29 | #include <QtXml/QtXml> | 
| 30 | #include <QtGui/QFontInfo> | 
| 31 | #include <QtGui/QFontMetrics> | 
| 32 |  | 
| 33 | #include "private/qcssparser_p.h" | 
| 34 |  | 
| 35 | class tst_QCssParser : public QObject | 
| 36 | { | 
| 37 |     Q_OBJECT | 
| 38 |  | 
| 39 | private slots: | 
| 40 |     void scanner_data(); | 
| 41 |     void scanner(); | 
| 42 |     void term_data(); | 
| 43 |     void term(); | 
| 44 |     void expr_data(); | 
| 45 |     void expr(); | 
| 46 |     void import(); | 
| 47 |     void media(); | 
| 48 |     void page(); | 
| 49 |     void ruleset(); | 
| 50 |     void selector_data(); | 
| 51 |     void selector(); | 
| 52 |     void prio(); | 
| 53 |     void escapes(); | 
| 54 |     void malformedDeclarations_data(); | 
| 55 |     void malformedDeclarations(); | 
| 56 |     void invalidAtKeywords(); | 
| 57 |     void marginValue(); | 
| 58 |     void marginValue_data(); | 
| 59 |     void colorValue_data(); | 
| 60 |     void colorValue(); | 
| 61 |     void styleSelector_data(); | 
| 62 |     void styleSelector(); | 
| 63 |     void specificity_data(); | 
| 64 |     void specificity(); | 
| 65 |     void specificitySort_data(); | 
| 66 |     void specificitySort(); | 
| 67 |     void rulesForNode_data(); | 
| 68 |     void rulesForNode(); | 
| 69 |     void shorthandBackgroundProperty_data(); | 
| 70 |     void shorthandBackgroundProperty(); | 
| 71 |     void pseudoElement_data(); | 
| 72 |     void pseudoElement(); | 
| 73 |     void gradient_data(); | 
| 74 |     void gradient(); | 
| 75 |     void extractFontFamily_data(); | 
| 76 |     void extractFontFamily(); | 
| 77 |     void extractBorder_data(); | 
| 78 |     void extractBorder(); | 
| 79 |     void noTextDecoration(); | 
| 80 |     void quotedAndUnquotedIdentifiers(); | 
| 81 |     void whitespaceValues_data(); | 
| 82 |     void whitespaceValues(); | 
| 83 | }; | 
| 84 |  | 
| 85 | void tst_QCssParser::scanner_data() | 
| 86 | { | 
| 87 |     QTest::addColumn<QString>(name: "input" ); | 
| 88 |     QTest::addColumn<QString>(name: "output" ); | 
| 89 |  | 
| 90 | #if defined(Q_OS_ANDROID) || defined(Q_OS_WINRT) | 
| 91 |     QDir d(":/" ); | 
| 92 | #else | 
| 93 |     QDir d(SRCDIR); | 
| 94 | #endif | 
| 95 |     d.cd(dirName: "testdata" ); | 
| 96 |     d.cd(dirName: "scanner" ); | 
| 97 |     foreach (QFileInfo test, d.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { | 
| 98 |         QString dir = test.absoluteFilePath() + QDir::separator(); | 
| 99 |         QTest::newRow(qPrintable(test.baseName())) | 
| 100 |             << dir + "input"  | 
| 101 |             << dir + "output"  | 
| 102 |             ; | 
| 103 |     } | 
| 104 | } | 
| 105 |  | 
| 106 |  | 
| 107 | static const char *tokenName(QCss::TokenType t) | 
| 108 | { | 
| 109 |     switch (t) { | 
| 110 |         case QCss::NONE: return "NONE" ; | 
| 111 |         case QCss::S: return "S" ; | 
| 112 |         case QCss::CDO: return "CDO" ; | 
| 113 |         case QCss::CDC: return "CDC" ; | 
| 114 |         case QCss::INCLUDES: return "INCLUDES" ; | 
| 115 |         case QCss::DASHMATCH: return "DASHMATCH" ; | 
| 116 |         case QCss::BEGINSWITH: return "BEGINSWITH" ; | 
| 117 |         case QCss::ENDSWITH: return "ENDSWITH" ; | 
| 118 |         case QCss::CONTAINS: return "CONTAINS" ; | 
| 119 |         case QCss::LBRACE: return "LBRACE" ; | 
| 120 |         case QCss::PLUS: return "PLUS" ; | 
| 121 |         case QCss::GREATER: return "GREATER" ; | 
| 122 |         case QCss::COMMA: return "COMMA" ; | 
| 123 |         case QCss::TILDE: return "TILDE" ; | 
| 124 |         case QCss::STRING: return "STRING" ; | 
| 125 |         case QCss::INVALID: return "INVALID" ; | 
| 126 |         case QCss::IDENT: return "IDENT" ; | 
| 127 |         case QCss::HASH: return "HASH" ; | 
| 128 |         case QCss::ATKEYWORD_SYM: return "ATKEYWORD_SYM" ; | 
| 129 |         case QCss::EXCLAMATION_SYM: return "EXCLAMATION_SYM" ; | 
| 130 |         case QCss::LENGTH: return "LENGTH" ; | 
| 131 |         case QCss::PERCENTAGE: return "PERCENTAGE" ; | 
| 132 |         case QCss::NUMBER: return "NUMBER" ; | 
| 133 |         case QCss::FUNCTION: return "FUNCTION" ; | 
| 134 |         case QCss::COLON: return "COLON" ; | 
| 135 |         case QCss::SEMICOLON: return "SEMICOLON" ; | 
| 136 |         case QCss::RBRACE: return "RBRACE" ; | 
| 137 |         case QCss::SLASH: return "SLASH" ; | 
| 138 |         case QCss::MINUS: return "MINUS" ; | 
| 139 |         case QCss::DOT: return "DOT" ; | 
| 140 |         case QCss::STAR: return "STAR" ; | 
| 141 |         case QCss::LBRACKET: return "LBRACKET" ; | 
| 142 |         case QCss::RBRACKET: return "RBRACKET" ; | 
| 143 |         case QCss::EQUAL: return "EQUAL" ; | 
| 144 |         case QCss::LPAREN: return "LPAREN" ; | 
| 145 |         case QCss::RPAREN: return "RPAREN" ; | 
| 146 |         case QCss::OR: return "OR" ; | 
| 147 |     } | 
| 148 |     return "" ; | 
| 149 | } | 
| 150 |  | 
| 151 | static void debug(const QVector<QCss::Symbol> &symbols, int index = -1) | 
| 152 | { | 
| 153 |     qDebug() << "all symbols:" ; | 
| 154 |     for (int i = 0; i < symbols.count(); ++i) | 
| 155 |         qDebug() << '(' << i << "); Token:"  << tokenName(t: symbols.at(i).token) << "; Lexem:"  << symbols.at(i).lexem(); | 
| 156 |     if (index != -1) | 
| 157 |         qDebug() << "failure at index"  << index; | 
| 158 | } | 
| 159 |  | 
| 160 | //static void debug(const QCss::Parser &p) { debug(p.symbols); } | 
| 161 |  | 
| 162 | void tst_QCssParser::scanner() | 
| 163 | { | 
| 164 |     QFETCH(QString, input); | 
| 165 |     QFETCH(QString, output); | 
| 166 |  | 
| 167 |     QFile inputFile(input); | 
| 168 |     QVERIFY(inputFile.open(QIODevice::ReadOnly|QIODevice::Text)); | 
| 169 |     QVector<QCss::Symbol> symbols; | 
| 170 |     QCss::Scanner::scan(preprocessedInput: QCss::Scanner::preprocess(input: QString::fromUtf8(str: inputFile.readAll())), symbols: &symbols); | 
| 171 |  | 
| 172 |     QVERIFY(symbols.count() > 1); | 
| 173 |     QCOMPARE(symbols.last().token, QCss::S); | 
| 174 |     QCOMPARE(symbols.last().lexem(), QLatin1String("\n" )); | 
| 175 |     symbols.remove(i: symbols.count() - 1, n: 1); | 
| 176 |  | 
| 177 |     QFile outputFile(output); | 
| 178 |     QVERIFY(outputFile.open(QIODevice::ReadOnly|QIODevice::Text)); | 
| 179 |     QStringList lines; | 
| 180 |     while (!outputFile.atEnd()) { | 
| 181 |         QString line = QString::fromUtf8(str: outputFile.readLine()); | 
| 182 |         if (line.endsWith(c: QLatin1Char('\n'))) | 
| 183 |             line.chop(n: 1); | 
| 184 |         lines.append(t: line); | 
| 185 |     } | 
| 186 |  | 
| 187 |     if (lines.count() != symbols.count()) { | 
| 188 |         debug(symbols); | 
| 189 |         QCOMPARE(lines.count(), symbols.count()); | 
| 190 |     } | 
| 191 |  | 
| 192 |     for (int i = 0; i < lines.count(); ++i) { | 
| 193 |         QStringList l = lines.at(i).split(sep: QChar::fromLatin1(c: '|')); | 
| 194 |         QCOMPARE(l.count(), 2); | 
| 195 |         const QString expectedToken = l.at(i: 0); | 
| 196 |         const QString expectedLexem = l.at(i: 1); | 
| 197 |         QString actualToken = QString::fromLatin1(str: tokenName(t: symbols.at(i).token)); | 
| 198 |         if (actualToken != expectedToken) { | 
| 199 |             debug(symbols, index: i); | 
| 200 |             QCOMPARE(actualToken, expectedToken); | 
| 201 |         } | 
| 202 |         if (symbols.at(i).lexem() != expectedLexem) { | 
| 203 |             debug(symbols, index: i); | 
| 204 |             QCOMPARE(symbols.at(i).lexem(), expectedLexem); | 
| 205 |         } | 
| 206 |     } | 
| 207 | } | 
| 208 |  | 
| 209 | Q_DECLARE_METATYPE(QCss::Value) | 
| 210 |  | 
| 211 | void tst_QCssParser::term_data() | 
| 212 | { | 
| 213 |     QTest::addColumn<bool>(name: "parseSuccess" ); | 
| 214 |     QTest::addColumn<QString>(name: "css" ); | 
| 215 |     QTest::addColumn<QCss::Value>(name: "expectedValue" ); | 
| 216 |  | 
| 217 |     QCss::Value val; | 
| 218 |  | 
| 219 |     val.type = QCss::Value::Percentage; | 
| 220 |     val.variant = QVariant(double(200)); | 
| 221 |     QTest::newRow(dataTag: "percentage" ) << true << "200%"  << val; | 
| 222 |  | 
| 223 |     val.type = QCss::Value::Length; | 
| 224 |     val.variant = QString("10px" ); | 
| 225 |     QTest::newRow(dataTag: "px" ) << true << "10px"  << val; | 
| 226 |  | 
| 227 |     val.type = QCss::Value::Length; | 
| 228 |     val.variant = QString("10cm" ); | 
| 229 |     QTest::newRow(dataTag: "cm" ) << true << "10cm"  << val; | 
| 230 |  | 
| 231 |     val.type = QCss::Value::Length; | 
| 232 |     val.variant = QString("10mm" ); | 
| 233 |     QTest::newRow(dataTag: "mm" ) << true << "10mm"  << val; | 
| 234 |  | 
| 235 |     val.type = QCss::Value::Length; | 
| 236 |     val.variant = QString("10pt" ); | 
| 237 |     QTest::newRow(dataTag: "pt" ) << true << "10pt"  << val; | 
| 238 |  | 
| 239 |     val.type = QCss::Value::Length; | 
| 240 |     val.variant = QString("10pc" ); | 
| 241 |     QTest::newRow(dataTag: "pc" ) << true << "10pc"  << val; | 
| 242 |  | 
| 243 |     val.type = QCss::Value::Length; | 
| 244 |     val.variant = QString("42in" ); | 
| 245 |     QTest::newRow(dataTag: "inch" ) << true << "42in"  << val; | 
| 246 |  | 
| 247 |     val.type = QCss::Value::Length; | 
| 248 |     val.variant = QString("10deg" ); | 
| 249 |     QTest::newRow(dataTag: "deg" ) << true << "10deg"  << val; | 
| 250 |  | 
| 251 |     val.type = QCss::Value::Length; | 
| 252 |     val.variant = QString("10rad" ); | 
| 253 |     QTest::newRow(dataTag: "rad" ) << true << "10rad"  << val; | 
| 254 |  | 
| 255 |     val.type = QCss::Value::Length; | 
| 256 |     val.variant = QString("10grad" ); | 
| 257 |     QTest::newRow(dataTag: "grad" ) << true << "10grad"  << val; | 
| 258 |  | 
| 259 |     val.type = QCss::Value::Length; | 
| 260 |     val.variant = QString("10ms" ); | 
| 261 |     QTest::newRow(dataTag: "time" ) << true << "10ms"  << val; | 
| 262 |  | 
| 263 |     val.type = QCss::Value::Length; | 
| 264 |     val.variant = QString("10s" ); | 
| 265 |     QTest::newRow(dataTag: "times" ) << true << "10s"  << val; | 
| 266 |  | 
| 267 |     val.type = QCss::Value::Length; | 
| 268 |     val.variant = QString("10hz" ); | 
| 269 |     QTest::newRow(dataTag: "hz" ) << true << "10hz"  << val; | 
| 270 |  | 
| 271 |     val.type = QCss::Value::Length; | 
| 272 |     val.variant = QString("10khz" ); | 
| 273 |     QTest::newRow(dataTag: "khz" ) << true << "10khz"  << val; | 
| 274 |  | 
| 275 |     val.type = QCss::Value::Length; | 
| 276 |     val.variant = QString("10myunit" ); | 
| 277 |     QTest::newRow(dataTag: "dimension" ) << true << "10myunit"  << val; | 
| 278 |  | 
| 279 |     val.type = QCss::Value::Percentage; | 
| 280 |  | 
| 281 |     val.type = QCss::Value::Percentage; | 
| 282 |     val.variant = QVariant(double(-200)); | 
| 283 |     QTest::newRow(dataTag: "minuspercentage" ) << true << "-200%"  << val; | 
| 284 |  | 
| 285 |     val.type = QCss::Value::Length; | 
| 286 |     val.variant = QString("10em" ); | 
| 287 |     QTest::newRow(dataTag: "ems" ) << true << "10em"  << val; | 
| 288 |  | 
| 289 |     val.type = QCss::Value::String; | 
| 290 |     val.variant = QVariant(QString("foo" )); | 
| 291 |     QTest::newRow(dataTag: "string" ) << true << "\"foo\""  << val; | 
| 292 |  | 
| 293 |     val.type = QCss::Value::Function; | 
| 294 |     val.variant = QVariant(QStringList() << "myFunc"  << "23, (nested text)" ); | 
| 295 |     QTest::newRow(dataTag: "function" ) << true << "myFunc(23, (nested text))"  << val; | 
| 296 |  | 
| 297 |     QTest::newRow(dataTag: "function_failure" ) << false << "myFunction((blah)"  << val; | 
| 298 |     QTest::newRow(dataTag: "function_failure2" ) << false << "+myFunc(23, (nested text))"  << val; | 
| 299 |  | 
| 300 |     val.type = QCss::Value::Color; | 
| 301 |     val.variant = QVariant(QColor("#12ff34" )); | 
| 302 |     QTest::newRow(dataTag: "hexcolor" ) << true << "#12ff34"  << val; | 
| 303 |  | 
| 304 |     val.type = QCss::Value::Color; | 
| 305 |     val.variant = QVariant(QColor("#ffbb00" )); | 
| 306 |     QTest::newRow(dataTag: "hexcolor2" ) << true << "#fb0"  << val; | 
| 307 |  | 
| 308 |     val.type = QCss::Value::Uri; | 
| 309 |     val.variant = QString("www.kde.org" ); | 
| 310 |     QTest::newRow(dataTag: "uri1" ) << true << "url(\"www.kde.org\")"  << val; | 
| 311 |  | 
| 312 |     QTest::newRow(dataTag: "uri2" ) << true << "url(www.kde.org)"  << val; | 
| 313 |  | 
| 314 |     val.type = QCss::Value::KnownIdentifier; | 
| 315 |     val.variant = int(QCss::Value_Italic); | 
| 316 |     QTest::newRow(dataTag: "italic" ) << true << "italic"  << val; | 
| 317 |  | 
| 318 |     val.type = QCss::Value::KnownIdentifier; | 
| 319 |     val.variant = int(QCss::Value_Italic); | 
| 320 |     QTest::newRow(dataTag: "ItaLIc" ) << true << "ItaLIc"  << val; | 
| 321 | } | 
| 322 |  | 
| 323 | void tst_QCssParser::term() | 
| 324 | { | 
| 325 |     QFETCH(bool, parseSuccess); | 
| 326 |     QFETCH(QString, css); | 
| 327 |     QFETCH(QCss::Value, expectedValue); | 
| 328 |  | 
| 329 |     QCss::Parser parser(css); | 
| 330 |     QCss::Value val; | 
| 331 |     QVERIFY(parser.testTerm()); | 
| 332 |     QCOMPARE(parser.parseTerm(&val), parseSuccess); | 
| 333 |     if (parseSuccess) { | 
| 334 |         QCOMPARE(int(val.type), int(expectedValue.type)); | 
| 335 |         if (val.variant != expectedValue.variant) { | 
| 336 |             qDebug() << "val.variant:"  << val.variant << "expectedValue.variant:"  << expectedValue.variant; | 
| 337 |             QCOMPARE(val.variant, expectedValue.variant); | 
| 338 |         } | 
| 339 |     } | 
| 340 | } | 
| 341 |  | 
| 342 | void tst_QCssParser::expr_data() | 
| 343 | { | 
| 344 |     QTest::addColumn<bool>(name: "parseSuccess" ); | 
| 345 |     QTest::addColumn<QString>(name: "css" ); | 
| 346 |     QTest::addColumn<QVector<QCss::Value> >(name: "expectedValues" ); | 
| 347 |  | 
| 348 |     QVector<QCss::Value> values; | 
| 349 |     QCss::Value val; | 
| 350 |  | 
| 351 |     QCss::Value comma; | 
| 352 |     comma.type = QCss::Value::TermOperatorComma; | 
| 353 |  | 
| 354 |     val.type = QCss::Value::Identifier; | 
| 355 |     val.variant = QLatin1String("foo" ); | 
| 356 |     values << val; | 
| 357 |     values << comma; | 
| 358 |     val.variant = QLatin1String("bar" ); | 
| 359 |     values << val; | 
| 360 |     values << comma; | 
| 361 |     val.variant = QLatin1String("baz" ); | 
| 362 |     values << val; | 
| 363 |     QTest::newRow(dataTag: "list" ) << true << "foo, bar, baz"  << values; | 
| 364 |     values.clear(); | 
| 365 | } | 
| 366 |  | 
| 367 | void tst_QCssParser::expr() | 
| 368 | { | 
| 369 |     QFETCH(bool, parseSuccess); | 
| 370 |     QFETCH(QString, css); | 
| 371 |     QFETCH(QVector<QCss::Value>, expectedValues); | 
| 372 |  | 
| 373 |     QCss::Parser parser(css); | 
| 374 |     QVector<QCss::Value> values; | 
| 375 |     QVERIFY(parser.testExpr()); | 
| 376 |     QCOMPARE(parser.parseExpr(&values), parseSuccess); | 
| 377 |     if (parseSuccess) { | 
| 378 |         QCOMPARE(values.count(), expectedValues.count()); | 
| 379 |  | 
| 380 |         for (int i = 0; i < values.count(); ++i) { | 
| 381 |             QCOMPARE(int(values.at(i).type), int(expectedValues.at(i).type)); | 
| 382 |             QCOMPARE(values.at(i).variant, expectedValues.at(i).variant); | 
| 383 |         } | 
| 384 |     } | 
| 385 | } | 
| 386 |  | 
| 387 | void tst_QCssParser::import() | 
| 388 | { | 
| 389 |     QCss::Parser parser("@import \"plainstring\";" ); | 
| 390 |     QVERIFY(parser.testImport()); | 
| 391 |     QCss::ImportRule rule; | 
| 392 |     QVERIFY(parser.parseImport(&rule)); | 
| 393 |     QCOMPARE(rule.href, QString("plainstring" )); | 
| 394 |  | 
| 395 |     parser = QCss::Parser("@import url(\"www.kde.org\") print/*comment*/,screen;" ); | 
| 396 |     QVERIFY(parser.testImport()); | 
| 397 |     QVERIFY(parser.parseImport(&rule)); | 
| 398 |     QCOMPARE(rule.href, QString("www.kde.org" )); | 
| 399 |     QCOMPARE(rule.media.count(), 2); | 
| 400 |     QCOMPARE(rule.media.at(0), QString("print" )); | 
| 401 |     QCOMPARE(rule.media.at(1), QString("screen" )); | 
| 402 | } | 
| 403 |  | 
| 404 | void tst_QCssParser::media() | 
| 405 | { | 
| 406 |     QCss::Parser parser("@media print/*comment*/,screen /*comment to ignore*/{ }" ); | 
| 407 |     QVERIFY(parser.testMedia()); | 
| 408 |     QCss::MediaRule rule; | 
| 409 |     QVERIFY(parser.parseMedia(&rule)); | 
| 410 |     QCOMPARE(rule.media.count(), 2); | 
| 411 |     QCOMPARE(rule.media.at(0), QString("print" )); | 
| 412 |     QCOMPARE(rule.media.at(1), QString("screen" )); | 
| 413 |     QVERIFY(rule.styleRules.isEmpty()); | 
| 414 | } | 
| 415 |  | 
| 416 | void tst_QCssParser::page() | 
| 417 | { | 
| 418 |     QCss::Parser parser("@page :first/*comment to ignore*/{ }" ); | 
| 419 |     QVERIFY(parser.testPage()); | 
| 420 |     QCss::PageRule rule; | 
| 421 |     QVERIFY(parser.parsePage(&rule)); | 
| 422 |     QCOMPARE(rule.selector, QString("first" )); | 
| 423 |     QVERIFY(rule.declarations.isEmpty()); | 
| 424 | } | 
| 425 |  | 
| 426 | void tst_QCssParser::ruleset() | 
| 427 | { | 
| 428 |     { | 
| 429 |         QCss::Parser parser("p/*foo*/{ }" ); | 
| 430 |         QVERIFY(parser.testRuleset()); | 
| 431 |         QCss::StyleRule rule; | 
| 432 |         QVERIFY(parser.parseRuleset(&rule)); | 
| 433 |         QCOMPARE(rule.selectors.count(), 1); | 
| 434 |         QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1); | 
| 435 |         QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("p" )); | 
| 436 |         QVERIFY(rule.declarations.isEmpty()); | 
| 437 |     } | 
| 438 |  | 
| 439 |     { | 
| 440 |         QCss::Parser parser("p/*comment*/,div{ }" ); | 
| 441 |         QVERIFY(parser.testRuleset()); | 
| 442 |         QCss::StyleRule rule; | 
| 443 |         QVERIFY(parser.parseRuleset(&rule)); | 
| 444 |         QCOMPARE(rule.selectors.count(), 2); | 
| 445 |         QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1); | 
| 446 |         QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("p" )); | 
| 447 |         QCOMPARE(rule.selectors.at(1).basicSelectors.count(), 1); | 
| 448 |         QCOMPARE(rule.selectors.at(1).basicSelectors.at(0).elementName, QString("div" )); | 
| 449 |         QVERIFY(rule.declarations.isEmpty()); | 
| 450 |     } | 
| 451 |  | 
| 452 |     { | 
| 453 |         QCss::Parser parser(":before, :after { }" ); | 
| 454 |         QVERIFY(parser.testRuleset()); | 
| 455 |         QCss::StyleRule rule; | 
| 456 |         QVERIFY(parser.parseRuleset(&rule)); | 
| 457 |         QCOMPARE(rule.selectors.count(), 2); | 
| 458 |  | 
| 459 |         QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1); | 
| 460 |         QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).pseudos.count(), 1); | 
| 461 |         QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).pseudos.at(0).name, QString("before" )); | 
| 462 |  | 
| 463 |         QCOMPARE(rule.selectors.at(1).basicSelectors.count(), 1); | 
| 464 |         QCOMPARE(rule.selectors.at(1).basicSelectors.at(0).pseudos.count(), 1); | 
| 465 |         QCOMPARE(rule.selectors.at(1).basicSelectors.at(0).pseudos.at(0).name, QString("after" )); | 
| 466 |  | 
| 467 |         QVERIFY(rule.declarations.isEmpty()); | 
| 468 |     } | 
| 469 |  | 
| 470 | } | 
| 471 |  | 
| 472 | Q_DECLARE_METATYPE(QCss::Selector) | 
| 473 |  | 
| 474 | void tst_QCssParser::selector_data() | 
| 475 | { | 
| 476 |     QTest::addColumn<QString>(name: "css" ); | 
| 477 |     QTest::addColumn<QCss::Selector>(name: "expectedSelector" ); | 
| 478 |  | 
| 479 |     { | 
| 480 |         QCss::Selector sel; | 
| 481 |         QCss::BasicSelector basic; | 
| 482 |  | 
| 483 |         basic.elementName = "p" ; | 
| 484 |         basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfDirectAdjecent; | 
| 485 |         sel.basicSelectors << basic; | 
| 486 |  | 
| 487 |         basic = QCss::BasicSelector(); | 
| 488 |         basic.elementName = "div" ; | 
| 489 |         sel.basicSelectors << basic; | 
| 490 |  | 
| 491 |         QTest::newRow(dataTag: "comment" ) << QString("p/* */+ div" ) << sel; | 
| 492 |     } | 
| 493 |  | 
| 494 |     { | 
| 495 |         QCss::Selector sel; | 
| 496 |         QCss::BasicSelector basic; | 
| 497 |  | 
| 498 |         basic.elementName = QString(); | 
| 499 |         sel.basicSelectors << basic; | 
| 500 |  | 
| 501 |         QTest::newRow(dataTag: "any" ) << QString("*" ) << sel; | 
| 502 |     } | 
| 503 |  | 
| 504 |     { | 
| 505 |         QCss::Selector sel; | 
| 506 |         QCss::BasicSelector basic; | 
| 507 |  | 
| 508 |         basic.elementName = "e" ; | 
| 509 |         sel.basicSelectors << basic; | 
| 510 |  | 
| 511 |         QTest::newRow(dataTag: "element" ) << QString("e" ) << sel; | 
| 512 |     } | 
| 513 |  | 
| 514 |     { | 
| 515 |         QCss::Selector sel; | 
| 516 |         QCss::BasicSelector basic; | 
| 517 |  | 
| 518 |         basic.elementName = "e" ; | 
| 519 |         basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfAncestor; | 
| 520 |         sel.basicSelectors << basic; | 
| 521 |  | 
| 522 |         basic.elementName = "f" ; | 
| 523 |         basic.relationToNext = QCss::BasicSelector::NoRelation; | 
| 524 |         sel.basicSelectors << basic; | 
| 525 |  | 
| 526 |         QTest::newRow(dataTag: "descendant" ) << QString("e f" ) << sel; | 
| 527 |     } | 
| 528 |  | 
| 529 |     { | 
| 530 |         QCss::Selector sel; | 
| 531 |         QCss::BasicSelector basic; | 
| 532 |  | 
| 533 |         basic.elementName = "e" ; | 
| 534 |         basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfParent; | 
| 535 |         sel.basicSelectors << basic; | 
| 536 |  | 
| 537 |         basic.elementName = "f" ; | 
| 538 |         basic.relationToNext = QCss::BasicSelector::NoRelation; | 
| 539 |         sel.basicSelectors << basic; | 
| 540 |  | 
| 541 |         QTest::newRow(dataTag: "parent" ) << QString("e > f" ) << sel; | 
| 542 |     } | 
| 543 |  | 
| 544 |     { | 
| 545 |         QCss::Selector sel; | 
| 546 |         QCss::BasicSelector basic; | 
| 547 |  | 
| 548 |         basic.elementName = "e" ; | 
| 549 |         QCss::Pseudo pseudo; | 
| 550 |         pseudo.name = "first-child" ; | 
| 551 |         basic.pseudos.append(t: pseudo); | 
| 552 |         sel.basicSelectors << basic; | 
| 553 |  | 
| 554 |         QTest::newRow(dataTag: "first-child" ) << QString("e:first-child" ) << sel; | 
| 555 |     } | 
| 556 |  | 
| 557 |     { | 
| 558 |         QCss::Selector sel; | 
| 559 |         QCss::BasicSelector basic; | 
| 560 |  | 
| 561 |         basic.elementName = "e" ; | 
| 562 |         QCss::Pseudo pseudo; | 
| 563 |         pseudo.name = "c" ; | 
| 564 |         pseudo.function = "lang" ; | 
| 565 |         basic.pseudos.append(t: pseudo); | 
| 566 |         sel.basicSelectors << basic; | 
| 567 |  | 
| 568 |         QTest::newRow(dataTag: "lang" ) << QString("e:lang(c)" ) << sel; | 
| 569 |     } | 
| 570 |  | 
| 571 |     { | 
| 572 |         QCss::Selector sel; | 
| 573 |         QCss::BasicSelector basic; | 
| 574 |  | 
| 575 |         basic.elementName = "e" ; | 
| 576 |         basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfDirectAdjecent; | 
| 577 |         sel.basicSelectors << basic; | 
| 578 |  | 
| 579 |         basic.elementName = "f" ; | 
| 580 |         basic.relationToNext = QCss::BasicSelector::NoRelation; | 
| 581 |         sel.basicSelectors << basic; | 
| 582 |  | 
| 583 |         QTest::newRow(dataTag: "lastsibling" ) << QString("e + f" ) << sel; | 
| 584 |     } | 
| 585 |  | 
| 586 |     { | 
| 587 |         QCss::Selector sel; | 
| 588 |         QCss::BasicSelector basic; | 
| 589 |  | 
| 590 |         basic.elementName = "e" ; | 
| 591 |         basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfIndirectAdjecent; | 
| 592 |         sel.basicSelectors << basic; | 
| 593 |  | 
| 594 |         basic.elementName = "f" ; | 
| 595 |         basic.relationToNext = QCss::BasicSelector::NoRelation; | 
| 596 |         sel.basicSelectors << basic; | 
| 597 |  | 
| 598 |         QTest::newRow(dataTag: "previoussibling" ) << QString("e ~ f" ) << sel; | 
| 599 |     } | 
| 600 |  | 
| 601 |     { | 
| 602 |         QCss::Selector sel; | 
| 603 |         QCss::BasicSelector basic; | 
| 604 |  | 
| 605 |         basic.elementName = "e" ; | 
| 606 |         QCss::AttributeSelector attrSel; | 
| 607 |         attrSel.name = "foo" ; | 
| 608 |         basic.attributeSelectors << attrSel; | 
| 609 |         sel.basicSelectors << basic; | 
| 610 |  | 
| 611 |         QTest::newRow(dataTag: "attr" ) << QString("e[foo]" ) << sel; | 
| 612 |     } | 
| 613 |  | 
| 614 |     { | 
| 615 |         QCss::Selector sel; | 
| 616 |         QCss::BasicSelector basic; | 
| 617 |  | 
| 618 |         basic.elementName = "e" ; | 
| 619 |         QCss::AttributeSelector attrSel; | 
| 620 |         attrSel.name = "foo" ; | 
| 621 |         attrSel.value = "warning" ; | 
| 622 |         attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchEqual; | 
| 623 |         basic.attributeSelectors << attrSel; | 
| 624 |         sel.basicSelectors << basic; | 
| 625 |  | 
| 626 |         QTest::newRow(dataTag: "attr-equal" ) << QString("e[foo=\"warning\"]" ) << sel; | 
| 627 |     } | 
| 628 |  | 
| 629 |     { | 
| 630 |         QCss::Selector sel; | 
| 631 |         QCss::BasicSelector basic; | 
| 632 |  | 
| 633 |         basic.elementName = "e" ; | 
| 634 |         QCss::AttributeSelector attrSel; | 
| 635 |         attrSel.name = "foo" ; | 
| 636 |         attrSel.value = "warning" ; | 
| 637 |         attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchIncludes; | 
| 638 |         basic.attributeSelectors << attrSel; | 
| 639 |         sel.basicSelectors << basic; | 
| 640 |  | 
| 641 |         QTest::newRow(dataTag: "attr-includes" ) << QString("e[foo~=\"warning\"]" ) << sel; | 
| 642 |     } | 
| 643 |  | 
| 644 |     { | 
| 645 |         QCss::Selector sel; | 
| 646 |         QCss::BasicSelector basic; | 
| 647 |  | 
| 648 |         basic.elementName = "e" ; | 
| 649 |         QCss::AttributeSelector attrSel; | 
| 650 |         attrSel.name = "lang" ; | 
| 651 |         attrSel.value = "en" ; | 
| 652 |         attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchDashMatch; | 
| 653 |         basic.attributeSelectors << attrSel; | 
| 654 |         sel.basicSelectors << basic; | 
| 655 |  | 
| 656 |         QTest::newRow(dataTag: "attr-dash" ) << QString("e[lang|=\"en\"]" ) << sel; | 
| 657 |     } | 
| 658 |  | 
| 659 |     { | 
| 660 |         QCss::Selector sel; | 
| 661 |         QCss::BasicSelector basic; | 
| 662 |  | 
| 663 |         basic.elementName = "e" ; | 
| 664 |         QCss::AttributeSelector attrSel; | 
| 665 |         attrSel.name = "foo" ; | 
| 666 |         attrSel.value = "warning" ; | 
| 667 |         attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchContains; | 
| 668 |         basic.attributeSelectors << attrSel; | 
| 669 |         sel.basicSelectors << basic; | 
| 670 |  | 
| 671 |         QTest::newRow(dataTag: "attr-contains" ) << QString("e[foo*=\"warning\"]" ) << sel; | 
| 672 |     } | 
| 673 |  | 
| 674 |     { | 
| 675 |         QCss::Selector sel; | 
| 676 |         QCss::BasicSelector basic; | 
| 677 |  | 
| 678 |         basic.elementName = "div" ; | 
| 679 |  | 
| 680 |         QCss::AttributeSelector attrSel; | 
| 681 |         attrSel.name = "class" ; | 
| 682 |         attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchIncludes; | 
| 683 |         attrSel.value = "warning" ; | 
| 684 |         basic.attributeSelectors.append(t: attrSel); | 
| 685 |  | 
| 686 |         attrSel.value = "foo" ; | 
| 687 |         basic.attributeSelectors.append(t: attrSel); | 
| 688 |  | 
| 689 |         sel.basicSelectors << basic; | 
| 690 |  | 
| 691 |         QTest::newRow(dataTag: "class" ) << QString("div.warning.foo" ) << sel; | 
| 692 |     } | 
| 693 |  | 
| 694 |     { | 
| 695 |         QCss::Selector sel; | 
| 696 |         QCss::BasicSelector basic; | 
| 697 |  | 
| 698 |         basic.elementName = "e" ; | 
| 699 |         basic.ids << "myid" ; | 
| 700 |         sel.basicSelectors << basic; | 
| 701 |  | 
| 702 |         QTest::newRow(dataTag: "id" ) << QString("e#myid" ) << sel; | 
| 703 |     } | 
| 704 | } | 
| 705 |  | 
| 706 | void tst_QCssParser::selector() | 
| 707 | { | 
| 708 |     QFETCH(QString, css); | 
| 709 |     QFETCH(QCss::Selector, expectedSelector); | 
| 710 |  | 
| 711 |     QCss::Parser parser(css); | 
| 712 |     QVERIFY(parser.testSelector()); | 
| 713 |     QCss::Selector selector; | 
| 714 |     QVERIFY(parser.parseSelector(&selector)); | 
| 715 |  | 
| 716 |     QCOMPARE(selector.basicSelectors.count(), expectedSelector.basicSelectors.count()); | 
| 717 |     for (int i = 0; i < selector.basicSelectors.count(); ++i) { | 
| 718 |         const QCss::BasicSelector sel = selector.basicSelectors.at(i); | 
| 719 |         const QCss::BasicSelector expectedSel = expectedSelector.basicSelectors.at(i); | 
| 720 |         QCOMPARE(sel.elementName, expectedSel.elementName); | 
| 721 |         QCOMPARE(int(sel.relationToNext), int(expectedSel.relationToNext)); | 
| 722 |  | 
| 723 |         QCOMPARE(sel.pseudos.count(), expectedSel.pseudos.count()); | 
| 724 |         for (int i = 0; i < sel.pseudos.count(); ++i) { | 
| 725 |             QCOMPARE(sel.pseudos.at(i).name, expectedSel.pseudos.at(i).name); | 
| 726 |             QCOMPARE(sel.pseudos.at(i).function, expectedSel.pseudos.at(i).function); | 
| 727 |         } | 
| 728 |  | 
| 729 |         QCOMPARE(sel.attributeSelectors.count(), expectedSel.attributeSelectors.count()); | 
| 730 |         for (int i = 0; i < sel.attributeSelectors.count(); ++i) { | 
| 731 |             QCOMPARE(sel.attributeSelectors.at(i).name, expectedSel.attributeSelectors.at(i).name); | 
| 732 |             QCOMPARE(sel.attributeSelectors.at(i).value, expectedSel.attributeSelectors.at(i).value); | 
| 733 |             QCOMPARE(int(sel.attributeSelectors.at(i).valueMatchCriterium), int(expectedSel.attributeSelectors.at(i).valueMatchCriterium)); | 
| 734 |         } | 
| 735 |     } | 
| 736 | } | 
| 737 |  | 
| 738 | void tst_QCssParser::prio() | 
| 739 | { | 
| 740 |     { | 
| 741 |         QCss::Parser parser("!important" ); | 
| 742 |         QVERIFY(parser.testPrio()); | 
| 743 |     } | 
| 744 |     { | 
| 745 |         QCss::Parser parser("!impOrTAnt" ); | 
| 746 |         QVERIFY(parser.testPrio()); | 
| 747 |     } | 
| 748 |     { | 
| 749 |         QCss::Parser parser("!\"important\"" ); | 
| 750 |         QVERIFY(!parser.testPrio()); | 
| 751 |         QCOMPARE(parser.index, 0); | 
| 752 |     } | 
| 753 |     { | 
| 754 |         QCss::Parser parser("!importbleh" ); | 
| 755 |         QVERIFY(!parser.testPrio()); | 
| 756 |         QCOMPARE(parser.index, 0); | 
| 757 |     } | 
| 758 | } | 
| 759 |  | 
| 760 | void tst_QCssParser::escapes() | 
| 761 | { | 
| 762 |     QCss::Parser parser("\\hello" ); | 
| 763 |     parser.test(t: QCss::IDENT); | 
| 764 |     QCOMPARE(parser.lexem(), QString("hello" )); | 
| 765 | } | 
| 766 |  | 
| 767 | void tst_QCssParser::malformedDeclarations_data() | 
| 768 | { | 
| 769 |     QTest::addColumn<QString>(name: "css" ); | 
| 770 |  | 
| 771 |     QTest::newRow(dataTag: "1" ) << QString("p { color:green }" ); | 
| 772 |     QTest::newRow(dataTag: "2" ) << QString("p { color:green; color }  /* malformed declaration missing ':', value */" ); | 
| 773 |     QTest::newRow(dataTag: "3" ) << QString("p { color:red;   color; color:green }  /* same with expected recovery */" ); | 
| 774 |     QTest::newRow(dataTag: "4" ) << QString("p { color:green; color: } /* malformed declaration missing value */" ); | 
| 775 |     QTest::newRow(dataTag: "5" ) << QString("p { color:red;   color:; color:green } /* same with expected recovery */" ); | 
| 776 |     QTest::newRow(dataTag: "6" ) << QString("p { color:green; color{;color:maroon} } /* unexpected tokens { } */" ); | 
| 777 |     QTest::newRow(dataTag: "7" ) << QString("p { color:red;   color{;color:maroon}; color:green } /* same with recovery */" ); | 
| 778 | } | 
| 779 |  | 
| 780 | void tst_QCssParser::malformedDeclarations() | 
| 781 | { | 
| 782 |     QFETCH(QString, css); | 
| 783 |     QCss::Parser parser(css); | 
| 784 |     QVERIFY(parser.testRuleset()); | 
| 785 |     QCss::StyleRule rule; | 
| 786 |     QVERIFY(parser.parseRuleset(&rule)); | 
| 787 |  | 
| 788 |     QCOMPARE(rule.selectors.count(), 1); | 
| 789 |     QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1); | 
| 790 |     QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("p" )); | 
| 791 |  | 
| 792 |     QVERIFY(rule.declarations.count() >= 1); | 
| 793 |     QCOMPARE(int(rule.declarations.last().d->propertyId), int(QCss::Color)); | 
| 794 |     QCOMPARE(rule.declarations.last().d->values.count(), 1); | 
| 795 |     QCOMPARE(int(rule.declarations.last().d->values.at(0).type), int(QCss::Value::Identifier)); | 
| 796 |     QCOMPARE(rule.declarations.last().d->values.at(0).variant.toString(), QString("green" )); | 
| 797 | } | 
| 798 |  | 
| 799 | void tst_QCssParser::invalidAtKeywords() | 
| 800 | { | 
| 801 |     QCss::Parser parser(""  | 
| 802 |     "@three-dee {"  | 
| 803 |     "  @background-lighting {"  | 
| 804 |     "    azimuth: 30deg;"  | 
| 805 |     "    elevation: 190deg;"  | 
| 806 |     "  }"  | 
| 807 |     "  h1 { color: red }"  | 
| 808 |     "}"  | 
| 809 |     "h1 { color: blue }" ); | 
| 810 |  | 
| 811 |     QCss::StyleSheet sheet; | 
| 812 |     QVERIFY(parser.parse(&sheet)); | 
| 813 |  | 
| 814 |     QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1); | 
| 815 |     QCss::StyleRule rule =  (!sheet.styleRules.isEmpty()) ? | 
| 816 |             sheet.styleRules.at(i: 0) : *sheet.nameIndex.begin(); | 
| 817 |  | 
| 818 |     QCOMPARE(rule.selectors.count(), 1); | 
| 819 |     QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1); | 
| 820 |     QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("h1" )); | 
| 821 |  | 
| 822 |     QCOMPARE(rule.declarations.count(), 1); | 
| 823 |     QCOMPARE(int(rule.declarations.at(0).d->propertyId), int(QCss::Color)); | 
| 824 |     QCOMPARE(rule.declarations.at(0).d->values.count(), 1); | 
| 825 |     QCOMPARE(int(rule.declarations.at(0).d->values.at(0).type), int(QCss::Value::Identifier)); | 
| 826 |     QCOMPARE(rule.declarations.at(0).d->values.at(0).variant.toString(), QString("blue" )); | 
| 827 | } | 
| 828 |  | 
| 829 |  | 
| 830 | void tst_QCssParser::colorValue_data() | 
| 831 | { | 
| 832 |     QTest::addColumn<QString>(name: "css" ); | 
| 833 |     QTest::addColumn<QColor>(name: "expectedColor" ); | 
| 834 |  | 
| 835 |     QTest::newRow(dataTag: "identifier" ) << "color: black"  << QColor("black" ); | 
| 836 |     QTest::newRow(dataTag: "string" ) << "color: \"green\""  << QColor("green" ); | 
| 837 |     QTest::newRow(dataTag: "hexcolor" ) << "color: #12af0e"  << QColor(0x12, 0xaf, 0x0e); | 
| 838 |     QTest::newRow(dataTag: "functional1" ) << "color: rgb(21, 45, 73)"  << QColor(21, 45, 73); | 
| 839 |     QTest::newRow(dataTag: "functional2" ) << "color: rgb(100%, 0%, 100%)"  << QColor(0xff, 0, 0xff); | 
| 840 |     QTest::newRow(dataTag: "rgb" ) << "color: rgb(10, 20, 30)"  << QColor(10, 20, 30); | 
| 841 |     QTest::newRow(dataTag: "rgba" ) << "color: rgba(10, 20, 30, 40)"  << QColor(10, 20, 30, 40); | 
| 842 |     QTest::newRow(dataTag: "rgbaf" ) << "color: rgba(10, 20, 30, 0.5)"  << QColor(10, 20, 30, 127); | 
| 843 |     QTest::newRow(dataTag: "hsv" ) << "color: hsv(10, 20, 30)"  << QColor::fromHsv(h: 10, s: 20, v: 30); | 
| 844 |     QTest::newRow(dataTag: "hsva" ) << "color: hsva(10, 20, 30, 40)"  << QColor::fromHsv(h: 10, s: 20, v: 30, a: 40); | 
| 845 |     // the percent and float values are well chosen to not get in trouble due to rounding errors | 
| 846 |     QTest::newRow(dataTag: "hsva-percent" ) << "color: hsva(100%, 20%, 40%, 60%)"  << QColor::fromHsv(h: 359, s: 51, v: 102, a: 153); | 
| 847 |     QTest::newRow(dataTag: "hsva-float" ) << "color: hsva(180, 20%, 40%, 0.6)"  << QColor::fromHsvF(h: 0.5, s: 0.2, v: 0.4, a: 0.6); | 
| 848 |     QTest::newRow(dataTag: "hsl" ) << "color: hsl(60, 100%, 50%)"  << QColor::fromHsl(h: 60., s: 255, l: 127); | 
| 849 |     QTest::newRow(dataTag: "hsla" ) << "color: hsla(240, 255, 127, 192)"  << QColor::fromHsl(h: 240, s: 255, l: 127, a: 192); | 
| 850 |     QTest::newRow(dataTag: "hsla-percent" ) << "color: hsla(100%, 80%, 40%, 0%)"  << QColor::fromHsl(h: 359, s: 204, l: 102, a: 0); | 
| 851 |     QTest::newRow(dataTag: "hsla-float" ) << "color: hsla(252, 40%, 60%, 0.2)"  << QColor::fromHslF(h: 0.7, s: 0.4, l: 0.6, a: 0.2); | 
| 852 |     QTest::newRow(dataTag: "invalid1" ) << "color: rgb(why, does, it, always, rain, on, me)"  << QColor(); | 
| 853 |     QTest::newRow(dataTag: "invalid2" ) << "color: rgba(i, meant, norway)"  << QColor(); | 
| 854 |     QTest::newRow(dataTag: "invalid3" ) << "color: rgb(21)"  << QColor(); | 
| 855 |     QTest::newRow(dataTag: "invalid4" ) << "color: rgbx(1, 2, 3)"  << QColor(); | 
| 856 |     QTest::newRow(dataTag: "invalid5" ) << "color: rgbax(1, 2, 3, 4)"  << QColor(); | 
| 857 |     QTest::newRow(dataTag: "invalid6" ) << "color: hsv(360, 0, 0)"  << QColor(); | 
| 858 |     QTest::newRow(dataTag: "invalid7" ) << "color: hsla(1, a, 1, 21)"  << QColor(); | 
| 859 |     QTest::newRow(dataTag: "role" ) << "color: palette(base)"  << qApp->palette().color(cr: QPalette::Base); | 
| 860 |     QTest::newRow(dataTag: "role2" ) << "color: palette( window-text ) "  << qApp->palette().color(cr: QPalette::WindowText); | 
| 861 |     QTest::newRow(dataTag: "transparent" ) << "color: transparent"  << QColor(Qt::transparent); | 
| 862 |  | 
| 863 |     // ### Qt6: no longer valid | 
| 864 |     QTest::newRow(dataTag: "rgb-invalid" ) << "color: rgb(10, 20, 30, 40)"  << QColor(10, 20, 30, 40); | 
| 865 |     QTest::newRow(dataTag: "rgba-invalid" ) << "color: rgba(10, 20, 30)"  << QColor(10, 20, 30, 255); | 
| 866 | } | 
| 867 |  | 
| 868 | void tst_QCssParser::colorValue() | 
| 869 | { | 
| 870 |     QFETCH(QString, css); | 
| 871 |     QFETCH(QColor, expectedColor); | 
| 872 |  | 
| 873 |     QCss::Parser parser(css); | 
| 874 |     QCss::Declaration decl; | 
| 875 |     QVERIFY(parser.parseNextDeclaration(&decl)); | 
| 876 |     const QColor col = decl.colorValue(); | 
| 877 |     QCOMPARE(expectedColor.isValid(), col.isValid()); | 
| 878 |     QCOMPARE(col, expectedColor); | 
| 879 | } | 
| 880 |  | 
| 881 | class DomStyleSelector : public QCss::StyleSelector | 
| 882 | { | 
| 883 | public: | 
| 884 |     inline DomStyleSelector(const QDomDocument &doc, const QCss::StyleSheet &sheet) | 
| 885 |         : doc(doc) | 
| 886 |     { | 
| 887 |         styleSheets.append(t: sheet); | 
| 888 |     } | 
| 889 |  | 
| 890 |     virtual QStringList nodeNames(NodePtr node) const { return QStringList(reinterpret_cast<QDomElement *>(node.ptr)->tagName()); } | 
| 891 |     virtual QString attribute(NodePtr node, const QString &name) const { return reinterpret_cast<QDomElement *>(node.ptr)->attribute(name); } | 
| 892 |     virtual bool hasAttribute(NodePtr node, const QString &name) const { return reinterpret_cast<QDomElement *>(node.ptr)->hasAttribute(name); } | 
| 893 |     virtual bool hasAttributes(NodePtr node) const { return reinterpret_cast<QDomElement *>(node.ptr)->hasAttributes(); } | 
| 894 |  | 
| 895 |     virtual bool isNullNode(NodePtr node) const { | 
| 896 |         return reinterpret_cast<QDomElement *>(node.ptr)->isNull(); | 
| 897 |     } | 
| 898 |     virtual NodePtr parentNode(NodePtr node) const { | 
| 899 |         NodePtr parent; | 
| 900 |         parent.ptr = new QDomElement(reinterpret_cast<QDomElement *>(node.ptr)->parentNode().toElement()); | 
| 901 |         return parent; | 
| 902 |     } | 
| 903 |     virtual NodePtr duplicateNode(NodePtr node) const { | 
| 904 |         NodePtr n; | 
| 905 |         n.ptr = new QDomElement(*reinterpret_cast<QDomElement *>(node.ptr)); | 
| 906 |         return n; | 
| 907 |     } | 
| 908 |     virtual NodePtr previousSiblingNode(NodePtr node) const { | 
| 909 |         NodePtr sibling; | 
| 910 |         sibling.ptr = new QDomElement(reinterpret_cast<QDomElement *>(node.ptr)->previousSiblingElement()); | 
| 911 |         return sibling; | 
| 912 |     } | 
| 913 |     virtual void freeNode(NodePtr node) const { | 
| 914 |         delete reinterpret_cast<QDomElement *>(node.ptr); | 
| 915 |     } | 
| 916 |  | 
| 917 | private: | 
| 918 |     QDomDocument doc; | 
| 919 | }; | 
| 920 |  | 
| 921 | Q_DECLARE_METATYPE(QDomDocument) | 
| 922 |  | 
| 923 | void tst_QCssParser::marginValue_data() | 
| 924 | { | 
| 925 |     QTest::addColumn<QString>(name: "css" ); | 
| 926 |     QTest::addColumn<QString>(name: "expectedMargin" ); | 
| 927 |  | 
| 928 |     QFont f; | 
| 929 |     int ex = QFontMetrics(f).xHeight(); | 
| 930 |     int em = QFontMetrics(f).height(); | 
| 931 |  | 
| 932 |     const QString ex1234 = QString::number(ex) + QLatin1Char(' ') + QString::number(2 * ex) | 
| 933 |         + QLatin1Char(' ') + QString::number(3 * ex) + QLatin1Char(' ') | 
| 934 |         + QString::number(4 * ex); | 
| 935 |     const QString em2ex4 = QLatin1String("1 " ) + QString::number(2*em) + QLatin1String(" 3 " ) | 
| 936 |         + QString::number(4 * ex); | 
| 937 |  | 
| 938 |     QTest::newRow(dataTag: "one value" ) << "margin: 1px"  << "1 1 1 1" ; | 
| 939 |     QTest::newRow(dataTag: "two values" ) << "margin: 1px 2px"  << "1 2 1 2" ; | 
| 940 |     QTest::newRow(dataTag: "three value" ) << "margin: 1px 2px 3px"  << "1 2 3 2" ; | 
| 941 |     QTest::newRow(dataTag: "four values" ) << "margin: 1px 2px 3px 4px"  << "1 2 3 4" ; | 
| 942 |     QTest::newRow(dataTag: "default px" ) << "margin: 1 2 3 4"  << "1 2 3 4" ; | 
| 943 |     QTest::newRow(dataTag: "no unit" ) << "margin: 1 2 3 4"  << "1 2 3 4" ; | 
| 944 |     QTest::newRow(dataTag: "em" ) << "margin: 1ex 2ex 3ex 4ex"  | 
| 945 |         << (QString::number(ex) + QLatin1Char(' ') + QString::number(2 * ex) | 
| 946 |             + QLatin1Char(' ') + QString::number(3 * ex) + QLatin1Char(' ') | 
| 947 |             + QString::number(4 * ex)); | 
| 948 |     QTest::newRow(dataTag: "ex" ) << "margin: 1 2em 3px 4ex"  | 
| 949 |         << (QLatin1String("1 " ) + QString::number(2 * em) + QLatin1String(" 3 " ) | 
| 950 |            + QString::number(4 * ex)); | 
| 951 |  | 
| 952 |     f.setPointSize(20); | 
| 953 |     f.setBold(true); | 
| 954 |     ex = QFontMetrics(f).xHeight(); | 
| 955 |     em = QFontMetrics(f).height(); | 
| 956 |     QTest::newRow(dataTag: "em2" ) << "font: bold 20pt; margin: 1ex 2ex 3ex 4ex"  | 
| 957 |         << (QString::number(ex) + QLatin1Char(' ') + QString::number(2 * ex) | 
| 958 |             + QLatin1Char(' ') + QString::number(3 * ex) + QLatin1Char(' ') | 
| 959 |             + QString::number(4 * ex)); | 
| 960 |     QTest::newRow(dataTag: "ex2" ) << "margin: 1 2em 3px 4ex; font-size: 20pt; font-weight: bold;"  | 
| 961 |         << (QLatin1String("1 " ) + QString::number(2 * em) + QLatin1String(" 3 " ) | 
| 962 |             + QString::number(4 * ex)); | 
| 963 |  | 
| 964 |     QTest::newRow(dataTag: "crap" ) << "margin: crap"  << "0 0 0 0" ; | 
| 965 | } | 
| 966 |  | 
| 967 | void tst_QCssParser::marginValue() | 
| 968 | { | 
| 969 |     QFETCH(QString, css); | 
| 970 |     QFETCH(QString, expectedMargin); | 
| 971 |  | 
| 972 |     QDomDocument doc; | 
| 973 |     QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>" ))); | 
| 974 |  | 
| 975 |     css.prepend(s: "dummy {" ); | 
| 976 |     css.append(s: "}" ); | 
| 977 |  | 
| 978 |     QCss::Parser parser(css); | 
| 979 |     QCss::StyleSheet sheet; | 
| 980 |     QVERIFY(parser.parse(&sheet)); | 
| 981 |  | 
| 982 |     DomStyleSelector testSelector(doc, sheet); | 
| 983 |     QDomElement e = doc.documentElement().firstChildElement(); | 
| 984 |     QCss::StyleSelector::NodePtr n; | 
| 985 |     n.ptr = &e; | 
| 986 |     QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(node: n); | 
| 987 |     QVector<QCss::Declaration> decls = rules.at(i: 0).declarations; | 
| 988 |     QCss::ValueExtractor v(decls); | 
| 989 |  | 
| 990 |     { | 
| 991 |     int m[4]; | 
| 992 |     int p[4]; | 
| 993 |     int spacing; | 
| 994 |     v.extractBox(margins: m, paddings: p, spacing: &spacing); | 
| 995 |     QString str = QString::number(m[0]) + QLatin1Char(' ') + QString::number(m[1]) | 
| 996 |         + QLatin1Char(' ') + QString::number(m[2]) + QLatin1Char(' ') + QString::number(m[3]); | 
| 997 |     QCOMPARE(str, expectedMargin); | 
| 998 |     } | 
| 999 | } | 
| 1000 |  | 
| 1001 | void tst_QCssParser::styleSelector_data() | 
| 1002 | { | 
| 1003 |     QTest::addColumn<bool>(name: "match" ); | 
| 1004 |     QTest::addColumn<QString>(name: "selector" ); | 
| 1005 |     QTest::addColumn<QString>(name: "xml" ); | 
| 1006 |     QTest::addColumn<QString>(name: "elementToCheck" ); | 
| 1007 |  | 
| 1008 |     QTest::newRow(dataTag: "plain" ) << true << QString("p" ) << QString("<p />" ) << QString(); | 
| 1009 |     QTest::newRow(dataTag: "noplain" ) << false << QString("bar" ) << QString("<p />" ) << QString(); | 
| 1010 |  | 
| 1011 |     QTest::newRow(dataTag: "class" ) << true << QString(".foo" ) << QString("<p class=\"foo\" />" ) << QString(); | 
| 1012 |     QTest::newRow(dataTag: "noclass" ) << false << QString(".bar" ) << QString("<p class=\"foo\" />" ) << QString(); | 
| 1013 |  | 
| 1014 |     QTest::newRow(dataTag: "attrset" ) << true << QString("[justset]" ) << QString("<p justset=\"bar\" />" ) << QString(); | 
| 1015 |     QTest::newRow(dataTag: "notattrset" ) << false << QString("[justset]" ) << QString("<p otherattribute=\"blub\" />" ) << QString(); | 
| 1016 |  | 
| 1017 |     QTest::newRow(dataTag: "attrmatch" ) << true << QString("[foo=bar]" ) << QString("<p foo=\"bar\" />" ) << QString(); | 
| 1018 |     QTest::newRow(dataTag: "noattrmatch" ) << false << QString("[foo=bar]" ) << QString("<p foo=\"xyz\" />" ) << QString(); | 
| 1019 |  | 
| 1020 |     QTest::newRow(dataTag: "includes" ) << true << QString("[foo~=bar]" ) << QString("<p foo=\"baz bleh bar\" />" ) << QString(); | 
| 1021 |     QTest::newRow(dataTag: "notincludes" ) << false << QString("[foo~=bar]" ) << QString("<p foo=\"bazblehbar\" />" ) << QString(); | 
| 1022 |  | 
| 1023 |     QTest::newRow(dataTag: "dashmatch" ) << true << QString("[foo|=bar]" ) << QString("<p foo=\"bar-bleh\" />" ) << QString(); | 
| 1024 |     QTest::newRow(dataTag: "nodashmatch" ) << false << QString("[foo|=bar]" ) << QString("<p foo=\"barbleh\" />" ) << QString(); | 
| 1025 |  | 
| 1026 |     QTest::newRow(dataTag: "beginswith" ) << true << QString("[foo^=bar]" ) << QString("<p foo=\"barbleh\" />" ) << QString(); | 
| 1027 |     QTest::newRow(dataTag: "nobeginswith" ) << false << QString("[foo^=bar]" ) << QString("<p foo=\"blehbleh\" />" ) << QString(); | 
| 1028 |  | 
| 1029 |     QTest::newRow(dataTag: "endswith" ) << true << QString("[foo$=bar]" ) << QString("<p foo=\"barbar\" />" ) << QString(); | 
| 1030 |     QTest::newRow(dataTag: "noendswith" ) << false << QString("[foo$=bar]" ) << QString("<p foo=\"blehbleh\" />" ) << QString(); | 
| 1031 |  | 
| 1032 |     QTest::newRow(dataTag: "contains" ) << true << QString("[foo*=bar]" ) << QString("<p foo=\"blehbarbleh\" />" ) << QString(); | 
| 1033 |     QTest::newRow(dataTag: "nocontains" ) << false << QString("[foo*=bar]" ) << QString("<p foo=\"blehbleh\" />" ) << QString(); | 
| 1034 |  | 
| 1035 |     QTest::newRow(dataTag: "attr2" ) << true << QString("[bar=foo]" ) << QString("<p bleh=\"bar\" bar=\"foo\" />" ) << QString(); | 
| 1036 |  | 
| 1037 |     QTest::newRow(dataTag: "universal1" ) << true << QString("*" ) << QString("<p />" ) << QString(); | 
| 1038 |  | 
| 1039 |     QTest::newRow(dataTag: "universal3" ) << false << QString("*[foo=bar]" ) << QString("<p foo=\"bleh\" />" ) << QString(); | 
| 1040 |     QTest::newRow(dataTag: "universal4" ) << true << QString("*[foo=bar]" ) << QString("<p foo=\"bar\" />" ) << QString(); | 
| 1041 |  | 
| 1042 |     QTest::newRow(dataTag: "universal5" ) << false << QString("[foo=bar]" ) << QString("<p foo=\"bleh\" />" ) << QString(); | 
| 1043 |     QTest::newRow(dataTag: "universal6" ) << true << QString("[foo=bar]" ) << QString("<p foo=\"bar\" />" ) << QString(); | 
| 1044 |  | 
| 1045 |     QTest::newRow(dataTag: "universal7" ) << true << QString(".charfmt1" ) << QString("<p class=\"charfmt1\" />" ) << QString(); | 
| 1046 |  | 
| 1047 |     QTest::newRow(dataTag: "id" ) << true << QString("#blub" ) << QString("<p id=\"blub\" />" ) << QString(); | 
| 1048 |     QTest::newRow(dataTag: "noid" ) << false << QString("#blub" ) << QString("<p id=\"other\" />" ) << QString(); | 
| 1049 |  | 
| 1050 |     QTest::newRow(dataTag: "childselector" ) << true << QString("parent > child" ) | 
| 1051 |                                    << QString("<parent><child /></parent>" ) | 
| 1052 |                                    << QString("parent/child" ); | 
| 1053 |  | 
| 1054 |     QTest::newRow(dataTag: "nochildselector2" ) << false << QString("parent > child" ) | 
| 1055 |                                    << QString("<child><parent /></child>" ) | 
| 1056 |                                    << QString("child/parent" ); | 
| 1057 |  | 
| 1058 |     QTest::newRow(dataTag: "nochildselector3" ) << false << QString("parent > child" ) | 
| 1059 |                                    << QString("<parent><intermediate><child /></intermediate></parent>" ) | 
| 1060 |                                    << QString("parent/intermediate/child" ); | 
| 1061 |  | 
| 1062 |     QTest::newRow(dataTag: "childselector2" ) << true << QString("parent[foo=bar] > child" ) | 
| 1063 |                                    << QString("<parent foo=\"bar\"><child /></parent>" ) | 
| 1064 |                                    << QString("parent/child" ); | 
| 1065 |  | 
| 1066 |     QTest::newRow(dataTag: "nochildselector4" ) << false << QString("parent[foo=bar] > child" ) | 
| 1067 |                                    << QString("<parent><child /></parent>" ) | 
| 1068 |                                    << QString("parent/child" ); | 
| 1069 |  | 
| 1070 |     QTest::newRow(dataTag: "nochildselector5" ) << false << QString("parent[foo=bar] > child" ) | 
| 1071 |                                    << QString("<parent foo=\"bar\"><parent><child /></parent></parent>" ) | 
| 1072 |                                    << QString("parent/parent/child" ); | 
| 1073 |  | 
| 1074 |     QTest::newRow(dataTag: "childselectors" ) << true << QString("grandparent > parent > child" ) | 
| 1075 |                                    << QString("<grandparent><parent><child /></parent></grandparent>" ) | 
| 1076 |                                    << QString("grandparent/parent/child" ); | 
| 1077 |  | 
| 1078 |     QTest::newRow(dataTag: "descendant" ) << true << QString("grandparent child" ) | 
| 1079 |                                    << QString("<grandparent><parent><child /></parent></grandparent>" ) | 
| 1080 |                                    << QString("grandparent/parent/child" ); | 
| 1081 |  | 
| 1082 |     QTest::newRow(dataTag: "nodescendant" ) << false << QString("grandparent child" ) | 
| 1083 |                                    << QString("<other><parent><child /></parent></other>" ) | 
| 1084 |                                    << QString("other/parent/child" ); | 
| 1085 |  | 
| 1086 |     QTest::newRow(dataTag: "descendant2" ) << true << QString("grandgrandparent grandparent child" ) | 
| 1087 |                                    << QString("<grandgrandparent><inbetween><grandparent><parent><child /></parent></grandparent></inbetween></grandgrandparent>" ) | 
| 1088 |                                    << QString("grandgrandparent/inbetween/grandparent/parent/child" ); | 
| 1089 |  | 
| 1090 |     QTest::newRow(dataTag: "combined" ) << true << QString("grandparent parent > child" ) | 
| 1091 |                               << QString("<grandparent><inbetween><parent><child /></parent></inbetween></grandparent>" ) | 
| 1092 |                               << QString("grandparent/inbetween/parent/child" ); | 
| 1093 |  | 
| 1094 |     QTest::newRow(dataTag: "combined2" ) << true << QString("grandparent > parent child" ) | 
| 1095 |                               << QString("<grandparent><parent><inbetween><child /></inbetween></parent></grandparent>" ) | 
| 1096 |                               << QString("grandparent/parent/inbetween/child" ); | 
| 1097 |  | 
| 1098 |     QTest::newRow(dataTag: "combined3" ) << true << QString("grandparent > parent child" ) | 
| 1099 |                               << QString("<grandparent><parent><inbetween><child /></inbetween></parent></grandparent>" ) | 
| 1100 |                               << QString("grandparent/parent/inbetween/child" ); | 
| 1101 |  | 
| 1102 |     QTest::newRow(dataTag: "nocombined" ) << false << QString("grandparent parent > child" ) | 
| 1103 |                               << QString("<inbetween><parent><child /></parent></inbetween>" ) | 
| 1104 |                               << QString("inbetween/parent/child" ); | 
| 1105 |  | 
| 1106 |     QTest::newRow(dataTag: "nocombined2" ) << false << QString("grandparent parent > child" ) | 
| 1107 |                               << QString("<parent><child /></parent>" ) | 
| 1108 |                               << QString("parent/child" ); | 
| 1109 |  | 
| 1110 |     QTest::newRow(dataTag: "previoussibling" ) << true << QString("p1 + p2" ) | 
| 1111 |                                      << QString("<p1 /><p2 />" ) | 
| 1112 |                                      << QString("p2" ); | 
| 1113 |  | 
| 1114 |     QTest::newRow(dataTag: "notprevioussibling" ) << false << QString("p2 + p1" ) | 
| 1115 |                                      << QString("<p1 /><p2 />" ) | 
| 1116 |                                      << QString("p2" ); | 
| 1117 |  | 
| 1118 |     QTest::newRow(dataTag: "anyprevioussibling" ) << true << QString("p1 ~ p3" ) | 
| 1119 |                                      << QString("<p1 /><p2 /><p3 />" ) | 
| 1120 |                                      << QString("p3" ); | 
| 1121 |  | 
| 1122 |     QTest::newRow(dataTag: "noprevioussibling" ) << false << QString("p3 ~ p2" ) | 
| 1123 |                                      << QString("<p1 /><p2 /><p3 />" ) | 
| 1124 |                                      << QString("p3" ); | 
| 1125 |  | 
| 1126 |  | 
| 1127 |     QTest::newRow(dataTag: "ancestry_firstmismatch" ) << false << QString("parent child[foo=bar]" ) | 
| 1128 |                                             << QString("<parent><child /></parent>" ) | 
| 1129 |                                             << QString("parent/child" ); | 
| 1130 |  | 
| 1131 |     QTest::newRow(dataTag: "unknown-pseudo" ) << false << QString("p:enabled:foobar" ) << QString("<p/>" ) << QString(); | 
| 1132 | } | 
| 1133 |  | 
| 1134 | void tst_QCssParser::styleSelector() | 
| 1135 | { | 
| 1136 |     QFETCH(bool, match); | 
| 1137 |     QFETCH(QString, selector); | 
| 1138 |     QFETCH(QString, xml); | 
| 1139 |     QFETCH(QString, elementToCheck); | 
| 1140 |  | 
| 1141 |     const QString css = selector + QLatin1String(" { background-color: green }" ); | 
| 1142 |     QCss::Parser parser(css); | 
| 1143 |     QCss::StyleSheet sheet; | 
| 1144 |     QVERIFY(parser.parse(&sheet)); | 
| 1145 |  | 
| 1146 |     QDomDocument doc; | 
| 1147 |     xml.prepend(s: "<!DOCTYPE test><test>" ); | 
| 1148 |     xml.append(s: "</test>" ); | 
| 1149 |     QVERIFY(doc.setContent(xml)); | 
| 1150 |  | 
| 1151 |     DomStyleSelector testSelector(doc, sheet); | 
| 1152 |  | 
| 1153 |     QDomElement e = doc.documentElement(); | 
| 1154 |     if (elementToCheck.isEmpty()) { | 
| 1155 |         e = e.firstChildElement(); | 
| 1156 |     } else { | 
| 1157 |         QStringList path = elementToCheck.split(sep: QLatin1Char('/')); | 
| 1158 |         do { | 
| 1159 |             e = e.namedItem(name: path.takeFirst()).toElement(); | 
| 1160 |         } while (!path.isEmpty()); | 
| 1161 |     } | 
| 1162 |     QVERIFY(!e.isNull()); | 
| 1163 |     QCss::StyleSelector::NodePtr n; | 
| 1164 |     n.ptr = &e; | 
| 1165 |     QVector<QCss::Declaration> decls = testSelector.declarationsForNode(node: n); | 
| 1166 |  | 
| 1167 |     if (match) { | 
| 1168 |         QCOMPARE(decls.count(), 1); | 
| 1169 |         QCOMPARE(int(decls.at(0).d->propertyId), int(QCss::BackgroundColor)); | 
| 1170 |         QCOMPARE(decls.at(0).d->values.count(), 1); | 
| 1171 |         QCOMPARE(int(decls.at(0).d->values.at(0).type), int(QCss::Value::Identifier)); | 
| 1172 |         QCOMPARE(decls.at(0).d->values.at(0).variant.toString(), QString("green" )); | 
| 1173 |     } else { | 
| 1174 |         QVERIFY(decls.isEmpty()); | 
| 1175 |     } | 
| 1176 | } | 
| 1177 |  | 
| 1178 | void tst_QCssParser::specificity_data() | 
| 1179 | { | 
| 1180 |     QTest::addColumn<QString>(name: "selector" ); | 
| 1181 |     QTest::addColumn<int>(name: "specificity" ); | 
| 1182 |  | 
| 1183 |     QTest::newRow(dataTag: "universal" ) << QString("*" ) << 0; | 
| 1184 |  | 
| 1185 |     QTest::newRow(dataTag: "elements+pseudos1" ) << QString("foo" ) << 1; | 
| 1186 |     QTest::newRow(dataTag: "elements+pseudos2" ) << QString("foo *[blah]" ) << 1 + (1 * 0x10); | 
| 1187 |  | 
| 1188 |     // should strictly speaking be '2', but we don't support pseudo-elements yet, | 
| 1189 |     // only pseudo-classes | 
| 1190 |     QTest::newRow(dataTag: "elements+pseudos3" ) << QString("li:first-line" ) << 1 + (1 * 0x10); | 
| 1191 |  | 
| 1192 |     QTest::newRow(dataTag: "elements+pseudos4" ) << QString("ul li" ) << 2; | 
| 1193 |     QTest::newRow(dataTag: "elements+pseudos5" ) << QString("ul ol+li" ) << 3; | 
| 1194 |     QTest::newRow(dataTag: "elements+pseudos6" ) << QString("h1 + *[rel=up]" ) << 1 + (1 * 0x10); | 
| 1195 |  | 
| 1196 |     QTest::newRow(dataTag: "elements+pseudos7" ) << QString("ul ol li.red" ) << 3 + (1 * 0x10); | 
| 1197 |     QTest::newRow(dataTag: "elements+pseudos8" ) << QString("li.red.level" ) << 1 + (2 * 0x10); | 
| 1198 |     QTest::newRow(dataTag: "id" ) << QString("#x34y" ) << 1 * 0x100; | 
| 1199 | } | 
| 1200 |  | 
| 1201 | void tst_QCssParser::specificity() | 
| 1202 | { | 
| 1203 |     QFETCH(QString, selector); | 
| 1204 |  | 
| 1205 |     QString css = selector + QLatin1String(" { }" ); | 
| 1206 |     QCss::Parser parser(css); | 
| 1207 |     QCss::StyleSheet sheet; | 
| 1208 |     QVERIFY(parser.parse(&sheet)); | 
| 1209 |  | 
| 1210 |     QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count() + sheet.idIndex.count() , 1); | 
| 1211 |     QCss::StyleRule rule =  (!sheet.styleRules.isEmpty()) ? sheet.styleRules.at(i: 0) | 
| 1212 |                         :  (!sheet.nameIndex.isEmpty())  ? *sheet.nameIndex.begin() | 
| 1213 |                         :  *sheet.idIndex.begin(); | 
| 1214 |     QCOMPARE(rule.selectors.count(), 1); | 
| 1215 |     QTEST(rule.selectors.at(0).specificity(), "specificity" ); | 
| 1216 | } | 
| 1217 |  | 
| 1218 | void tst_QCssParser::specificitySort_data() | 
| 1219 | { | 
| 1220 |     QTest::addColumn<QString>(name: "firstSelector" ); | 
| 1221 |     QTest::addColumn<QString>(name: "secondSelector" ); | 
| 1222 |     QTest::addColumn<QString>(name: "xml" ); | 
| 1223 |  | 
| 1224 |     QTest::newRow(dataTag: "universal1" ) << QString("*" ) << QString("p" ) << QString("<p />" ); | 
| 1225 |     QTest::newRow(dataTag: "attr" ) << QString("p" ) << QString("p[foo=bar]" ) << QString("<p foo=\"bar\" />" ); | 
| 1226 |     QTest::newRow(dataTag: "id" ) << QString("p" ) << QString("#hey" ) << QString("<p id=\"hey\" />" ); | 
| 1227 |     QTest::newRow(dataTag: "id2" ) << QString("[id=hey]" ) << QString("#hey" ) << QString("<p id=\"hey\" />" ); | 
| 1228 |     QTest::newRow(dataTag: "class" ) << QString("p" ) << QString(".hey" ) << QString("<p class=\"hey\" />" ); | 
| 1229 | } | 
| 1230 |  | 
| 1231 | void tst_QCssParser::specificitySort() | 
| 1232 | { | 
| 1233 |     QFETCH(QString, firstSelector); | 
| 1234 |     QFETCH(QString, secondSelector); | 
| 1235 |     QFETCH(QString, xml); | 
| 1236 |  | 
| 1237 |     firstSelector.append(s: " { color: green; }" ); | 
| 1238 |     secondSelector.append(s: " { color: red; }" ); | 
| 1239 |  | 
| 1240 |     QDomDocument doc; | 
| 1241 |     xml.prepend(s: "<!DOCTYPE test><test>" ); | 
| 1242 |     xml.append(s: "</test>" ); | 
| 1243 |     QVERIFY(doc.setContent(xml)); | 
| 1244 |  | 
| 1245 |     for (int i = 0; i < 2; ++i) { | 
| 1246 |         QString css; | 
| 1247 |         if (i == 0) | 
| 1248 |             css = firstSelector + secondSelector; | 
| 1249 |         else | 
| 1250 |             css = secondSelector + firstSelector; | 
| 1251 |  | 
| 1252 |         QCss::Parser parser(css); | 
| 1253 |         QCss::StyleSheet sheet; | 
| 1254 |         QVERIFY(parser.parse(&sheet)); | 
| 1255 |  | 
| 1256 |         DomStyleSelector testSelector(doc, sheet); | 
| 1257 |  | 
| 1258 |         QDomElement e = doc.documentElement().firstChildElement(); | 
| 1259 |         QCss::StyleSelector::NodePtr n; | 
| 1260 |         n.ptr = &e; | 
| 1261 |         QVector<QCss::Declaration> decls = testSelector.declarationsForNode(node: n); | 
| 1262 |  | 
| 1263 |         QCOMPARE(decls.count(), 2); | 
| 1264 |  | 
| 1265 |         QCOMPARE(int(decls.at(0).d->propertyId), int(QCss::Color)); | 
| 1266 |         QCOMPARE(decls.at(0).d->values.count(), 1); | 
| 1267 |         QCOMPARE(int(decls.at(0).d->values.at(0).type), int(QCss::Value::Identifier)); | 
| 1268 |         QCOMPARE(decls.at(0).d->values.at(0).variant.toString(), QString("green" )); | 
| 1269 |  | 
| 1270 |         QCOMPARE(int(decls.at(1).d->propertyId), int(QCss::Color)); | 
| 1271 |         QCOMPARE(decls.at(1).d->values.count(), 1); | 
| 1272 |         QCOMPARE(int(decls.at(1).d->values.at(0).type), int(QCss::Value::Identifier)); | 
| 1273 |         QCOMPARE(decls.at(1).d->values.at(0).variant.toString(), QString("red" )); | 
| 1274 |     } | 
| 1275 | } | 
| 1276 |  | 
| 1277 | void tst_QCssParser::rulesForNode_data() | 
| 1278 | { | 
| 1279 |     QTest::addColumn<QString>(name: "xml" ); | 
| 1280 |     QTest::addColumn<QString>(name: "css" ); | 
| 1281 |     QTest::addColumn<quint64>(name: "pseudoClass" ); | 
| 1282 |     QTest::addColumn<int>(name: "declCount" ); | 
| 1283 |     QTest::addColumn<QString>(name: "value0" ); | 
| 1284 |     QTest::addColumn<QString>(name: "value1" ); | 
| 1285 |  | 
| 1286 |     QTest::newRow(dataTag: "universal1" ) << QString("<p/>" ) << QString("* { color: red }" ) | 
| 1287 |                                 << (quint64)QCss::PseudoClass_Unspecified << 1 << "red"  << "" ; | 
| 1288 |  | 
| 1289 |     QTest::newRow(dataTag: "basic" ) << QString("<p/>" ) << QString("p:enabled { color: red; bg:blue; }" ) | 
| 1290 |         << (quint64)QCss::PseudoClass_Enabled << 2 << "red"  << "blue" ; | 
| 1291 |  | 
| 1292 |     QTest::newRow(dataTag: "single" ) << QString("<p/>" ) | 
| 1293 |         << QString("p:enabled { color: red; } *:hover { color: white }" ) | 
| 1294 |         << (quint64)QCss::PseudoClass_Hover << 1 << "white"  << "" ; | 
| 1295 |  | 
| 1296 |     QTest::newRow(dataTag: "multisel" ) << QString("<p/>" ) | 
| 1297 |         << QString("p:enabled { color: red; } p:hover { color: gray } *:hover { color: white } " ) | 
| 1298 |         << (quint64)QCss::PseudoClass_Hover << 2 << "white"  << "gray" ; | 
| 1299 |  | 
| 1300 |     QTest::newRow(dataTag: "multisel2" ) << QString("<p/>" ) | 
| 1301 |         << QString("p:enabled { color: red; } p:hover:focus { color: gray } *:hover { color: white } " ) | 
| 1302 |         << quint64(QCss::PseudoClass_Hover|QCss::PseudoClass_Focus) << 2 << "white"  << "gray" ; | 
| 1303 |  | 
| 1304 |     QTest::newRow(dataTag: "multisel3-diffspec" ) << QString("<p/>" ) | 
| 1305 |         << QString("p:enabled { color: red; } p:hover:focus { color: gray } *:hover { color: white } " ) | 
| 1306 |         << quint64(QCss::PseudoClass_Hover) << 1 << "white"  << "" ; | 
| 1307 |  | 
| 1308 |     QTest::newRow(dataTag: "!-1" ) << QString("<p/>" ) | 
| 1309 |         << QString("p:checked:!hover { color: red; } p:checked:hover { color: gray } p:checked { color: white }" ) | 
| 1310 |         << quint64(QCss::PseudoClass_Hover|QCss::PseudoClass_Checked) << 2 << "white"  << "gray" ; | 
| 1311 |  | 
| 1312 |     QTest::newRow(dataTag: "!-2" ) << QString("<p/>" ) | 
| 1313 |         << QString("p:checked:!hover:!pressed { color: red; } p:!checked:hover { color: gray } p:!focus { color: blue }" ) | 
| 1314 |         << quint64(QCss::PseudoClass_Focus) << 0 << ""  << "" ; | 
| 1315 |  | 
| 1316 |     QTest::newRow(dataTag: "!-3" ) << QString("<p/>" ) | 
| 1317 |         << QString("p:checked:!hover:!pressed { color: red; } p:!checked:hover { color: gray } p:!focus { color: blue; }" ) | 
| 1318 |         << quint64(QCss::PseudoClass_Pressed) << 1 << "blue"  << "" ; | 
| 1319 | } | 
| 1320 |  | 
| 1321 | void tst_QCssParser::rulesForNode() | 
| 1322 | { | 
| 1323 |     QFETCH(QString, xml); | 
| 1324 |     QFETCH(QString, css); | 
| 1325 |     QFETCH(quint64, pseudoClass); | 
| 1326 |     QFETCH(int, declCount); | 
| 1327 |     QFETCH(QString, value0); | 
| 1328 |     QFETCH(QString, value1); | 
| 1329 |  | 
| 1330 |     QDomDocument doc; | 
| 1331 |     xml.prepend(s: "<!DOCTYPE test><test>" ); | 
| 1332 |     xml.append(s: "</test>" ); | 
| 1333 |     QVERIFY(doc.setContent(xml)); | 
| 1334 |  | 
| 1335 |     QCss::Parser parser(css); | 
| 1336 |     QCss::StyleSheet sheet; | 
| 1337 |     QVERIFY(parser.parse(&sheet)); | 
| 1338 |  | 
| 1339 |     DomStyleSelector testSelector(doc, sheet); | 
| 1340 |     QDomElement e = doc.documentElement().firstChildElement(); | 
| 1341 |     QCss::StyleSelector::NodePtr n; | 
| 1342 |     n.ptr = &e; | 
| 1343 |     QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(node: n); | 
| 1344 |  | 
| 1345 |     QVector<QCss::Declaration> decls; | 
| 1346 |     for (int i = 0; i < rules.count(); i++) { | 
| 1347 |         const QCss::Selector &selector = rules.at(i).selectors.at(i: 0); | 
| 1348 |         quint64 negated = 0; | 
| 1349 |         quint64 cssClass = selector.pseudoClass(negated: &negated); | 
| 1350 |         if ((cssClass == QCss::PseudoClass_Unspecified) | 
| 1351 |             || ((((cssClass & pseudoClass) == cssClass)) && ((negated & pseudoClass) == 0))) | 
| 1352 |             decls += rules.at(i).declarations; | 
| 1353 |     } | 
| 1354 |  | 
| 1355 |     QCOMPARE(decls.count(), declCount); | 
| 1356 |  | 
| 1357 |     if (declCount > 0) | 
| 1358 |         QCOMPARE(decls.at(0).d->values.at(0).variant.toString(), value0); | 
| 1359 |     if (declCount > 1) | 
| 1360 |         QCOMPARE(decls.at(1).d->values.at(0).variant.toString(), value1); | 
| 1361 | } | 
| 1362 |  | 
| 1363 | void tst_QCssParser::shorthandBackgroundProperty_data() | 
| 1364 | { | 
| 1365 |     QTest::addColumn<QString>(name: "css" ); | 
| 1366 |     QTest::addColumn<QBrush>(name: "expectedBrush" ); | 
| 1367 |     QTest::addColumn<QString>(name: "expectedImage" ); | 
| 1368 |     QTest::addColumn<int>(name: "expectedRepeatValue" ); | 
| 1369 |     QTest::addColumn<int>(name: "expectedAlignment" ); | 
| 1370 |  | 
| 1371 |     QTest::newRow(dataTag: "simple color" ) << "background: red"  << QBrush(QColor("red" )) << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop); | 
| 1372 |     QTest::newRow(dataTag: "plain color" ) << "background-color: red"  << QBrush(QColor("red" )) << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop); | 
| 1373 |     QTest::newRow(dataTag: "palette color" ) << "background-color: palette(mid)"  << qApp->palette().mid() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop); | 
| 1374 |     QTest::newRow(dataTag: "multiple" ) << "background: url(chess.png) blue repeat-y"  << QBrush(QColor("blue" )) << QString("chess.png" ) << int(QCss::Repeat_Y) << int(Qt::AlignLeft | Qt::AlignTop); | 
| 1375 |     QTest::newRow(dataTag: "plain alignment" ) << "background-position: center"  << QBrush() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignCenter); | 
| 1376 |     QTest::newRow(dataTag: "plain alignment2" ) << "background-position: left top"  << QBrush() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop); | 
| 1377 |     QTest::newRow(dataTag: "plain alignment3" ) << "background-position: left"  << QBrush() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignVCenter); | 
| 1378 |     QTest::newRow(dataTag: "multi" ) << "background: left url(blah.png) repeat-x"  << QBrush() << QString("blah.png" ) << int(QCss::Repeat_X) << int(Qt::AlignLeft | Qt::AlignVCenter); | 
| 1379 |     QTest::newRow(dataTag: "multi2" ) << "background: url(blah.png) repeat-x top"  << QBrush() << QString("blah.png" ) << int(QCss::Repeat_X) << int(Qt::AlignTop | Qt::AlignHCenter); | 
| 1380 |     QTest::newRow(dataTag: "multi3" ) << "background: url(blah.png) top right"  << QBrush() << QString("blah.png" ) << int(QCss::Repeat_XY) << int(Qt::AlignTop | Qt::AlignRight); | 
| 1381 | } | 
| 1382 |  | 
| 1383 | void tst_QCssParser::shorthandBackgroundProperty() | 
| 1384 | { | 
| 1385 |     QFETCH(QString, css); | 
| 1386 |  | 
| 1387 |     QDomDocument doc; | 
| 1388 |     QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>" ))); | 
| 1389 |  | 
| 1390 |     css.prepend(s: "dummy {" ); | 
| 1391 |     css.append(c: QLatin1Char('}')); | 
| 1392 |  | 
| 1393 |     QCss::Parser parser(css); | 
| 1394 |     QCss::StyleSheet sheet; | 
| 1395 |     QVERIFY(parser.parse(&sheet)); | 
| 1396 |  | 
| 1397 |     DomStyleSelector testSelector(doc, sheet); | 
| 1398 |     QDomElement e = doc.documentElement().firstChildElement(); | 
| 1399 |     QCss::StyleSelector::NodePtr n; | 
| 1400 |     n.ptr = &e; | 
| 1401 |     QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(node: n); | 
| 1402 |     QVector<QCss::Declaration> decls = rules.at(i: 0).declarations; | 
| 1403 |     QCss::ValueExtractor v(decls); | 
| 1404 |  | 
| 1405 |     QBrush brush; | 
| 1406 |     QString image; | 
| 1407 |     QCss::Repeat repeat = QCss::Repeat_XY; | 
| 1408 |     Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft; | 
| 1409 |     QCss::Origin origin = QCss::Origin_Padding; | 
| 1410 |     QCss::Attachment attachment; | 
| 1411 |     QCss::Origin ignoredOrigin; | 
| 1412 |     v.extractBackground(&brush, &image, &repeat, &alignment, &origin, &attachment, &ignoredOrigin); | 
| 1413 |  | 
| 1414 |     QFETCH(QBrush, expectedBrush); | 
| 1415 |     QCOMPARE(expectedBrush.color(), brush.color()); | 
| 1416 |  | 
| 1417 |     QTEST(image, "expectedImage" ); | 
| 1418 |     QTEST(int(repeat), "expectedRepeatValue" ); | 
| 1419 |     QTEST(int(alignment), "expectedAlignment" ); | 
| 1420 |  | 
| 1421 |     //QTBUG-9674  : a second evaluation should give the same results | 
| 1422 |     QVERIFY(v.extractBackground(&brush, &image, &repeat, &alignment, &origin, &attachment, &ignoredOrigin)); | 
| 1423 |     QCOMPARE(expectedBrush.color(), brush.color()); | 
| 1424 |     QTEST(image, "expectedImage" ); | 
| 1425 |     QTEST(int(repeat), "expectedRepeatValue" ); | 
| 1426 |     QTEST(int(alignment), "expectedAlignment" ); | 
| 1427 | } | 
| 1428 |  | 
| 1429 | void tst_QCssParser::pseudoElement_data() | 
| 1430 | { | 
| 1431 |     QTest::addColumn<QString>(name: "css" ); | 
| 1432 |     QTest::addColumn<QString>(name: "pseudoElement" ); | 
| 1433 |     QTest::addColumn<int>(name: "declCount" ); | 
| 1434 |  | 
| 1435 |     // QComboBox::dropDown { border-image: blah; } | 
| 1436 |     QTest::newRow(dataTag: "no pseudo-elements" ) << QString("dummy:hover { color: red }" ) << ""  << 1; | 
| 1437 |     QTest::newRow(dataTag: "no pseudo-elements" ) << QString("dummy:hover { color: red }" ) << "pe"  << 0; | 
| 1438 |  | 
| 1439 |     QTest::newRow(dataTag: "1 pseudo-element (1)" ) << QString("dummy::pe:hover { color: red }" ) << "pe"  << 1; | 
| 1440 |     QTest::newRow(dataTag: "1 pseudo-element (2)" ) << QString("dummy::pe:hover { color: red }" ) << "x"  << 0; | 
| 1441 |     QTest::newRow(dataTag: "1 pseudo-element (2)" ) << QString("whatever::pe:hover { color: red }" ) << "pe"  << 0; | 
| 1442 |  | 
| 1443 |     QTest::newRow(dataTag: "1 pseudo-element (3)" ) | 
| 1444 |         << QString("dummy { color: white; } dummy::pe:hover { color: red }" ) << "x"  << 0; | 
| 1445 |     QTest::newRow(dataTag: "1 pseudo-element (4)" ) | 
| 1446 |         << QString("dummy { color: white; } dummy::pe:hover { color: red } dummy { x:y }" ) << ""  << 2; | 
| 1447 |     QTest::newRow(dataTag: "1 pseudo-element (5)" ) | 
| 1448 |         << QString("dummy { color: white; } dummy::pe:hover { color: red }" ) << "pe"  << 1; | 
| 1449 |     QTest::newRow(dataTag: "1 pseudo-element (6)" ) | 
| 1450 |       << QString("dummy { color: white; } dummy::pe:hover { color: red } dummy::pe:checked { x: y} " ) << "pe"  << 2; | 
| 1451 |  | 
| 1452 |     QTest::newRow(dataTag: "2 pseudo-elements (1)" ) | 
| 1453 |       << QString("dummy { color: white; } dummy::pe1:hover { color: red } dummy::pe2:checked { x: y} " ) | 
| 1454 |       << ""  << 1; | 
| 1455 |     QTest::newRow(dataTag: "2 pseudo-elements (1)" ) | 
| 1456 |       << QString("dummy { color: white; } dummy::pe1:hover { color: red } dummy::pe2:checked { x: y} " ) | 
| 1457 |       << "pe1"  << 1; | 
| 1458 |     QTest::newRow(dataTag: "2 pseudo-elements (2)" ) | 
| 1459 |       << QString("dummy { color: white; } dummy::pe1:hover { color: red } dummy::pe2:checked { x: y} " ) | 
| 1460 |       << "pe2"  << 1; | 
| 1461 | } | 
| 1462 |  | 
| 1463 | void tst_QCssParser::pseudoElement() | 
| 1464 | { | 
| 1465 |     QFETCH(QString, css); | 
| 1466 |     QFETCH(QString, pseudoElement); | 
| 1467 |     QFETCH(int, declCount); | 
| 1468 |  | 
| 1469 |     QDomDocument doc; | 
| 1470 |     QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>" ))); | 
| 1471 |  | 
| 1472 |     QCss::Parser parser(css); | 
| 1473 |     QCss::StyleSheet sheet; | 
| 1474 |     QVERIFY(parser.parse(&sheet)); | 
| 1475 |  | 
| 1476 |     DomStyleSelector testSelector(doc, sheet); | 
| 1477 |     QDomElement e = doc.documentElement().firstChildElement(); | 
| 1478 |     QCss::StyleSelector::NodePtr n; | 
| 1479 |     n.ptr = &e; | 
| 1480 |     QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(node: n); | 
| 1481 |     QVector<QCss::Declaration> decls; | 
| 1482 |     for (int i = 0; i < rules.count(); i++) { | 
| 1483 |         const QCss::Selector& selector = rules.at(i).selectors.at(i: 0); | 
| 1484 |         if (pseudoElement.compare(s: selector.pseudoElement(), cs: Qt::CaseInsensitive) != 0) | 
| 1485 |             continue; | 
| 1486 |         decls += rules.at(i).declarations; | 
| 1487 |  | 
| 1488 |     } | 
| 1489 |     QCOMPARE(decls.count(), declCount); | 
| 1490 | } | 
| 1491 |  | 
| 1492 | void tst_QCssParser::gradient_data() | 
| 1493 | { | 
| 1494 |     QTest::addColumn<QString>(name: "css" ); | 
| 1495 |     QTest::addColumn<QString>(name: "type" ); | 
| 1496 |     QTest::addColumn<QPointF>(name: "start" ); | 
| 1497 |     QTest::addColumn<QPointF>(name: "finalStop" ); | 
| 1498 |     QTest::addColumn<int>(name: "spread" ); | 
| 1499 |     QTest::addColumn<qreal>(name: "stop0" ); | 
| 1500 |     QTest::addColumn<QColor>(name: "color0" ); | 
| 1501 |     QTest::addColumn<qreal>(name: "stop1" ); | 
| 1502 |     QTest::addColumn<QColor>(name: "color1" ); | 
| 1503 |  | 
| 1504 |     QTest::newRow(dataTag: "color-string" ) << | 
| 1505 |      "selection-background-color: qlineargradient(x1:1, y1:2, x2:3, y2:4, "  | 
| 1506 |          "stop:0.2 red, stop:0.5 green)"  << "linear"  << QPointF(1, 2) << QPointF(3, 4) | 
| 1507 |                                   << 0 << qreal(0.2) << QColor("red" ) << qreal(0.5) << QColor("green" ); | 
| 1508 |  | 
| 1509 |     QTest::newRow(dataTag: "color-#" ) << | 
| 1510 |      "selection-background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, "  | 
| 1511 |          "spread: reflect, stop:0.2 #123, stop:0.5 #456)"  << "linear"  << QPointF(0, 0) << QPointF(0, 1) | 
| 1512 |                              << 1 << qreal(0.2) << QColor("#123" ) << qreal(0.5) << QColor("#456" ); | 
| 1513 |  | 
| 1514 |     QTest::newRow(dataTag: "color-rgb" ) << | 
| 1515 |      "selection-background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, "  | 
| 1516 |          "spread: reflect, stop:0.2 rgb(1, 2, 3), stop:0.5 rgba(1, 2, 3, 4))"  << "linear"  << QPointF(0, 0) << QPointF(0, 1) | 
| 1517 |                              << 1 << qreal(0.2) << QColor(1, 2, 3) << qreal(0.5) << QColor(1, 2, 3, 4); | 
| 1518 |  | 
| 1519 |     QTest::newRow(dataTag: "color-spaces" ) << | 
| 1520 |      "selection-background-color: qlineargradient(x1: 0, y1 :0,x2:0, y2 : 1 , "  | 
| 1521 |          "spread: reflect, stop:0.2 rgb(1, 2, 3), stop: 0.5   rgba(1, 2, 3, 4))"  << "linear"  << QPointF(0, 0) << QPointF(0, 1) | 
| 1522 |                              << 1 << qreal(0.2) << QColor(1, 2, 3) << qreal(0.5) << QColor(1, 2, 3, 4); | 
| 1523 |  | 
| 1524 |     QTest::newRow(dataTag: "conical gradient" ) << | 
| 1525 |      "selection-background-color: qconicalgradient(cx: 4, cy : 2, angle: 23, "  | 
| 1526 |          "spread: repeat, stop:0.2 rgb(1, 2, 3), stop:0.5 rgba(1, 2, 3, 4))"  << "conical"  << QPointF(4, 2) << QPointF() | 
| 1527 |                              << 2 << qreal(0.2) << QColor(1, 2, 3) << qreal(0.5) << QColor(1, 2, 3, 4); | 
| 1528 |  | 
| 1529 |     // spaces before first function parameter lead to parser errors | 
| 1530 |     QTest::newRow(dataTag: "QTBUG-61795" ) << | 
| 1531 |      "selection-background-color: qconicalgradient( cx: 4, cy : 2, angle: 23, "  | 
| 1532 |          "spread: repeat, stop:0.2 rgb( 1, 2, 3), stop:0.5 rgba( 1, 2, 3, 4))"  << "conical"  << QPointF(4, 2) << QPointF() | 
| 1533 |                              << 2 << qreal(0.2) << QColor(1, 2, 3) << qreal(0.5) << QColor(1, 2, 3, 4); | 
| 1534 |  | 
| 1535 |     /* won't pass: stop values are expected to be sorted | 
| 1536 |      QTest::newRow("unsorted-stop") << | 
| 1537 |      "selection-background: lineargradient(x1:0, y1:0, x2:0, y2:1, " | 
| 1538 |          "stop:0.5 green, stop:0.2 red)" << QPointF(0, 0) << QPointF(0, 1) | 
| 1539 |          0 << 0.2 << QColor("red") << 0.5 << QColor("green"); | 
| 1540 |     */ | 
| 1541 | } | 
| 1542 |  | 
| 1543 | void tst_QCssParser::gradient() | 
| 1544 | { | 
| 1545 |     QFETCH(QString, css); | 
| 1546 |     QFETCH(QString, type); | 
| 1547 |     QFETCH(QPointF, finalStop); | 
| 1548 |     QFETCH(QPointF, start); | 
| 1549 |     QFETCH(int, spread); | 
| 1550 |     QFETCH(qreal, stop0); QFETCH(QColor, color0); | 
| 1551 |     QFETCH(qreal, stop1); QFETCH(QColor, color1); | 
| 1552 |  | 
| 1553 |     QDomDocument doc; | 
| 1554 |     QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>" ))); | 
| 1555 |  | 
| 1556 |     css.prepend(s: "dummy {" ); | 
| 1557 |     css.append(c: QLatin1Char('}')); | 
| 1558 |  | 
| 1559 |     QCss::Parser parser(css); | 
| 1560 |     QCss::StyleSheet sheet; | 
| 1561 |     QVERIFY(parser.parse(&sheet)); | 
| 1562 |  | 
| 1563 |     DomStyleSelector testSelector(doc, sheet); | 
| 1564 |     QDomElement e = doc.documentElement().firstChildElement(); | 
| 1565 |     QCss::StyleSelector::NodePtr n; | 
| 1566 |     n.ptr = &e; | 
| 1567 |     QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(node: n); | 
| 1568 |     QVector<QCss::Declaration> decls = rules.at(i: 0).declarations; | 
| 1569 |     QCss::ValueExtractor ve(decls); | 
| 1570 |     QBrush fg, sfg; | 
| 1571 |     QBrush sbg, abg; | 
| 1572 |     QVERIFY(ve.extractPalette(&fg, &sfg, &sbg, &abg)); | 
| 1573 |     if (type == "linear" ) { | 
| 1574 |         QCOMPARE(sbg.style(), Qt::LinearGradientPattern); | 
| 1575 |         const QLinearGradient *lg = static_cast<const QLinearGradient *>(sbg.gradient()); | 
| 1576 |         QCOMPARE(lg->start(), start); | 
| 1577 |         QCOMPARE(lg->finalStop(), finalStop); | 
| 1578 |     } else if (type == "conical" ) { | 
| 1579 |         QCOMPARE(sbg.style(), Qt::ConicalGradientPattern); | 
| 1580 |         const QConicalGradient *cg = static_cast<const QConicalGradient *>(sbg.gradient()); | 
| 1581 |         QCOMPARE(cg->center(), start); | 
| 1582 |     } | 
| 1583 |     const QGradient *g = sbg.gradient(); | 
| 1584 |     QCOMPARE(g->spread(), QGradient::Spread(spread)); | 
| 1585 |     QCOMPARE(g->stops().at(0).first, stop0); | 
| 1586 |     QCOMPARE(g->stops().at(0).second, color0); | 
| 1587 |     QCOMPARE(g->stops().at(1).first, stop1); | 
| 1588 |     QCOMPARE(g->stops().at(1).second, color1); | 
| 1589 | } | 
| 1590 |  | 
| 1591 | void tst_QCssParser::() | 
| 1592 | { | 
| 1593 |     if (QFontInfo(QFont("Times New Roman" )).family() != "Times New Roman" ) | 
| 1594 |         QSKIP("'Times New Roman' font not found" ); | 
| 1595 |  | 
| 1596 |     QTest::addColumn<QString>(name: "css" ); | 
| 1597 |     QTest::addColumn<QString>(name: "expectedFamily" ); | 
| 1598 |  | 
| 1599 |     QTest::newRow(dataTag: "quoted-family-name" ) << "font-family: 'Times New Roman'"  << QString("Times New Roman" ); | 
| 1600 |     QTest::newRow(dataTag: "unquoted-family-name" ) << "font-family: Times New Roman"  << QString("Times New Roman" ); | 
| 1601 |     QTest::newRow(dataTag: "unquoted-family-name2" ) << "font-family: Times        New     Roman"  << QString("Times New Roman" ); | 
| 1602 |     QTest::newRow(dataTag: "multiple" ) << "font-family: Times New Roman  , foobar, 'baz'"  << QString("Times New Roman" ); | 
| 1603 |     QTest::newRow(dataTag: "multiple2" ) << "font-family: invalid,  Times New   Roman "  << QString("Times New Roman" ); | 
| 1604 |     QTest::newRow(dataTag: "invalid" ) << "font-family: invalid"  << QFontInfo(QFont("invalid font" )).family(); | 
| 1605 |     QTest::newRow(dataTag: "shorthand" ) << "font: 12pt Times New Roman"  << QString("Times New Roman" ); | 
| 1606 |     QTest::newRow(dataTag: "shorthand multiple quote" ) << "font: 12pt invalid, \"Times New Roman\" "  << QString("Times New Roman" ); | 
| 1607 |     QTest::newRow(dataTag: "shorthand multiple" ) << "font: 12pt invalid, Times New Roman "  << QString("Times New Roman" ); | 
| 1608 |     QTest::newRow(dataTag: "invalid spaces" ) << "font-family: invalid spaces, Times New Roman "  << QString("Times New Roman" ); | 
| 1609 |     QTest::newRow(dataTag: "invalid spaces quotes" ) << "font-family: 'invalid spaces', 'Times New Roman' "  << QString("Times New Roman" ); | 
| 1610 | } | 
| 1611 |  | 
| 1612 |  | 
| 1613 | void tst_QCssParser::() | 
| 1614 | { | 
| 1615 |     QFETCH(QString, css); | 
| 1616 |     css.prepend(s: "dummy {" ); | 
| 1617 |     css.append(c: QLatin1Char('}')); | 
| 1618 |  | 
| 1619 |     QCss::Parser parser(css); | 
| 1620 |     QCss::StyleSheet sheet; | 
| 1621 |     QVERIFY(parser.parse(&sheet)); | 
| 1622 |  | 
| 1623 |     QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1); | 
| 1624 |     QCss::StyleRule rule =  (!sheet.styleRules.isEmpty()) ? | 
| 1625 |             sheet.styleRules.at(i: 0) : *sheet.nameIndex.begin(); | 
| 1626 |  | 
| 1627 |     const QVector<QCss::Declaration> decls = rule.declarations; | 
| 1628 |     QVERIFY(!decls.isEmpty()); | 
| 1629 |     QCss::ValueExtractor (decls); | 
| 1630 |  | 
| 1631 |     int adjustment = 0; | 
| 1632 |     QFont fnt; | 
| 1633 |     extractor.extractFont(font: &fnt, fontSizeAdjustment: &adjustment); | 
| 1634 |     QFontInfo info(fnt); | 
| 1635 |  | 
| 1636 |     QTEST(info.family(), "expectedFamily" ); | 
| 1637 | } | 
| 1638 |  | 
| 1639 | void tst_QCssParser::() | 
| 1640 | { | 
| 1641 |     QTest::addColumn<QString>(name: "css" ); | 
| 1642 |     QTest::addColumn<int>(name: "expectedTopWidth" ); | 
| 1643 |     QTest::addColumn<int>(name: "expectedTopStyle" ); | 
| 1644 |     QTest::addColumn<QColor>(name: "expectedTopColor" ); | 
| 1645 |  | 
| 1646 |     QTest::newRow(dataTag: "all values" ) << "border: 2px solid green"  << 2 << (int)QCss::BorderStyle_Solid << QColor("green" ); | 
| 1647 |     QTest::newRow(dataTag: "palette" ) << "border: 2px solid palette(highlight)"  << 2 << (int)QCss::BorderStyle_Solid << qApp->palette().color(cr: QPalette::Highlight); | 
| 1648 |     QTest::newRow(dataTag: "just width" ) << "border: 2px"  << 2 << (int)QCss::BorderStyle_None << QColor(); | 
| 1649 |     QTest::newRow(dataTag: "just style" ) << "border: solid"  << 0 << (int)QCss::BorderStyle_Solid << QColor(); | 
| 1650 |     QTest::newRow(dataTag: "just color" ) << "border: green"  << 0 << (int)QCss::BorderStyle_None << QColor("green" ); | 
| 1651 |     QTest::newRow(dataTag: "width+style" ) << "border: 2px solid"  << 2 << (int)QCss::BorderStyle_Solid << QColor(); | 
| 1652 |     QTest::newRow(dataTag: "style+color" ) << "border: solid green"  << 0 << (int)QCss::BorderStyle_Solid << QColor("green" ); | 
| 1653 |     QTest::newRow(dataTag: "width+color" ) << "border: 3px green"  << 3 << (int)QCss::BorderStyle_None << QColor("green" ); | 
| 1654 |     QTest::newRow(dataTag: "groove style" ) << "border: groove"  << 0 << (int)QCss::BorderStyle_Groove << QColor(); | 
| 1655 |     QTest::newRow(dataTag: "ridge style" ) << "border: ridge"  << 0 << (int)QCss::BorderStyle_Ridge << QColor(); | 
| 1656 |     QTest::newRow(dataTag: "double style" ) << "border: double"  << 0 << (int)QCss::BorderStyle_Double << QColor(); | 
| 1657 |     QTest::newRow(dataTag: "inset style" ) << "border: inset"  << 0 << (int)QCss::BorderStyle_Inset << QColor(); | 
| 1658 |     QTest::newRow(dataTag: "outset style" ) << "border: outset"  << 0 << (int)QCss::BorderStyle_Outset << QColor(); | 
| 1659 |     QTest::newRow(dataTag: "dashed style" ) << "border: dashed"  << 0 << (int)QCss::BorderStyle_Dashed << QColor(); | 
| 1660 |     QTest::newRow(dataTag: "dotted style" ) << "border: dotted"  << 0 << (int)QCss::BorderStyle_Dotted << QColor(); | 
| 1661 |     QTest::newRow(dataTag: "dot-dash style" ) << "border: dot-dash"  << 0 << (int)QCss::BorderStyle_DotDash << QColor(); | 
| 1662 |     QTest::newRow(dataTag: "dot-dot-dash style" ) << "border: dot-dot-dash"  << 0 << (int)QCss::BorderStyle_DotDotDash << QColor(); | 
| 1663 |  | 
| 1664 |     QTest::newRow(dataTag: "top-width+color" ) << "border-top: 3px green"  << 3 << (int)QCss::BorderStyle_None << QColor("green" ); | 
| 1665 | } | 
| 1666 |  | 
| 1667 | void tst_QCssParser::() | 
| 1668 | { | 
| 1669 |     QFETCH(QString, css); | 
| 1670 |     QFETCH(int, expectedTopWidth); | 
| 1671 |     QFETCH(int, expectedTopStyle); | 
| 1672 |     QFETCH(QColor, expectedTopColor); | 
| 1673 |  | 
| 1674 |     css.prepend(s: "dummy {" ); | 
| 1675 |     css.append(c: QLatin1Char('}')); | 
| 1676 |  | 
| 1677 |     QCss::Parser parser(css); | 
| 1678 |     QCss::StyleSheet sheet; | 
| 1679 |     QVERIFY(parser.parse(&sheet)); | 
| 1680 |  | 
| 1681 |     QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1); | 
| 1682 |     QCss::StyleRule rule =  (!sheet.styleRules.isEmpty()) ? | 
| 1683 |             sheet.styleRules.at(i: 0) : *sheet.nameIndex.begin(); | 
| 1684 |     const QVector<QCss::Declaration> decls = rule.declarations; | 
| 1685 |     QVERIFY(!decls.isEmpty()); | 
| 1686 |     QCss::ValueExtractor (decls); | 
| 1687 |  | 
| 1688 |     int widths[4]; | 
| 1689 |     QBrush colors[4]; | 
| 1690 |     QCss::BorderStyle styles[4]; | 
| 1691 |     QSize radii[4]; | 
| 1692 |  | 
| 1693 |     extractor.extractBorder(borders: widths, colors, Styles: styles, radii); | 
| 1694 |     QCOMPARE(widths[QCss::TopEdge], expectedTopWidth); | 
| 1695 |     QCOMPARE(int(styles[QCss::TopEdge]), expectedTopStyle); | 
| 1696 |     QCOMPARE(colors[QCss::TopEdge].color(), expectedTopColor); | 
| 1697 |  | 
| 1698 |     //QTBUG-9674  : a second evaluation should give the same results | 
| 1699 |     QVERIFY(extractor.extractBorder(widths, colors, styles, radii)); | 
| 1700 |     QCOMPARE(widths[QCss::TopEdge], expectedTopWidth); | 
| 1701 |     QCOMPARE(int(styles[QCss::TopEdge]), expectedTopStyle); | 
| 1702 |     QCOMPARE(colors[QCss::TopEdge].color(), expectedTopColor); | 
| 1703 | } | 
| 1704 |  | 
| 1705 | void tst_QCssParser::noTextDecoration() | 
| 1706 | { | 
| 1707 |     QCss::Parser parser("dummy { text-decoration: none; }" ); | 
| 1708 |     QCss::StyleSheet sheet; | 
| 1709 |     QVERIFY(parser.parse(&sheet)); | 
| 1710 |  | 
| 1711 |     QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1); | 
| 1712 |     QCss::StyleRule rule =  (!sheet.styleRules.isEmpty()) ? | 
| 1713 |             sheet.styleRules.at(i: 0) : *sheet.nameIndex.begin(); | 
| 1714 |     const QVector<QCss::Declaration> decls = rule.declarations; | 
| 1715 |     QVERIFY(!decls.isEmpty()); | 
| 1716 |     QCss::ValueExtractor (decls); | 
| 1717 |  | 
| 1718 |     int adjustment = 0; | 
| 1719 |     QFont f; | 
| 1720 |     f.setUnderline(true); | 
| 1721 |     f.setOverline(true); | 
| 1722 |     f.setStrikeOut(true); | 
| 1723 |     QVERIFY(extractor.extractFont(&f, &adjustment)); | 
| 1724 |  | 
| 1725 |     QVERIFY(!f.underline()); | 
| 1726 |     QVERIFY(!f.overline()); | 
| 1727 |     QVERIFY(!f.strikeOut()); | 
| 1728 | } | 
| 1729 |  | 
| 1730 | void tst_QCssParser::quotedAndUnquotedIdentifiers() | 
| 1731 | { | 
| 1732 |     QCss::Parser parser("foo { font-style: \"italic\"; font-weight: bold }" ); | 
| 1733 |     QCss::StyleSheet sheet; | 
| 1734 |     QVERIFY(parser.parse(&sheet)); | 
| 1735 |  | 
| 1736 |     QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1); | 
| 1737 |     QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ? | 
| 1738 |            sheet.styleRules.at(i: 0) : *sheet.nameIndex.begin(); | 
| 1739 |     const QVector<QCss::Declaration> decls = rule.declarations; | 
| 1740 |     QCOMPARE(decls.size(), 2); | 
| 1741 |  | 
| 1742 |     QCOMPARE(decls.at(0).d->values.first().type, QCss::Value::String); | 
| 1743 |     QCOMPARE(decls.at(0).d->property, QLatin1String("font-style" )); | 
| 1744 |     QCOMPARE(decls.at(0).d->values.first().toString(), QLatin1String("italic" )); | 
| 1745 |  | 
| 1746 |     QCOMPARE(decls.at(1).d->values.first().type, QCss::Value::KnownIdentifier); | 
| 1747 |     QCOMPARE(decls.at(1).d->property, QLatin1String("font-weight" )); | 
| 1748 |     QCOMPARE(decls.at(1).d->values.first().toString(), QLatin1String("bold" )); | 
| 1749 | } | 
| 1750 |  | 
| 1751 | void tst_QCssParser::whitespaceValues_data() | 
| 1752 | { | 
| 1753 |     QTest::addColumn<QString>(name: "value" ); | 
| 1754 |  | 
| 1755 |     QTest::newRow(dataTag: "normal" ) << "normal" ; | 
| 1756 |     QTest::newRow(dataTag: "inherit" ) << "inherit" ; | 
| 1757 |     QTest::newRow(dataTag: "nowrap" ) << "nowrap" ; | 
| 1758 |     QTest::newRow(dataTag: "pre" ) << "pre" ; | 
| 1759 |     QTest::newRow(dataTag: "pre-wrap" ) << "pre-wrap" ; | 
| 1760 |     QTest::newRow(dataTag: "pre-line" ) << "pre-line" ; | 
| 1761 | } | 
| 1762 |  | 
| 1763 | void tst_QCssParser::whitespaceValues() | 
| 1764 | { | 
| 1765 |     QFETCH(QString, value); | 
| 1766 |     QCss::Parser parser(QString("foo { white-space: %1 }" ).arg(a: value)); | 
| 1767 |     QCss::StyleSheet sheet; | 
| 1768 |     QVERIFY(parser.parse(&sheet)); | 
| 1769 |  | 
| 1770 |     QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ? | 
| 1771 |            sheet.styleRules.at(i: 0) : *sheet.nameIndex.begin(); | 
| 1772 |     QCOMPARE(rule.declarations.size(), 1); | 
| 1773 |  | 
| 1774 |     QCOMPARE(rule.declarations.at(0).d->property, QLatin1String("white-space" )); | 
| 1775 |     QCOMPARE(rule.declarations.at(0).d->values.first().toString(), value); | 
| 1776 | } | 
| 1777 |  | 
| 1778 | QTEST_MAIN(tst_QCssParser) | 
| 1779 | #include "tst_qcssparser.moc" | 
| 1780 |  | 
| 1781 |  |