| 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 QtTest module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 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 Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or (at your option) the GNU General |
| 28 | ** Public license version 3 or any later version approved by the KDE Free |
| 29 | ** Qt Foundation. The licenses are as published by the Free Software |
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 31 | ** included in the packaging of this file. Please review the following |
| 32 | ** information to ensure the GNU General Public License requirements will |
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 35 | ** |
| 36 | ** $QT_END_LICENSE$ |
| 37 | ** |
| 38 | ****************************************************************************/ |
| 39 | |
| 40 | #include <QtTest/private/qtestresult_p.h> |
| 41 | #include <QtCore/qglobal.h> |
| 42 | #include <QtCore/qstringview.h> |
| 43 | |
| 44 | #include <QtTest/private/qtestlog_p.h> |
| 45 | #include <QtTest/qtest.h> // toString() specializations for QStringView |
| 46 | #include <QtTest/qtestdata.h> |
| 47 | #include <QtTest/qtestcase.h> |
| 48 | #include <QtTest/qtestassert.h> |
| 49 | |
| 50 | #include <stdlib.h> |
| 51 | #include <stdio.h> |
| 52 | #include <string.h> |
| 53 | |
| 54 | static const char *currentAppName = nullptr; |
| 55 | |
| 56 | QT_BEGIN_NAMESPACE |
| 57 | |
| 58 | namespace QTest |
| 59 | { |
| 60 | static QTestData *currentTestData = nullptr; |
| 61 | static QTestData *currentGlobalTestData = nullptr; |
| 62 | static const char *currentTestFunc = nullptr; |
| 63 | static const char *currentTestObjectName = nullptr; |
| 64 | static bool failed = false; |
| 65 | static bool skipCurrentTest = false; |
| 66 | static bool blacklistCurrentTest = false; |
| 67 | |
| 68 | static const char * = nullptr; |
| 69 | static int expectFailMode = 0; |
| 70 | } |
| 71 | |
| 72 | void QTestResult::reset() |
| 73 | { |
| 74 | QTest::currentTestData = nullptr; |
| 75 | QTest::currentGlobalTestData = nullptr; |
| 76 | QTest::currentTestFunc = nullptr; |
| 77 | QTest::currentTestObjectName = nullptr; |
| 78 | QTest::failed = false; |
| 79 | |
| 80 | QTest::expectFailComment = nullptr; |
| 81 | QTest::expectFailMode = 0; |
| 82 | QTest::blacklistCurrentTest = false; |
| 83 | |
| 84 | QTestLog::resetCounters(); |
| 85 | } |
| 86 | |
| 87 | void QTestResult::setBlacklistCurrentTest(bool b) |
| 88 | { |
| 89 | QTest::blacklistCurrentTest = b; |
| 90 | } |
| 91 | |
| 92 | bool QTestResult::currentTestFailed() |
| 93 | { |
| 94 | return QTest::failed; |
| 95 | } |
| 96 | |
| 97 | QTestData *QTestResult::currentGlobalTestData() |
| 98 | { |
| 99 | return QTest::currentGlobalTestData; |
| 100 | } |
| 101 | |
| 102 | QTestData *QTestResult::currentTestData() |
| 103 | { |
| 104 | return QTest::currentTestData; |
| 105 | } |
| 106 | |
| 107 | void QTestResult::setCurrentGlobalTestData(QTestData *data) |
| 108 | { |
| 109 | QTest::currentGlobalTestData = data; |
| 110 | } |
| 111 | |
| 112 | void QTestResult::setCurrentTestData(QTestData *data) |
| 113 | { |
| 114 | QTest::currentTestData = data; |
| 115 | QTest::failed = false; |
| 116 | if (data) |
| 117 | QTestLog::enterTestData(data); |
| 118 | } |
| 119 | |
| 120 | void QTestResult::setCurrentTestFunction(const char *func) |
| 121 | { |
| 122 | QTest::currentTestFunc = func; |
| 123 | QTest::failed = false; |
| 124 | if (func) |
| 125 | QTestLog::enterTestFunction(func); |
| 126 | } |
| 127 | |
| 128 | static void clearExpectFail() |
| 129 | { |
| 130 | QTest::expectFailMode = 0; |
| 131 | delete [] const_cast<char *>(QTest::expectFailComment); |
| 132 | QTest::expectFailComment = nullptr; |
| 133 | } |
| 134 | |
| 135 | void QTestResult::finishedCurrentTestData() |
| 136 | { |
| 137 | if (QTest::expectFailMode) { |
| 138 | addFailure("QEXPECT_FAIL was called without any subsequent verification statements" , |
| 139 | "Unknown File" , 0); |
| 140 | } |
| 141 | clearExpectFail(); |
| 142 | |
| 143 | if (!QTest::failed && QTestLog::unhandledIgnoreMessages()) { |
| 144 | QTestLog::printUnhandledIgnoreMessages(); |
| 145 | addFailure("Not all expected messages were received" , "Unknown File" , 0); |
| 146 | } |
| 147 | QTestLog::clearIgnoreMessages(); |
| 148 | } |
| 149 | |
| 150 | void QTestResult::finishedCurrentTestDataCleanup() |
| 151 | { |
| 152 | // If the current test hasn't failed or been skipped, then it passes. |
| 153 | if (!QTest::failed && !QTest::skipCurrentTest) { |
| 154 | if (QTest::blacklistCurrentTest) |
| 155 | QTestLog::addBPass("" ); |
| 156 | else |
| 157 | QTestLog::addPass("" ); |
| 158 | } |
| 159 | |
| 160 | QTest::failed = false; |
| 161 | } |
| 162 | |
| 163 | void QTestResult::finishedCurrentTestFunction() |
| 164 | { |
| 165 | QTest::currentTestFunc = nullptr; |
| 166 | QTest::failed = false; |
| 167 | |
| 168 | QTestLog::leaveTestFunction(); |
| 169 | } |
| 170 | |
| 171 | const char *QTestResult::currentTestFunction() |
| 172 | { |
| 173 | return QTest::currentTestFunc; |
| 174 | } |
| 175 | |
| 176 | const char *QTestResult::currentDataTag() |
| 177 | { |
| 178 | return QTest::currentTestData ? QTest::currentTestData->dataTag() : nullptr; |
| 179 | } |
| 180 | |
| 181 | const char *QTestResult::currentGlobalDataTag() |
| 182 | { |
| 183 | return QTest::currentGlobalTestData ? QTest::currentGlobalTestData->dataTag() : nullptr; |
| 184 | } |
| 185 | |
| 186 | static bool isExpectFailData(const char *dataIndex) |
| 187 | { |
| 188 | if (!dataIndex || dataIndex[0] == '\0') |
| 189 | return true; |
| 190 | if (!QTest::currentTestData) |
| 191 | return false; |
| 192 | if (strcmp(dataIndex, QTest::currentTestData->dataTag()) == 0) |
| 193 | return true; |
| 194 | return false; |
| 195 | } |
| 196 | |
| 197 | bool QTestResult::expectFail(const char *dataIndex, const char *, |
| 198 | QTest::TestFailMode mode, const char *file, int line) |
| 199 | { |
| 200 | QTEST_ASSERT(comment); |
| 201 | QTEST_ASSERT(mode > 0); |
| 202 | |
| 203 | if (!isExpectFailData(dataIndex)) { |
| 204 | delete[] comment; |
| 205 | return true; // we don't care |
| 206 | } |
| 207 | |
| 208 | if (QTest::expectFailMode) { |
| 209 | delete[] comment; |
| 210 | clearExpectFail(); |
| 211 | addFailure("Already expecting a fail" , file, line); |
| 212 | return false; |
| 213 | } |
| 214 | |
| 215 | QTest::expectFailMode = mode; |
| 216 | QTest::expectFailComment = comment; |
| 217 | return true; |
| 218 | } |
| 219 | |
| 220 | static bool checkStatement(bool statement, const char *msg, const char *file, int line) |
| 221 | { |
| 222 | if (statement) { |
| 223 | if (QTest::expectFailMode) { |
| 224 | if (QTest::blacklistCurrentTest) |
| 225 | QTestLog::addBXPass(msg, file, line); |
| 226 | else |
| 227 | QTestLog::addXPass(msg, file, line); |
| 228 | |
| 229 | QTest::failed = true; |
| 230 | bool doContinue = (QTest::expectFailMode == QTest::Continue); |
| 231 | clearExpectFail(); |
| 232 | return doContinue; |
| 233 | } |
| 234 | return true; |
| 235 | } |
| 236 | |
| 237 | if (QTest::expectFailMode) { |
| 238 | if (QTest::blacklistCurrentTest) |
| 239 | QTestLog::addBXFail(QTest::expectFailComment, file, line); |
| 240 | else |
| 241 | QTestLog::addXFail(QTest::expectFailComment, file, line); |
| 242 | bool doContinue = (QTest::expectFailMode == QTest::Continue); |
| 243 | clearExpectFail(); |
| 244 | return doContinue; |
| 245 | } |
| 246 | |
| 247 | QTestResult::addFailure(msg, file, line); |
| 248 | return false; |
| 249 | } |
| 250 | |
| 251 | bool QTestResult::verify(bool statement, const char *statementStr, |
| 252 | const char *description, const char *file, int line) |
| 253 | { |
| 254 | QTEST_ASSERT(statementStr); |
| 255 | |
| 256 | char msg[1024] = {'\0'}; |
| 257 | |
| 258 | if (QTestLog::verboseLevel() >= 2) { |
| 259 | qsnprintf(msg, 1024, "QVERIFY(%s)" , statementStr); |
| 260 | QTestLog::info(msg, file, line); |
| 261 | } |
| 262 | |
| 263 | if (!statement && !QTest::expectFailMode) |
| 264 | qsnprintf(msg, 1024, "'%s' returned FALSE. (%s)" , statementStr, description ? description : "" ); |
| 265 | else if (statement && QTest::expectFailMode) |
| 266 | qsnprintf(msg, 1024, "'%s' returned TRUE unexpectedly. (%s)" , statementStr, description ? description : "" ); |
| 267 | |
| 268 | return checkStatement(statement, msg, file, line); |
| 269 | } |
| 270 | |
| 271 | // Format failures using the toString() template |
| 272 | template <class Actual, class Expected> |
| 273 | void formatFailMessage(char *msg, size_t maxMsgLen, |
| 274 | const char *failureMsg, |
| 275 | const Actual &val1, const Expected &val2, |
| 276 | const char *actual, const char *expected) |
| 277 | { |
| 278 | auto val1S = QTest::toString(val1); |
| 279 | auto val2S = QTest::toString(val2); |
| 280 | |
| 281 | size_t len1 = mbstowcs(nullptr, actual, maxMsgLen); // Last parameter is not ignored on QNX |
| 282 | size_t len2 = mbstowcs(nullptr, expected, maxMsgLen); // (result is never larger than this). |
| 283 | qsnprintf(msg, maxMsgLen, "%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s" , |
| 284 | failureMsg, |
| 285 | actual, qMax(len1, len2) - len1 + 1, ":" , val1S ? val1S : "<null>" , |
| 286 | expected, qMax(len1, len2) - len2 + 1, ":" , val2S ? val2S : "<null>" ); |
| 287 | |
| 288 | delete [] val1S; |
| 289 | delete [] val2S; |
| 290 | } |
| 291 | |
| 292 | // Overload to format failures for "const char *" - no need to strdup(). |
| 293 | void formatFailMessage(char *msg, size_t maxMsgLen, |
| 294 | const char *failureMsg, |
| 295 | const char *val1, const char *val2, |
| 296 | const char *actual, const char *expected) |
| 297 | { |
| 298 | size_t len1 = mbstowcs(nullptr, actual, maxMsgLen); // Last parameter is not ignored on QNX |
| 299 | size_t len2 = mbstowcs(nullptr, expected, maxMsgLen); // (result is never larger than this). |
| 300 | qsnprintf(msg, maxMsgLen, "%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s" , |
| 301 | failureMsg, |
| 302 | actual, qMax(len1, len2) - len1 + 1, ":" , val1 ? val1 : "<null>" , |
| 303 | expected, qMax(len1, len2) - len2 + 1, ":" , val2 ? val2 : "<null>" ); |
| 304 | } |
| 305 | |
| 306 | template <class Actual, class Expected> |
| 307 | static bool compareHelper(bool success, const char *failureMsg, |
| 308 | const Actual &val1, const Expected &val2, |
| 309 | const char *actual, const char *expected, |
| 310 | const char *file, int line, |
| 311 | bool hasValues = true) |
| 312 | { |
| 313 | const size_t maxMsgLen = 1024; |
| 314 | char msg[maxMsgLen] = {'\0'}; |
| 315 | |
| 316 | QTEST_ASSERT(expected); |
| 317 | QTEST_ASSERT(actual); |
| 318 | |
| 319 | if (QTestLog::verboseLevel() >= 2) { |
| 320 | qsnprintf(msg, maxMsgLen, "QCOMPARE(%s, %s)" , actual, expected); |
| 321 | QTestLog::info(msg, file, line); |
| 322 | } |
| 323 | |
| 324 | if (!failureMsg) |
| 325 | failureMsg = "Compared values are not the same" ; |
| 326 | |
| 327 | if (success) { |
| 328 | if (QTest::expectFailMode) { |
| 329 | qsnprintf(msg, maxMsgLen, |
| 330 | "QCOMPARE(%s, %s) returned TRUE unexpectedly." , actual, expected); |
| 331 | } |
| 332 | return checkStatement(success, msg, file, line); |
| 333 | } |
| 334 | |
| 335 | |
| 336 | if (!hasValues) { |
| 337 | qsnprintf(msg, maxMsgLen, "%s" , failureMsg); |
| 338 | return checkStatement(success, msg, file, line); |
| 339 | } |
| 340 | |
| 341 | formatFailMessage(msg, maxMsgLen, failureMsg, val1, val2, actual, expected); |
| 342 | |
| 343 | return checkStatement(success, msg, file, line); |
| 344 | } |
| 345 | |
| 346 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 347 | char *val1, char *val2, |
| 348 | const char *actual, const char *expected, |
| 349 | const char *file, int line) |
| 350 | { |
| 351 | const bool result = compareHelper(success, failureMsg, |
| 352 | val1 != nullptr ? val1 : "<null>" , |
| 353 | val2 != nullptr ? val2 : "<null>" , |
| 354 | actual, expected, file, line, |
| 355 | val1 != nullptr && val2 != nullptr); |
| 356 | |
| 357 | // Our caller got these from QTest::toString() |
| 358 | delete [] val1; |
| 359 | delete [] val2; |
| 360 | |
| 361 | return result; |
| 362 | } |
| 363 | |
| 364 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 365 | double val1, double val2, |
| 366 | const char *actual, const char *expected, |
| 367 | const char *file, int line) |
| 368 | { |
| 369 | return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); |
| 370 | } |
| 371 | |
| 372 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 373 | float val1, float val2, |
| 374 | const char *actual, const char *expected, |
| 375 | const char *file, int line) |
| 376 | { |
| 377 | return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); |
| 378 | } |
| 379 | |
| 380 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 381 | int val1, int val2, |
| 382 | const char *actual, const char *expected, |
| 383 | const char *file, int line) |
| 384 | { |
| 385 | return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); |
| 386 | } |
| 387 | |
| 388 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 389 | unsigned val1, unsigned val2, |
| 390 | const char *actual, const char *expected, |
| 391 | const char *file, int line) |
| 392 | { |
| 393 | return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); |
| 394 | } |
| 395 | |
| 396 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 397 | QStringView val1, QStringView val2, |
| 398 | const char *actual, const char *expected, |
| 399 | const char *file, int line) |
| 400 | { |
| 401 | return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); |
| 402 | } |
| 403 | |
| 404 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 405 | QStringView val1, const QLatin1String &val2, |
| 406 | const char *actual, const char *expected, |
| 407 | const char *file, int line) |
| 408 | { |
| 409 | return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); |
| 410 | } |
| 411 | |
| 412 | bool QTestResult::compare(bool success, const char *failureMsg, |
| 413 | const QLatin1String & val1, QStringView val2, |
| 414 | const char *actual, const char *expected, |
| 415 | const char *file, int line) |
| 416 | { |
| 417 | return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); |
| 418 | } |
| 419 | |
| 420 | void QTestResult::addFailure(const char *message, const char *file, int line) |
| 421 | { |
| 422 | clearExpectFail(); |
| 423 | |
| 424 | if (QTest::blacklistCurrentTest) |
| 425 | QTestLog::addBFail(message, file, line); |
| 426 | else |
| 427 | QTestLog::addFail(message, file, line); |
| 428 | QTest::failed = true; |
| 429 | } |
| 430 | |
| 431 | void QTestResult::addSkip(const char *message, const char *file, int line) |
| 432 | { |
| 433 | clearExpectFail(); |
| 434 | |
| 435 | QTestLog::addSkip(message, file, line); |
| 436 | } |
| 437 | |
| 438 | void QTestResult::setCurrentTestObject(const char *name) |
| 439 | { |
| 440 | QTest::currentTestObjectName = name; |
| 441 | } |
| 442 | |
| 443 | const char *QTestResult::currentTestObjectName() |
| 444 | { |
| 445 | return QTest::currentTestObjectName ? QTest::currentTestObjectName : "" ; |
| 446 | } |
| 447 | |
| 448 | void QTestResult::setSkipCurrentTest(bool value) |
| 449 | { |
| 450 | QTest::skipCurrentTest = value; |
| 451 | } |
| 452 | |
| 453 | bool QTestResult::skipCurrentTest() |
| 454 | { |
| 455 | return QTest::skipCurrentTest; |
| 456 | } |
| 457 | |
| 458 | void QTestResult::setCurrentAppName(const char *appName) |
| 459 | { |
| 460 | ::currentAppName = appName; |
| 461 | } |
| 462 | |
| 463 | const char *QTestResult::currentAppName() |
| 464 | { |
| 465 | return ::currentAppName; |
| 466 | } |
| 467 | |
| 468 | QT_END_NAMESPACE |
| 469 | |