TestCase.qml 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202
  1. // Copyright (C) 2016 The Qt Company Ltd.
  2. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
  3. // Qt-Security score:significant reason:default
  4. import QtQuick 2.0
  5. import QtQuick.Window 2.0 // used for qtest_verifyItem
  6. import QtTest 1.2
  7. import "testlogger.js" as TestLogger
  8. /*!
  9. \qmltype TestCase
  10. \inqmlmodule QtTest
  11. \brief Represents a unit test case.
  12. \since 4.8
  13. \ingroup qtquicktest
  14. \section1 Introduction to QML Test Cases
  15. Test cases are written as JavaScript functions within a TestCase
  16. type:
  17. \code
  18. import QtQuick 2.0
  19. import QtTest 1.2
  20. TestCase {
  21. name: "MathTests"
  22. function test_math() {
  23. compare(2 + 2, 4, "2 + 2 = 4")
  24. }
  25. function test_fail() {
  26. compare(2 + 2, 5, "2 + 2 = 5")
  27. }
  28. }
  29. \endcode
  30. Functions whose names start with "test_" are treated as test cases
  31. to be executed. The \l name property is used to prefix the functions
  32. in the output:
  33. \code
  34. ********* Start testing of MathTests *********
  35. Config: Using QTest library 4.7.2, Qt 4.7.2
  36. PASS : MathTests::initTestCase()
  37. FAIL! : MathTests::test_fail() 2 + 2 = 5
  38. Actual (): 4
  39. Expected (): 5
  40. Loc: [/home/.../tst_math.qml(12)]
  41. PASS : MathTests::test_math()
  42. PASS : MathTests::cleanupTestCase()
  43. Totals: 3 passed, 1 failed, 0 skipped
  44. ********* Finished testing of MathTests *********
  45. \endcode
  46. Because of the way JavaScript properties work, the order in which the
  47. test functions are found is unpredictable. To assist with predictability,
  48. the test framework will sort the functions on ascending order of name.
  49. This can help when there are two tests that must be run in order.
  50. Multiple TestCase types can be supplied. The test program will exit
  51. once they have all completed. If a test case doesn't need to run
  52. (because a precondition has failed), then \l optional can be set to true.
  53. \section1 Data-driven Tests
  54. Table data can be provided to a test using a function name that ends
  55. with "_data". Alternatively, the \c init_data() function can be used
  56. to provide default test data for all test functions without a matching
  57. "_data" function in a TestCase type:
  58. \code
  59. import QtQuick 2.0
  60. import QtTest 1.2
  61. TestCase {
  62. name: "DataTests"
  63. function init_data() {
  64. return [
  65. {tag:"init_data_1", a:1, b:2, answer: 3},
  66. {tag:"init_data_2", a:2, b:4, answer: 6}
  67. ];
  68. }
  69. function test_table_data() {
  70. return [
  71. {tag: "2 + 2 = 4", a: 2, b: 2, answer: 4 },
  72. {tag: "2 + 6 = 8", a: 2, b: 6, answer: 8 },
  73. ]
  74. }
  75. function test_table(data) {
  76. //data comes from test_table_data
  77. compare(data.a + data.b, data.answer)
  78. }
  79. function test_default_table(data) {
  80. //data comes from init_data
  81. compare(data.a + data.b, data.answer)
  82. }
  83. }
  84. \endcode
  85. The test framework will iterate over all of the rows in the table
  86. and pass each row to the test function. As shown, the columns can be
  87. extracted for use in the test. The \c tag column is special - it is
  88. printed by the test framework when a row fails, to help the reader
  89. identify which case failed amongst a set of otherwise passing tests.
  90. \section1 Benchmarks
  91. Functions whose names start with "benchmark_" will be run multiple
  92. times with the Qt benchmark framework, with an average timing value
  93. reported for the runs. This is equivalent to using the \c{QBENCHMARK}
  94. macro in the C++ version of QTestLib.
  95. \code
  96. TestCase {
  97. id: top
  98. name: "CreateBenchmark"
  99. function benchmark_create_component() {
  100. let component = Qt.createComponent("item.qml")
  101. let obj = component.createObject(top)
  102. obj.destroy()
  103. component.destroy()
  104. }
  105. }
  106. RESULT : CreateBenchmark::benchmark_create_component:
  107. 0.23 msecs per iteration (total: 60, iterations: 256)
  108. PASS : CreateBenchmark::benchmark_create_component()
  109. \endcode
  110. To get the effect of the \c{QBENCHMARK_ONCE} macro, prefix the test
  111. function name with "benchmark_once_".
  112. \section1 Simulating Keyboard and Mouse Events
  113. The keyPress(), keyRelease(), and keyClick() methods can be used
  114. to simulate keyboard events within unit tests. The events are
  115. delivered to the currently focused QML item. You can pass either
  116. a Qt.Key enum value or a latin1 char (string of length one)
  117. \code
  118. Rectangle {
  119. width: 50; height: 50
  120. focus: true
  121. TestCase {
  122. name: "KeyClick"
  123. when: windowShown
  124. function test_key_click() {
  125. keyClick(Qt.Key_Left)
  126. keyClick("a")
  127. ...
  128. }
  129. }
  130. }
  131. \endcode
  132. The mousePress(), mouseRelease(), mouseClick(), mouseDoubleClickSequence()
  133. and mouseMove() methods can be used to simulate mouse events in a
  134. similar fashion.
  135. If your test creates other windows, it's possible that those windows
  136. become active, stealing the focus from the TestCase's window. To ensure
  137. that the TestCase's window is active, use the following code:
  138. \code
  139. testCase.Window.window.requestActivate()
  140. tryCompare(testCase.Window.window, "active", true)
  141. \endcode
  142. \b{Note:} keyboard and mouse events can only be delivered once the
  143. main window has been shown. Attempts to deliver events before then
  144. will fail. Use the \l when and windowShown properties to track
  145. when the main window has been shown.
  146. \section1 Managing Dynamically Created Test Objects
  147. A typical pattern with QML tests is to
  148. \l {Dynamic QML Object Creation from JavaScript}{dynamically create}
  149. an item and then destroy it at the end of the test function:
  150. \code
  151. TestCase {
  152. id: testCase
  153. name: "MyTest"
  154. when: windowShown
  155. function test_click() {
  156. let item = Qt.createQmlObject("import QtQuick 2.0; Item {}", testCase);
  157. verify(item);
  158. // Test item...
  159. item.destroy();
  160. }
  161. }
  162. \endcode
  163. The problem with this pattern is that any failures in the test function
  164. will cause the call to \c item.destroy() to be skipped, leaving the item
  165. hanging around in the scene until the test case has finished. This can
  166. result in interference with future tests; for example, by blocking input
  167. events or producing unrelated debug output that makes it difficult to
  168. follow the code's execution.
  169. By calling \l createTemporaryQmlObject() instead, the object is guaranteed
  170. to be destroyed at the end of the test function:
  171. \code
  172. TestCase {
  173. id: testCase
  174. name: "MyTest"
  175. when: windowShown
  176. function test_click() {
  177. let item = createTemporaryQmlObject("import QtQuick 2.0; Item {}", testCase);
  178. verify(item);
  179. // Test item...
  180. // Don't need to worry about destroying "item" here.
  181. }
  182. }
  183. \endcode
  184. For objects that are created via the \l {Component::}{createObject()} function
  185. of \l Component, the \l createTemporaryObject() function can be used.
  186. \sa {QtTest::SignalSpy}{SignalSpy}, {Qt Quick Test}
  187. \section1 Separating Tests from Application Logic
  188. In most cases, you would want to separate your tests from the application
  189. logic by splitting them into different projects and linking them.
  190. For example, you could have the following project structure:
  191. \badcode
  192. .
  193. | — CMakeLists.txt
  194. | — main.cpp
  195. | - main.qml
  196. | — MyModule
  197. | — MyButton.qml
  198. | — CMakeLists.txt
  199. | — tests
  200. | — tst_testqml.qml
  201. | — main.cpp
  202. | — setup.cpp
  203. | — setup.h
  204. \endcode
  205. Now, to test \c MyModule/MyButton.qml, create a library for
  206. \c MyModule in \c MyModule/CMakeLists.txt and link it to your
  207. test project, \c tests/UnitQMLTests/CMakeLists.txt:
  208. \if defined(onlinedocs)
  209. \tab {build-qt-app}{tab-cmake-add-library}{MyModule/CMakeLists.txt}{checked}
  210. \tab {build-qt-app}{tab-cmake-link-against-library}{tests/CMakeLists.txt}{}
  211. \tab {build-qt-app}{tab-tests_main}{tests/main.cpp}{}
  212. \tab {build-qt-app}{tab-tests-setup-cpp}{tests/setup.cpp}{}
  213. \tab
  214. {build-qt-app}{tab-tests-setup-h}{tests/setup.h}{}
  215. \tab {build-qt-app}{tab-project-cmake}{CMakeLists.txt}{}
  216. \tabcontent {tab-cmake-add-library}
  217. \else
  218. \section2 Add Library
  219. \endif
  220. \dots
  221. \snippet testApp/MyModule/CMakeLists.txt add library
  222. \dots
  223. \if defined(onlinedocs)
  224. \endtabcontent
  225. \tabcontent {tab-cmake-link-against-library}
  226. \else
  227. \section2 Link Against Library
  228. \endif
  229. \dots
  230. \snippet testApp/tests/CMakeLists.txt link against library
  231. \dots
  232. \if defined(onlinedocs)
  233. \endtabcontent
  234. \tabcontent {tab-tests_main}
  235. \else
  236. \section2 Test main.cpp
  237. \endif
  238. \snippet testApp/tests/main.cpp main
  239. \if defined(onlinedocs)
  240. \endtabcontent
  241. \tabcontent {tab-tests-setup-cpp}
  242. \else
  243. \section2 Test Setup C++
  244. \endif
  245. \snippet testApp/tests/setup.cpp setup
  246. \if defined(onlinedocs)
  247. \endtabcontent
  248. \tabcontent {tab-tests-setup-h}
  249. \else
  250. \section2 Test Setup Header
  251. \endif
  252. \snippet testApp/tests/setup.h setup
  253. \if defined(onlinedocs)
  254. \endtabcontent
  255. \tabcontent {tab-project-cmake}
  256. \else
  257. \section2 Project CMakeLists
  258. \endif
  259. \dots
  260. \snippet testApp/CMakeLists.txt project-cmake
  261. \dots
  262. \if defined(onlinedocs)
  263. \endtabcontent
  264. \endif
  265. Then, in \c tests/tst_testqml.qml, you can import
  266. \c MyModule/MyButton.qml:
  267. \if defined(onlinedocs)
  268. \tab {test-qml}{tab-qml-import}{tests/tst_testqml.qml}{checked}
  269. \tab {test-qml}{tab-qml-my-button}{MyModule/MyButton.qml}{}
  270. \tabcontent {tab-qml-import}
  271. \else
  272. \section2 Import QML
  273. \endif
  274. \snippet testApp/tests/tst_testqml.qml import
  275. \if defined(onlinedocs)
  276. \endtabcontent
  277. \tabcontent {tab-qml-my-button}
  278. \else
  279. \section2 Define QML Button
  280. \endif
  281. \snippet testApp/MyModule/MyButton.qml define
  282. \if defined(onlinedocs)
  283. \endtabcontent
  284. \endif
  285. */
  286. Item {
  287. id: testCase
  288. visible: false
  289. TestUtil {
  290. id:util
  291. }
  292. /*!
  293. \qmlproperty string TestCase::name
  294. This property defines the name of the test case for result reporting.
  295. The default value is an empty string.
  296. \code
  297. TestCase {
  298. name: "ButtonTests"
  299. ...
  300. }
  301. \endcode
  302. */
  303. property string name
  304. /*!
  305. \qmlproperty bool TestCase::when
  306. This property should be set to true when the application wants
  307. the test cases to run. The default value is true. In the following
  308. example, a test is run when the user presses the mouse button:
  309. \code
  310. Rectangle {
  311. id: foo
  312. width: 640; height: 480
  313. color: "cyan"
  314. MouseArea {
  315. id: area
  316. anchors.fill: parent
  317. }
  318. property bool bar: true
  319. TestCase {
  320. name: "ItemTests"
  321. when: area.pressed
  322. id: test1
  323. function test_bar() {
  324. verify(bar)
  325. }
  326. }
  327. }
  328. \endcode
  329. The test application will exit once all \l TestCase types
  330. have been triggered and have run. The \l optional property can
  331. be used to exclude a \l TestCase type.
  332. \sa optional, completed
  333. */
  334. property bool when: true
  335. /*!
  336. \qmlproperty bool TestCase::completed
  337. This property will be set to true once the test case has completed
  338. execution. Test cases are only executed once. The initial value
  339. is false.
  340. \sa running, when
  341. */
  342. property bool completed: false
  343. /*!
  344. \qmlproperty bool TestCase::running
  345. This property will be set to true while the test case is running.
  346. The initial value is false, and the value will become false again
  347. once the test case completes.
  348. \sa completed, when
  349. */
  350. property bool running: false
  351. /*!
  352. \qmlproperty bool TestCase::optional
  353. Multiple \l TestCase types can be supplied in a test application.
  354. The application will exit once they have all completed. If a test case
  355. does not need to run (because a precondition has failed), then this
  356. property can be set to true. The default value is false.
  357. \code
  358. TestCase {
  359. when: false
  360. optional: true
  361. function test_not_run() {
  362. verify(false)
  363. }
  364. }
  365. \endcode
  366. \sa when, completed
  367. */
  368. property bool optional: false
  369. /*!
  370. \qmlproperty bool TestCase::windowShown
  371. This property will be set to true after the QML viewing window has
  372. been displayed. Normally test cases run as soon as the test application
  373. is loaded and before a window is displayed. If the test case involves
  374. visual types and behaviors, then it may need to be delayed until
  375. after the window is shown.
  376. \code
  377. Button {
  378. id: button
  379. onClicked: text = "Clicked"
  380. TestCase {
  381. name: "ClickTest"
  382. when: windowShown
  383. function test_click() {
  384. button.clicked();
  385. compare(button.text, "Clicked");
  386. }
  387. }
  388. }
  389. \endcode
  390. */
  391. property bool windowShown: QTestRootObject.windowShown
  392. // Internal private state. Identifiers prefixed with qtest are reserved.
  393. /*! \internal */
  394. property bool qtest_prevWhen: true
  395. /*! \internal */
  396. property int qtest_testId: -1
  397. /*! \internal */
  398. property bool qtest_componentCompleted : false
  399. /*! \internal */
  400. property var qtest_testCaseResult
  401. /*! \internal */
  402. property var qtest_results: qtest_results_normal
  403. /*! \internal */
  404. TestResult { id: qtest_results_normal }
  405. /*! \internal */
  406. property var qtest_events: qtest_events_normal
  407. TestEvent { id: qtest_events_normal }
  408. /*! \internal */
  409. property var qtest_temporaryObjects: []
  410. /*!
  411. \qmlmethod TestCase::fail(message = "")
  412. Fails the current test case, with the optional \a message.
  413. Similar to \c{QFAIL(message)} in C++.
  414. */
  415. function fail(msg) {
  416. if (msg === undefined)
  417. msg = "";
  418. qtest_results.fail(msg, util.callerFile(), util.callerLine())
  419. throw new Error("QtQuickTest::fail")
  420. }
  421. /*! \internal */
  422. function qtest_fail(msg, frame) {
  423. if (msg === undefined)
  424. msg = "";
  425. qtest_results.fail(msg, util.callerFile(frame), util.callerLine(frame))
  426. throw new Error("QtQuickTest::fail")
  427. }
  428. /*!
  429. \qmlmethod TestCase::verify(condition, message = "")
  430. Fails the current test case if \a condition is false, and
  431. displays the optional \a message. Similar to \c{QVERIFY(condition)}
  432. or \c{QVERIFY2(condition, message)} in C++.
  433. */
  434. function verify(cond, msg, ...args) {
  435. if (args.length > 0)
  436. qtest_fail("More than two arguments given to verify(). Did you mean tryVerify() or tryCompare()?", 1)
  437. if (msg === undefined)
  438. msg = "";
  439. if (!qtest_results.verify(cond, msg, util.callerFile(), util.callerLine()))
  440. throw new Error("QtQuickTest::fail")
  441. }
  442. /*!
  443. \since 5.8
  444. \qmlmethod TestCase::tryVerify(function, timeout = 5000, message = "")
  445. Fails the current test case if \a function does not evaluate to
  446. \c true before the specified \a timeout (in milliseconds) has elapsed.
  447. The function is evaluated multiple times until the timeout is
  448. reached. An optional \a message is displayed upon failure.
  449. This function is intended for testing applications where a condition
  450. changes based on asynchronous events. Use verify() for testing
  451. synchronous condition changes, and tryCompare() for testing
  452. asynchronous property changes.
  453. For example, in the code below, it's not possible to use tryCompare(),
  454. because the \c currentItem property might be \c null for a short period
  455. of time:
  456. \code
  457. tryCompare(listView.currentItem, "text", "Hello");
  458. \endcode
  459. Instead, we can use tryVerify() to first check that \c currentItem
  460. isn't \c null, and then use a regular compare afterwards:
  461. \code
  462. tryVerify(function(){ return listView.currentItem })
  463. compare(listView.currentItem.text, "Hello")
  464. \endcode
  465. \sa verify(), compare(), tryCompare(), SignalSpy::wait()
  466. */
  467. function tryVerify(expressionFunction, timeout, msg) {
  468. if (!expressionFunction || !(expressionFunction instanceof Function)) {
  469. qtest_results.fail("First argument must be a function", util.callerFile(), util.callerLine())
  470. throw new Error("QtQuickTest::fail")
  471. }
  472. if (timeout && typeof(timeout) !== "number") {
  473. qtest_results.fail("timeout argument must be a number", util.callerFile(), util.callerLine())
  474. throw new Error("QtQuickTest::fail")
  475. }
  476. if (msg && typeof(msg) !== "string") {
  477. qtest_results.fail("message argument must be a string", util.callerFile(), util.callerLine())
  478. throw new Error("QtQuickTest::fail")
  479. }
  480. if (!timeout)
  481. timeout = 5000
  482. if (msg === undefined)
  483. msg = "function returned false"
  484. if (!expressionFunction())
  485. wait(0)
  486. let i = 0
  487. while (i < timeout && !expressionFunction()) {
  488. wait(50)
  489. i += 50
  490. }
  491. if (!qtest_results.verify(expressionFunction(), msg, util.callerFile(), util.callerLine()))
  492. throw new Error("QtQuickTest::fail")
  493. }
  494. /*!
  495. \since 5.13
  496. \qmlmethod bool TestCase::isPolishScheduled(object itemOrWindow)
  497. If \a itemOrWindow is an \l Item, this function returns \c true if
  498. \l {QQuickItem::}{updatePolish()} has not been called on it since the
  499. last call to \l {QQuickItem::}{polish()}, otherwise returns \c false.
  500. Since Qt 6.5, if \a itemOrWindow is a \l Window, this function returns
  501. \c true if \l {QQuickItem::}{updatePolish()} has not been called on any
  502. item it manages since the last call to \l {QQuickItem::}{polish()} on
  503. those items, otherwise returns \c false.
  504. When assigning values to properties in QML, any layouting the item
  505. must do as a result of the assignment might not take effect immediately,
  506. but can instead be postponed until the item is polished. For these cases,
  507. you can use this function to ensure that items have been polished
  508. before the execution of the test continues. For example:
  509. \code
  510. verify(isPolishScheduled(item))
  511. verify(waitForItemPolished(item))
  512. \endcode
  513. Without the call to \c isPolishScheduled() above, the
  514. call to \c waitForItemPolished() might see that no polish
  515. was scheduled and therefore pass instantly, assuming that
  516. the item had already been polished. This function
  517. makes it obvious why an item wasn't polished and allows tests to
  518. fail early under such circumstances.
  519. \sa waitForPolish(), QQuickItem::polish(), QQuickItem::updatePolish()
  520. */
  521. function isPolishScheduled(itemOrWindow) {
  522. if (!itemOrWindow || typeof itemOrWindow !== "object") {
  523. qtest_results.fail("Argument must be a valid Item or Window; actual type is " + typeof itemOrWindow,
  524. util.callerFile(), util.callerLine())
  525. throw new Error("QtQuickTest::fail")
  526. }
  527. return qtest_results.isPolishScheduled(itemOrWindow)
  528. }
  529. /*!
  530. \since 5.13
  531. \deprecated [6.5] Use \l waitForPolish() instead.
  532. \qmlmethod bool waitForItemPolished(object item, int timeout = 5000)
  533. Waits for \a timeout milliseconds or until
  534. \l {QQuickItem::}{updatePolish()} has been called on \a item.
  535. Returns \c true if \c updatePolish() was called on \a item within
  536. \a timeout milliseconds, otherwise returns \c false.
  537. \sa isPolishScheduled(), QQuickItem::polish(), QQuickItem::updatePolish()
  538. */
  539. function waitForItemPolished(item, timeout) {
  540. return waitForPolish(item, timeout)
  541. }
  542. /*!
  543. \since 6.5
  544. \qmlmethod bool waitForPolish(object windowOrItem, int timeout = 5000)
  545. If \a windowOrItem is an Item, this functions waits for \a timeout
  546. milliseconds or until \c isPolishScheduled(windowOrItem) returns \c false.
  547. Returns \c true if \c isPolishScheduled(windowOrItem) returns \c false within
  548. \a timeout milliseconds, otherwise returns \c false.
  549. If \c windowOrItem is a Window, this functions waits for \c timeout
  550. milliseconds or until \c isPolishScheduled() returns \c false for
  551. all items managed by the window. Returns \c true if
  552. \c isPolishScheduled() returns \c false for all items within
  553. \a timeout milliseconds, otherwise returns \c false.
  554. \sa isPolishScheduled(), QQuickItem::polish(), QQuickItem::updatePolish()
  555. */
  556. function waitForPolish(windowOrItem, timeout) {
  557. if (!windowOrItem || typeof windowOrItem !== "object") {
  558. qtest_results.fail("First argument must be a valid Item or Window; actual type is " + typeof windowOrItem,
  559. util.callerFile(), util.callerLine())
  560. throw new Error("QtQuickTest::fail")
  561. }
  562. if (timeout !== undefined && typeof(timeout) !== "number") {
  563. qtest_results.fail("Second argument must be a number; actual type is " + typeof timeout,
  564. util.callerFile(), util.callerLine())
  565. throw new Error("QtQuickTest::fail")
  566. }
  567. if (!timeout)
  568. timeout = 5000
  569. return qtest_results.waitForPolish(windowOrItem, timeout)
  570. }
  571. /*!
  572. \since 5.9
  573. \qmlmethod object TestCase::createTemporaryQmlObject(string qml, object parent, string filePath)
  574. This function dynamically creates a QML object from the given \a qml
  575. string with the specified \a parent. The returned object will be
  576. destroyed (if it was not already) after \l cleanup() has finished
  577. executing, meaning that objects created with this function are
  578. guaranteed to be destroyed after each test, regardless of whether or
  579. not the tests fail.
  580. If there was an error while creating the object, \c null will be
  581. returned.
  582. If \a filePath is specified, it will be used for error reporting for
  583. the created object.
  584. This function calls
  585. \l {QtQml::Qt::createQmlObject()}{Qt.createQmlObject()} internally.
  586. \sa {Managing Dynamically Created Test Objects}
  587. */
  588. function createTemporaryQmlObject(qml, parent, filePath) {
  589. if (typeof qml !== "string") {
  590. qtest_results.fail("First argument must be a string of QML; actual type is " + typeof qml,
  591. util.callerFile(), util.callerLine());
  592. throw new Error("QtQuickTest::fail");
  593. }
  594. if (!parent || typeof parent !== "object") {
  595. qtest_results.fail("Second argument must be a valid parent object; actual type is " + typeof parent,
  596. util.callerFile(), util.callerLine());
  597. throw new Error("QtQuickTest::fail");
  598. }
  599. if (filePath !== undefined && typeof filePath !== "string") {
  600. qtest_results.fail("Third argument must be a file path string; actual type is " + typeof filePath,
  601. util.callerFile(), util.callerLine());
  602. throw new Error("QtQuickTest::fail");
  603. }
  604. let object = Qt.createQmlObject(qml, parent, filePath);
  605. qtest_temporaryObjects.push(object);
  606. return object;
  607. }
  608. /*!
  609. \since 5.9
  610. \qmlmethod object TestCase::createTemporaryObject(Component component, object parent, object properties)
  611. This function dynamically creates a QML object from the given
  612. \a component with the specified optional \a parent and \a properties.
  613. The returned object will be destroyed (if it was not already) after
  614. \l cleanup() has finished executing, meaning that objects created with
  615. this function are guaranteed to be destroyed after each test,
  616. regardless of whether or not the tests fail.
  617. If there was an error while creating the object, \c null will be
  618. returned.
  619. This function calls
  620. \l {QtQml::Component::createObject()}{component.createObject()}
  621. internally.
  622. \sa {Managing Dynamically Created Test Objects}
  623. */
  624. function createTemporaryObject(component, parent, properties) {
  625. if (typeof component !== "object") {
  626. qtest_results.fail("First argument must be a Component; actual type is " + typeof component,
  627. util.callerFile(), util.callerLine());
  628. throw new Error("QtQuickTest::fail");
  629. }
  630. if (properties && typeof properties !== "object") {
  631. qtest_results.fail("Third argument must be an object; actual type is " + typeof properties,
  632. util.callerFile(), util.callerLine());
  633. throw new Error("QtQuickTest::fail");
  634. }
  635. if (parent === undefined)
  636. parent = null
  637. let object = component.createObject(parent, properties ? properties : ({}));
  638. qtest_temporaryObjects.push(object);
  639. return object;
  640. }
  641. /*!
  642. \internal
  643. Destroys all temporary objects that still exist.
  644. */
  645. function qtest_destroyTemporaryObjects() {
  646. for (let i = 0; i < qtest_temporaryObjects.length; ++i) {
  647. let temporaryObject = qtest_temporaryObjects[i];
  648. // ### the typeof check can be removed when QTBUG-57749 is fixed
  649. if (temporaryObject && typeof temporaryObject.destroy === "function")
  650. temporaryObject.destroy();
  651. }
  652. qtest_temporaryObjects = [];
  653. }
  654. /*! \internal */
  655. // Determine what is o.
  656. // Discussions and reference: http://philrathe.com/articles/equiv
  657. // Test suites: http://philrathe.com/tests/equiv
  658. // Author: Philippe Rathé <prathe@gmail.com>
  659. function qtest_typeof(o) {
  660. if (typeof o === "undefined") {
  661. return "undefined";
  662. // consider: typeof null === object
  663. } else if (o === null) {
  664. return "null";
  665. } else if (o.constructor === String) {
  666. return "string";
  667. } else if (o.constructor === Boolean) {
  668. return "boolean";
  669. } else if (o.constructor === Number) {
  670. if (isNaN(o)) {
  671. return "nan";
  672. } else {
  673. return "number";
  674. }
  675. // consider: typeof [] === object
  676. } else if (o instanceof Array) {
  677. return "array";
  678. // consider: typeof new Date() === object
  679. } else if (o instanceof Date) {
  680. return "date";
  681. // consider: /./ instanceof Object;
  682. // /./ instanceof RegExp;
  683. // typeof /./ === "function"; // => false in IE and Opera,
  684. // true in FF and Safari
  685. } else if (o instanceof RegExp) {
  686. return "regexp";
  687. } else if (typeof o === "object") {
  688. if ("mapFromItem" in o && "mapToItem" in o) {
  689. return "declarativeitem"; // @todo improve detection of declarative items
  690. } else if ("x" in o && "y" in o && "z" in o) {
  691. return "vector3d"; // Qt 3D vector
  692. }
  693. return "object";
  694. } else if (o instanceof Function) {
  695. return "function";
  696. } else {
  697. return undefined;
  698. }
  699. }
  700. /*! \internal */
  701. // Test for equality
  702. // Large parts contain sources from QUnit or http://philrathe.com
  703. // Discussions and reference: http://philrathe.com/articles/equiv
  704. // Test suites: http://philrathe.com/tests/equiv
  705. // Author: Philippe Rathé <prathe@gmail.com>
  706. function qtest_compareInternal(act, exp) {
  707. let success = false;
  708. if (act === exp) {
  709. success = true; // catch the most you can
  710. } else if (act === null || exp === null || typeof act === "undefined" || typeof exp === "undefined") {
  711. success = false; // don't lose time with error prone cases
  712. } else {
  713. let typeExp = qtest_typeof(exp), typeAct = qtest_typeof(act)
  714. if (typeExp !== typeAct) {
  715. // allow object vs string comparison (e.g. for colors)
  716. // else break on different types
  717. if ((typeExp === "string" && (typeAct === "object") || typeAct === "declarativeitem")
  718. || ((typeExp === "object" || typeExp === "declarativeitem") && typeAct === "string")) {
  719. success = (act == exp) // @disable-check M126
  720. }
  721. } else if (typeExp === "string" || typeExp === "boolean" ||
  722. typeExp === "null" || typeExp === "undefined") {
  723. if (exp instanceof act.constructor || act instanceof exp.constructor) {
  724. // to catch short annotaion VS 'new' annotation of act declaration
  725. // e.g. let i = 1;
  726. // let j = new Number(1);
  727. success = (act == exp) // @disable-check M126
  728. } else {
  729. success = (act === exp)
  730. }
  731. } else if (typeExp === "nan") {
  732. success = isNaN(act);
  733. } else if (typeExp === "number") {
  734. // Use act fuzzy compare if the two values are floats
  735. if (Math.abs(act - exp) <= 0.00001) {
  736. success = true
  737. }
  738. } else if (typeExp === "array") {
  739. success = qtest_compareInternalArrays(act, exp)
  740. } else if (typeExp === "object") {
  741. success = qtest_compareInternalObjects(act, exp)
  742. } else if (typeExp === "declarativeitem") {
  743. success = qtest_compareInternalObjects(act, exp) // @todo improve comparison of declarative items
  744. } else if (typeExp === "vector3d") {
  745. success = (Math.abs(act.x - exp.x) <= 0.00001 &&
  746. Math.abs(act.y - exp.y) <= 0.00001 &&
  747. Math.abs(act.z - exp.z) <= 0.00001)
  748. } else if (typeExp === "date") {
  749. success = (act.valueOf() === exp.valueOf())
  750. } else if (typeExp === "regexp") {
  751. success = (act.source === exp.source && // the regex itself
  752. act.global === exp.global && // and its modifers (gmi) ...
  753. act.ignoreCase === exp.ignoreCase &&
  754. act.multiline === exp.multiline)
  755. }
  756. }
  757. return success
  758. }
  759. /*! \internal */
  760. function qtest_compareInternalObjects(act, exp) {
  761. let i;
  762. let eq = true; // unless we can proove it
  763. let aProperties = [], bProperties = []; // collection of strings
  764. // comparing constructors is more strict than using instanceof
  765. if (act.constructor !== exp.constructor) {
  766. return false;
  767. }
  768. for (i in act) { // be strict: don't ensures hasOwnProperty and go deep
  769. aProperties.push(i); // collect act's properties
  770. if (!qtest_compareInternal(act[i], exp[i])) {
  771. eq = false;
  772. break;
  773. }
  774. }
  775. for (i in exp) {
  776. bProperties.push(i); // collect exp's properties
  777. }
  778. if (aProperties.length === 0 && bProperties.length === 0) { // at least a special case for QUrl
  779. return eq && (JSON.stringify(act) === JSON.stringify(exp));
  780. }
  781. // Ensures identical properties name
  782. return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort());
  783. }
  784. /*! \internal */
  785. function qtest_compareInternalArrays(actual, expected) {
  786. if (actual.length !== expected.length) {
  787. return false
  788. }
  789. for (let i = 0, len = actual.length; i < len; i++) {
  790. if (!qtest_compareInternal(actual[i], expected[i])) {
  791. return false
  792. }
  793. }
  794. return true
  795. }
  796. /*!
  797. \qmlmethod TestCase::compare(actual, expected, message = "")
  798. Fails the current test case if \a actual is not the same as
  799. \a expected, and displays the optional \a message. Similar
  800. to \c{QCOMPARE(actual, expected)} in C++.
  801. \sa tryCompare(), fuzzyCompare
  802. */
  803. function compare(actual, expected, msg) {
  804. let act = qtest_results.stringify(actual)
  805. let exp = qtest_results.stringify(expected)
  806. let success = qtest_compareInternal(actual, expected)
  807. if (msg === undefined) {
  808. if (success)
  809. msg = "COMPARE()"
  810. else
  811. msg = "Compared values are not the same"
  812. }
  813. if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine())) {
  814. throw new Error("QtQuickTest::fail")
  815. }
  816. }
  817. /*!
  818. \qmlmethod TestCase::fuzzyCompare(actual, expected, delta, message = "")
  819. Fails the current test case if the difference betwen \a actual and \a expected
  820. is greater than \a delta, and displays the optional \a message. Similar
  821. to \c{qFuzzyCompare(actual, expected)} in C++ but with a required \a delta value.
  822. This function can also be used for color comparisons if both the \a actual and
  823. \a expected values can be converted into color values. If any of the differences
  824. for RGBA channel values are greater than \a delta, the test fails.
  825. \sa tryCompare(), compare()
  826. */
  827. function fuzzyCompare(actual, expected, delta, msg) {
  828. if (delta === undefined)
  829. qtest_fail("A delta value is required for fuzzyCompare", 2)
  830. let success = qtest_results.fuzzyCompare(actual, expected, delta)
  831. if (msg === undefined) {
  832. if (success)
  833. msg = "FUZZYCOMPARE()"
  834. else
  835. msg = "Compared values are not the same with delta(" + delta + ")"
  836. }
  837. if (!qtest_results.compare(success, msg, actual, expected, util.callerFile(), util.callerLine())) {
  838. throw new Error("QtQuickTest::fail")
  839. }
  840. }
  841. /*!
  842. \qmlmethod object TestCase::grabImage(item)
  843. Returns a snapshot image object of the given \a item.
  844. The returned image object has the following properties:
  845. \list
  846. \li width Returns the width of the underlying image (since 5.10)
  847. \li height Returns the height of the underlying image (since 5.10)
  848. \li size Returns the size of the underlying image (since 5.10)
  849. \endlist
  850. Additionally, the returned image object has the following methods:
  851. \list
  852. \li \c {red(x, y)} Returns the red channel value of the pixel at \e x, \e y position
  853. \li \c {green(x, y)} Returns the green channel value of the pixel at \e x, \e y position
  854. \li \c {blue(x, y)} Returns the blue channel value of the pixel at \e x, \e y position
  855. \li \c {alpha(x, y)} Returns the alpha channel value of the pixel at \e x, \e y position
  856. \li \c {pixel(x, y)} Returns the color value of the pixel at \e x, \e y position
  857. \li \c {equals(image)} Returns \c true if this image is identical to \e image -
  858. see \l QImage::operator== (since 5.6)
  859. For example:
  860. \code
  861. let image = grabImage(rect);
  862. compare(image.red(10, 10), 255);
  863. compare(image.pixel(20, 20), Qt.rgba(255, 0, 0, 255));
  864. rect.width += 10;
  865. let newImage = grabImage(rect);
  866. verify(!newImage.equals(image));
  867. \endcode
  868. \li \c {save(path)} Saves the image to the given \e path. If the image cannot
  869. be saved, an exception will be thrown. (since 5.10)
  870. This can be useful to perform postmortem analysis on failing tests, for
  871. example:
  872. \code
  873. let image = grabImage(rect);
  874. try {
  875. compare(image.width, 100);
  876. } catch (ex) {
  877. image.save("debug.png");
  878. throw ex;
  879. }
  880. \endcode
  881. \endlist
  882. */
  883. function grabImage(item) {
  884. return qtest_results.grabImage(item);
  885. }
  886. /*!
  887. \since 5.4
  888. \qmlmethod QtObject TestCase::findChild(parent, objectName)
  889. Returns the first child of \a parent with \a objectName, or \c null if
  890. no such item exists. Both visual and non-visual children are searched
  891. recursively, with visual children being searched first.
  892. \code
  893. compare(findChild(item, "childObject"), expectedChildObject);
  894. \endcode
  895. */
  896. function findChild(parent, objectName) {
  897. // First, search the visual item hierarchy.
  898. let child = qtest_findVisualChild(parent, objectName);
  899. if (child)
  900. return child;
  901. // If it's not a visual child, it might be a QObject child.
  902. return qtest_results.findChild(parent, objectName);
  903. }
  904. /*! \internal */
  905. function qtest_findVisualChild(parent, objectName) {
  906. if (!parent || parent.children === undefined)
  907. return null;
  908. for (let i = 0; i < parent.children.length; ++i) {
  909. // Is this direct child of ours the child we're after?
  910. let child = parent.children[i];
  911. if (child.objectName === objectName)
  912. return child;
  913. }
  914. for (let i = 0; i < parent.children.length; ++i) {
  915. // Try the direct child's children.
  916. let child = qtest_findVisualChild(parent.children[i], objectName);
  917. if (child)
  918. return child;
  919. }
  920. return null;
  921. }
  922. /*!
  923. \qmlmethod TestCase::tryCompare(obj, property, expected, timeout = 5000, message = "")
  924. Fails the current test case if the specified \a property on \a obj
  925. is not the same as \a expected, and displays the optional \a message.
  926. The test will be retried multiple times until the
  927. \a timeout (in milliseconds) is reached.
  928. This function is intended for testing applications where a property
  929. changes value based on asynchronous events. Use compare() for testing
  930. synchronous property changes.
  931. \code
  932. tryCompare(img, "status", BorderImage.Ready)
  933. compare(img.width, 120)
  934. compare(img.height, 120)
  935. compare(img.horizontalTileMode, BorderImage.Stretch)
  936. compare(img.verticalTileMode, BorderImage.Stretch)
  937. \endcode
  938. SignalSpy::wait() provides an alternative method to wait for a
  939. signal to be emitted.
  940. \sa compare(), SignalSpy::wait()
  941. */
  942. function tryCompare(obj, prop, ...args) {
  943. if (typeof(prop) !== "string" && typeof(prop) !== "number") {
  944. qtest_results.fail("A property name as string or index is required for tryCompare",
  945. util.callerFile(), util.callerLine())
  946. throw new Error("QtQuickTest::fail")
  947. }
  948. if (args.length === 0) {
  949. qtest_results.fail("A value is required for tryCompare",
  950. util.callerFile(), util.callerLine())
  951. throw new Error("QtQuickTest::fail")
  952. }
  953. let [value, timeout, msg] = args
  954. if (timeout !== undefined && typeof(timeout) !== "number") {
  955. qtest_results.fail("timeout should be a number",
  956. util.callerFile(), util.callerLine())
  957. throw new Error("QtQuickTest::fail")
  958. }
  959. if (!timeout)
  960. timeout = 5000
  961. if (msg === undefined)
  962. msg = "property " + prop
  963. if (!qtest_compareInternal(obj[prop], value))
  964. wait(0)
  965. let i = 0
  966. while (i < timeout && !qtest_compareInternal(obj[prop], value)) {
  967. wait(50)
  968. i += 50
  969. }
  970. let actual = obj[prop]
  971. let act = qtest_results.stringify(actual)
  972. let exp = qtest_results.stringify(value)
  973. let success = qtest_compareInternal(actual, value)
  974. if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine()))
  975. throw new Error("QtQuickTest::fail")
  976. }
  977. /*!
  978. \qmlmethod TestCase::skip(message = "")
  979. Skips the current test case and prints the optional \a message.
  980. If this is a data-driven test, then only the current row is skipped.
  981. Similar to \c{QSKIP(message)} in C++.
  982. */
  983. function skip(msg) {
  984. if (msg === undefined)
  985. msg = ""
  986. qtest_results.skip(msg, util.callerFile(), util.callerLine())
  987. throw new Error("QtQuickTest::skip")
  988. }
  989. /*!
  990. \qmlmethod TestCase::expectFail(tag, message)
  991. In a data-driven test, marks the row associated with \a tag as
  992. expected to fail. When the fail occurs, display the \a message,
  993. abort the test, and mark the test as passing. Similar to
  994. \c{QEXPECT_FAIL(tag, message, Abort)} in C++.
  995. If the test is not data-driven, then \a tag must be set to
  996. an empty string.
  997. \sa expectFailContinue()
  998. */
  999. function expectFail(tag, msg) {
  1000. if (tag === undefined) {
  1001. warn("tag argument missing from expectFail()")
  1002. tag = ""
  1003. }
  1004. if (msg === undefined) {
  1005. warn("message argument missing from expectFail()")
  1006. msg = ""
  1007. }
  1008. if (!qtest_results.expectFail(tag, msg, util.callerFile(), util.callerLine()))
  1009. throw new Error("QtQuickTest::expectFail")
  1010. }
  1011. /*!
  1012. \qmlmethod TestCase::expectFailContinue(tag, message)
  1013. In a data-driven test, marks the row associated with \a tag as
  1014. expected to fail. When the fail occurs, display the \a message,
  1015. and then continue the test. Similar to
  1016. \c{QEXPECT_FAIL(tag, message, Continue)} in C++.
  1017. If the test is not data-driven, then \a tag must be set to
  1018. an empty string.
  1019. \sa expectFail()
  1020. */
  1021. function expectFailContinue(tag, msg) {
  1022. if (tag === undefined) {
  1023. warn("tag argument missing from expectFailContinue()")
  1024. tag = ""
  1025. }
  1026. if (msg === undefined) {
  1027. warn("message argument missing from expectFailContinue()")
  1028. msg = ""
  1029. }
  1030. if (!qtest_results.expectFailContinue(tag, msg, util.callerFile(), util.callerLine()))
  1031. throw new Error("QtQuickTest::expectFail")
  1032. }
  1033. /*!
  1034. \qmlmethod TestCase::warn(message)
  1035. Prints \a message as a warning message. Similar to
  1036. \c{qWarning(message)} in C++.
  1037. \sa ignoreWarning()
  1038. */
  1039. function warn(msg) {
  1040. if (msg === undefined)
  1041. msg = ""
  1042. qtest_results.warn(msg, util.callerFile(), util.callerLine());
  1043. }
  1044. /*!
  1045. \qmlmethod TestCase::ignoreWarning(message)
  1046. Marks \a message as an ignored warning message. When it occurs,
  1047. the warning will not be printed and the test passes. If the message
  1048. does not occur, then the test will fail. Similar to
  1049. \c{QTest::ignoreMessage(QtWarningMsg, message)} in C++.
  1050. Since Qt 5.12, \a message can be either a string, or a regular
  1051. expression providing a pattern of messages to ignore.
  1052. For example, the following snippet will ignore a string warning message:
  1053. \qml
  1054. ignoreWarning("Something sort of bad happened")
  1055. \endqml
  1056. And the following snippet will ignore a regular expression matching a
  1057. number of possible warning messages:
  1058. \qml
  1059. ignoreWarning(new RegExp("[0-9]+ bad things happened"))
  1060. \endqml
  1061. \note Despite being a JavaScript RegExp object, it will not be
  1062. interpreted as such; instead, the pattern will be passed to
  1063. \l QRegularExpression.
  1064. \sa warn()
  1065. */
  1066. function ignoreWarning(msg) {
  1067. if (msg === undefined)
  1068. msg = ""
  1069. qtest_results.ignoreWarning(msg)
  1070. }
  1071. /*!
  1072. \qmlmethod TestCase::failOnWarning(message)
  1073. \since 6.3
  1074. Appends a test failure to the test log for each warning that matches
  1075. \a message. The test function will continue execution when a failure
  1076. is added.
  1077. \a message can be either a string, or a regular expression providing a
  1078. pattern of messages. In the latter case, for each warning encountered,
  1079. the first pattern that matches will cause a failure, and the remaining
  1080. patterns will be ignored.
  1081. All patterns are cleared at the end of each test function.
  1082. For example, the following snippet will fail a test if a warning with
  1083. the text "Something bad happened" is produced:
  1084. \qml
  1085. failOnWarning("Something bad happened")
  1086. \endqml
  1087. The following snippet will fail a test if any warning matching the
  1088. given pattern is encountered:
  1089. \qml
  1090. failOnWarning(/[0-9]+ bad things happened/)
  1091. \endqml
  1092. To fail every test that triggers a given warning, pass a suitable regular
  1093. expression to this function in \l init():
  1094. \qml
  1095. function init() {
  1096. failOnWarning(/.?/)
  1097. }
  1098. \endqml
  1099. \note Despite being a JavaScript RegExp object, it will not be
  1100. interpreted as such; instead, the pattern will be passed to \l
  1101. QRegularExpression.
  1102. \note ignoreMessage() takes precedence over this function, so any
  1103. warnings that match a pattern given to both \c ignoreMessage() and \c
  1104. failOnWarning() will be ignored.
  1105. \sa QTest::failOnWarning(), warn()
  1106. */
  1107. function failOnWarning(msg) {
  1108. if (msg === undefined)
  1109. msg = ""
  1110. qtest_results.failOnWarning(msg)
  1111. }
  1112. /*!
  1113. \qmlmethod TestCase::wait(ms)
  1114. Waits for \a ms milliseconds while processing Qt events.
  1115. \note This methods uses a precise timer to do the actual waiting. The
  1116. event you are waiting for may not. In particular, any animations as
  1117. well as the \l{Timer} QML type can use either precise or coarse
  1118. timers, depending on various factors. For a coarse timer you have
  1119. to expect a drift of around 5% in relation to the precise timer used
  1120. by TestCase::wait(). Qt cannot give hard guarantees on the drift,
  1121. though, because the operating system usually doesn't offer hard
  1122. guarantees on timers.
  1123. \sa sleep(), waitForRendering(), Qt::TimerType
  1124. */
  1125. function wait(ms) {
  1126. qtest_results.wait(ms)
  1127. }
  1128. /*!
  1129. \qmlmethod TestCase::waitForRendering(item, timeout = 5000)
  1130. Waits for \a timeout milliseconds or until the \a item is rendered by the renderer.
  1131. Returns true if \c item is rendered in \a timeout milliseconds, otherwise returns false.
  1132. The default \a timeout value is 5000.
  1133. \sa sleep(), wait()
  1134. */
  1135. function waitForRendering(item, timeout) {
  1136. if (timeout === undefined)
  1137. timeout = 5000
  1138. if (!qtest_verifyItem(item, "waitForRendering"))
  1139. return
  1140. return qtest_results.waitForRendering(item, timeout)
  1141. }
  1142. /*!
  1143. \qmlmethod TestCase::sleep(ms)
  1144. Sleeps for \a ms milliseconds without processing Qt events.
  1145. \sa wait(), waitForRendering()
  1146. */
  1147. function sleep(ms) {
  1148. qtest_results.sleep(ms)
  1149. }
  1150. /*!
  1151. \qmlmethod TestCase::keyPress(key, modifiers = Qt.NoModifier, delay = -1)
  1152. Simulates pressing a \a key with optional \a modifiers on the currently
  1153. focused item. If \a delay is larger than 0, the test will wait for
  1154. \a delay milliseconds.
  1155. The event will be sent to the TestCase window or, in case of multiple windows,
  1156. to the current active window. See \l QGuiApplication::focusWindow() for more details.
  1157. \b{Note:} At some point you should release the key using keyRelease().
  1158. \sa keyRelease(), keyClick()
  1159. */
  1160. function keyPress(key, modifiers, delay) {
  1161. if (modifiers === undefined)
  1162. modifiers = Qt.NoModifier
  1163. if (delay === undefined)
  1164. delay = -1
  1165. if (typeof(key) === "string" && key.length === 1) {
  1166. if (!qtest_events.keyPressChar(key, modifiers, delay))
  1167. qtest_fail("window not shown", 2)
  1168. } else {
  1169. if (!qtest_events.keyPress(key, modifiers, delay))
  1170. qtest_fail("window not shown", 2)
  1171. }
  1172. }
  1173. /*!
  1174. \qmlmethod TestCase::keyRelease(key, modifiers = Qt.NoModifier, delay = -1)
  1175. Simulates releasing a \a key with optional \a modifiers on the currently
  1176. focused item. If \a delay is larger than 0, the test will wait for
  1177. \a delay milliseconds.
  1178. The event will be sent to the TestCase window or, in case of multiple windows,
  1179. to the current active window. See \l QGuiApplication::focusWindow() for more details.
  1180. \sa keyPress(), keyClick()
  1181. */
  1182. function keyRelease(key, modifiers, delay) {
  1183. if (modifiers === undefined)
  1184. modifiers = Qt.NoModifier
  1185. if (delay === undefined)
  1186. delay = -1
  1187. if (typeof(key) === "string" && key.length === 1) {
  1188. if (!qtest_events.keyReleaseChar(key, modifiers, delay))
  1189. qtest_fail("window not shown", 2)
  1190. } else {
  1191. if (!qtest_events.keyRelease(key, modifiers, delay))
  1192. qtest_fail("window not shown", 2)
  1193. }
  1194. }
  1195. /*!
  1196. \qmlmethod TestCase::keyClick(key, modifiers = Qt.NoModifier, delay = -1)
  1197. Simulates clicking of \a key with optional \a modifiers on the currently
  1198. focused item. If \a delay is larger than 0, the test will wait for
  1199. \a delay milliseconds.
  1200. The event will be sent to the TestCase window or, in case of multiple windows,
  1201. to the current active window. See \l QGuiApplication::focusWindow() for more details.
  1202. \sa keyPress(), keyRelease()
  1203. */
  1204. function keyClick(key, modifiers, delay) {
  1205. if (modifiers === undefined)
  1206. modifiers = Qt.NoModifier
  1207. if (delay === undefined)
  1208. delay = -1
  1209. if (typeof(key) === "string" && key.length === 1) {
  1210. if (!qtest_events.keyClickChar(key, modifiers, delay))
  1211. qtest_fail("window not shown", 2)
  1212. } else {
  1213. if (!qtest_events.keyClick(key, modifiers, delay))
  1214. qtest_fail("window not shown", 2)
  1215. }
  1216. }
  1217. /*!
  1218. \since 5.10
  1219. \qmlmethod TestCase::keySequence(keySequence)
  1220. Simulates typing of \a keySequence. The key sequence can be set
  1221. to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, or
  1222. it can be described with a string containing a sequence of up to four key
  1223. presses.
  1224. Each event shall be sent to the TestCase window or, in case of multiple windows,
  1225. to the current active window. See \l QGuiApplication::focusWindow() for more details.
  1226. \sa keyPress(), keyRelease(), {GNU Emacs Style Key Sequences},
  1227. {QtQuick::Shortcut::sequence}{Shortcut.sequence}
  1228. */
  1229. function keySequence(keySequence) {
  1230. if (!qtest_events.keySequence(keySequence))
  1231. qtest_fail("window not shown", 2)
  1232. }
  1233. /*!
  1234. \qmlmethod TestCase::mousePress(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
  1235. Simulates pressing a mouse \a button with optional \a modifiers
  1236. on an \a item. The position is defined by \a x and \a y.
  1237. If \a x or \a y are not defined the position will be the center of \a item.
  1238. If \a delay is specified, the test will wait for the specified amount of
  1239. milliseconds before the press.
  1240. The position given by \a x and \a y is transformed from the co-ordinate
  1241. system of \a item into window co-ordinates and then delivered.
  1242. If \a item is obscured by another item, or a child of \a item occupies
  1243. that position, then the event will be delivered to the other item instead.
  1244. \sa mouseRelease(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel()
  1245. */
  1246. function mousePress(item, x, y, button, modifiers, delay) {
  1247. if (!qtest_verifyItem(item, "mousePress"))
  1248. return
  1249. if (button === undefined)
  1250. button = Qt.LeftButton
  1251. if (modifiers === undefined)
  1252. modifiers = Qt.NoModifier
  1253. if (delay === undefined)
  1254. delay = -1
  1255. if (x === undefined)
  1256. x = item.width / 2
  1257. if (y === undefined)
  1258. y = item.height / 2
  1259. if (!qtest_events.mousePress(item, x, y, button, modifiers, delay))
  1260. qtest_fail("window not shown", 2)
  1261. }
  1262. /*!
  1263. \qmlmethod TestCase::mouseRelease(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
  1264. Simulates releasing a mouse \a button with optional \a modifiers
  1265. on an \a item. The position of the release is defined by \a x and \a y.
  1266. If \a x or \a y are not defined the position will be the center of \a item.
  1267. If \a delay is specified, the test will wait for the specified amount of
  1268. milliseconds before releasing the button.
  1269. The position given by \a x and \a y is transformed from the co-ordinate
  1270. system of \a item into window co-ordinates and then delivered.
  1271. If \a item is obscured by another item, or a child of \a item occupies
  1272. that position, then the event will be delivered to the other item instead.
  1273. \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel()
  1274. */
  1275. function mouseRelease(item, x, y, button, modifiers, delay) {
  1276. if (!qtest_verifyItem(item, "mouseRelease"))
  1277. return
  1278. if (button === undefined)
  1279. button = Qt.LeftButton
  1280. if (modifiers === undefined)
  1281. modifiers = Qt.NoModifier
  1282. if (delay === undefined)
  1283. delay = -1
  1284. if (x === undefined)
  1285. x = item.width / 2
  1286. if (y === undefined)
  1287. y = item.height / 2
  1288. if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay))
  1289. qtest_fail("window not shown", 2)
  1290. }
  1291. /*!
  1292. \qmlmethod TestCase::mouseDrag(item, x, y, dx, dy, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
  1293. Simulates dragging the mouse on an \a item with \a button pressed and optional \a modifiers
  1294. The initial drag position is defined by \a x and \a y,
  1295. and drag distance is defined by \a dx and \a dy. If \a delay is specified,
  1296. the test will wait for the specified amount of milliseconds before releasing the button.
  1297. The position given by \a x and \a y is transformed from the co-ordinate
  1298. system of \a item into window co-ordinates and then delivered.
  1299. If \a item is obscured by another item, or a child of \a item occupies
  1300. that position, then the event will be delivered to the other item instead.
  1301. \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseWheel()
  1302. */
  1303. function mouseDrag(item, x, y, dx, dy, button, modifiers, delay) {
  1304. if (!qtest_verifyItem(item, "mouseDrag"))
  1305. return
  1306. if (item.x === undefined || item.y === undefined)
  1307. return
  1308. if (button === undefined)
  1309. button = Qt.LeftButton
  1310. if (modifiers === undefined)
  1311. modifiers = Qt.NoModifier
  1312. if (delay === undefined)
  1313. delay = -1
  1314. let moveDelay = Math.max(1, delay === -1 ? qtest_events.defaultMouseDelay : delay)
  1315. // Divide dx and dy to have intermediate mouseMove while dragging
  1316. // Fractions of dx/dy need be superior to the dragThreshold
  1317. // to make the drag works though
  1318. let intermediateDx = Math.round(dx/3)
  1319. if (Math.abs(intermediateDx) < (util.dragThreshold + 1))
  1320. intermediateDx = 0
  1321. let intermediateDy = Math.round(dy/3)
  1322. if (Math.abs(intermediateDy) < (util.dragThreshold + 1))
  1323. intermediateDy = 0
  1324. mousePress(item, x, y, button, modifiers, delay)
  1325. // Trigger dragging by dragging past the drag threshold, but making sure to only drag
  1326. // along a certain axis if a distance greater than zero was given for that axis.
  1327. let dragTriggerXDistance = dx > 0 ? (util.dragThreshold + 1) : 0
  1328. let dragTriggerYDistance = dy > 0 ? (util.dragThreshold + 1) : 0
  1329. mouseMove(item, x + dragTriggerXDistance, y + dragTriggerYDistance, moveDelay, button, modifiers)
  1330. if (intermediateDx !== 0 || intermediateDy !== 0) {
  1331. mouseMove(item, x + intermediateDx, y + intermediateDy, moveDelay, button, modifiers)
  1332. mouseMove(item, x + 2*intermediateDx, y + 2*intermediateDy, moveDelay, button, modifiers)
  1333. }
  1334. mouseMove(item, x + dx, y + dy, moveDelay, button, modifiers)
  1335. mouseRelease(item, x + dx, y + dy, button, modifiers, delay)
  1336. }
  1337. /*!
  1338. \qmlmethod TestCase::mouseClick(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
  1339. Simulates clicking a mouse \a button with optional \a modifiers
  1340. on an \a item. The position of the click is defined by \a x and \a y.
  1341. If \a x and \a y are not defined the position will be the center of \a item.
  1342. If \a delay is specified, the test will wait for the specified amount of
  1343. milliseconds before pressing and before releasing the button.
  1344. The position given by \a x and \a y is transformed from the co-ordinate
  1345. system of \a item into window co-ordinates and then delivered.
  1346. If \a item is obscured by another item, or a child of \a item occupies
  1347. that position, then the event will be delivered to the other item instead.
  1348. \sa mousePress(), mouseRelease(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel()
  1349. */
  1350. function mouseClick(item, x, y, button, modifiers, delay) {
  1351. if (!qtest_verifyItem(item, "mouseClick"))
  1352. return
  1353. if (button === undefined)
  1354. button = Qt.LeftButton
  1355. if (modifiers === undefined)
  1356. modifiers = Qt.NoModifier
  1357. if (delay === undefined)
  1358. delay = -1
  1359. if (x === undefined)
  1360. x = item.width / 2
  1361. if (y === undefined)
  1362. y = item.height / 2
  1363. if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay))
  1364. qtest_fail("window not shown", 2)
  1365. }
  1366. /*!
  1367. \qmlmethod TestCase::mouseDoubleClickSequence(item, x = item.width / 2, y = item.height / 2, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
  1368. Simulates the full sequence of events generated by double-clicking a mouse
  1369. \a button with optional \a modifiers on an \a item.
  1370. This method reproduces the sequence of mouse events generated when a user makes
  1371. a double click: Press-Release-Press-DoubleClick-Release.
  1372. The position of the click is defined by \a x and \a y.
  1373. If \a x and \a y are not defined the position will be the center of \a item.
  1374. If \a delay is specified, the test will wait for the specified amount of
  1375. milliseconds before pressing and before releasing the button.
  1376. The position given by \a x and \a y is transformed from the co-ordinate
  1377. system of \a item into window co-ordinates and then delivered.
  1378. If \a item is obscured by another item, or a child of \a item occupies
  1379. that position, then the event will be delivered to the other item instead.
  1380. This QML method was introduced in Qt 5.5.
  1381. \sa mousePress(), mouseRelease(), mouseClick(), mouseMove(), mouseDrag(), mouseWheel()
  1382. */
  1383. function mouseDoubleClickSequence(item, x, y, button, modifiers, delay) {
  1384. if (!qtest_verifyItem(item, "mouseDoubleClickSequence"))
  1385. return
  1386. if (button === undefined)
  1387. button = Qt.LeftButton
  1388. if (modifiers === undefined)
  1389. modifiers = Qt.NoModifier
  1390. if (delay === undefined)
  1391. delay = -1
  1392. if (x === undefined)
  1393. x = item.width / 2
  1394. if (y === undefined)
  1395. y = item.height / 2
  1396. if (!qtest_events.mouseDoubleClickSequence(item, x, y, button, modifiers, delay))
  1397. qtest_fail("window not shown", 2)
  1398. }
  1399. /*!
  1400. \qmlmethod TestCase::mouseMove(item, x = item.width / 2, y = item.height / 2, delay = -1, buttons = Qt.NoButton)
  1401. Moves the mouse pointer to the position given by \a x and \a y within
  1402. \a item, while holding \a buttons if given. Since Qt 6.0, if \a x and
  1403. \a y are not defined, the position will be the center of \a item.
  1404. If a \a delay (in milliseconds) is given, the test will wait before
  1405. moving the mouse pointer.
  1406. The position given by \a x and \a y is transformed from the co-ordinate
  1407. system of \a item into window co-ordinates and then delivered.
  1408. If \a item is obscured by another item, or a child of \a item occupies
  1409. that position, then the event will be delivered to the other item instead.
  1410. \sa mousePress(), mouseRelease(), mouseClick(), mouseDoubleClickSequence(), mouseDrag(), mouseWheel()
  1411. */
  1412. function mouseMove(item, x, y, delay, buttons, modifiers) {
  1413. if (!qtest_verifyItem(item, "mouseMove"))
  1414. return
  1415. if (delay === undefined)
  1416. delay = -1
  1417. if (buttons === undefined)
  1418. buttons = Qt.NoButton
  1419. if (modifiers === undefined)
  1420. modifiers = Qt.NoModifiers
  1421. if (x === undefined)
  1422. x = item.width / 2
  1423. if (y === undefined)
  1424. y = item.height / 2
  1425. if (!qtest_events.mouseMove(item, x, y, delay, buttons, modifiers))
  1426. qtest_fail("window not shown", 2)
  1427. }
  1428. /*!
  1429. \qmlmethod TestCase::mouseWheel(item, x, y, xDelta, yDelta, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1)
  1430. Simulates rotating the mouse wheel on an \a item with \a button pressed and optional \a modifiers.
  1431. The position of the wheel event is defined by \a x and \a y.
  1432. If \a delay is specified, the test will wait for the specified amount of milliseconds before releasing the button.
  1433. The position given by \a x and \a y is transformed from the co-ordinate
  1434. system of \a item into window co-ordinates and then delivered.
  1435. If \a item is obscured by another item, or a child of \a item occupies
  1436. that position, then the event will be delivered to the other item instead.
  1437. The \a xDelta and \a yDelta contain the wheel rotation distance in eighths of a degree. see \l QWheelEvent::angleDelta() for more details.
  1438. \sa mousePress(), mouseClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseDrag(), QWheelEvent::angleDelta()
  1439. */
  1440. function mouseWheel(item, x, y, xDelta, yDelta, buttons, modifiers, delay) {
  1441. if (!qtest_verifyItem(item, "mouseWheel"))
  1442. return
  1443. if (delay === undefined)
  1444. delay = -1
  1445. if (buttons === undefined)
  1446. buttons = Qt.NoButton
  1447. if (modifiers === undefined)
  1448. modifiers = Qt.NoModifier
  1449. if (xDelta === undefined)
  1450. xDelta = 0
  1451. if (yDelta === undefined)
  1452. yDelta = 0
  1453. if (!qtest_events.mouseWheel(item, x, y, buttons, modifiers, xDelta, yDelta, delay))
  1454. qtest_fail("window not shown", 2)
  1455. }
  1456. /*!
  1457. \qmlmethod TouchEventSequence TestCase::touchEvent(object item)
  1458. \since 5.9
  1459. Begins a sequence of touch events through a simulated touchscreen (QPointingDevice).
  1460. Events are delivered to the window containing \a item.
  1461. The returned object is used to enumerate events to be delivered through a single
  1462. QTouchEvent. Touches are delivered to the window containing the TestCase unless
  1463. otherwise specified.
  1464. \code
  1465. Rectangle {
  1466. width: 640; height: 480
  1467. MultiPointTouchArea {
  1468. id: area
  1469. anchors.fill: parent
  1470. property bool touched: false
  1471. onPressed: touched = true
  1472. }
  1473. TestCase {
  1474. name: "ItemTests"
  1475. when: windowShown
  1476. id: test1
  1477. function test_touch() {
  1478. let touch = touchEvent(area);
  1479. touch.press(0, area, 10, 10);
  1480. touch.commit();
  1481. verify(area.touched);
  1482. }
  1483. }
  1484. }
  1485. \endcode
  1486. \sa TouchEventSequence::press(), TouchEventSequence::move(), TouchEventSequence::release(), TouchEventSequence::stationary(), TouchEventSequence::commit(), QInputDevice::DeviceType
  1487. */
  1488. function touchEvent(item) {
  1489. if (!qtest_verifyItem(item, "touchEvent"))
  1490. return
  1491. return {
  1492. _defaultItem: item,
  1493. _sequence: qtest_events.touchEvent(item),
  1494. press: function (id, target, x, y) {
  1495. if (!target)
  1496. target = this._defaultItem;
  1497. if (id === undefined)
  1498. qtest_fail("No id given to TouchEventSequence::press", 1);
  1499. if (x === undefined)
  1500. x = target.width / 2;
  1501. if (y === undefined)
  1502. y = target.height / 2;
  1503. this._sequence.press(id, target, x, y);
  1504. return this;
  1505. },
  1506. move: function (id, target, x, y) {
  1507. if (!target)
  1508. target = this._defaultItem;
  1509. if (id === undefined)
  1510. qtest_fail("No id given to TouchEventSequence::move", 1);
  1511. if (x === undefined)
  1512. x = target.width / 2;
  1513. if (y === undefined)
  1514. y = target.height / 2;
  1515. this._sequence.move(id, target, x, y);
  1516. return this;
  1517. },
  1518. stationary: function (id) {
  1519. if (id === undefined)
  1520. qtest_fail("No id given to TouchEventSequence::stationary", 1);
  1521. this._sequence.stationary(id);
  1522. return this;
  1523. },
  1524. release: function (id, target, x, y) {
  1525. if (!target)
  1526. target = this._defaultItem;
  1527. if (id === undefined)
  1528. qtest_fail("No id given to TouchEventSequence::release", 1);
  1529. if (x === undefined)
  1530. x = target.width / 2;
  1531. if (y === undefined)
  1532. y = target.height / 2;
  1533. this._sequence.release(id, target, x, y);
  1534. return this;
  1535. },
  1536. commit: function () {
  1537. this._sequence.commit();
  1538. return this;
  1539. }
  1540. };
  1541. }
  1542. // Functions that can be overridden in subclasses for init/cleanup duties.
  1543. /*!
  1544. \qmlmethod TestCase::initTestCase()
  1545. This function is called before any other test functions in the
  1546. \l TestCase type. The default implementation does nothing.
  1547. The application can provide its own implementation to perform
  1548. test case initialization.
  1549. \sa cleanupTestCase(), init()
  1550. */
  1551. function initTestCase() {}
  1552. /*!
  1553. \qmlmethod TestCase::cleanupTestCase()
  1554. This function is called after all other test functions in the
  1555. \l TestCase type have completed. The default implementation
  1556. does nothing. The application can provide its own implementation
  1557. to perform test case cleanup.
  1558. \sa initTestCase(), cleanup()
  1559. */
  1560. function cleanupTestCase() {}
  1561. /*!
  1562. \qmlmethod TestCase::init()
  1563. This function is called before each test function that is
  1564. executed in the \l TestCase type. The default implementation
  1565. does nothing. The application can provide its own implementation
  1566. to perform initialization before each test function.
  1567. \sa cleanup(), initTestCase()
  1568. */
  1569. function init() {}
  1570. /*!
  1571. \qmlmethod TestCase::cleanup()
  1572. This function is called after each test function that is
  1573. executed in the \l TestCase type. The default implementation
  1574. does nothing. The application can provide its own implementation
  1575. to perform cleanup after each test function.
  1576. \sa init(), cleanupTestCase()
  1577. */
  1578. function cleanup() {}
  1579. /*! \internal */
  1580. function qtest_verifyItem(item, method) {
  1581. try {
  1582. if (!(item instanceof Item) &&
  1583. !(item instanceof Window)) {
  1584. // it's a QObject, but not a type
  1585. qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 2);
  1586. return false;
  1587. }
  1588. } catch (e) { // it's not a QObject
  1589. qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 3);
  1590. return false;
  1591. }
  1592. return true;
  1593. }
  1594. /*! \internal */
  1595. function qtest_runInternal(prop, arg) {
  1596. try {
  1597. qtest_testCaseResult = testCase[prop](arg)
  1598. } catch (e) {
  1599. qtest_testCaseResult = []
  1600. if (e.message.indexOf("QtQuickTest::") !== 0) {
  1601. // Test threw an unrecognized exception - fail.
  1602. qtest_results.fail("Uncaught exception: " + e.message,
  1603. e.fileName, e.lineNumber)
  1604. }
  1605. }
  1606. return !qtest_results.failed
  1607. }
  1608. /*! \internal */
  1609. function qtest_runFunction(prop, arg) {
  1610. qtest_runInternal("init")
  1611. if (!qtest_results.skipped) {
  1612. qtest_runInternal(prop, arg)
  1613. qtest_results.finishTestData()
  1614. qtest_runInternal("cleanup")
  1615. qtest_destroyTemporaryObjects()
  1616. // wait(0) will call processEvents() so objects marked for deletion
  1617. // in the test function will be deleted.
  1618. wait(0)
  1619. qtest_results.finishTestDataCleanup()
  1620. }
  1621. }
  1622. /*! \internal */
  1623. function qtest_runBenchmarkFunction(prop, arg) {
  1624. qtest_results.startMeasurement()
  1625. do {
  1626. qtest_results.beginDataRun()
  1627. do {
  1628. // Run the initialization function.
  1629. qtest_runInternal("init")
  1630. if (qtest_results.skipped)
  1631. break
  1632. // Execute the benchmark function.
  1633. if (prop.indexOf("benchmark_once_") !== 0)
  1634. qtest_results.startBenchmark(TestResult.RepeatUntilValidMeasurement, qtest_results.dataTag)
  1635. else
  1636. qtest_results.startBenchmark(TestResult.RunOnce, qtest_results.dataTag)
  1637. while (!qtest_results.isBenchmarkDone()) {
  1638. let success = qtest_runInternal(prop, arg)
  1639. qtest_results.finishTestData()
  1640. if (!success)
  1641. break
  1642. qtest_results.nextBenchmark()
  1643. }
  1644. qtest_results.stopBenchmark()
  1645. // Run the cleanup function.
  1646. qtest_runInternal("cleanup")
  1647. qtest_results.finishTestDataCleanup()
  1648. // wait(0) will call processEvents() so objects marked for deletion
  1649. // in the test function will be deleted.
  1650. wait(0)
  1651. } while (!qtest_results.measurementAccepted())
  1652. qtest_results.endDataRun()
  1653. } while (qtest_results.needsMoreMeasurements())
  1654. }
  1655. /*! \internal */
  1656. function qtest_run() {
  1657. if (!when || completed || running || !qtest_componentCompleted)
  1658. return;
  1659. if (!TestLogger.log_can_start_test(qtest_testId)) {
  1660. console.error("Interleaved test execution detected. This shouldn't happen")
  1661. return;
  1662. }
  1663. if (TestLogger.log_start_test(qtest_testId)) {
  1664. qtest_results.reset()
  1665. qtest_results.testCaseName = name
  1666. qtest_results.startLogging()
  1667. } else {
  1668. qtest_results.testCaseName = name
  1669. }
  1670. running = true
  1671. // Check the run list to see if this class is mentioned.
  1672. let checkNames = false
  1673. let testsToRun = {} // explicitly provided function names to run and their tags for data-driven tests
  1674. if (qtest_results.functionsToRun.length > 0) {
  1675. checkNames = true
  1676. let found = false
  1677. if (name.length > 0) {
  1678. for (let index in qtest_results.functionsToRun) {
  1679. let caseFuncName = qtest_results.functionsToRun[index]
  1680. if (caseFuncName.indexOf(name + "::") !== 0)
  1681. continue
  1682. found = true
  1683. let funcName = caseFuncName.substring(name.length + 2)
  1684. if (!(funcName in testsToRun))
  1685. testsToRun[funcName] = []
  1686. let tagName = qtest_results.tagsToRun[index]
  1687. if (tagName.length > 0) // empty tags mean run all rows
  1688. testsToRun[funcName].push(tagName)
  1689. }
  1690. }
  1691. if (!found) {
  1692. completed = true
  1693. if (!TestLogger.log_complete_test(qtest_testId)) {
  1694. qtest_results.stopLogging()
  1695. Qt.quit()
  1696. }
  1697. qtest_results.testCaseName = ""
  1698. return
  1699. }
  1700. }
  1701. // Run the initTestCase function.
  1702. qtest_results.functionName = "initTestCase"
  1703. let runTests = true
  1704. if (!qtest_runInternal("initTestCase"))
  1705. runTests = false
  1706. qtest_results.finishTestData()
  1707. qtest_results.finishTestDataCleanup()
  1708. qtest_results.finishTestFunction()
  1709. // Run the test methods.
  1710. let testList = []
  1711. if (runTests) {
  1712. for (let prop in testCase) {
  1713. if (prop.indexOf("test_") !== 0 && prop.indexOf("benchmark_") !== 0)
  1714. continue
  1715. let tail = prop.lastIndexOf("_data");
  1716. if (tail !== -1 && tail === (prop.length - 5))
  1717. continue
  1718. testList.push(prop)
  1719. }
  1720. testList.sort()
  1721. }
  1722. for (let index in testList) {
  1723. let prop = testList[index]
  1724. if (checkNames && !(prop in testsToRun))
  1725. continue
  1726. let datafunc = prop + "_data"
  1727. let isBenchmark = (prop.indexOf("benchmark_") === 0)
  1728. qtest_results.functionName = prop
  1729. if (!(datafunc in testCase))
  1730. datafunc = "init_data";
  1731. if (datafunc in testCase) {
  1732. if (qtest_runInternal(datafunc)) {
  1733. let table = qtest_testCaseResult
  1734. let haveData = false
  1735. let checkTags = (checkNames && testsToRun[prop].length > 0)
  1736. qtest_results.initTestTable()
  1737. for (let index in table) {
  1738. haveData = true
  1739. let row = table[index]
  1740. if (!row.tag)
  1741. row.tag = "row " + index // Must have something
  1742. if (checkTags) {
  1743. let tags = testsToRun[prop]
  1744. let tagIdx = tags.indexOf(row.tag)
  1745. if (tagIdx < 0)
  1746. continue
  1747. tags.splice(tagIdx, 1)
  1748. }
  1749. qtest_results.dataTag = row.tag
  1750. if (isBenchmark)
  1751. qtest_runBenchmarkFunction(prop, row)
  1752. else
  1753. qtest_runFunction(prop, row)
  1754. qtest_results.dataTag = ""
  1755. qtest_results.skipped = false
  1756. }
  1757. if (!haveData) {
  1758. if (datafunc === "init_data")
  1759. qtest_runFunction(prop, null, isBenchmark)
  1760. else
  1761. qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()"
  1762. , util.callerFile(), util.callerLine());
  1763. }
  1764. qtest_results.clearTestTable()
  1765. }
  1766. } else if (isBenchmark) {
  1767. qtest_runBenchmarkFunction(prop, null, isBenchmark)
  1768. } else {
  1769. qtest_runFunction(prop, null, isBenchmark)
  1770. }
  1771. qtest_results.finishTestFunction()
  1772. qtest_results.skipped = false
  1773. if (checkNames && testsToRun[prop].length <= 0)
  1774. delete testsToRun[prop]
  1775. }
  1776. // Run the cleanupTestCase function.
  1777. qtest_results.skipped = false
  1778. qtest_results.functionName = "cleanupTestCase"
  1779. qtest_runInternal("cleanupTestCase")
  1780. // Complain about missing functions that we were supposed to run.
  1781. if (checkNames) {
  1782. let missingTests = []
  1783. for (let func in testsToRun) {
  1784. let caseFuncName = name + '::' + func
  1785. let tags = testsToRun[func]
  1786. if (tags.length <= 0)
  1787. missingTests.push(caseFuncName)
  1788. else
  1789. for (let i in tags)
  1790. missingTests.push(caseFuncName + ':' + tags[i])
  1791. }
  1792. missingTests.sort()
  1793. if (missingTests.length > 0)
  1794. qtest_results.fail("Could not find test functions: " + missingTests, "", 0)
  1795. }
  1796. // Clean up and exit.
  1797. running = false
  1798. completed = true
  1799. qtest_results.finishTestData()
  1800. qtest_results.finishTestDataCleanup()
  1801. qtest_results.finishTestFunction()
  1802. qtest_results.functionName = ""
  1803. // Stop if there are no more tests to be run.
  1804. if (!TestLogger.log_complete_test(qtest_testId)) {
  1805. qtest_results.stopLogging()
  1806. Qt.quit()
  1807. }
  1808. qtest_results.testCaseName = ""
  1809. }
  1810. onWhenChanged: {
  1811. if (when !== qtest_prevWhen) {
  1812. qtest_prevWhen = when
  1813. if (when)
  1814. TestSchedule.testCases.push(testCase)
  1815. }
  1816. }
  1817. onOptionalChanged: {
  1818. if (!completed) {
  1819. if (optional)
  1820. TestLogger.log_optional_test(qtest_testId)
  1821. else
  1822. TestLogger.log_mandatory_test(qtest_testId)
  1823. }
  1824. }
  1825. Component.onCompleted: {
  1826. QTestRootObject.hasTestCase = true;
  1827. qtest_componentCompleted = true;
  1828. qtest_testId = TestLogger.log_register_test(name)
  1829. if (optional)
  1830. TestLogger.log_optional_test(qtest_testId)
  1831. qtest_prevWhen = when
  1832. if (when)
  1833. TestSchedule.testCases.push(testCase)
  1834. }
  1835. }