summaryrefslogtreecommitdiffstats
path: root/src/testlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/testlib')
-rw-r--r--src/testlib/3rdparty/linux_perf_event_p.h889
-rw-r--r--src/testlib/3rdparty/qt_attribution.json22
-rw-r--r--src/testlib/3rdparty/valgrind_p.h568
-rw-r--r--src/testlib/CMakeLists.txt85
-rw-r--r--src/testlib/configure.cmake10
-rw-r--r--src/testlib/doc/includes/building-examples.qdocinc40
-rw-r--r--src/testlib/doc/qttestlib.qdocconf12
-rw-r--r--src/testlib/doc/snippets/CMakeLists.txt5
-rw-r--r--src/testlib/doc/snippets/code/CMakeLists.txt4
-rw-r--r--src/testlib/doc/snippets/code/doc_src_cmakelists.txt4
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp54
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qtestevent.cpp51
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qtestevent.h51
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qtestlib.cpp51
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qtestlib.qdoc111
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qtqskip.cpp55
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qtqskip_snippet.cpp51
-rw-r--r--src/testlib/doc/snippets/code/doc_src_qttest.cpp53
-rw-r--r--src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp71
-rw-r--r--src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core_snippet.cpp57
-rw-r--r--src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp70
-rw-r--r--src/testlib/doc/snippets/code/src_qtestlib_qtestcase_snippet.cpp64
-rw-r--r--src/testlib/doc/src/dontdocument.qdoc28
-rw-r--r--src/testlib/doc/src/qt-webpages.qdoc37
-rw-r--r--src/testlib/doc/src/qt6-changes.qdoc30
-rw-r--r--src/testlib/doc/src/qttest-best-practices.qdoc139
-rw-r--r--src/testlib/doc/src/qttest-index.qdoc71
-rw-r--r--src/testlib/doc/src/qttest.qdoc28
-rw-r--r--src/testlib/doc/src/qttestlib-manual.qdoc576
-rw-r--r--src/testlib/doc/src/qttestlib-tutorial1.qdoc76
-rw-r--r--src/testlib/doc/src/qttestlib-tutorial2.qdoc132
-rw-r--r--src/testlib/doc/src/qttestlib-tutorial3.qdoc75
-rw-r--r--src/testlib/doc/src/qttestlib-tutorial4.qdoc95
-rw-r--r--src/testlib/doc/src/qttestlib-tutorial5.qdoc57
-rw-r--r--src/testlib/doc/src/qttestlib-tutorial6.qdoc50
-rw-r--r--src/testlib/qabstractitemmodeltester.cpp287
-rw-r--r--src/testlib/qabstractitemmodeltester.h41
-rw-r--r--src/testlib/qabstracttestlogger.cpp372
-rw-r--r--src/testlib/qabstracttestlogger_p.h69
-rw-r--r--src/testlib/qappletestlogger.cpp78
-rw-r--r--src/testlib/qappletestlogger_p.h40
-rw-r--r--src/testlib/qasciikey.cpp49
-rw-r--r--src/testlib/qbenchmark.cpp72
-rw-r--r--src/testlib/qbenchmark.h52
-rw-r--r--src/testlib/qbenchmark_p.h77
-rw-r--r--src/testlib/qbenchmarkevent.cpp56
-rw-r--r--src/testlib/qbenchmarkevent_p.h47
-rw-r--r--src/testlib/qbenchmarkmeasurement.cpp75
-rw-r--r--src/testlib/qbenchmarkmeasurement_p.h54
-rw-r--r--src/testlib/qbenchmarkmetric.cpp44
-rw-r--r--src/testlib/qbenchmarkmetric.h42
-rw-r--r--src/testlib/qbenchmarkmetric_p.h41
-rw-r--r--src/testlib/qbenchmarkperfevents.cpp256
-rw-r--r--src/testlib/qbenchmarkperfevents_p.h52
-rw-r--r--src/testlib/qbenchmarktimemeasurers_p.h52
-rw-r--r--src/testlib/qbenchmarkvalgrind.cpp105
-rw-r--r--src/testlib/qbenchmarkvalgrind_p.h46
-rw-r--r--src/testlib/qcomparisontesthelper.cpp22
-rw-r--r--src/testlib/qcomparisontesthelper_p.h373
-rw-r--r--src/testlib/qcsvbenchmarklogger.cpp55
-rw-r--r--src/testlib/qcsvbenchmarklogger_p.h40
-rw-r--r--src/testlib/qemulationdetector_p.h31
-rw-r--r--src/testlib/qjunittestlogger.cpp418
-rw-r--r--src/testlib/qjunittestlogger_p.h62
-rw-r--r--src/testlib/qplaintestlogger.cpp399
-rw-r--r--src/testlib/qplaintestlogger_p.h56
-rw-r--r--src/testlib/qpropertytesthelper_p.h159
-rw-r--r--src/testlib/qsignaldumper.cpp69
-rw-r--r--src/testlib/qsignaldumper_p.h42
-rw-r--r--src/testlib/qsignalspy.cpp318
-rw-r--r--src/testlib/qsignalspy.h236
-rw-r--r--src/testlib/qsignalspy.qdoc148
-rw-r--r--src/testlib/qt_cmdline.cmake1
-rw-r--r--src/testlib/qtaptestlogger.cpp539
-rw-r--r--src/testlib/qtaptestlogger_p.h56
-rw-r--r--src/testlib/qteamcitylogger.cpp269
-rw-r--r--src/testlib/qteamcitylogger_p.h52
-rw-r--r--src/testlib/qtest.h527
-rw-r--r--src/testlib/qtest_gui.h77
-rw-r--r--src/testlib/qtest_network.h49
-rw-r--r--src/testlib/qtest_widgets.h51
-rw-r--r--src/testlib/qtestaccessible.h48
-rw-r--r--src/testlib/qtestassert.h45
-rw-r--r--src/testlib/qtestblacklist.cpp119
-rw-r--r--src/testlib/qtestblacklist_p.h44
-rw-r--r--src/testlib/qtestcase.cpp1923
-rw-r--r--src/testlib/qtestcase.h535
-rw-r--r--src/testlib/qtestcase.qdoc586
-rw-r--r--src/testlib/qtestcase_p.h35
-rw-r--r--src/testlib/qtestcoreelement_p.h77
-rw-r--r--src/testlib/qtestcorelist_p.h123
-rw-r--r--src/testlib/qtestcrashhandler.cpp663
-rw-r--r--src/testlib/qtestcrashhandler_p.h251
-rw-r--r--src/testlib/qtestdata.cpp40
-rw-r--r--src/testlib/qtestdata.h48
-rw-r--r--src/testlib/qtestelement.cpp51
-rw-r--r--src/testlib/qtestelement_p.h53
-rw-r--r--src/testlib/qtestelementattribute.cpp121
-rw-r--r--src/testlib/qtestelementattribute_p.h95
-rw-r--r--src/testlib/qtestevent.h44
-rw-r--r--src/testlib/qtestevent.qdoc36
-rw-r--r--src/testlib/qtesteventloop.h61
-rw-r--r--src/testlib/qtesthelpers_p.h54
-rw-r--r--src/testlib/qtestjunitstreamer.cpp164
-rw-r--r--src/testlib/qtestjunitstreamer_p.h51
-rw-r--r--src/testlib/qtestkeyboard.h47
-rw-r--r--src/testlib/qtestlog.cpp241
-rw-r--r--src/testlib/qtestlog_p.h53
-rw-r--r--src/testlib/qtestmouse.cpp40
-rw-r--r--src/testlib/qtestmouse.h95
-rw-r--r--src/testlib/qtestregistry.cpp36
-rw-r--r--src/testlib/qtestregistry_p.h47
-rw-r--r--src/testlib/qtestresult.cpp314
-rw-r--r--src/testlib/qtestresult_p.h64
-rw-r--r--src/testlib/qtestspontaneevent.h40
-rw-r--r--src/testlib/qtestsystem.h40
-rw-r--r--src/testlib/qtesttable.cpp53
-rw-r--r--src/testlib/qtesttable_p.h41
-rw-r--r--src/testlib/qtesttostring.h499
-rw-r--r--src/testlib/qtesttouch.h42
-rw-r--r--src/testlib/qtestutil_macos.mm49
-rw-r--r--src/testlib/qtestutil_macos_p.h42
-rw-r--r--src/testlib/qtestwheel.h72
-rw-r--r--src/testlib/qttestglobal.h49
-rw-r--r--src/testlib/qxctestlogger.mm43
-rw-r--r--src/testlib/qxctestlogger_p.h40
-rw-r--r--src/testlib/qxmltestlogger.cpp410
-rw-r--r--src/testlib/qxmltestlogger_p.h50
-rw-r--r--src/testlib/removed_api.cpp29
-rw-r--r--src/testlib/selfcover.cmake5
130 files changed, 9760 insertions, 7776 deletions
diff --git a/src/testlib/3rdparty/linux_perf_event_p.h b/src/testlib/3rdparty/linux_perf_event_p.h
index 4f63c05d27..6f034f0e96 100644
--- a/src/testlib/3rdparty/linux_perf_event_p.h
+++ b/src/testlib/3rdparty/linux_perf_event_p.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Performance events:
*
@@ -37,6 +38,21 @@ enum perf_type_id {
};
/*
+ * attr.config layout for type PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE
+ * PERF_TYPE_HARDWARE: 0xEEEEEEEE000000AA
+ * AA: hardware event ID
+ * EEEEEEEE: PMU type ID
+ * PERF_TYPE_HW_CACHE: 0xEEEEEEEE00DDCCBB
+ * BB: hardware cache ID
+ * CC: hardware cache op ID
+ * DD: hardware cache op result ID
+ * EEEEEEEE: PMU type ID
+ * If the PMU type ID is 0, the PERF_TYPE_RAW will be applied.
+ */
+#define PERF_PMU_TYPE_SHIFT 32
+#define PERF_HW_EVENT_MASK 0xffffffff
+
+/*
* Generalized performance event event_id types, used by the
* attr.event_id parameter of the sys_perf_event_open()
* syscall:
@@ -109,6 +125,9 @@ enum perf_sw_ids {
PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
PERF_COUNT_SW_EMULATION_FAULTS = 8,
+ PERF_COUNT_SW_DUMMY = 9,
+ PERF_COUNT_SW_BPF_OUTPUT = 10,
+ PERF_COUNT_SW_CGROUP_SWITCHES = 11,
PERF_COUNT_SW_MAX, /* non-ABI */
};
@@ -132,10 +151,24 @@ enum perf_event_sample_format {
PERF_SAMPLE_BRANCH_STACK = 1U << 11,
PERF_SAMPLE_REGS_USER = 1U << 12,
PERF_SAMPLE_STACK_USER = 1U << 13,
-
- PERF_SAMPLE_MAX = 1U << 14, /* non-ABI */
+ PERF_SAMPLE_WEIGHT = 1U << 14,
+ PERF_SAMPLE_DATA_SRC = 1U << 15,
+ PERF_SAMPLE_IDENTIFIER = 1U << 16,
+ PERF_SAMPLE_TRANSACTION = 1U << 17,
+ PERF_SAMPLE_REGS_INTR = 1U << 18,
+ PERF_SAMPLE_PHYS_ADDR = 1U << 19,
+ PERF_SAMPLE_AUX = 1U << 20,
+ PERF_SAMPLE_CGROUP = 1U << 21,
+ PERF_SAMPLE_DATA_PAGE_SIZE = 1U << 22,
+ PERF_SAMPLE_CODE_PAGE_SIZE = 1U << 23,
+ PERF_SAMPLE_WEIGHT_STRUCT = 1U << 24,
+
+ PERF_SAMPLE_MAX = 1U << 25, /* non-ABI */
+
+ __PERF_SAMPLE_CALLCHAIN_EARLY = 1ULL << 63, /* non-ABI; internal use */
};
+#define PERF_SAMPLE_WEIGHT_TYPE (PERF_SAMPLE_WEIGHT | PERF_SAMPLE_WEIGHT_STRUCT)
/*
* values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set
*
@@ -146,17 +179,81 @@ enum perf_event_sample_format {
* The branch types can be combined, however BRANCH_ANY covers all types
* of branches and therefore it supersedes all the other types.
*/
+enum perf_branch_sample_type_shift {
+ PERF_SAMPLE_BRANCH_USER_SHIFT = 0, /* user branches */
+ PERF_SAMPLE_BRANCH_KERNEL_SHIFT = 1, /* kernel branches */
+ PERF_SAMPLE_BRANCH_HV_SHIFT = 2, /* hypervisor branches */
+
+ PERF_SAMPLE_BRANCH_ANY_SHIFT = 3, /* any branch types */
+ PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT = 4, /* any call branch */
+ PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT = 5, /* any return branch */
+ PERF_SAMPLE_BRANCH_IND_CALL_SHIFT = 6, /* indirect calls */
+ PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT = 7, /* transaction aborts */
+ PERF_SAMPLE_BRANCH_IN_TX_SHIFT = 8, /* in transaction */
+ PERF_SAMPLE_BRANCH_NO_TX_SHIFT = 9, /* not in transaction */
+ PERF_SAMPLE_BRANCH_COND_SHIFT = 10, /* conditional branches */
+
+ PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT = 11, /* call/ret stack */
+ PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT = 12, /* indirect jumps */
+ PERF_SAMPLE_BRANCH_CALL_SHIFT = 13, /* direct call */
+
+ PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT = 14, /* no flags */
+ PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT = 15, /* no cycles */
+
+ PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /* save branch type */
+
+ PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */
+
+ PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */
+};
+
enum perf_branch_sample_type {
- PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */
- PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */
- PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */
+ PERF_SAMPLE_BRANCH_USER = 1U << PERF_SAMPLE_BRANCH_USER_SHIFT,
+ PERF_SAMPLE_BRANCH_KERNEL = 1U << PERF_SAMPLE_BRANCH_KERNEL_SHIFT,
+ PERF_SAMPLE_BRANCH_HV = 1U << PERF_SAMPLE_BRANCH_HV_SHIFT,
+
+ PERF_SAMPLE_BRANCH_ANY = 1U << PERF_SAMPLE_BRANCH_ANY_SHIFT,
+ PERF_SAMPLE_BRANCH_ANY_CALL = 1U << PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT,
+ PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT,
+ PERF_SAMPLE_BRANCH_IND_CALL = 1U << PERF_SAMPLE_BRANCH_IND_CALL_SHIFT,
+ PERF_SAMPLE_BRANCH_ABORT_TX = 1U << PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT,
+ PERF_SAMPLE_BRANCH_IN_TX = 1U << PERF_SAMPLE_BRANCH_IN_TX_SHIFT,
+ PERF_SAMPLE_BRANCH_NO_TX = 1U << PERF_SAMPLE_BRANCH_NO_TX_SHIFT,
+ PERF_SAMPLE_BRANCH_COND = 1U << PERF_SAMPLE_BRANCH_COND_SHIFT,
+
+ PERF_SAMPLE_BRANCH_CALL_STACK = 1U << PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT,
+ PERF_SAMPLE_BRANCH_IND_JUMP = 1U << PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT,
+ PERF_SAMPLE_BRANCH_CALL = 1U << PERF_SAMPLE_BRANCH_CALL_SHIFT,
- PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */
- PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */
- PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */
- PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */
+ PERF_SAMPLE_BRANCH_NO_FLAGS = 1U << PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT,
+ PERF_SAMPLE_BRANCH_NO_CYCLES = 1U << PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT,
- PERF_SAMPLE_BRANCH_MAX = 1U << 7, /* non-ABI */
+ PERF_SAMPLE_BRANCH_TYPE_SAVE =
+ 1U << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT,
+
+ PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT,
+
+ PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
+};
+
+/*
+ * Common flow change classification
+ */
+enum {
+ PERF_BR_UNKNOWN = 0, /* unknown */
+ PERF_BR_COND = 1, /* conditional */
+ PERF_BR_UNCOND = 2, /* unconditional */
+ PERF_BR_IND = 3, /* indirect */
+ PERF_BR_CALL = 4, /* function call */
+ PERF_BR_IND_CALL = 5, /* indirect function call */
+ PERF_BR_RET = 6, /* function return */
+ PERF_BR_SYSCALL = 7, /* syscall */
+ PERF_BR_SYSRET = 8, /* syscall return */
+ PERF_BR_COND_CALL = 9, /* conditional function call */
+ PERF_BR_COND_RET = 10, /* conditional function return */
+ PERF_BR_ERET = 11, /* exception return */
+ PERF_BR_IRQ = 12, /* irq */
+ PERF_BR_MAX,
};
#define PERF_SAMPLE_BRANCH_PLM_ALL \
@@ -174,6 +271,28 @@ enum perf_sample_regs_abi {
};
/*
+ * Values for the memory transaction event qualifier, mostly for
+ * abort events. Multiple bits can be set.
+ */
+enum {
+ PERF_TXN_ELISION = (1 << 0), /* From elision */
+ PERF_TXN_TRANSACTION = (1 << 1), /* From transaction */
+ PERF_TXN_SYNC = (1 << 2), /* Instruction is related */
+ PERF_TXN_ASYNC = (1 << 3), /* Instruction not related */
+ PERF_TXN_RETRY = (1 << 4), /* Retry possible */
+ PERF_TXN_CONFLICT = (1 << 5), /* Conflict abort */
+ PERF_TXN_CAPACITY_WRITE = (1 << 6), /* Capacity write abort */
+ PERF_TXN_CAPACITY_READ = (1 << 7), /* Capacity read abort */
+
+ PERF_TXN_MAX = (1 << 8), /* non-ABI */
+
+ /* bits 32..63 are reserved for the abort code */
+
+ PERF_TXN_ABORT_MASK = (0xffffffffULL << 32),
+ PERF_TXN_ABORT_SHIFT = 32,
+};
+
+/*
* The format of the data returned by read() on a perf event fd,
* as specified by attr.read_format:
*
@@ -182,6 +301,7 @@ enum perf_sample_regs_abi {
* { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
* { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
* { u64 id; } && PERF_FORMAT_ID
+ * { u64 lost; } && PERF_FORMAT_LOST
* } && !PERF_FORMAT_GROUP
*
* { u64 nr;
@@ -189,6 +309,7 @@ enum perf_sample_regs_abi {
* { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
* { u64 value;
* { u64 id; } && PERF_FORMAT_ID
+ * { u64 lost; } && PERF_FORMAT_LOST
* } cntr[nr];
* } && PERF_FORMAT_GROUP
* };
@@ -198,8 +319,9 @@ enum perf_event_read_format {
PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
PERF_FORMAT_ID = 1U << 2,
PERF_FORMAT_GROUP = 1U << 3,
+ PERF_FORMAT_LOST = 1U << 4,
- PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
+ PERF_FORMAT_MAX = 1U << 5, /* non-ABI */
};
#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
@@ -207,9 +329,16 @@ enum perf_event_read_format {
#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
/* add: sample_stack_user */
+#define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */
+#define PERF_ATTR_SIZE_VER5 112 /* add: aux_watermark */
+#define PERF_ATTR_SIZE_VER6 120 /* add: aux_sample_size */
+#define PERF_ATTR_SIZE_VER7 128 /* add: sig_data */
/*
* Hardware event_id to monitor via a performance monitoring event:
+ *
+ * @sample_max_stack: Max number of frame pointers in a callchain,
+ * should be < /proc/sys/kernel/perf_event_max_stack
*/
struct perf_event_attr {
@@ -270,8 +399,22 @@ struct perf_event_attr {
exclude_callchain_kernel : 1, /* exclude kernel callchains */
exclude_callchain_user : 1, /* exclude user callchains */
-
- __reserved_1 : 41;
+ mmap2 : 1, /* include mmap with inode data */
+ comm_exec : 1, /* flag comm events that are due to an exec */
+ use_clockid : 1, /* use @clockid for time fields */
+ context_switch : 1, /* context switch data */
+ write_backward : 1, /* Write ring buffer from end to beginning */
+ namespaces : 1, /* include namespaces data */
+ ksymbol : 1, /* include ksymbol events */
+ bpf_event : 1, /* include bpf events */
+ aux_output : 1, /* generate AUX records instead of events */
+ cgroup : 1, /* include cgroup events */
+ text_poke : 1, /* include text poke events */
+ build_id : 1, /* use build id in mmap2 events */
+ inherit_thread : 1, /* children only inherit if cloned with CLONE_THREAD */
+ remove_on_exec : 1, /* event is removed from task on exec */
+ sigtrap : 1, /* send synchronous SIGTRAP on event */
+ __reserved_1 : 26;
union {
__u32 wakeup_events; /* wakeup every n events */
@@ -281,10 +424,14 @@ struct perf_event_attr {
__u32 bp_type;
union {
__u64 bp_addr;
+ __u64 kprobe_func; /* for perf_kprobe */
+ __u64 uprobe_path; /* for perf_uprobe */
__u64 config1; /* extension of config */
};
union {
__u64 bp_len;
+ __u64 kprobe_addr; /* when kprobe_func == NULL */
+ __u64 probe_offset; /* for perf_[k,u]probe */
__u64 config2; /* extension of config1 */
};
__u64 branch_sample_type; /* enum perf_branch_sample_type */
@@ -300,22 +447,71 @@ struct perf_event_attr {
*/
__u32 sample_stack_user;
- /* Align to u64. */
- __u32 __reserved_2;
+ __s32 clockid;
+ /*
+ * Defines set of regs to dump for each sample
+ * state captured on:
+ * - precise = 0: PMU interrupt
+ * - precise > 0: sampled instruction
+ *
+ * See asm/perf_regs.h for details.
+ */
+ __u64 sample_regs_intr;
+
+ /*
+ * Wakeup watermark for AUX area
+ */
+ __u32 aux_watermark;
+ __u16 sample_max_stack;
+ __u16 __reserved_2;
+ __u32 aux_sample_size;
+ __u32 __reserved_3;
+
+ /*
+ * User provided data if sigtrap=1, passed back to user via
+ * siginfo_t::si_perf_data, e.g. to permit user to identify the event.
+ * Note, siginfo_t::si_perf_data is long-sized, and sig_data will be
+ * truncated accordingly on 32 bit architectures.
+ */
+ __u64 sig_data;
};
-#define perf_flags(attr) (*(&(attr)->read_format + 1))
+/*
+ * Structure used by below PERF_EVENT_IOC_QUERY_BPF command
+ * to query bpf programs attached to the same perf tracepoint
+ * as the given perf event.
+ */
+struct perf_event_query_bpf {
+ /*
+ * The below ids array length
+ */
+ __u32 ids_len;
+ /*
+ * Set by the kernel to indicate the number of
+ * available programs
+ */
+ __u32 prog_cnt;
+ /*
+ * User provided buffer to store program ids
+ */
+ __u32 ids[];
+};
/*
* Ioctls that can be done on a perf event fd:
*/
-#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
-#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
-#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
-#define PERF_EVENT_IOC_RESET _IO ('$', 3)
-#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
-#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
-#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
+#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
+#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
+#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
+#define PERF_EVENT_IOC_RESET _IO ('$', 3)
+#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
+#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
+#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
+#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
+#define PERF_EVENT_IOC_SET_BPF _IOW('$', 8, __u32)
+#define PERF_EVENT_IOC_PAUSE_OUTPUT _IOW('$', 9, __u32)
+#define PERF_EVENT_IOC_QUERY_BPF _IOWR('$', 10, struct perf_event_query_bpf *)
+#define PERF_EVENT_IOC_MODIFY_ATTRIBUTES _IOW('$', 11, struct perf_event_attr *)
enum perf_event_ioc_flags {
PERF_IOC_FLAG_GROUP = 1U << 0,
@@ -331,7 +527,7 @@ struct perf_event_mmap_page {
/*
* Bits needed to read the hw events in user-space.
*
- * u32 seq, time_mult, time_shift, idx, width;
+ * u32 seq, time_mult, time_shift, index, width;
* u64 count, enabled, running;
* u64 cyc, time_offset;
* s64 pmc = 0;
@@ -350,11 +546,11 @@ struct perf_event_mmap_page {
* time_shift = pc->time_shift;
* }
*
- * idx = pc->index;
+ * index = pc->index;
* count = pc->offset;
- * if (pc->cap_usr_rdpmc && idx) {
+ * if (pc->cap_user_rdpmc && index) {
* width = pc->pmc_width;
- * pmc = rdpmc(idx - 1);
+ * pmc = rdpmc(index - 1);
* }
*
* barrier();
@@ -370,13 +566,20 @@ struct perf_event_mmap_page {
__u64 time_running; /* time event on cpu */
union {
__u64 capabilities;
- __u64 cap_usr_time : 1,
- cap_usr_rdpmc : 1,
- cap_____res : 62;
+ struct {
+ __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */
+ cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */
+
+ cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */
+ cap_user_time : 1, /* The time_{shift,mult,offset} fields are used */
+ cap_user_time_zero : 1, /* The time_zero field is used */
+ cap_user_time_short : 1, /* the time_{cycle,mask} fields are used */
+ cap_____res : 58;
+ };
};
/*
- * If cap_usr_rdpmc this field provides the bit-width of the value
+ * If cap_user_rdpmc this field provides the bit-width of the value
* read using the rdpmc() or equivalent instruction. This can be used
* to sign extend the result like:
*
@@ -394,16 +597,16 @@ struct perf_event_mmap_page {
* u64 delta;
*
* quot = (cyc >> time_shift);
- * rem = cyc & ((1 << time_shift) - 1);
+ * rem = cyc & (((u64)1 << time_shift) - 1);
* delta = time_offset + quot * time_mult +
* ((rem * time_mult) >> time_shift);
*
* Where time_offset,time_mult,time_shift and cyc are read in the
* seqcount loop described above. This delta can then be added to
- * enabled and possible running (if idx), improving the scaling:
+ * enabled and possible running (if index), improving the scaling:
*
* enabled += delta;
- * if (idx)
+ * if (index)
* running += delta;
*
* quot = count / running;
@@ -413,28 +616,101 @@ struct perf_event_mmap_page {
__u16 time_shift;
__u32 time_mult;
__u64 time_offset;
+ /*
+ * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated
+ * from sample timestamps.
+ *
+ * time = timestamp - time_zero;
+ * quot = time / time_mult;
+ * rem = time % time_mult;
+ * cyc = (quot << time_shift) + (rem << time_shift) / time_mult;
+ *
+ * And vice versa:
+ *
+ * quot = cyc >> time_shift;
+ * rem = cyc & (((u64)1 << time_shift) - 1);
+ * timestamp = time_zero + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ */
+ __u64 time_zero;
+
+ __u32 size; /* Header size up to __reserved[] fields. */
+ __u32 __reserved_1;
+
+ /*
+ * If cap_usr_time_short, the hardware clock is less than 64bit wide
+ * and we must compute the 'cyc' value, as used by cap_usr_time, as:
+ *
+ * cyc = time_cycles + ((cyc - time_cycles) & time_mask)
+ *
+ * NOTE: this form is explicitly chosen such that cap_usr_time_short
+ * is a correction on top of cap_usr_time, and code that doesn't
+ * know about cap_usr_time_short still works under the assumption
+ * the counter doesn't wrap.
+ */
+ __u64 time_cycles;
+ __u64 time_mask;
/*
* Hole for extension of the self monitor capabilities
*/
- __u64 __reserved[120]; /* align to 1k */
+ __u8 __reserved[116*8]; /* align to 1k. */
/*
* Control data for the mmap() data buffer.
*
- * User-space reading the @data_head value should issue an rmb(), on
- * SMP capable platforms, after reading this value -- see
- * perf_event_wakeup().
+ * User-space reading the @data_head value should issue an smp_rmb(),
+ * after reading this value.
*
* When the mapping is PROT_WRITE the @data_tail value should be
- * written by userspace to reflect the last read data. In this case
- * the kernel will not over-write unread data.
+ * written by userspace to reflect the last read data, after issueing
+ * an smp_mb() to separate the data read from the ->data_tail store.
+ * In this case the kernel will not over-write unread data.
+ *
+ * See perf_output_put_handle() for the data ordering.
+ *
+ * data_{offset,size} indicate the location and size of the perf record
+ * buffer within the mmapped area.
*/
__u64 data_head; /* head in the data section */
__u64 data_tail; /* user-space written tail */
+ __u64 data_offset; /* where the buffer starts */
+ __u64 data_size; /* data buffer size */
+
+ /*
+ * AUX area is defined by aux_{offset,size} fields that should be set
+ * by the userspace, so that
+ *
+ * aux_offset >= data_offset + data_size
+ *
+ * prior to mmap()ing it. Size of the mmap()ed area should be aux_size.
+ *
+ * Ring buffer pointers aux_{head,tail} have the same semantics as
+ * data_{head,tail} and same ordering rules apply.
+ */
+ __u64 aux_head;
+ __u64 aux_tail;
+ __u64 aux_offset;
+ __u64 aux_size;
};
+/*
+ * The current state of perf_event_header::misc bits usage:
+ * ('|' used bit, '-' unused bit)
+ *
+ * 012 CDEF
+ * |||---------||||
+ *
+ * Where:
+ * 0-2 CPUMODE_MASK
+ *
+ * C PROC_MAP_PARSE_TIMEOUT
+ * D MMAP_DATA / COMM_EXEC / FORK_EXEC / SWITCH_OUT
+ * E MMAP_BUILD_ID / EXACT_IP / SCHED_OUT_PREEMPT
+ * F (reserved)
+ */
+
#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0)
#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0)
#define PERF_RECORD_MISC_KERNEL (1 << 0)
@@ -444,11 +720,45 @@ struct perf_event_mmap_page {
#define PERF_RECORD_MISC_GUEST_USER (5 << 0)
/*
- * Indicates that the content of PERF_SAMPLE_IP points to
- * the actual instruction that triggered the event. See also
- * perf_event_attr::precise_ip.
+ * Indicates that /proc/PID/maps parsing are truncated by time out.
+ */
+#define PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT (1 << 12)
+/*
+ * Following PERF_RECORD_MISC_* are used on different
+ * events, so can reuse the same bit position:
+ *
+ * PERF_RECORD_MISC_MMAP_DATA - PERF_RECORD_MMAP* events
+ * PERF_RECORD_MISC_COMM_EXEC - PERF_RECORD_COMM event
+ * PERF_RECORD_MISC_FORK_EXEC - PERF_RECORD_FORK event (perf internal)
+ * PERF_RECORD_MISC_SWITCH_OUT - PERF_RECORD_SWITCH* events
+ */
+#define PERF_RECORD_MISC_MMAP_DATA (1 << 13)
+#define PERF_RECORD_MISC_COMM_EXEC (1 << 13)
+#define PERF_RECORD_MISC_FORK_EXEC (1 << 13)
+#define PERF_RECORD_MISC_SWITCH_OUT (1 << 13)
+/*
+ * These PERF_RECORD_MISC_* flags below are safely reused
+ * for the following events:
+ *
+ * PERF_RECORD_MISC_EXACT_IP - PERF_RECORD_SAMPLE of precise events
+ * PERF_RECORD_MISC_SWITCH_OUT_PREEMPT - PERF_RECORD_SWITCH* events
+ * PERF_RECORD_MISC_MMAP_BUILD_ID - PERF_RECORD_MMAP2 event
+ *
+ *
+ * PERF_RECORD_MISC_EXACT_IP:
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ *
+ * PERF_RECORD_MISC_SWITCH_OUT_PREEMPT:
+ * Indicates that thread was preempted in TASK_RUNNING state.
+ *
+ * PERF_RECORD_MISC_MMAP_BUILD_ID:
+ * Indicates that mmap2 event carries build id data.
*/
#define PERF_RECORD_MISC_EXACT_IP (1 << 14)
+#define PERF_RECORD_MISC_SWITCH_OUT_PREEMPT (1 << 14)
+#define PERF_RECORD_MISC_MMAP_BUILD_ID (1 << 14)
/*
* Reserve the last bit to indicate some extended misc field
*/
@@ -460,18 +770,50 @@ struct perf_event_header {
__u16 size;
};
+struct perf_ns_link_info {
+ __u64 dev;
+ __u64 ino;
+};
+
+enum {
+ NET_NS_INDEX = 0,
+ UTS_NS_INDEX = 1,
+ IPC_NS_INDEX = 2,
+ PID_NS_INDEX = 3,
+ USER_NS_INDEX = 4,
+ MNT_NS_INDEX = 5,
+ CGROUP_NS_INDEX = 6,
+
+ NR_NAMESPACES, /* number of available namespaces */
+};
+
enum perf_event_type {
/*
* If perf_event_attr.sample_id_all is set then all event types will
* have the sample_type selected fields related to where/when
- * (identity) an event took place (TID, TIME, ID, CPU, STREAM_ID)
- * described in PERF_RECORD_SAMPLE below, it will be stashed just after
- * the perf_event_header and the fields already present for the existing
- * fields, i.e. at the end of the payload. That way a newer perf.data
- * file will be supported by older perf tools, with these new optional
- * fields being ignored.
+ * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+ * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+ * just after the perf_event_header and the fields already present for
+ * the existing fields, i.e. at the end of the payload. That way a newer
+ * perf.data file will be supported by older perf tools, with these new
+ * optional fields being ignored.
*
+ * struct sample_id {
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * } && perf_event_attr::sample_id_all
+ *
+ * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
+ * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+ * relative to header.size.
+ */
+
+ /*
* The MMAP events record the PROT_EXEC mappings so that we can
* correlate userspace IPs to code. They have the following structure:
*
@@ -483,6 +825,7 @@ enum perf_event_type {
* u64 len;
* u64 pgoff;
* char filename[];
+ * struct sample_id sample_id;
* };
*/
PERF_RECORD_MMAP = 1,
@@ -492,6 +835,7 @@ enum perf_event_type {
* struct perf_event_header header;
* u64 id;
* u64 lost;
+ * struct sample_id sample_id;
* };
*/
PERF_RECORD_LOST = 2,
@@ -502,6 +846,7 @@ enum perf_event_type {
*
* u32 pid, tid;
* char comm[];
+ * struct sample_id sample_id;
* };
*/
PERF_RECORD_COMM = 3,
@@ -512,6 +857,7 @@ enum perf_event_type {
* u32 pid, ppid;
* u32 tid, ptid;
* u64 time;
+ * struct sample_id sample_id;
* };
*/
PERF_RECORD_EXIT = 4,
@@ -522,6 +868,7 @@ enum perf_event_type {
* u64 time;
* u64 id;
* u64 stream_id;
+ * struct sample_id sample_id;
* };
*/
PERF_RECORD_THROTTLE = 5,
@@ -533,6 +880,7 @@ enum perf_event_type {
* u32 pid, ppid;
* u32 tid, ptid;
* u64 time;
+ * struct sample_id sample_id;
* };
*/
PERF_RECORD_FORK = 7,
@@ -543,6 +891,7 @@ enum perf_event_type {
* u32 pid, tid;
*
* struct read_format values;
+ * struct sample_id sample_id;
* };
*/
PERF_RECORD_READ = 8,
@@ -551,6 +900,13 @@ enum perf_event_type {
* struct {
* struct perf_event_header header;
*
+ * #
+ * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+ * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+ * # is fixed relative to header.
+ * #
+ *
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
* { u64 ip; } && PERF_SAMPLE_IP
* { u32 pid, tid; } && PERF_SAMPLE_TID
* { u64 time; } && PERF_SAMPLE_TIME
@@ -579,7 +935,10 @@ enum perf_event_type {
* { u32 size;
* char data[size];}&& PERF_SAMPLE_RAW
*
- * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ * { u64 nr;
+ * { u64 hw_idx; } && PERF_SAMPLE_BRANCH_HW_INDEX
+ * { u64 from, to, flags } lbr[nr];
+ * } && PERF_SAMPLE_BRANCH_STACK
*
* { u64 abi; # enum perf_sample_regs_abi
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
@@ -587,14 +946,248 @@ enum perf_event_type {
* { u64 size;
* char data[size];
* u64 dyn_size; } && PERF_SAMPLE_STACK_USER
+ *
+ * { union perf_sample_weight
+ * {
+ * u64 full; && PERF_SAMPLE_WEIGHT
+ * #if defined(__LITTLE_ENDIAN_BITFIELD)
+ * struct {
+ * u32 var1_dw;
+ * u16 var2_w;
+ * u16 var3_w;
+ * } && PERF_SAMPLE_WEIGHT_STRUCT
+ * #elif defined(__BIG_ENDIAN_BITFIELD)
+ * struct {
+ * u16 var3_w;
+ * u16 var2_w;
+ * u32 var1_dw;
+ * } && PERF_SAMPLE_WEIGHT_STRUCT
+ * #endif
+ * }
+ * }
+ * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
+ * { u64 transaction; } && PERF_SAMPLE_TRANSACTION
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
+ * { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR
+ * { u64 size;
+ * char data[size]; } && PERF_SAMPLE_AUX
+ * { u64 data_page_size;} && PERF_SAMPLE_DATA_PAGE_SIZE
+ * { u64 code_page_size;} && PERF_SAMPLE_CODE_PAGE_SIZE
* };
*/
PERF_RECORD_SAMPLE = 9,
+ /*
+ * The MMAP2 records are an augmented version of MMAP, they add
+ * maj, min, ino numbers to be used to uniquely identify each mapping
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * union {
+ * struct {
+ * u32 maj;
+ * u32 min;
+ * u64 ino;
+ * u64 ino_generation;
+ * };
+ * struct {
+ * u8 build_id_size;
+ * u8 __reserved_1;
+ * u16 __reserved_2;
+ * u8 build_id[20];
+ * };
+ * };
+ * u32 prot, flags;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP2 = 10,
+
+ /*
+ * Records that new data landed in the AUX buffer part.
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u64 aux_offset;
+ * u64 aux_size;
+ * u64 flags;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_AUX = 11,
+
+ /*
+ * Indicates that instruction trace has started
+ *
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid;
+ * u32 tid;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_ITRACE_START = 12,
+
+ /*
+ * Records the dropped/lost sample number.
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u64 lost;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_LOST_SAMPLES = 13,
+
+ /*
+ * Records a context switch in or out (flagged by
+ * PERF_RECORD_MISC_SWITCH_OUT). See also
+ * PERF_RECORD_SWITCH_CPU_WIDE.
+ *
+ * struct {
+ * struct perf_event_header header;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_SWITCH = 14,
+
+ /*
+ * CPU-wide version of PERF_RECORD_SWITCH with next_prev_pid and
+ * next_prev_tid that are the next (switching out) or previous
+ * (switching in) pid/tid.
+ *
+ * struct {
+ * struct perf_event_header header;
+ * u32 next_prev_pid;
+ * u32 next_prev_tid;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_SWITCH_CPU_WIDE = 15,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid;
+ * u32 tid;
+ * u64 nr_namespaces;
+ * { u64 dev, inode; } [nr_namespaces];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_NAMESPACES = 16,
+
+ /*
+ * Record ksymbol register/unregister events:
+ *
+ * struct {
+ * struct perf_event_header header;
+ * u64 addr;
+ * u32 len;
+ * u16 ksym_type;
+ * u16 flags;
+ * char name[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_KSYMBOL = 17,
+
+ /*
+ * Record bpf events:
+ * enum perf_bpf_event_type {
+ * PERF_BPF_EVENT_UNKNOWN = 0,
+ * PERF_BPF_EVENT_PROG_LOAD = 1,
+ * PERF_BPF_EVENT_PROG_UNLOAD = 2,
+ * };
+ *
+ * struct {
+ * struct perf_event_header header;
+ * u16 type;
+ * u16 flags;
+ * u32 id;
+ * u8 tag[BPF_TAG_SIZE];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_BPF_EVENT = 18,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * char path[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_CGROUP = 19,
+
+ /*
+ * Records changes to kernel text i.e. self-modified code. 'old_len' is
+ * the number of old bytes, 'new_len' is the number of new bytes. Either
+ * 'old_len' or 'new_len' may be zero to indicate, for example, the
+ * addition or removal of a trampoline. 'bytes' contains the old bytes
+ * followed immediately by the new bytes.
+ *
+ * struct {
+ * struct perf_event_header header;
+ * u64 addr;
+ * u16 old_len;
+ * u16 new_len;
+ * u8 bytes[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_TEXT_POKE = 20,
+
+ /*
+ * Data written to the AUX area by hardware due to aux_output, may need
+ * to be matched to the event by an architecture-specific hardware ID.
+ * This records the hardware ID, but requires sample_id to provide the
+ * event ID. e.g. Intel PT uses this record to disambiguate PEBS-via-PT
+ * records from multiple events.
+ *
+ * struct {
+ * struct perf_event_header header;
+ * u64 hw_id;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_AUX_OUTPUT_HW_ID = 21,
+
PERF_RECORD_MAX, /* non-ABI */
};
+enum perf_record_ksymbol_type {
+ PERF_RECORD_KSYMBOL_TYPE_UNKNOWN = 0,
+ PERF_RECORD_KSYMBOL_TYPE_BPF = 1,
+ /*
+ * Out of line code such as kprobe-replaced instructions or optimized
+ * kprobes or ftrace trampolines.
+ */
+ PERF_RECORD_KSYMBOL_TYPE_OOL = 2,
+ PERF_RECORD_KSYMBOL_TYPE_MAX /* non-ABI */
+};
+
+#define PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER (1 << 0)
+
+enum perf_bpf_event_type {
+ PERF_BPF_EVENT_UNKNOWN = 0,
+ PERF_BPF_EVENT_PROG_LOAD = 1,
+ PERF_BPF_EVENT_PROG_UNLOAD = 2,
+ PERF_BPF_EVENT_MAX, /* non-ABI */
+};
+
#define PERF_MAX_STACK_DEPTH 127
+#define PERF_MAX_CONTEXTS_PER_STACK 8
enum perf_callchain_context {
PERF_CONTEXT_HV = (__u64)-32,
@@ -608,8 +1201,198 @@ enum perf_callchain_context {
PERF_CONTEXT_MAX = (__u64)-4095,
};
-#define PERF_FLAG_FD_NO_GROUP (1U << 0)
-#define PERF_FLAG_FD_OUTPUT (1U << 1)
-#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */
+/**
+ * PERF_RECORD_AUX::flags bits
+ */
+#define PERF_AUX_FLAG_TRUNCATED 0x01 /* record was truncated to fit */
+#define PERF_AUX_FLAG_OVERWRITE 0x02 /* snapshot from overwrite mode */
+#define PERF_AUX_FLAG_PARTIAL 0x04 /* record contains gaps */
+#define PERF_AUX_FLAG_COLLISION 0x08 /* sample collided with another */
+#define PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK 0xff00 /* PMU specific trace format type */
+
+/* CoreSight PMU AUX buffer formats */
+#define PERF_AUX_FLAG_CORESIGHT_FORMAT_CORESIGHT 0x0000 /* Default for backward compatibility */
+#define PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW 0x0100 /* Raw format of the source */
+
+#define PERF_FLAG_FD_NO_GROUP (1UL << 0)
+#define PERF_FLAG_FD_OUTPUT (1UL << 1)
+#define PERF_FLAG_PID_CGROUP (1UL << 2) /* pid=cgroup id, per-cpu mode only */
+#define PERF_FLAG_FD_CLOEXEC (1UL << 3) /* O_CLOEXEC */
+
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+union perf_mem_data_src {
+ __u64 val;
+ struct {
+ __u64 mem_op:5, /* type of opcode */
+ mem_lvl:14, /* memory hierarchy level */
+ mem_snoop:5, /* snoop mode */
+ mem_lock:2, /* lock instr */
+ mem_dtlb:7, /* tlb access */
+ mem_lvl_num:4, /* memory hierarchy level number */
+ mem_remote:1, /* remote */
+ mem_snoopx:2, /* snoop mode, ext */
+ mem_blk:3, /* access blocked */
+ mem_hops:3, /* hop level */
+ mem_rsvd:18;
+ };
+};
+#elif defined(__BIG_ENDIAN_BITFIELD)
+union perf_mem_data_src {
+ __u64 val;
+ struct {
+ __u64 mem_rsvd:18,
+ mem_hops:3, /* hop level */
+ mem_blk:3, /* access blocked */
+ mem_snoopx:2, /* snoop mode, ext */
+ mem_remote:1, /* remote */
+ mem_lvl_num:4, /* memory hierarchy level number */
+ mem_dtlb:7, /* tlb access */
+ mem_lock:2, /* lock instr */
+ mem_snoop:5, /* snoop mode */
+ mem_lvl:14, /* memory hierarchy level */
+ mem_op:5; /* type of opcode */
+ };
+};
+#else
+#error "Unknown endianness"
+#endif
+
+/* type of opcode (load/store/prefetch,code) */
+#define PERF_MEM_OP_NA 0x01 /* not available */
+#define PERF_MEM_OP_LOAD 0x02 /* load instruction */
+#define PERF_MEM_OP_STORE 0x04 /* store instruction */
+#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */
+#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */
+#define PERF_MEM_OP_SHIFT 0
+
+/*
+ * PERF_MEM_LVL_* namespace being depricated to some extent in the
+ * favour of newer composite PERF_MEM_{LVLNUM_,REMOTE_,SNOOPX_} fields.
+ * Supporting this namespace inorder to not break defined ABIs.
+ *
+ * memory hierarchy (memory level, hit or miss)
+ */
+#define PERF_MEM_LVL_NA 0x01 /* not available */
+#define PERF_MEM_LVL_HIT 0x02 /* hit level */
+#define PERF_MEM_LVL_MISS 0x04 /* miss level */
+#define PERF_MEM_LVL_L1 0x08 /* L1 */
+#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */
+#define PERF_MEM_LVL_L2 0x20 /* L2 */
+#define PERF_MEM_LVL_L3 0x40 /* L3 */
+#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */
+#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */
+#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */
+#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */
+#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */
+#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */
+#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */
+#define PERF_MEM_LVL_SHIFT 5
+
+#define PERF_MEM_REMOTE_REMOTE 0x01 /* Remote */
+#define PERF_MEM_REMOTE_SHIFT 37
+
+#define PERF_MEM_LVLNUM_L1 0x01 /* L1 */
+#define PERF_MEM_LVLNUM_L2 0x02 /* L2 */
+#define PERF_MEM_LVLNUM_L3 0x03 /* L3 */
+#define PERF_MEM_LVLNUM_L4 0x04 /* L4 */
+/* 5-0xa available */
+#define PERF_MEM_LVLNUM_ANY_CACHE 0x0b /* Any cache */
+#define PERF_MEM_LVLNUM_LFB 0x0c /* LFB */
+#define PERF_MEM_LVLNUM_RAM 0x0d /* RAM */
+#define PERF_MEM_LVLNUM_PMEM 0x0e /* PMEM */
+#define PERF_MEM_LVLNUM_NA 0x0f /* N/A */
+
+#define PERF_MEM_LVLNUM_SHIFT 33
+
+/* snoop mode */
+#define PERF_MEM_SNOOP_NA 0x01 /* not available */
+#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */
+#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */
+#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */
+#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */
+#define PERF_MEM_SNOOP_SHIFT 19
+
+#define PERF_MEM_SNOOPX_FWD 0x01 /* forward */
+/* 1 free */
+#define PERF_MEM_SNOOPX_SHIFT 38
+
+/* locked instruction */
+#define PERF_MEM_LOCK_NA 0x01 /* not available */
+#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */
+#define PERF_MEM_LOCK_SHIFT 24
+
+/* TLB access */
+#define PERF_MEM_TLB_NA 0x01 /* not available */
+#define PERF_MEM_TLB_HIT 0x02 /* hit level */
+#define PERF_MEM_TLB_MISS 0x04 /* miss level */
+#define PERF_MEM_TLB_L1 0x08 /* L1 */
+#define PERF_MEM_TLB_L2 0x10 /* L2 */
+#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/
+#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */
+#define PERF_MEM_TLB_SHIFT 26
+
+/* Access blocked */
+#define PERF_MEM_BLK_NA 0x01 /* not available */
+#define PERF_MEM_BLK_DATA 0x02 /* data could not be forwarded */
+#define PERF_MEM_BLK_ADDR 0x04 /* address conflict */
+#define PERF_MEM_BLK_SHIFT 40
+
+/* hop level */
+#define PERF_MEM_HOPS_0 0x01 /* remote core, same node */
+#define PERF_MEM_HOPS_1 0x02 /* remote node, same socket */
+#define PERF_MEM_HOPS_2 0x03 /* remote socket, same board */
+#define PERF_MEM_HOPS_3 0x04 /* remote board */
+/* 5-7 available */
+#define PERF_MEM_HOPS_SHIFT 43
+
+#define PERF_MEM_S(a, s) \
+ (((__u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT)
+
+/*
+ * single taken branch record layout:
+ *
+ * from: source instruction (may not always be a branch insn)
+ * to: branch target
+ * mispred: branch target was mispredicted
+ * predicted: branch target was predicted
+ *
+ * support for mispred, predicted is optional. In case it
+ * is not supported mispred = predicted = 0.
+ *
+ * in_tx: running in a hardware transaction
+ * abort: aborting a hardware transaction
+ * cycles: cycles from last branch (or 0 if not supported)
+ * type: branch type
+ */
+struct perf_branch_entry {
+ __u64 from;
+ __u64 to;
+ __u64 mispred:1, /* target mispredicted */
+ predicted:1,/* target predicted */
+ in_tx:1, /* in transaction */
+ abort:1, /* transaction abort */
+ cycles:16, /* cycle count to last branch */
+ type:4, /* branch type */
+ reserved:40;
+};
+
+union perf_sample_weight {
+ __u64 full;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ struct {
+ __u32 var1_dw;
+ __u16 var2_w;
+ __u16 var3_w;
+ };
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ struct {
+ __u16 var3_w;
+ __u16 var2_w;
+ __u32 var1_dw;
+ };
+#else
+#error "Unknown endianness"
+#endif
+};
#endif /* _UAPI_LINUX_PERF_EVENT_H */
diff --git a/src/testlib/3rdparty/qt_attribution.json b/src/testlib/3rdparty/qt_attribution.json
index 4dd844c232..77f58e7d75 100644
--- a/src/testlib/3rdparty/qt_attribution.json
+++ b/src/testlib/3rdparty/qt_attribution.json
@@ -4,16 +4,18 @@
"Name": "Valgrind",
"QDocModule": "qttestlib",
"QtUsage": "Used on Linux ond MacOS in the Qt Test module.",
- "Files": "valgrind_p.h callgrind_p.h",
+ "Comment": { "UpstreamFiles": [ "include/valgrind.h.in", "callgrind/callgrind.h" ],
+ "License": "These two files are BSD; the rest of valgrind is GPL" },
+ "Files": [ "valgrind_p.h", "callgrind_p.h" ],
"Description": "An instrumentation framework for building dynamic analysis tools.",
"Homepage": "http://valgrind.org/",
- "Version": "3.14.0",
+ "Version": "3.22.0",
"License": "BSD 4-clause \"Original\" or \"Old\" License",
"LicenseId": "BSD-4-Clause",
"LicenseFile": "VALGRIND_LICENSE.txt",
- "Copyright": "Copyright (C) 2000-2017 Julian Seward
-Copyright (C) 2003-2017 Josef Weidendorfer."
+ "Copyright": ["Copyright (C) 2000-2017 Julian Seward",
+ "Copyright (C) 2003-2017 Josef Weidendorfer."]
},
{
"Id": "cycle",
@@ -26,8 +28,8 @@ Copyright (C) 2003-2017 Josef Weidendorfer."
"License": "MIT License",
"LicenseId": "MIT",
"LicenseFile": "CYCLE_LICENSE.txt",
- "Copyright": "Copyright (c) 2003, 2006 Matteo Frigo
-Copyright (c) 2003, 2006 Massachusetts Institute of Technology"
+ "Copyright": ["Copyright (c) 2003, 2006 Matteo Frigo",
+ "Copyright (c) 2003, 2006 Massachusetts Institute of Technology"]
},
{
"Id": "linuxperf",
@@ -40,10 +42,10 @@ Copyright (c) 2003, 2006 Massachusetts Institute of Technology"
"Homepage": "https://www.kernel.org",
"Version": "3.7",
"License": "GNU General Public License v2.0 only with Linux Syscall Note",
- "LicenseId": "GPL-2.0 WITH Linux-syscall-note",
+ "LicenseId": "GPL-2.0-only WITH Linux-syscall-note",
"LicenseFile": "LINUX_LICENSE.txt",
- "Copyright": "Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>
-Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar
-Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra"
+ "Copyright": ["Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>",
+ "Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar",
+ "Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra"]
}
]
diff --git a/src/testlib/3rdparty/valgrind_p.h b/src/testlib/3rdparty/valgrind_p.h
index 577c8f05e5..f5e5518265 100644
--- a/src/testlib/3rdparty/valgrind_p.h
+++ b/src/testlib/3rdparty/valgrind_p.h
@@ -89,7 +89,7 @@
|| (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6))
*/
#define __VALGRIND_MAJOR__ 3
-#define __VALGRIND_MINOR__ 14
+#define __VALGRIND_MINOR__ 20
#include <stdarg.h>
@@ -110,6 +110,8 @@
*/
#undef PLAT_x86_darwin
#undef PLAT_amd64_darwin
+#undef PLAT_x86_freebsd
+#undef PLAT_amd64_freebsd
#undef PLAT_x86_win32
#undef PLAT_amd64_win64
#undef PLAT_x86_linux
@@ -122,6 +124,7 @@
#undef PLAT_s390x_linux
#undef PLAT_mips32_linux
#undef PLAT_mips64_linux
+#undef PLAT_nanomips_linux
#undef PLAT_x86_solaris
#undef PLAT_amd64_solaris
@@ -130,12 +133,17 @@
# define PLAT_x86_darwin 1
#elif defined(__APPLE__) && defined(__x86_64__)
# define PLAT_amd64_darwin 1
-#elif (defined(__MINGW32__) && !defined(__MINGW64__)) \
+#elif defined(__FreeBSD__) && defined(__i386__)
+# define PLAT_x86_freebsd 1
+#elif defined(__FreeBSD__) && defined(__amd64__)
+# define PLAT_amd64_freebsd 1
+#elif (defined(__MINGW32__) && defined(__i386__)) \
|| defined(__CYGWIN32__) \
|| (defined(_WIN32) && defined(_M_IX86))
# define PLAT_x86_win32 1
-#elif defined(__MINGW64__) \
- || (defined(_WIN64) && defined(_M_X64))
+#elif (defined(__MINGW32__) && defined(__x86_64__)) \
+ || (defined(_WIN32) && defined(_M_X64))
+/* __MINGW32__ and _WIN32 are defined in 64 bit mode as well. */
# define PLAT_amd64_win64 1
#elif defined(__linux__) && defined(__i386__)
# define PLAT_x86_linux 1
@@ -157,8 +165,10 @@
# define PLAT_s390x_linux 1
#elif defined(__linux__) && defined(__mips__) && (__mips==64)
# define PLAT_mips64_linux 1
-#elif defined(__linux__) && defined(__mips__) && (__mips!=64)
+#elif defined(__linux__) && defined(__mips__) && (__mips==32)
# define PLAT_mips32_linux 1
+#elif defined(__linux__) && defined(__nanomips__)
+# define PLAT_nanomips_linux 1
#elif defined(__sun) && defined(__i386__)
# define PLAT_x86_solaris 1
#elif defined(__sun) && defined(__x86_64__)
@@ -254,7 +264,7 @@
#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \
|| (defined(PLAT_x86_win32) && defined(__GNUC__)) \
- || defined(PLAT_x86_solaris)
+ || defined(PLAT_x86_solaris) || defined(PLAT_x86_freebsd)
typedef
struct {
@@ -394,6 +404,7 @@ valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request,
#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \
|| defined(PLAT_amd64_solaris) \
+ || defined(PLAT_amd64_freebsd) \
|| (defined(PLAT_amd64_win64) && defined(__GNUC__))
typedef
@@ -872,7 +883,8 @@ typedef
/* results = r3 */ \
"lgr %0, 3\n\t" \
: "=d" (_zzq_result) \
- : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "a" (&_zzq_args[0]), \
+ "0" ((unsigned long int)_zzq_default) \
: "cc", "2", "3", "memory" \
); \
_zzq_result; \
@@ -1045,6 +1057,75 @@ typedef
#endif /* PLAT_mips64_linux */
+#if defined(PLAT_nanomips_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+/*
+ 8000 c04d srl zero, zero, 13
+ 8000 c05d srl zero, zero, 29
+ 8000 c043 srl zero, zero, 3
+ 8000 c053 srl zero, zero, 19
+*/
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE "srl[32] $zero, $zero, 13 \n\t" \
+ "srl[32] $zero, $zero, 29 \n\t" \
+ "srl[32] $zero, $zero, 3 \n\t" \
+ "srl[32] $zero, $zero, 19 \n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({ volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile("move $a7, %1\n\t" /* default */ \
+ "move $t0, %2\n\t" /* ptr */ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* $a7 = client_request( $t0 ) */ \
+ "or[32] $t0, $t0, $t0\n\t" \
+ "move %0, $a7\n\t" /* result */ \
+ : "=r" (_zzq_result) \
+ : "r" (_zzq_default), "r" (&_zzq_args[0]) \
+ : "$a7", "$t0", "memory"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* $a7 = guest_NRADDR */ \
+ "or[32] $t1, $t1, $t1\n\t" \
+ "move %0, $a7" /*result*/ \
+ : "=r" (__addr) \
+ : \
+ : "$a7"); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_T9 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir $25 */ \
+ "or[32] $t2, $t2, $t2\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "or[32] $t3, $t3, $t3\n\t" \
+ ); \
+ } while (0)
+
+#endif
/* Insert assembly code for other platforms here... */
#endif /* NVALGRIND */
@@ -1145,7 +1226,7 @@ typedef
/* ----------------- x86-{linux,darwin,solaris} ---------------- */
#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \
- || defined(PLAT_x86_solaris)
+ || defined(PLAT_x86_solaris) || defined(PLAT_x86_freebsd)
/* These regs are trashed by the hidden call. No need to mention eax
as gcc can already see that, plus causes gcc to bomb. */
@@ -1577,7 +1658,7 @@ typedef
/* ---------------- amd64-{linux,darwin,solaris} --------------- */
#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \
- || defined(PLAT_amd64_solaris)
+ || defined(PLAT_amd64_solaris) || defined(PLAT_amd64_freebsd)
/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */
@@ -4667,7 +4748,7 @@ typedef
"lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \
"lgr 7,11\n\t" \
"lgr 11,%2\n\t" \
- ".cfi_def_cfa r11, 0\n\t"
+ ".cfi_def_cfa 11, 0\n\t"
# define VALGRIND_CFI_EPILOGUE \
"lgr 11, 7\n\t" \
".cfi_restore_state\n\t"
@@ -4687,8 +4768,16 @@ typedef
r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the
function a proper return address. All others are ABI defined call
clobbers. */
-#define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \
- "f0","f1","f2","f3","f4","f5","f6","f7"
+#if defined(__VX__) || defined(__S390_VX__)
+#define __CALLER_SAVED_REGS "0", "1", "2", "3", "4", "5", "14", \
+ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", \
+ "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", \
+ "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", \
+ "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
+#else
+#define __CALLER_SAVED_REGS "0", "1", "2", "3", "4", "5", "14", \
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"
+#endif
/* Nb: Although r11 is modified in the asm snippets below (inside
VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for
@@ -4710,9 +4799,9 @@ typedef
"aghi 15,-160\n\t" \
"lg 1, 0(1)\n\t" /* target->r1 */ \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,160\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
@@ -4734,9 +4823,9 @@ typedef
"lg 2, 8(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,160\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
@@ -4759,9 +4848,9 @@ typedef
"lg 3,16(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,160\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
@@ -4786,9 +4875,9 @@ typedef
"lg 4,24(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,160\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
@@ -4815,9 +4904,9 @@ typedef
"lg 5,32(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,160\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
@@ -4846,9 +4935,9 @@ typedef
"lg 6,40(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,160\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -4880,9 +4969,9 @@ typedef
"mvc 160(8,15), 48(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,168\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -4916,9 +5005,9 @@ typedef
"mvc 168(8,15), 56(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,176\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -4954,9 +5043,9 @@ typedef
"mvc 176(8,15), 64(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,184\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -4994,9 +5083,9 @@ typedef
"mvc 184(8,15), 72(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,192\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -5036,9 +5125,9 @@ typedef
"mvc 192(8,15), 80(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,200\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -5080,9 +5169,9 @@ typedef
"mvc 200(8,15), 88(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,208\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -5126,9 +5215,9 @@ typedef
"mvc 208(8,15), 96(1)\n\t" \
"lg 1, 0(1)\n\t" \
VALGRIND_CALL_NOREDIR_R1 \
- "lgr %0, 2\n\t" \
"aghi 15,216\n\t" \
VALGRIND_CFI_EPILOGUE \
+ "lgr %0, 2\n\t" \
: /*out*/ "=d" (_res) \
: /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
: /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
@@ -5678,6 +5767,422 @@ typedef
#endif /* PLAT_mips32_linux */
+/* ------------------------- nanomips-linux -------------------- */
+
+#if defined(PLAT_nanomips_linux)
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS "$t4", "$t5", "$a0", "$a1", "$a2", \
+"$a3", "$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3", \
+"$t8","$t9", "$at"
+
+/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ "lw $a1, 8(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ "lw $a1, 8(%1)\n\t" \
+ "lw $a2,12(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ "lw $a1, 8(%1)\n\t" \
+ "lw $a2,12(%1)\n\t" \
+ "lw $a3,16(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ "lw $a1, 8(%1)\n\t" \
+ "lw $a2,12(%1)\n\t" \
+ "lw $a3,16(%1)\n\t" \
+ "lw $a4,20(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ "lw $a1, 8(%1)\n\t" \
+ "lw $a2,12(%1)\n\t" \
+ "lw $a3,16(%1)\n\t" \
+ "lw $a4,20(%1)\n\t" \
+ "lw $a5,24(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ "lw $a1, 8(%1)\n\t" \
+ "lw $a2,12(%1)\n\t" \
+ "lw $a3,16(%1)\n\t" \
+ "lw $a4,20(%1)\n\t" \
+ "lw $a5,24(%1)\n\t" \
+ "lw $a6,28(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "lw $t9, 0(%1)\n\t" \
+ "lw $a0, 4(%1)\n\t" \
+ "lw $a1, 8(%1)\n\t" \
+ "lw $a2,12(%1)\n\t" \
+ "lw $a3,16(%1)\n\t" \
+ "lw $a4,20(%1)\n\t" \
+ "lw $a5,24(%1)\n\t" \
+ "lw $a6,28(%1)\n\t" \
+ "lw $a7,32(%1)\n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "addiu $sp, $sp, -16 \n\t" \
+ "lw $t9,36(%1) \n\t" \
+ "sw $t9, 0($sp) \n\t" \
+ "lw $t9, 0(%1) \n\t" \
+ "lw $a0, 4(%1) \n\t" \
+ "lw $a1, 8(%1) \n\t" \
+ "lw $a2,12(%1) \n\t" \
+ "lw $a3,16(%1) \n\t" \
+ "lw $a4,20(%1) \n\t" \
+ "lw $a5,24(%1) \n\t" \
+ "lw $a6,28(%1) \n\t" \
+ "lw $a7,32(%1) \n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0 \n\t" \
+ "addiu $sp, $sp, 16 \n\t" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "addiu $sp, $sp, -16 \n\t" \
+ "lw $t9,36(%1) \n\t" \
+ "sw $t9, 0($sp) \n\t" \
+ "lw $t9,40(%1) \n\t" \
+ "sw $t9, 4($sp) \n\t" \
+ "lw $t9, 0(%1) \n\t" \
+ "lw $a0, 4(%1) \n\t" \
+ "lw $a1, 8(%1) \n\t" \
+ "lw $a2,12(%1) \n\t" \
+ "lw $a3,16(%1) \n\t" \
+ "lw $a4,20(%1) \n\t" \
+ "lw $a5,24(%1) \n\t" \
+ "lw $a6,28(%1) \n\t" \
+ "lw $a7,32(%1) \n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0 \n\t" \
+ "addiu $sp, $sp, 16 \n\t" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "addiu $sp, $sp, -16 \n\t" \
+ "lw $t9,36(%1) \n\t" \
+ "sw $t9, 0($sp) \n\t" \
+ "lw $t9,40(%1) \n\t" \
+ "sw $t9, 4($sp) \n\t" \
+ "lw $t9,44(%1) \n\t" \
+ "sw $t9, 8($sp) \n\t" \
+ "lw $t9, 0(%1) \n\t" \
+ "lw $a0, 4(%1) \n\t" \
+ "lw $a1, 8(%1) \n\t" \
+ "lw $a2,12(%1) \n\t" \
+ "lw $a3,16(%1) \n\t" \
+ "lw $a4,20(%1) \n\t" \
+ "lw $a5,24(%1) \n\t" \
+ "lw $a6,28(%1) \n\t" \
+ "lw $a7,32(%1) \n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0 \n\t" \
+ "addiu $sp, $sp, 16 \n\t" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "addiu $sp, $sp, -16 \n\t" \
+ "lw $t9,36(%1) \n\t" \
+ "sw $t9, 0($sp) \n\t" \
+ "lw $t9,40(%1) \n\t" \
+ "sw $t9, 4($sp) \n\t" \
+ "lw $t9,44(%1) \n\t" \
+ "sw $t9, 8($sp) \n\t" \
+ "lw $t9,48(%1) \n\t" \
+ "sw $t9,12($sp) \n\t" \
+ "lw $t9, 0(%1) \n\t" \
+ "lw $a0, 4(%1) \n\t" \
+ "lw $a1, 8(%1) \n\t" \
+ "lw $a2,12(%1) \n\t" \
+ "lw $a3,16(%1) \n\t" \
+ "lw $a4,20(%1) \n\t" \
+ "lw $a5,24(%1) \n\t" \
+ "lw $a6,28(%1) \n\t" \
+ "lw $a7,32(%1) \n\t" \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $a0 \n\t" \
+ "addiu $sp, $sp, 16 \n\t" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_nanomips_linux */
+
/* ------------------------- mips64-linux ------------------------- */
#if defined(PLAT_mips64_linux)
@@ -6146,6 +6651,10 @@ typedef
command. */
VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202,
+ /* Allows the client program to change a dynamic command line
+ option. */
+ VG_USERREQ__CLO_CHANGE = 0x1203,
+
/* These are useful and can be interpreted by any tool that
tracks malloc() et al, by using vg_replace_malloc.c. */
VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
@@ -6628,6 +7137,14 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
command, 0, 0, 0, 0)
+/* Change the value of a dynamic command line option.
+ Note that unknown or not dynamically changeable options
+ will cause a warning message to be output. */
+#define VALGRIND_CLO_CHANGE(option) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CLO_CHANGE, \
+ option, 0, 0, 0, 0)
+
+
#undef PLAT_x86_darwin
#undef PLAT_amd64_darwin
#undef PLAT_x86_win32
@@ -6641,6 +7158,7 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
#undef PLAT_s390x_linux
#undef PLAT_mips32_linux
#undef PLAT_mips64_linux
+#undef PLAT_nanomips_linux
#undef PLAT_x86_solaris
#undef PLAT_amd64_solaris
diff --git a/src/testlib/CMakeLists.txt b/src/testlib/CMakeLists.txt
index 18fd9d2cee..e956a47cf1 100644
--- a/src/testlib/CMakeLists.txt
+++ b/src/testlib/CMakeLists.txt
@@ -1,8 +1,7 @@
-# Generated from testlib.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-# special case begin
include(selfcover.cmake)
-# special case end
#####################################################################
## Test Module:
@@ -13,6 +12,8 @@ qt_internal_add_module(Test
QMAKE_MODULE_CONFIG console testlib_defines
EXCEPTIONS
SOURCES
+ 3rdparty/cycle_p.h
+ removed_api.cpp # keep first
qabstracttestlogger.cpp qabstracttestlogger_p.h
qasciikey.cpp
qbenchmark.cpp qbenchmark.h qbenchmark_p.h
@@ -21,13 +22,14 @@ qt_internal_add_module(Test
qbenchmarkmetric.cpp qbenchmarkmetric.h qbenchmarkmetric_p.h
qbenchmarkperfevents.cpp qbenchmarkperfevents_p.h
qbenchmarktimemeasurers_p.h
+ qcomparisontesthelper.cpp qcomparisontesthelper_p.h
qcsvbenchmarklogger.cpp qcsvbenchmarklogger_p.h
qemulationdetector_p.h
qjunittestlogger.cpp qjunittestlogger_p.h
qplaintestlogger.cpp qplaintestlogger_p.h
qpropertytesthelper_p.h
qsignaldumper.cpp qsignaldumper_p.h
- qsignalspy.h
+ qsignalspy.cpp qsignalspy.h
qtaptestlogger.cpp qtaptestlogger_p.h
qteamcitylogger.cpp qteamcitylogger_p.h
qtest.h
@@ -37,9 +39,9 @@ qt_internal_add_module(Test
qtestaccessible.h
qtestassert.h
qtestblacklist.cpp qtestblacklist_p.h
- qtestcase.cpp qtestcase.h
+ qtestcase.cpp qtestcase.h qtestcase_p.h
qtestcoreelement_p.h
- qtestcorelist_p.h
+ qtestcrashhandler.cpp qtestcrashhandler_p.h
qtestdata.cpp qtestdata.h
qtestelement.cpp qtestelement_p.h
qtestelementattribute.cpp qtestelementattribute_p.h
@@ -55,15 +57,23 @@ qt_internal_add_module(Test
qtestsystem.h
qtesttable.cpp qtesttable_p.h
qtesttouch.h
+ qtesttostring.h
+ qtestwheel.h
qttestglobal.h
qxmltestlogger.cpp qxmltestlogger_p.h
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
+ QT_NO_CONTEXTLESS_CONNECT
QT_NO_DATASTREAM
QT_NO_FOREACH
+ QT_USE_NODISCARD_FILE_OPEN
+ # Ensure uniform location info between release and debug builds
+ QT_NO_MESSAGELOGCONTEXT
LIBRARIES
Qt::CorePrivate
+ NO_PCH_SOURCES
+ removed_api.cpp
PUBLIC_LIBRARIES
Qt::Core
PRIVATE_MODULE_INTERFACE
@@ -71,22 +81,28 @@ qt_internal_add_module(Test
GENERATE_CPP_EXPORTS
)
-#### Keys ignored in scope 1:.:.:testlib.pro:<TRUE>:
-# MODULE_CONFIG = "console" "testlib_defines"
+if(TARGET Gui)
+ set_property(TARGET Test
+ APPEND PROPERTY _qt_internal_sync_headers_deps Gui_sync_headers)
+endif()
## Scopes:
#####################################################################
-#### Keys ignored in scope 2:.:.:testlib.pro:UNIX AND NOT embedded:
-# QMAKE_PKGCONFIG_DESCRIPTION = "Qt" "Unit" "Testing" "Library"
-
qt_internal_extend_target(Test CONDITION QT_FEATURE_itemmodeltester
SOURCES
qabstractitemmodeltester.cpp qabstractitemmodeltester.h
)
+qt_internal_extend_target(Test CONDITION QT_FEATURE_batch_test_support
+ SOURCES
+ qtestregistry.cpp qtestregistry_p.h
+)
+
qt_internal_extend_target(Test CONDITION QT_FEATURE_valgrind
SOURCES
+ 3rdparty/callgrind_p.h
+ 3rdparty/valgrind_p.h
qbenchmarkvalgrind.cpp qbenchmarkvalgrind_p.h
)
@@ -117,37 +133,44 @@ set(qt_bool_tc_build_dir "$<BOOL:${qt_tc_build_dir}>")
set(qt_tc_build_dir_def
"$<IF:${qt_bool_tc_build_dir},${qt_tc_build_dir},$<TARGET_PROPERTY:BINARY_DIR>>"
)
-set_property(TARGET Test PROPERTY INTERFACE_COMPILE_DEFINITIONS
+set_property(TARGET Test APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS
QT_TESTCASE_BUILDDIR="${qt_tc_build_dir_def}"
QT_TESTCASE_SOURCEDIR="$<TARGET_PROPERTY:SOURCE_DIR>"
)
-# special case begin
-# Do not bother with disabled stuff:
-# qt_internal_extend_target(Test CONDITION (MACOS) AND (OFF AND NOT lessThan(QMAKE_XCODE_VERSION, "6.0")) ...
-# qt_internal_extend_target(Test CONDITION ((MACOS) AND (OFF AND NOT lessThan(QMAKE_XCODE_VERSION, "6.0"))) AND (NOT QMAKE_MAC_SDK_PLATFORM_PATH_ISEMPTY) ...
-# special case end
-
-#### Keys ignored in scope 9:.:.:testlib.pro:NOT QMAKE_MAC_SDK_PLATFORM_PATH_ISEMPTY:
-# MODULE_CONFIG = "xctest"
-
-#### Keys ignored in scope 10:.:.:testlib.pro:NOT TARGET Qt::Gui:
-# HEADERSCLEAN_EXCLUDE = "qtest_gui.h" "qtestaccessible.h" "qtestkeyboard.h" "qtestmouse.h" "qtesttouch.h"
-
-#### Keys ignored in scope 11:.:.:testlib.pro:NOT TARGET Qt::Widgets:
-# HEADERSCLEAN_EXCLUDE = "qtest_widgets.h"
-
-#### Keys ignored in scope 12:.:.:testlib.pro:NOT TARGET Qt::Network:
-# HEADERSCLEAN_EXCLUDE = "qtest_network.h"
qt_internal_add_docs(Test
doc/qttestlib.qdocconf
)
-# special case begin
qt_internal_apply_testlib_coverage_options(Test)
-# special case end
# include the snippet projects for developer-builds
if(QT_FEATURE_private_tests)
add_subdirectory(doc/snippets/code)
endif()
+
+if(WASM)
+ # Keep in sync with is_test files in Qt6WasmMacros.cmake
+ set(testrunner_files
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/batchedtestrunner.html"
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/batchedtestrunner.js"
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/emrunadapter.js"
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/qwasmjsruntime.js"
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/qwasmtestmain.js"
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/util.js"
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/qtestoutputreporter.js"
+ "${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/qtestoutputreporter.css")
+
+ qt_path_join(install_dir_libexec "${QT_INSTALL_DIR}" "${INSTALL_LIBEXECDIR}")
+ qt_copy_or_install(FILES ${testrunner_files}
+ DESTINATION "${install_dir_libexec}")
+
+ # In a prefix build, the above copy_or_install won't put the files
+ # in the build dir, but they are required there for tests at configure time
+ if(QT_WILL_INSTALL)
+ qt_path_join(build_dir_libexec "${QT_BUILD_DIR}" "${INSTALL_LIBEXECDIR}")
+ foreach(testrunner_file ${testrunner_files})
+ file(COPY "${testrunner_file}" DESTINATION "${build_dir_libexec}")
+ endforeach()
+ endif()
+endif()
diff --git a/src/testlib/configure.cmake b/src/testlib/configure.cmake
index d83b4f09b0..3490c64874 100644
--- a/src/testlib/configure.cmake
+++ b/src/testlib/configure.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#### Inputs
@@ -29,6 +32,13 @@ qt_feature("valgrind" PUBLIC
PURPOSE "Profiling support with callgrind."
CONDITION ( LINUX OR APPLE ) AND QT_FEATURE_process AND QT_FEATURE_regularexpression
)
+qt_feature("batch_test_support" PUBLIC
+ LABEL "Batch tests"
+ PURPOSE "Allows merging of all tests into a single executable on demand"
+ AUTODETECT QT_BUILD_TESTS_BATCHED
+ ENABLE INPUT_batch_tests STREQUAL 'yes'
+)
qt_configure_add_summary_section(NAME "Qt Testlib")
qt_configure_add_summary_entry(ARGS "itemmodeltester")
+qt_configure_add_summary_entry(ARGS "batch_test_support")
qt_configure_end_summary_section() # end of "Qt Testlib" section
diff --git a/src/testlib/doc/includes/building-examples.qdocinc b/src/testlib/doc/includes/building-examples.qdocinc
new file mode 100644
index 0000000000..8f10812a11
--- /dev/null
+++ b/src/testlib/doc/includes/building-examples.qdocinc
@@ -0,0 +1,40 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+//! [building the executable]
+ You can build the test case executable using CMake or qmake.
+
+ \section2 Building with CMake
+
+ Configure your build settings in your CMakeLists.txt file:
+
+ \quotefile \1/CMakeLists.txt
+
+ Next, from the command line, run either \c cmake or use the \c qt-cmake
+ convenience script located in
+ \c Qt-prefix/<version>/<platform>/bin/qt-cmake:
+
+ \badcode
+ <Qt-prefix>/<version>/<platform>/bin/qt-cmake <source-dir> <build-dir> -G Ninja
+ \endcode
+
+ Then, run your preferred generator tool to build the executable. Here, we're
+ using Ninja:
+
+ \badcode
+ ninja
+ \endcode
+
+ \section2 Building with qmake
+
+ Configure your build settings in your \c .pro file:
+
+ \quotefile \1/\1.pro
+
+ Next, run \c qmake, and, finally, run \c make to build your executable:
+
+ \badcode
+ qmake
+ make
+ \endcode
+//! [building the executable]
diff --git a/src/testlib/doc/qttestlib.qdocconf b/src/testlib/doc/qttestlib.qdocconf
index 6ed91593ed..8b245a864f 100644
--- a/src/testlib/doc/qttestlib.qdocconf
+++ b/src/testlib/doc/qttestlib.qdocconf
@@ -16,10 +16,6 @@ qhp.QtTestLib.virtualFolder = qttest
qhp.QtTestLib.indexTitle = Qt Test
qhp.QtTestLib.indexRoot =
-qhp.QtTestLib.filterAttributes = qttest $QT_VERSION qtrefdoc
-qhp.QtTestLib.customFilters.Qt.name = QtTest $QT_VERSION
-qhp.QtTestLib.customFilters.Qt.filterAttributes = qttest $QT_VERSION
-
qhp.QtTestLib.subprojects = classes
qhp.QtTestLib.subprojects.classes.title = C++ Classes
qhp.QtTestLib.subprojects.classes.indexTitle = Qt Test C++ Classes
@@ -45,14 +41,20 @@ sources += ../../corelib/kernel/qtestsupport_core.cpp \
exampledirs += ../../../examples/qtestlib \
.. \
. \
- snippets
+ snippets \
+ includes
excludedirs += ../../../examples/widgets/doc
imagedirs += images
+defines += QT_FEATURE_batch_test_support
+
# Add a thumbnail for examples that do not have images
manifestmeta.thumbnail.names = "QtTestLib/Chapter *"
navigation.landingpage = "Qt Test"
navigation.cppclassespage = "Qt Test C++ Classes"
+
+# Enforce zero documentation warnings
+warninglimit = 0
diff --git a/src/testlib/doc/snippets/CMakeLists.txt b/src/testlib/doc/snippets/CMakeLists.txt
index 1dac3c91aa..77e4781c44 100644
--- a/src/testlib/doc/snippets/CMakeLists.txt
+++ b/src/testlib/doc/snippets/CMakeLists.txt
@@ -1,4 +1,7 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
#! [cmake_use]
-find_package(Qt6 COMPONENTS Test REQUIRED)
+find_package(Qt6 REQUIRED COMPONENTS Test)
target_link_libraries(mytarget PRIVATE Qt6::Test)
#! [cmake_use]
diff --git a/src/testlib/doc/snippets/code/CMakeLists.txt b/src/testlib/doc/snippets/code/CMakeLists.txt
index 8a244e4dd6..54c655a521 100644
--- a/src/testlib/doc/snippets/code/CMakeLists.txt
+++ b/src/testlib/doc/snippets/code/CMakeLists.txt
@@ -1,6 +1,8 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
add_library(testlib_code_snippets OBJECT
doc_src_qtqskip.cpp
- doc_src_qttest.cpp
src_corelib_kernel_qtestsupport_core.cpp
)
diff --git a/src/testlib/doc/snippets/code/doc_src_cmakelists.txt b/src/testlib/doc/snippets/code/doc_src_cmakelists.txt
index 5e292a6417..946f09c09f 100644
--- a/src/testlib/doc/snippets/code/doc_src_cmakelists.txt
+++ b/src/testlib/doc/snippets/code/doc_src_cmakelists.txt
@@ -1,6 +1,6 @@
project(mytest LANGUAGES CXX)
-find_package(Qt6 COMPONENTS Test REQUIRED)
+find_package(Qt6 REQUIRED COMPONENTS Test)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -8,7 +8,7 @@ set(CMAKE_AUTOMOC ON)
enable_testing(true)
-add_executable(mytest tst_mytest.cpp)
+qt_add_executable(mytest tst_mytest.cpp)
add_test(NAME mytest COMMAND mytest)
target_link_libraries(mytest PRIVATE Qt::Test)
diff --git a/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp b/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp
index 42d4fe88de..99760ea730 100644
--- a/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp
+++ b/src/testlib/doc/snippets/code/doc_src_qsignalspy.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QCheckBox *box = ...;
@@ -90,9 +43,6 @@ SomeStruct result = qvariant_cast<SomeStruct>(spy.at(0).at(0));
QSignalSpy spy(myPushButton, SIGNAL(clicked(bool)));
//! [4]
-//! [5]
-QVERIFY(spy.wait(1000));
-//! [5]
//! [6]
QSignalSpy spy(myPushButton, &QPushButton::clicked);
diff --git a/src/testlib/doc/snippets/code/doc_src_qtestevent.cpp b/src/testlib/doc/snippets/code/doc_src_qtestevent.cpp
index a987cf79fb..6579c9df39 100644
--- a/src/testlib/doc/snippets/code/doc_src_qtestevent.cpp
+++ b/src/testlib/doc/snippets/code/doc_src_qtestevent.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QTest>
#include <QLineEdit>
diff --git a/src/testlib/doc/snippets/code/doc_src_qtestevent.h b/src/testlib/doc/snippets/code/doc_src_qtestevent.h
index af2bf607f1..7fdd531e52 100644
--- a/src/testlib/doc/snippets/code/doc_src_qtestevent.h
+++ b/src/testlib/doc/snippets/code/doc_src_qtestevent.h
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QObject>
// dummy
diff --git a/src/testlib/doc/snippets/code/doc_src_qtestlib.cpp b/src/testlib/doc/snippets/code/doc_src_qtestlib.cpp
index 26798e63d5..a5cc4e5e02 100644
--- a/src/testlib/doc/snippets/code/doc_src_qtestlib.cpp
+++ b/src/testlib/doc/snippets/code/doc_src_qtestlib.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QTest>
#include "src_qtestlib_qtestcase.cpp"
diff --git a/src/testlib/doc/snippets/code/doc_src_qtestlib.qdoc b/src/testlib/doc/snippets/code/doc_src_qtestlib.qdoc
index 1f372faded..9b592bdb6a 100644
--- a/src/testlib/doc/snippets/code/doc_src_qtestlib.qdoc
+++ b/src/testlib/doc/snippets/code/doc_src_qtestlib.qdoc
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
//! [2]
testname [options] [testfunctions[:testdata]]...
@@ -69,9 +22,16 @@ testname [options] [testfunctions[:testdata]]...
//! [6]
-cetest [options] ...
+./testqlocale roundTripInt:zero
//! [6]
+//! [7]
+./testqlocale roundTripInt:C
+//! [7]
+
+//! [8]
+./testqlocale roundTripInt:C:zero
+//! [8]
//! [9]
/myTestDirectory$ qmake -project "QT += testlib"
@@ -89,3 +49,54 @@ PASS : TestQString::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped
********* Finished testing of TestQString *********
//! [10]
+
+//! [11]
+********* Start testing of TestQString *********
+Config: Using QtTest library %VERSION%, Qt %VERSION%
+PASS : TestQString::initTestCase()
+PASS : TestQString::toUpper(all-lower)
+PASS : TestQString::toUpper(mixed)
+PASS : TestQString::toUpper(all-upper)
+PASS : TestQString::cleanupTestCase()
+Totals: 5 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms
+********* Finished testing of TestQString *********
+//! [11]
+
+//! [12]
+********* Start testing of TestGui *********
+Config: Using QtTest library %VERSION%, Qt %VERSION%
+PASS : TestGui::initTestCase()
+PASS : TestGui::testGui()
+PASS : TestGui::cleanupTestCase()
+Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 20ms
+********* Finished testing of TestGui **
+//! [12]
+
+//! [13]
+********* Start testing of TestGui *********
+Config: Using QtTest library %VERSION%, Qt %VERSION%
+PASS : TestGui::initTestCase()
+PASS : TestGui::testGui(char)
+PASS : TestGui::testGui(there+back-again)
+PASS : TestGui::cleanupTestCase()
+Totals: 4 passed, 0 failed, 0 skipped, 0 blacklisted, 18ms
+********* Finished testing of TestGui *********
+//! [13]
+
+//! [14]
+********* Start testing of TestBenchmark *********
+Config: Using QtTest library %VERSION%, Qt %VERSION%
+PASS : TestBenchmark::initTestCase()
+PASS : TestBenchmark::simple()
+RESULT : TestBenchmark::simple():
+ 0.00030 msecs per iteration (total: 79, iterations: 262144)
+PASS : TestBenchmark::multiple(locale-aware-compare)
+RESULT : TestBenchmark::multiple():"locale-aware-compare":
+ 0.00029 msecs per iteration (total: 78, iterations: 262144)
+.....
+PASS : TestBenchmark::series(locale-aware-compare:8001)
+RESULT : TestBenchmark::series():"locale-aware-compare:8001":
+ 0.039 msecs per iteration (total: 81, iterations: 2048)
+Totals: 15 passed, 0 failed, 0 skipped, 0 blacklisted, 3971ms
+********* Finished testing of TestBenchmark *********
+//! [14]
diff --git a/src/testlib/doc/snippets/code/doc_src_qtqskip.cpp b/src/testlib/doc/snippets/code/doc_src_qtqskip.cpp
index 550f85dd8a..85aed2870d 100644
--- a/src/testlib/doc/snippets/code/doc_src_qtqskip.cpp
+++ b/src/testlib/doc/snippets/code/doc_src_qtqskip.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QTest>
//dummy class
@@ -60,8 +13,8 @@ void tst_Skip::test_data()
{
//! [1]
QTest::addColumn<bool>("bool");
- QTest::newRow("local 1") << false;
- QTest::newRow("local 2") << true;
+ QTest::newRow("local.1") << false;
+ QTest::newRow("local.2") << true;
QSKIP("skipping all");
//! [1]
diff --git a/src/testlib/doc/snippets/code/doc_src_qtqskip_snippet.cpp b/src/testlib/doc/snippets/code/doc_src_qtqskip_snippet.cpp
index 98a209d4bb..6eb6f4ffe4 100644
--- a/src/testlib/doc/snippets/code/doc_src_qtqskip_snippet.cpp
+++ b/src/testlib/doc/snippets/code/doc_src_qtqskip_snippet.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
if (tst_Databases::getMySqlVersion(db).section(QChar('.'), 0, 0).toInt() < 5)
QSKIP("Test requires MySQL >= 5.0");
diff --git a/src/testlib/doc/snippets/code/doc_src_qttest.cpp b/src/testlib/doc/snippets/code/doc_src_qttest.cpp
deleted file mode 100644
index 7f7e04988f..0000000000
--- a/src/testlib/doc/snippets/code/doc_src_qttest.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [0]
-#include <QTest>
-//! [0]
diff --git a/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp b/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp
index 93337dc300..7faf40d9b7 100644
--- a/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp
+++ b/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core.cpp
@@ -1,59 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QTest>
// dummy class
class MyObject
{
- public:
- int isReady();
+public:
+ int isReady();
+ void startup() {}
};
// dummy function
@@ -65,9 +19,22 @@ int myNetworkServerNotResponding()
int MyObject::isReady()
{
//! [1]
+ using namespace std::chrono_literals;
int i = 0;
while (myNetworkServerNotResponding() && i++ < 50)
- QTest::qWait(250);
+ QTest::qWait(250ms);
//! [1]
return 1;
}
+
+[[maybe_unused]] static bool startup()
+{
+//! [2]
+ MyObject obj;
+ obj.startup();
+ using namespace std::chrono_literals;
+ const bool result = QTest::qWaitFor([&obj]() { return obj.isReady(); },
+ QDeadlineTimer(3s));
+//! [2]
+ return result;
+}
diff --git a/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core_snippet.cpp b/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core_snippet.cpp
deleted file mode 100644
index c8a8b859cd..0000000000
--- a/src/testlib/doc/snippets/code/src_corelib_kernel_qtestsupport_core_snippet.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//! [0]
- MyObject obj;
- obj.startup();
- QTest::qWaitFor([&]() {
- return obj.isReady();
- }, 3000);
-//! [0]
diff --git a/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp b/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp
index bea1ee6d6c..4716d06e55 100644
--- a/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp
+++ b/src/testlib/doc/snippets/code/src_qtestlib_qtestcase.cpp
@@ -1,58 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QTest>
#include <QSqlDatabase>
#include <QFontDatabase>
#include <initializer_list>
+using namespace Qt::StringLiterals;
+
// dummy
class TestBenchmark : public QObject
{
@@ -101,8 +56,8 @@ void TestQString::toInt_data()
QTest::addColumn<QString>("aString");
QTest::addColumn<int>("expected");
- QTest::newRow("positive value") << "42" << 42;
- QTest::newRow("negative value") << "-42" << -42;
+ QTest::newRow("positive+value") << "42" << 42;
+ QTest::newRow("negative-value") << "-42" << -42;
QTest::newRow("zero") << "0" << 0;
}
//! [3]
@@ -180,8 +135,8 @@ dir.mkdir("");
void MyTestClass::addSingleStringRows()
{
QTest::addColumn<QString>("aString");
- QTest::newRow("just hello") << QString("hello");
- QTest::newRow("a null string") << QString();
+ QTest::newRow("just.hello") << QString("hello");
+ QTest::newRow("a.null.string") << QString();
}
//! [20]
@@ -215,18 +170,19 @@ void MyTestClass::cleanup()
}
//! [22]
-void mySleep()
+void quarterSecondSleep()
{
//! [23]
-QTest::qSleep(250);
+using namespace std::chrono_literals;
+QTest::qSleep(250ms);
//! [23]
}
//! [27]
void TestBenchmark::simple()
{
- QString str1 = QLatin1String("This is a test string");
- QString str2 = QLatin1String("This is a test string");
+ QString str1 = u"This is a test string"_s;
+ QString str2 = u"This is a test string"_s;
QCOMPARE(str1.localeAwareCompare(str2), 0);
QBENCHMARK {
str1.localeAwareCompare(str2);
diff --git a/src/testlib/doc/snippets/code/src_qtestlib_qtestcase_snippet.cpp b/src/testlib/doc/snippets/code/src_qtestlib_qtestcase_snippet.cpp
index f414ee4252..aa24746522 100644
--- a/src/testlib/doc/snippets/code/src_qtestlib_qtestcase_snippet.cpp
+++ b/src/testlib/doc/snippets/code/src_qtestlib_qtestcase_snippet.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QVERIFY(spy.isValid());
//! [0]
@@ -166,3 +119,16 @@ char *toString(const MyType &t)
return repr;
}
//! [34]
+
+//! [35]
+QSignalSpy doubleClickSpy(target, &TargetClass::doubleClicked);
+const QPoint p(1, 2);
+QTest::mousePress(&myWindow, Qt::LeftButton, Qt::NoModifier, p);
+QVERIFY(target.isPressed());
+QTest::mouseRelease(&myWindow, Qt::LeftButton, Qt::NoModifier, p, 10);
+QCOMPARE(target.isPressed(), false);
+QTest::mousePress(&myWindow, Qt::LeftButton, Qt::NoModifier, p, 10);
+QCOMPARE(target.pressCount(), 2);
+QTest::mouseRelease(&myWindow, Qt::LeftButton, Qt::NoModifier, p, 10);
+QCOMPARE(doubleClickSpy.count(), 1);
+//! [35]
diff --git a/src/testlib/doc/src/dontdocument.qdoc b/src/testlib/doc/src/dontdocument.qdoc
index 59270c7a4f..7a5bb9bfa7 100644
--- a/src/testlib/doc/src/dontdocument.qdoc
+++ b/src/testlib/doc/src/dontdocument.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\dontdocument (QTestEventLoop QTestData QEventSizeOfChecker QSpontaneKeyEvent
diff --git a/src/testlib/doc/src/qt-webpages.qdoc b/src/testlib/doc/src/qt-webpages.qdoc
index 976435e668..611f3795ba 100644
--- a/src/testlib/doc/src/qt-webpages.qdoc
+++ b/src/testlib/doc/src/qt-webpages.qdoc
@@ -1,37 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \externalpage https://blog.qt.io/blog/2008/12/05/qtestlib-now-with-nice-graphs-pointing-upwards/
- \title qtestlib-tools Announcement
-*/
-
-/*!
- \externalpage https://www.froglogic.com/coco/
- \title Froglogic Coco Code Coverage
+ \externalpage https://www.qt.io/product/quality-assurance/coco
+ \title Coco
*/
/*!
diff --git a/src/testlib/doc/src/qt6-changes.qdoc b/src/testlib/doc/src/qt6-changes.qdoc
index 1b5b5e88ba..77e7b04afd 100644
--- a/src/testlib/doc/src/qt6-changes.qdoc
+++ b/src/testlib/doc/src/qt6-changes.qdoc
@@ -1,35 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page testlib-changes-qt6.html
\title Changes to Qt Test
\ingroup changes-qt-5-to-6
- \brief Migrate Qt Test to Qt 6.
+ \brief Touch-related functionality changes.
Qt 6 is a result of the conscious effort to make the framework more
efficient and easy to use.
diff --git a/src/testlib/doc/src/qttest-best-practices.qdoc b/src/testlib/doc/src/qttest-best-practices.qdoc
index 7143e644fd..cea5b3a8de 100644
--- a/src/testlib/doc/src/qttest-best-practices.qdoc
+++ b/src/testlib/doc/src/qttest-best-practices.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qttest-best-practices.qdoc
@@ -132,7 +108,11 @@
Do not simply number the test-case, or use bug-tracking identifiers. Someone
reading the test output will have no idea what the numbers or identifiers
mean. You can add a comment on the test-row that mentions the bug-tracking
- identifier, when relevant.
+ identifier, when relevant. It's best to avoid spacing characters and
+ characters that may be significant to command-line shells on which you may
+ want to run tests. This makes it easier to specify the test and tag on \l{Qt
+ Test Command Line Arguments}{the command-line} to your test program - for
+ example, to limit a test run to just one test-case.
\section2 Write Self-contained Test Functions
@@ -174,17 +154,24 @@
\section2 Use Data-driven Testing
- Data-driven tests make it easier to add new tests for boundary conditions
- found in later bug reports.
+ \l{Chapter 2: Data Driven Testing}{Data-driven tests} make it easier to add
+ new tests for boundary conditions found in later bug reports.
Using a data-driven test rather than testing several items in sequence in
a test saves repetition of very similar code and ensures later cases are
tested even when earlier ones fail. It also encourages systematic and
uniform testing, because the same tests are applied to each data sample.
+ When a test is data-driven, you can specify its data-tag along with the
+ test-function name, as \c{function:tag}, on the command-line of the test to
+ run the test on just one specific test-case, rather than all test-cases of
+ the function. This can be used for either a global data tag or a local tag,
+ identifying a row from the function's own data; you can even combine them as
+ \c{function:global:local}.
+
\section2 Use Coverage Tools
- Use a coverage tool such as \l {Froglogic Coco Code Coverage} or \l {gcov}
+ Use a coverage tool such as \l {Coco} or \l {gcov}
to help write tests that cover as many statements, branches, and conditions
as possible in the function or class being tested. The earlier this is done
in the development cycle for a new feature, the easier it will be to catch
@@ -193,14 +180,19 @@
\section2 Select Appropriate Mechanisms to Exclude Tests
It is important to select the appropriate mechanism to exclude inapplicable
- tests: \l QSKIP(), using conditional statements to exclude parts of a test
- function, or not building the test for a particular platform.
+ tests.
+
+ Use \l QSKIP() to handle cases where a whole test function is found at
+ run-time to be inapplicable in the current test environment. When just a
+ part of a test function is to be skipped, a conditional statement can be
+ used, optionally with a \c qDebug() call to report the reason for skipping
+ the inapplicable part.
- Use QSKIP() to handle cases where a whole test function is found at run-time
- to be inapplicable in the current test environment. When just a part of a
- test function is to be skipped, a conditional statement can be used,
- optionally with a \c qDebug() call to report the reason for skipping the
- inapplicable part.
+ When there are known test failures that should eventually be fixed,
+ \l QEXPECT_FAIL is recommended, as it supports running the rest of the
+ test, when possible. It also verifies that the issue still exists, and
+ lets the code's maintainer know if they unwittingly fix it, a benefit
+ which is gained even when using the \l {QTest::}{Abort} flag.
Test functions or data rows of a data-driven test can be limited to
particular platforms, or to particular features being enabled using
@@ -215,16 +207,28 @@
attempt to run a slot that is not implemented. In the second case, the
test will not attempt to run a test slot even though it should.
- If an entire test program is inapplicable for a specific platform or
- unless a particular feature is enabled, the best approach is to use the
- parent directory's \c .pro file to avoid building the test. For example,
- if the \c tests/auto/gui/someclass test is not valid for \macOS, add the
- following line to \c tests/auto/gui.pro:
+ If an entire test program is inapplicable for a specific platform or unless
+ a particular feature is enabled, the best approach is to use the parent
+ directory's build configuration to avoid building the test. For example, if
+ the \c tests/auto/gui/someclass test is not valid for \macOS, wrap its
+ inclusion as a subdirectory in \c{tests/auto/gui/CMakeLists.txt} in a
+ platform check:
+
+ \badcode
+ if(NOT APPLE)
+ add_subdirectory(someclass)
+ endif
+ \endcode
+
+ or, if using \c qmake, add the following line to \c tests/auto/gui.pro:
\badcode
mac*: SUBDIRS -= someclass
\endcode
+ See also \l {Chapter 6: Skipping Tests with QSKIP}
+ {Skipping Tests with QSKIP}.
+
\section2 Avoid Q_ASSERT
The \l Q_ASSERT macro causes a program to abort whenever the asserted
@@ -356,24 +360,47 @@
helpful test output:
\list
- \li \l {Explicitly Ignore Expected Warnings}
+ \li \l {Test for Warnings}
\li \l {Avoid Printing Debug Messages from Autotests}
\li \l {Write Well-structured Diagnostic Code}
\endlist
- \section2 Explicitly Ignore Expected Warnings
-
- If a test is expected to cause Qt to output a warning or debug message
- on the console, it should call \l QTest::ignoreMessage() to filter that
- message out of the test output and to fail the test if the message is
- not output.
-
- If such a message is only output when Qt is built in debug mode, use
- \l QLibraryInfo::isDebugBuild() to determine whether the Qt libraries
- were built in debug mode. Using \c{#ifdef QT_DEBUG} is not enough, as
- it will only tell you whether the test was built in debug mode, and
- that does not guarantee that the Qt libraries were also built in debug
- mode.
+ \section2 Test for Warnings
+
+ Just as when building your software, if test output is cluttered with
+ warnings you will find it harder to notice a warning that really is a clue
+ to the emergence of a bug. It is thus prudent to regularly check your test
+ logs for warnings, and other extraneous output, and investigate the
+ causes. When they are signs of a bug, you can make warnings trigger test
+ failure.
+
+ When the code under test \e should produce messages, such as warnings
+ about misguided use, it is also important to test that it \e does produce
+ them when so used. You can test for expected messages from the code under
+ test, produced by \l qWarning(), \l qDebug(), \l qInfo() and friends,
+ using \l QTest::ignoreMessage(). This will verify that the message is
+ produced and filter it out of the output of the test run. If the message
+ is not produced, the test will fail.
+
+ If an expected message is only output when Qt is built in debug mode, use
+ \l QLibraryInfo::isDebugBuild() to determine whether the Qt libraries were
+ built in debug mode. Using \c{#ifdef QT_DEBUG} is not enough, as it will
+ only tell you whether \e{the test} was built in debug mode, and that does
+ not guarantee that the \e{Qt libraries} were also built in debug mode.
+
+ Your tests can (since Qt 6.3) verify that they do not trigger calls to
+ \l qWarning() by calling \l QTest::failOnWarning(). This takes the warning
+ message to test for or a \l QRegularExpression to match against warnings; if
+ a matching warning is produced, it will be reported and cause the test to
+ fail. For example, a test that should produce no warnings at all can
+ \c{QTest::failOnWarning(QRegularExpression(u".*"_s))}, which will match any
+ warning at all.
+
+ You can also set the environment variable \c QT_FATAL_WARNINGS to cause
+ warnings to be treated as fatal errors. See \l qWarning() for details; this
+ is not specific to autotests. If warnings would otherwise be lost in vast
+ test logs, the occasional run with this environment variable set can help
+ you to find and eliminate any that do arise.
\section2 Avoid Printing Debug Messages from Autotests
diff --git a/src/testlib/doc/src/qttest-index.qdoc b/src/testlib/doc/src/qttest-index.qdoc
index 37fd5a9832..59ce61eb0d 100644
--- a/src/testlib/doc/src/qttest-index.qdoc
+++ b/src/testlib/doc/src/qttest-index.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qttest-index.html
\title Qt Test
@@ -40,13 +16,17 @@
to work with the Qt version it was developed against. However, source
compatibility is guaranteed.
- \include module-use.qdocinc using qt module
- \snippet snippets/CMakeLists.txt cmake_use
+ \section1 Using the Module
- See also the \l {Build with CMake} overview.
+ \include {module-use.qdocinc} {using the c++ api}
- \include module-use.qdocinc building with qmake
- \snippet snippets/code/code.pro qmake_use
+ \section2 Building with CMake
+
+ \include {module-use.qdocinc} {building with cmake} {Test}
+
+ \section2 Building with qmake
+
+ \include {module-use.qdocinc} {building_with_qmake} {testlib}
\section1 Articles and Guides
@@ -56,6 +36,19 @@
\li \l{Qt Test Tutorial}
\endlist
+ \section1 Reference
+
+ \list
+ \li \l{Qt Test C++ Classes}
+ \endlist
+
+ The \l {Qt Quick Test} module enables unit testing of Qt Quick applications.
+
+ \list
+ \li \l{Qt Quick Test QML Types}
+ \li \l{Qt Quick Test C++ API}
+ \endlist
+
\section1 Module Evolution
\l{Changes to Qt Test} lists important changes in the module API
and functionality that were done for the Qt 6 series of Qt.
@@ -69,21 +62,7 @@
See \l{Qt Licensing} for further details.
Furthermore, Qt Test in Qt \QtVersion may contain third party
- modules under following permissive licenses:
+ modules under the following permissive licenses:
\generatelist{groupsbymodule attributions-qttestlib}
-
- \section1 Reference
-
- \list
- \li \l{Qt Test C++ Classes}
- \endlist
-
- The \l {Qt Quick Test} module enables unit testing Qt Quick applications.
-
- \list
- \li \l{Qt Quick Test QML Types}
- \li \l{Qt Quick Test C++ API}
- \endlist
-
*/
diff --git a/src/testlib/doc/src/qttest.qdoc b/src/testlib/doc/src/qttest.qdoc
index fc192644b1..c558defa59 100644
--- a/src/testlib/doc/src/qttest.qdoc
+++ b/src/testlib/doc/src/qttest.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\module QtTest
diff --git a/src/testlib/doc/src/qttestlib-manual.qdoc b/src/testlib/doc/src/qttestlib-manual.qdoc
index 9073cc7c88..1b6f534045 100644
--- a/src/testlib/doc/src/qttestlib-manual.qdoc
+++ b/src/testlib/doc/src/qttestlib-manual.qdoc
@@ -1,30 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtest-overview.html
@@ -133,6 +109,47 @@
For more examples, refer to the \l{Qt Test Tutorial}.
+ \section1 Increasing Test Function Timeout
+
+ QtTest limits the run-time of each test to catch infinite loops and similar
+ bugs. By default, any test function call will be interrupted after five
+ minutes. For data-driven tests, this applies to each call with a distinct
+ data-tag. This timeout can be configured by setting the \c QTEST_FUNCTION_TIMEOUT
+ environment variable to the maximum number of milliseconds that is acceptable
+ for a single call to take. If a test takes longer than the configured timeout,
+ it is interrupted, and \c qFatal() is called. As a result, the test aborts by
+ default, as if it had crashed.
+
+ To set \c QTEST_FUNCTION_TIMEOUT from the command line on Linux or macOS, enter:
+
+ \badcode
+ QTEST_FUNCTION_TIMEOUT=900000
+ export QTEST_FUNCTION_TIMEOUT
+ \endcode
+
+ On Windows:
+ \badcode
+ SET QTEST_FUNCTION_TIMEOUT=900000
+ \endcode
+
+ Then run the test inside this environment.
+
+ Alternatively, you can set the environment variable programmatically in the
+ test code itself, for example by calling, from the
+ \l{Creating a Test}{initMain()} special method of your test class:
+
+ \badcode
+ qputenv("QTEST_FUNCTION_TIMEOUT", "900000");
+ \endcode
+
+ To calculate a suitable value for the timeout, see how long the test usually
+ takes and decide how much longer it can take without that being a symptom of
+ some problem. Convert that longer time to milliseconds to get the timeout value.
+ For example, if you decide that a test that takes several minutes could
+ reasonably take up to twenty minutes, for example on a slow machine,
+ multiply \c{20 * 60 * 1000 = 1200000} and set the environment variable to
+ \c 1200000 instead of the \c 900000 above.
+
\if !defined(qtforpython)
\section1 Building a Test
@@ -153,6 +170,12 @@
All labeled targets will be run when \c {test} target is called on the
command line.
+ \note On Android, if you have one connected device or emulator, tests will
+ run on that device. If you have more than one device connected, set the
+ environment variable \c {ANDROID_DEVICE_SERIAL} to the
+ \l {Android: Query for devices}{ADB serial number} of the device that
+ you want to run tests on.
+
There are several other advantages with CMake. For example, the result of
a test run can be published on a web server using CDash with virtually no
effort.
@@ -222,9 +245,9 @@
\snippet code/doc_src_qtestlib.qdoc 4
Runs the \c toUpper test function with all available test data,
- and the \c toInt test function with the test data called \c
+ and the \c toInt test function with the test data row called \c
zero (if the specified test data doesn't exist, the associated test
- will fail).
+ will fail and the available data tags are reported).
\snippet code/doc_src_qtestlib.qdoc 5
@@ -240,26 +263,28 @@
\list
\li \c -o \e{filename,format} \br
- Writes output to the specified file, in the specified format (one of
- \c txt, \c xml, \c lightxml, \c junitxml or \c tap). The special filename \c -
- may be used to log to standard output.
+ Writes output to the specified file, in the specified format (one
+ of \c txt, \c csv, \c junitxml, \c xml, \c lightxml, \c teamcity
+ or \c tap). Use the special filename \c{-} (hyphen) to log to
+ standard output.
\li \c -o \e filename \br
Writes output to the specified file.
\li \c -txt \br
Outputs results in plain text.
+ \li \c -csv \br
+ Outputs results as comma-separated values (CSV) suitable for
+ import into spreadsheets. This mode is only suitable for
+ benchmarks, since it suppresses normal pass/fail messages.
+ \li \c -junitxml \br
+ Outputs results as a \l{JUnit XML} document.
\li \c -xml \br
Outputs results as an XML document.
\li \c -lightxml \br
Outputs results as a stream of XML tags.
- \li \c -junitxml \br
- Outputs results as an JUnit XML document.
- \li \c -csv \br
- Outputs results as comma-separated values (CSV). This mode is only suitable for
- benchmarks, since it suppresses normal pass/fail messages.
\li \c -teamcity \br
- Outputs results in TeamCity format.
+ Outputs results in \l{TeamCity} format.
\li \c -tap \br
- Outputs results in Test Anything Protocol (TAP) format.
+ Outputs results in \l{Test Anything Protocol} (TAP) format.
\endlist
The first version of the \c -o option may be repeated in order to log
@@ -323,6 +348,16 @@
Disables the crash handler on Unix platforms.
On Windows, it re-enables the Windows Error Reporting dialog, which is
turned off by default. This is useful for debugging crashes.
+ \li \c -repeat \e n \br
+ Run the testsuite n times or until the test fails. Useful for finding
+ flaky tests. If negative, the tests are repeated forever. This is intended
+ as a developer tool, and is only supported with the plain text logger.
+ \li \c -skipblacklisted \br
+ Skip the blacklisted tests. This option is intended to allow more accurate
+ measurement of test coverage by preventing blacklisted tests from inflating
+ coverage statistics. When not measuring test coverage, it is recommended to
+ execute blacklisted tests to reveal any changes in their results, such as
+ a new crash or the issue that caused blacklisting being resolved.
\li \c -platform \e name \br
This command line argument applies to all Qt applications, but might be
@@ -378,8 +413,14 @@
Setting this variable to a non-zero value will cause a failure in
an autotest to immediately abort the entire autotest. This is useful
to e.g. debug an unstable or intermittent failure in a test, by
- launching the test in a debugger. Support for this variable has been
+ launching the test in a debugger. Support for this variable was
added in Qt 6.1.
+ \li \c QTEST_THROW_ON_FAIL (since 6.8) \br
+ Setting this variable to a non-zero value will cause QCOMPARE()/QVERIFY()
+ etc to throw on failure (as opposed to just returning from the
+ immediately-surrounding function scope).
+ \li \c QTEST_THROW_ON_SKIP (since 6.8) \br
+ Same as \c QTEST_THROW_ON_FAIL, except affecting QSKIP().
\endlist
\section1 Creating a Benchmark
@@ -477,7 +518,7 @@
You can define \c{initTestCase_data()} to set up a global test data table.
Each test is run once for each row in the global test data table. When the
- test function itself \l{Chapter 2: Data-driven Testing}{is data-driven},
+ test function itself \l{Chapter 2: Data Driven Testing}{is data-driven},
it is run for each local data row, for each global data row. So, if there
are \c g rows in the global data table and \c d rows in the test's own
data-table, the number of runs of this test is \c g times \c d.
@@ -500,6 +541,32 @@
each locale provided by \c {initTestCase_data()}:
\snippet code/src_qtestlib_qtestcase_snippet.cpp 31
+
+ On the command-line of a test you can pass the name of a function (with no
+ test-class-name prefix) to run only that one function's tests. If the test
+ class has global data, or the function is data-driven, you can append a data
+ tag, after a colon, to run only that tag's data-set for the function. To
+ specify both a global tag and a tag specific to the test function, combine
+ them with a colon between, putting the global data tag first. For example
+
+ \snippet code/doc_src_qtestlib.qdoc 6
+
+ will run the \c zero test-case of the \c roundTripInt() test above (assuming
+ its \c TestQLocale class has been compiled to an executable \c testqlocale)
+ in each of the locales specified by \c initTestCase_data(), while
+
+ \snippet code/doc_src_qtestlib.qdoc 7
+
+ will run all three test-cases of \c roundTripInt() only in the C locale and
+
+ \snippet code/doc_src_qtestlib.qdoc 8
+
+ will only run the \c zero test-case in the C locale.
+
+ Providing such fine-grained control over which tests are to be run can make
+ it considerably easier to debug a problem, as you only need to step through
+ the one test-case that has been seen to fail.
+
*/
/*!
@@ -510,9 +577,8 @@
\title Qt Test Tutorial
- This tutorial gives a short introduction to how to use some of the
- features of the Qt Test framework. It is divided into five
- chapters:
+ This tutorial introduces some of the features of the Qt Test framework. It
+ is divided into six chapters:
\list 1
\li \l {Chapter 1: Writing a Unit Test}{Writing a Unit Test}
@@ -523,427 +589,5 @@
\li \l {Chapter 6: Skipping Tests with QSKIP}{Skipping Tests}
\endlist
-*/
-
-
-/*!
- \example tutorial1
-
- \nextpage {Chapter 2: Data Driven Testing}{Chapter 2}
-
- \title Chapter 1: Writing a Unit Test
- \brief How to write a unit test.
-
- In this first chapter we will see how to write a simple unit test
- for a class, and how to execute it.
-
- \section1 Writing a Test
-
- Let's assume you want to test the behavior of our QString class.
- First, you need a class that contains your test functions. This class
- has to inherit from QObject:
-
- \snippet tutorial1/testqstring.cpp 0
-
- \note You need to include the QTest header and declare the test functions as
- private slots so the test framework finds and executes it.
-
- Then you need to implement the test function itself. The
- implementation could look like this:
-
- \snippet code/doc_src_qtestlib.cpp 8
-
- The \l QVERIFY() macro evaluates the expression passed as its
- argument. If the expression evaluates to true, the execution of
- the test function continues. Otherwise, a message describing the
- failure is appended to the test log, and the test function stops
- executing.
-
- But if you want a more verbose output to the test log, you should
- use the \l QCOMPARE() macro instead:
-
- \snippet tutorial1/testqstring.cpp 1
-
- If the strings are not equal, the contents of both strings are
- appended to the test log, making it immediately visible why the
- comparison failed.
-
- Finally, to make our test case a stand-alone executable, the
- following two lines are needed:
-
- \snippet tutorial1/testqstring.cpp 2
-
- The \l QTEST_MAIN() macro expands to a simple \c main()
- method that runs all the test functions. Note that if both the
- declaration and the implementation of our test class are in a \c
- .cpp file, we also need to include the generated moc file to make
- Qt's introspection work.
-
- \section1 Executing a Test
-
- Now that we finished writing our test, we want to execute
- it. Assuming that our test was saved as \c testqstring.cpp in an
- empty directory, we build the test using qmake to create a project
- and generate a makefile.
-
- \snippet code/doc_src_qtestlib.qdoc 9
-
- \note If you're using windows, replace \c make with \c
- nmake or whatever build tool you use.
-
- Running the resulting executable should give you the following
- output:
-
- \snippet code/doc_src_qtestlib.qdoc 10
-
- Congratulations! You just wrote and executed your first unit test
- using the Qt Test framework.
-*/
-
-/*!
- \example tutorial2
-
- \previouspage {Chapter 1: Writing a Unit Test}{Chapter 1}
- \nextpage {Chapter 3: Simulating Gui Events}{Chapter 3}
-
- \title Chapter 2: Data Driven Testing
- \brief How to create data driven tests.
-
- In this chapter we will demonstrate how to execute a test
- multiple times with different test data.
-
- So far, we have hard coded the data we wanted to test into our
- test function. If we add more test data, the function might look like
- this:
-
- \snippet code/doc_src_qtestlib.cpp 11
-
- To prevent that the function ends up being cluttered by repetitive
- code, Qt Test supports adding test data to a test function. All
- we need is to add another private slot to our test class:
-
- \snippet tutorial2/testqstring.cpp 0
-
- \section1 Writing the Data Function
-
- A test function's associated data function carries the same name,
- appended by \c{_data}. Our data function looks like this:
-
- \snippet tutorial2/testqstring.cpp 1
-
- First, we define the two elements of our test table using the \l
- QTest::addColumn() function: a test string, and the
- expected result of applying the QString::toUpper() function to
- that string.
-
- Then we add some data to the table using the \l
- QTest::newRow() function. Each set of data will become a
- separate row in the test table.
-
- \l QTest::newRow() takes one argument: a name that will be associated
- with the data set and used in the test log to identify the data set.
- Then we stream the data set into the new table row. First an arbitrary
- string, and then the expected result of applying the
- QString::toUpper() function to that string.
-
- You can think of the test data as a two-dimensional table. In
- our case, it has two columns called \c string and \c result and
- three rows. In addition a name as well as an index is associated
- with each row:
-
- \table
- \header
- \li index
- \li name
- \li string
- \li result
- \row
- \li 0
- \li all lower
- \li "hello"
- \li HELLO
- \row
- \li 1
- \li mixed
- \li "Hello"
- \li HELLO
- \row
- \li 2
- \li all upper
- \li "HELLO"
- \li HELLO
- \endtable
-
- When data is streamed into the row, each datum is asserted to match
- the type of the column whose value it supplies. If any assertion fails,
- the test is aborted.
-
- \section1 Rewriting the Test Function
-
- Our test function can now be rewritten:
-
- \snippet tutorial2/testqstring.cpp 2
-
- The TestQString::toUpper() function will be executed three times,
- once for each entry in the test table that we created in the
- associated TestQString::toUpper_data() function.
-
- First, we fetch the two elements of the data set using the \l
- QFETCH() macro. \l QFETCH() takes two arguments: The data type of
- the element and the element name. Then we perform the test using
- the \l QCOMPARE() macro.
-
- This approach makes it very easy to add new data to the test
- without modifying the test itself.
-
- And again, to make our test case a stand-alone executable,
- the following two lines are needed:
-
- \snippet tutorial2/testqstring.cpp 3
-
- As before, the QTEST_MAIN() macro expands to a simple main()
- method that runs all the test functions, and since both the
- declaration and the implementation of our test class are in a .cpp
- file, we also need to include the generated moc file to make Qt's
- introspection work.
-*/
-
-/*!
- \example tutorial3
-
- \previouspage {Chapter 2: Data Driven Testing}{Chapter 2}
- \nextpage {Chapter 4: Replaying GUI Events}{Chapter 4}
-
- \title Chapter 3: Simulating GUI Events
- \brief Howe to simulate GUI events.
-
- Qt Test features some mechanisms to test graphical user
- interfaces. Instead of simulating native window system events,
- Qt Test sends internal Qt events. That means there are no
- side-effects on the machine the tests are running on.
-
- In this chapter we will see how to write a simple GUI test.
-
- \section1 Writing a GUI Test
-
- This time, let's assume you want to test the behavior of our
- QLineEdit class. As before, you will need a class that contains
- your test function:
-
- \snippet tutorial3/testgui.cpp 0
-
- The only difference is that you need to include the Qt GUI class
- definitions in addition to the QTest namespace.
-
- \snippet tutorial3/testgui.cpp 1
-
- In the implementation of the test function we first create a
- QLineEdit. Then we simulate writing "hello world" in the line edit
- using the \l QTest::keyClicks() function.
-
- \note The widget must also be shown in order to correctly test keyboard
- shortcuts.
-
- QTest::keyClicks() simulates clicking a sequence of keys on a
- widget. Optionally, a keyboard modifier can be specified as well
- as a delay (in milliseconds) of the test after each key click. In
- a similar way, you can use the QTest::keyClick(),
- QTest::keyPress(), QTest::keyRelease(), QTest::mouseClick(),
- QTest::mouseDClick(), QTest::mouseMove(), QTest::mousePress()
- and QTest::mouseRelease() functions to simulate the associated
- GUI events.
-
- Finally, we use the \l QCOMPARE() macro to check if the line edit's
- text is as expected.
-
- As before, to make our test case a stand-alone executable, the
- following two lines are needed:
-
- \snippet tutorial3/testgui.cpp 2
-
- The QTEST_MAIN() macro expands to a simple main() method that
- runs all the test functions, and since both the declaration and
- the implementation of our test class are in a .cpp file, we also
- need to include the generated moc file to make Qt's introspection
- work.
-*/
-
-/*!
- \example tutorial4
-
- \previouspage {Chapter 3: Simulating GUI Events}{Chapter 3}
- \nextpage {Chapter 5: Writing a Benchmark}{Chapter 5}
-
- \title Chapter 4: Replaying GUI Events
- \brief How to replay GUI events.
-
- In this chapter, we will show how to simulate a GUI event,
- and how to store a series of GUI events as well as replay them on
- a widget.
-
- The approach to storing a series of events and replaying them is
- quite similar to the approach explained in \l {Chapter 2:
- Data Driven Testing}{chapter 2}. All you need to do is to add a data
- function to your test class:
-
- \snippet tutorial4/testgui.cpp 0
-
- \section1 Writing the Data Function
-
- As before, a test function's associated data function carries the
- same name, appended by \c{_data}.
-
- \snippet tutorial4/testgui.cpp 1
-
- First, we define the elements of the table using the
- QTest::addColumn() function: A list of GUI events, and the
- expected result of applying the list of events on a QWidget. Note
- that the type of the first element is \l QTestEventList.
-
- A QTestEventList can be populated with GUI events that can be
- stored as test data for later usage, or be replayed on any
- QWidget.
-
- In our current data function, we create two \l
- {QTestEventList} elements. The first list consists of a single click to
- the 'a' key. We add the event to the list using the
- QTestEventList::addKeyClick() function. Then we use the
- QTest::newRow() function to give the data set a name, and
- stream the event list and the expected result into the table.
-
- The second list consists of two key clicks: an 'a' with a
- following 'backspace'. Again we use the
- QTestEventList::addKeyClick() to add the events to the list, and
- QTest::newRow() to put the event list and the expected
- result into the table with an associated name.
-
- \section1 Rewriting the Test Function
-
- Our test can now be rewritten:
-
- \snippet tutorial4/testgui.cpp 2
-
- The TestGui::testGui() function will be executed two times,
- once for each entry in the test data that we created in the
- associated TestGui::testGui_data() function.
-
- First, we fetch the two elements of the data set using the \l
- QFETCH() macro. \l QFETCH() takes two arguments: the data type of
- the element and the element name. Then we create a QLineEdit, and
- apply the list of events on that widget using the
- QTestEventList::simulate() function.
-
- Finally, we use the QCOMPARE() macro to check if the line edit's
- text is as expected.
-
- As before, to make our test case a stand-alone executable,
- the following two lines are needed:
-
- \snippet tutorial4/testgui.cpp 3
-
- The QTEST_MAIN() macro expands to a simple main() method that
- runs all the test functions, and since both the declaration and
- the implementation of our test class are in a .cpp file, we also
- need to include the generated moc file to make Qt's introspection
- work.
-*/
-
-/*!
- \example tutorial5
-
- \previouspage {Chapter 4: Replaying GUI Events}{Chapter 4}
- \nextpage {Chapter 6: Skipping Tests with QSKIP}{Chapter 6}
-
- \title Chapter 5: Writing a Benchmark
- \brief How to write a benchmark.
-
- In this final chapter we will demonstrate how to write benchmarks
- using Qt Test.
-
- \section1 Writing a Benchmark
- To create a benchmark we extend a test function with a QBENCHMARK macro.
- A benchmark test function will then typically consist of setup code and
- a QBENCHMARK macro that contains the code to be measured. This test
- function benchmarks QString::localeAwareCompare().
-
- \snippet tutorial5/benchmarking.cpp 0
-
- Setup can be done at the beginning of the function, the clock is not
- running at this point. The code inside the QBENCHMARK macro will be
- measured, and possibly repeated several times in order to get an
- accurate measurement.
-
- Several \l {testlib-benchmarking-measurement}{back-ends} are available
- and can be selected on the command line.
-
- \section1 Data Functions
-
- Data functions are useful for creating benchmarks that compare
- multiple data inputs, for example locale aware compare against standard
- compare.
-
- \snippet tutorial5/benchmarking.cpp 1
-
- The test function then uses the data to determine what to benchmark.
-
- \snippet tutorial5/benchmarking.cpp 2
-
- The "if (useLocaleCompare)" switch is placed outside the QBENCHMARK
- macro to avoid measuring its overhead. Each benchmark test function
- can have one active QBENCHMARK macro.
-
- \section1 External Tools
-
- Tools for handling and visualizing test data are available as part of
- the \l {qtestlib-tools} project.
- These include a tool for comparing performance data obtained from test
- runs and a utility to generate Web-based graphs of performance data.
-
- See the \l{qtestlib-tools Announcement}{qtestlib-tools announcement}
- for more information on these tools and a simple graphing example.
-
-*/
-/*!
- \page qttestlib-tutorial6.html
-
- \previouspage {Chapter 5: Writing a Benchmark}{Chapter 5}
-
- \title Chapter 6: Skipping Tests with QSKIP
- \brief How to skip tests in certain cases.
-
- \section2 Using QSKIP(\a description) in a test function
-
- If the QSKIP() macro is called from a test function, it stops
- the execution of the test without adding a failure to the test log.
- It can be used to skip tests that are certain to fail. The text in
- the QSKIP \a description parameter is appended to the test log,
- and explains why the test was not carried out.
-
- QSKIP can be used to skip testing when the implementation is not yet
- complete or not supported on a certain platform. When there are known
- failures, it is recommended to use QEXPECT_FAIL, so that the test is
- always completely executed.
-
- Example of QSKIP in a test function:
-
- \snippet code/doc_src_qtqskip_snippet.cpp 0
-
- In a data-driven test, each call to QSKIP() skips only the current
- row of test data. If the data-driven test contains an unconditional
- call to QSKIP, it produces a skip message for each row of test data.
-
- \section2 Using QSKIP in a _data function
-
- If called from a _data function, the QSKIP() macro stops
- execution of the _data function. This prevents execution of the
- associated test function.
-
- See below for an example:
-
- \snippet code/doc_src_qtqskip.cpp 1
-
- \section2 Using QSKIP from initTestCase() or initTestCase_data()
- If called from \c initTestCase() or \c initTestCase_data(), the
- QSKIP() macro will skip all test and _data functions.
*/
diff --git a/src/testlib/doc/src/qttestlib-tutorial1.qdoc b/src/testlib/doc/src/qttestlib-tutorial1.qdoc
new file mode 100644
index 0000000000..41d9264c0d
--- /dev/null
+++ b/src/testlib/doc/src/qttestlib-tutorial1.qdoc
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qttestlib-tutorial1-example.html
+ \previouspage {Qt Test Tutorial}{Qt Test Tutorial Overview}
+ \nextpage {Chapter 2: Data Driven Testing}{Chapter 2}
+
+ \title Chapter 1: Writing a Unit Test
+ \brief How to write a unit test.
+
+ This first chapter demonstrates how to write a simple unit test and how to
+ run the test case as a stand-alone executable.
+
+ \section1 Writing a Test
+
+ Let's assume you want to test the behavior of our QString class.
+ First, you need a class that contains your test functions. This class
+ has to inherit from QObject:
+
+ \snippet tutorial1/testqstring.cpp 0
+
+ \note You need to include the QTest header and declare the test functions as
+ private slots so the test framework finds and executes it.
+
+ Then you need to implement the test function itself. The
+ implementation could look like this:
+
+ \snippet code/doc_src_qtestlib.cpp 8
+
+ The \l QVERIFY() macro evaluates the expression passed as its
+ argument. If the expression evaluates to true, the execution of
+ the test function continues. Otherwise, a message describing the
+ failure is appended to the test log, and the test function stops
+ executing.
+
+ But if you want a more verbose output to the test log, you should
+ use the \l QCOMPARE() macro instead:
+
+ \snippet tutorial1/testqstring.cpp 1
+
+ If the strings are not equal, the contents of both strings are
+ appended to the test log, making it immediately visible why the
+ comparison failed.
+
+ \section1 Preparing the Stand-Alone Executable
+
+ Finally, to make our test case a stand-alone executable, the
+ following two lines are needed:
+
+ \snippet tutorial1/testqstring.cpp 2
+
+ The \l QTEST_MAIN() macro expands to a simple \c main()
+ method that runs all the test functions. Note that if both the
+ declaration and the implementation of our test class are in a \c
+ .cpp file, we also need to include the generated moc file to make
+ Qt's introspection work.
+
+ \section1 Building the Executable
+
+ \include {building-examples.qdocinc} {building the executable} {tutorial1}
+
+ \note If you're using windows, replace \c make with \c
+ nmake or whatever build tool you use.
+
+ \section1 Running the Executable
+
+ Running the resulting executable should give you the following
+ output:
+
+ \snippet code/doc_src_qtestlib.qdoc 10
+
+ Congratulations! You just wrote and executed your first unit test
+ using the Qt Test framework.
+*/
diff --git a/src/testlib/doc/src/qttestlib-tutorial2.qdoc b/src/testlib/doc/src/qttestlib-tutorial2.qdoc
new file mode 100644
index 0000000000..bd828b3963
--- /dev/null
+++ b/src/testlib/doc/src/qttestlib-tutorial2.qdoc
@@ -0,0 +1,132 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qttestlib-tutorial2-example.html
+ \previouspage {Chapter 1: Writing a Unit Test}{Chapter 1}
+ \nextpage {Chapter 3: Simulating Gui Events}{Chapter 3}
+
+ \title Chapter 2: Data Driven Testing
+ \brief How to create data driven tests.
+
+ This chapter demonstrates how to execute a test multiple times with
+ different test data.
+
+ So far, we have hard coded the data we wanted to test into our
+ test function. If we add more test data, the function might look like
+ this:
+
+ \snippet code/doc_src_qtestlib.cpp 11
+
+ To prevent the function from being cluttered with repetitive code, Qt Test
+ supports adding test data to a test function. All we need is to add another
+ private slot to our test class:
+
+ \snippet tutorial2/testqstring.cpp 0
+
+ \section1 Writing the Data Function
+
+ A test function's associated data function has \c _data appended to its
+ name. Our data function looks like this:
+
+ \snippet tutorial2/testqstring.cpp 1
+
+ First, we define the two elements of our test table using the \l
+ QTest::addColumn() function: a test string and the
+ expected result of applying the QString::toUpper() function to
+ that string.
+
+ Then, we add some data to the table using the \l QTest::newRow()
+ function. We can also use \l QTest::addRow() if we need to format some data
+ in the row name, for example when generating many data rows iteratively.
+ Each row of data will become a separate row in the test table.
+
+ \l QTest::newRow() takes one argument: a name that will be associated with
+ the data set and used in the test log to identify the data row. \l
+ QTest::addRow() takes a (\c{printf}-style) format string followed by the
+ parameters to be represented in place of the formatting tokens in the format
+ string. Then, we stream the data set into the new table row. First an
+ arbitrary string, and then the expected result of applying the
+ QString::toUpper() function to that string.
+
+ You can think of the test data as a two-dimensional table. In
+ our case, it has two columns called \c string and \c result and
+ three rows. In addition, a name and an index are associated
+ with each row:
+
+ \table
+ \header
+ \li index
+ \li name
+ \li string
+ \li result
+ \row
+ \li 0
+ \li all-lower
+ \li "hello"
+ \li HELLO
+ \row
+ \li 1
+ \li mixed
+ \li "Hello"
+ \li HELLO
+ \row
+ \li 2
+ \li all-upper
+ \li "HELLO"
+ \li HELLO
+ \endtable
+
+ When data is streamed into the row, each datum is asserted to match
+ the type of the column whose value it supplies. If any assertion fails,
+ the test is aborted.
+
+ The names of rows and columns, in a given test function's data table, should
+ be unique: if two rows share a name, or two columns share a name, a warning
+ will (since Qt 6.5) be produced. See \l qWarning() for how you can cause
+ warnings to be treated as errors and \l {Test for Warnings} for how to get
+ your tests clear of other warnings.
+
+ \section1 Rewriting the Test Function
+
+ Our test function can now be rewritten:
+
+ \snippet tutorial2/testqstring.cpp 2
+
+ The TestQString::toUpper() function will be executed three times,
+ once for each entry in the test table that we created in the
+ associated TestQString::toUpper_data() function.
+
+ First, we fetch the two elements of the data set using the \l
+ QFETCH() macro. \l QFETCH() takes two arguments: The data type of
+ the element and the element name. Then, we perform the test using
+ the \l QCOMPARE() macro.
+
+ This approach makes it very easy to add new data to the test
+ without modifying the test itself.
+
+ \section1 Preparing the Stand-Alone Executable
+
+ And again, to make our test case a stand-alone executable,
+ the following two lines are needed:
+
+ \snippet tutorial2/testqstring.cpp 3
+
+ As before, the QTEST_MAIN() macro expands to a simple main()
+ method that runs all the test functions, and since both the
+ declaration and the implementation of our test class are in a .cpp
+ file, we also need to include the generated moc file to make Qt's
+ introspection work.
+
+ \section1 Building the Executable
+
+ \include {building-examples.qdocinc} {building the executable} {tutorial2}
+
+ \section1 Running the Executable
+
+ Running the resulting executable should give you the following
+ output:
+
+ \snippet code/doc_src_qtestlib.qdoc 11
+*/
diff --git a/src/testlib/doc/src/qttestlib-tutorial3.qdoc b/src/testlib/doc/src/qttestlib-tutorial3.qdoc
new file mode 100644
index 0000000000..2b7fe25c96
--- /dev/null
+++ b/src/testlib/doc/src/qttestlib-tutorial3.qdoc
@@ -0,0 +1,75 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qttestlib-tutorial3-example.html
+ \previouspage {Chapter 2: Data Driven Testing}{Chapter 2}
+ \nextpage {Chapter 4: Replaying GUI Events}{Chapter 4}
+
+ \title Chapter 3: Simulating GUI Events
+ \brief How to simulate GUI events.
+
+ Qt Test features some mechanisms to test graphical user
+ interfaces. Instead of simulating native window system events,
+ Qt Test sends internal Qt events. That means there are no
+ side-effects on the machine the tests are running on.
+
+ This chapter demonstrates how to write a simple GUI test.
+
+ \section1 Writing a GUI Test
+
+ This time, let's assume you want to test the behavior of our
+ QLineEdit class. As before, you will need a class that contains
+ your test function:
+
+ \snippet tutorial3/testgui.cpp 0
+
+ The only difference is that you need to include the Qt GUI class
+ definitions in addition to the QTest namespace.
+
+ \snippet tutorial3/testgui.cpp 1
+
+ In the implementation of the test function, we first create a
+ QLineEdit. Then, we simulate writing "hello world" in the line edit
+ using the \l QTest::keyClicks() function.
+
+ \note The widget must also be shown in order to correctly test keyboard
+ shortcuts.
+
+ QTest::keyClicks() simulates clicking a sequence of keys on a
+ widget. Optionally, a keyboard modifier can be specified as well
+ as a delay (in milliseconds) of the test after each key click. In
+ a similar way, you can use the QTest::keyClick(),
+ QTest::keyPress(), QTest::keyRelease(), QTest::mouseClick(),
+ QTest::mouseDClick(), QTest::mouseMove(), QTest::mousePress()
+ and QTest::mouseRelease() functions to simulate the associated
+ GUI events.
+
+ Finally, we use the \l QCOMPARE() macro to check if the line edit's
+ text is as expected.
+
+ \section1 Preparing the Stand-Alone Executable
+
+ As before, to make our test case a stand-alone executable, the
+ following two lines are needed:
+
+ \snippet tutorial3/testgui.cpp 2
+
+ The QTEST_MAIN() macro expands to a simple main() method that
+ runs all the test functions, and since both the declaration and
+ the implementation of our test class are in a .cpp file, we also
+ need to include the generated moc file to make Qt's introspection
+ work.
+
+ \section1 Building the Executable
+
+ \include {building-examples.qdocinc} {building the executable} {tutorial3}
+
+ \section1 Running the Executable
+
+ Running the resulting executable should give you the following
+ output:
+
+ \snippet code/doc_src_qtestlib.qdoc 12
+*/
diff --git a/src/testlib/doc/src/qttestlib-tutorial4.qdoc b/src/testlib/doc/src/qttestlib-tutorial4.qdoc
new file mode 100644
index 0000000000..d5a0121f67
--- /dev/null
+++ b/src/testlib/doc/src/qttestlib-tutorial4.qdoc
@@ -0,0 +1,95 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qttestlib-tutorial4-example.html
+ \previouspage {Chapter 3: Simulating GUI Events}{Chapter 3}
+ \nextpage {Chapter 5: Writing a Benchmark}{Chapter 5}
+
+ \title Chapter 4: Replaying GUI Events
+ \brief How to replay GUI events.
+
+ In this chapter, we will show how to simulate a GUI event,
+ and how to store a series of GUI events as well as replay them on
+ a widget.
+
+ The approach to storing a series of events and replaying them is
+ quite similar to the approach explained in \l {Chapter 2:
+ Data Driven Testing}{chapter 2}. All you need to do is to add a data
+ function to your test class:
+
+ \snippet tutorial4/testgui.cpp 0
+
+ \section1 Writing the Data Function
+
+ As before, a test function's associated data function carries the
+ same name, appended by \c{_data}.
+
+ \snippet tutorial4/testgui.cpp 1
+
+ First, we define the elements of the table using the
+ QTest::addColumn() function: A list of GUI events, and the
+ expected result of applying the list of events on a QWidget. Note
+ that the type of the first element is \l QTestEventList.
+
+ A QTestEventList can be populated with GUI events that can be
+ stored as test data for later usage, or be replayed on any
+ QWidget.
+
+ In our current data function, we create two \l
+ {QTestEventList} elements. The first list consists of a single click to
+ the 'a' key. We add the event to the list using the
+ QTestEventList::addKeyClick() function. Then we use the
+ QTest::newRow() function to give the data set a name, and
+ stream the event list and the expected result into the table.
+
+ The second list consists of two key clicks: an 'a' with a
+ following 'backspace'. Again we use the
+ QTestEventList::addKeyClick() to add the events to the list, and
+ QTest::newRow() to put the event list and the expected
+ result into the table with an associated name.
+
+ \section1 Rewriting the Test Function
+
+ Our test can now be rewritten:
+
+ \snippet tutorial4/testgui.cpp 2
+
+ The TestGui::testGui() function will be executed two times,
+ once for each entry in the test data that we created in the
+ associated TestGui::testGui_data() function.
+
+ First, we fetch the two elements of the data set using the \l
+ QFETCH() macro. \l QFETCH() takes two arguments: the data type of
+ the element and the element name. Then we create a QLineEdit, and
+ apply the list of events on that widget using the
+ QTestEventList::simulate() function.
+
+ Finally, we use the QCOMPARE() macro to check if the line edit's
+ text is as expected.
+
+ \section1 Preparing the Stand-Alone Executable
+
+ As before, to make our test case a stand-alone executable,
+ the following two lines are needed:
+
+ \snippet tutorial4/testgui.cpp 3
+
+ The QTEST_MAIN() macro expands to a simple main() method that
+ runs all the test functions, and since both the declaration and
+ the implementation of our test class are in a .cpp file, we also
+ need to include the generated moc file to make Qt's introspection
+ work.
+
+ \section1 Building the Executable
+
+ \include {building-examples.qdocinc} {building the executable} {tutorial4}
+
+ \section1 Running the Executable
+
+ Running the resulting executable should give you the following
+ output:
+
+ \snippet code/doc_src_qtestlib.qdoc 13
+*/
diff --git a/src/testlib/doc/src/qttestlib-tutorial5.qdoc b/src/testlib/doc/src/qttestlib-tutorial5.qdoc
new file mode 100644
index 0000000000..7569019b4e
--- /dev/null
+++ b/src/testlib/doc/src/qttestlib-tutorial5.qdoc
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qttestlib-tutorial5-example.html
+ \previouspage {Chapter 4: Replaying GUI Events}{Chapter 4}
+ \nextpage {Chapter 6: Skipping Tests with QSKIP}{Chapter 6}
+
+ \title Chapter 5: Writing a Benchmark
+ \brief How to write a benchmark.
+
+ This chapter demonstrates how to write benchmarks using Qt Test.
+
+ \section1 Writing a Benchmark
+ To create a benchmark we extend a test function with a QBENCHMARK macro.
+ A benchmark test function will then typically consist of setup code and
+ a QBENCHMARK macro that contains the code to be measured. This test
+ function benchmarks QString::localeAwareCompare().
+
+ \snippet tutorial5/benchmarking.cpp 0
+
+ Setup can be done at the beginning of the function. At this point, the clock
+ is not running. The code inside the QBENCHMARK macro will be
+ measured, and possibly repeated several times in order to get an
+ accurate measurement.
+
+ Several \l {testlib-benchmarking-measurement}{back-ends} are available
+ and can be selected on the command line.
+
+ \section1 Data Functions
+
+ Data functions are useful for creating benchmarks that compare
+ multiple data inputs, for example locale aware compare against standard
+ compare.
+
+ \snippet tutorial5/benchmarking.cpp 1
+
+ The test function then uses the data to determine what to benchmark.
+
+ \snippet tutorial5/benchmarking.cpp 2
+
+ The \c{if (useLocaleCompare)} switch is placed outside the QBENCHMARK
+ macro to avoid measuring its overhead. Each benchmark test function
+ can have one active QBENCHMARK macro.
+
+ \section1 Building the Executable
+
+ \include {building-examples.qdocinc} {building the executable} {tutorial5}
+
+ \section1 Running the Executable
+
+ Running the resulting executable should give you the following
+ output:
+
+ \snippet code/doc_src_qtestlib.qdoc 14
+*/
diff --git a/src/testlib/doc/src/qttestlib-tutorial6.qdoc b/src/testlib/doc/src/qttestlib-tutorial6.qdoc
new file mode 100644
index 0000000000..602ca0b28e
--- /dev/null
+++ b/src/testlib/doc/src/qttestlib-tutorial6.qdoc
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qttestlib-tutorial6.html
+
+ \previouspage {Chapter 5: Writing a Benchmark}{Chapter 5}
+
+ \title Chapter 6: Skipping Tests with QSKIP
+ \brief How to skip tests in certain cases.
+
+ \section2 Using QSKIP(\a description) in a test function
+
+ If the \l QSKIP() macro is called from a test function, it stops
+ the execution of the test without adding a failure to the test log.
+ It can be used to skip tests that are certain to fail. The text in
+ the QSKIP \a description parameter is appended to the test log,
+ and should explain why the test was not carried out.
+
+ QSKIP can be used to skip testing when the implementation is not yet
+ complete or not supported on a certain platform. When there are known
+ failures, \l QEXPECT_FAIL is recommended, as it supports running the rest
+ of the test, when possible.
+
+ Example of QSKIP in a test function:
+
+ \snippet code/doc_src_qtqskip_snippet.cpp 0
+
+ In a data-driven test, each call to QSKIP() skips only the current
+ row of test data. If the data-driven test contains an unconditional
+ call to QSKIP, it produces a skip message for each row of test data.
+
+ \section2 Using QSKIP in a _data function
+
+ If called from a _data function, the QSKIP() macro stops
+ execution of the _data function. This prevents execution of the
+ associated test function.
+
+ See below for an example:
+
+ \snippet code/doc_src_qtqskip.cpp 1
+
+ \section2 Using QSKIP from initTestCase() or initTestCase_data()
+
+ If called from \c initTestCase() or \c initTestCase_data(), the
+ QSKIP() macro will skip all test and _data functions.
+
+ \sa {Select Appropriate Mechanisms to Exclude Tests}
+*/
diff --git a/src/testlib/qabstractitemmodeltester.cpp b/src/testlib/qabstractitemmodeltester.cpp
index ec7f43b5a4..eb52d28d59 100644
--- a/src/testlib/qabstractitemmodeltester.cpp
+++ b/src/testlib/qabstractitemmodeltester.cpp
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qabstractitemmodeltester.h"
#include <private/qobject_p.h>
+#include <private/qabstractitemmodel_p.h>
#include <QtCore/QPointer>
#include <QtCore/QAbstractItemModel>
#include <QtCore/QStack>
@@ -77,10 +42,28 @@ public:
void data();
void runAllTests();
+
void layoutAboutToBeChanged();
void layoutChanged();
+
+ void modelAboutToBeReset();
+ void modelReset();
+
+ void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last);
+ void columnsInserted(const QModelIndex &parent, int first, int last);
+ void columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
+ const QModelIndex &destinationParent, int destinationColumn);
+ void columnsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination,
+ int column);
+ void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
+ void columnsRemoved(const QModelIndex &parent, int first, int last);
+
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
void rowsInserted(const QModelIndex &parent, int start, int end);
+ void rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
+ const QModelIndex &destinationParent, int destinationRow);
+ void rowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination,
+ int row);
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
void rowsRemoved(const QModelIndex &parent, int start, int end);
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
@@ -108,8 +91,22 @@ private:
QStack<Changing> insert;
QStack<Changing> remove;
+ bool useFetchMore = true;
bool fetchingMore;
+ enum class ChangeInFlight {
+ None,
+ ColumnsInserted,
+ ColumnsMoved,
+ ColumnsRemoved,
+ LayoutChanged,
+ ModelReset,
+ RowsInserted,
+ RowsMoved,
+ RowsRemoved
+ };
+ ChangeInFlight changeInFlight = ChangeInFlight::None;
+
QList<QPersistentModelIndex> changing;
};
@@ -184,8 +181,7 @@ private:
This enumeration specifies how QAbstractItemModelTester should report
a failure when it tests a QAbstractItemModel subclass.
- \value QtTest The failures will be reported through
- QtTest's logging mechanism.
+ \value QtTest The failures will be reported as QtTest test failures.
\value Warning The failures will be reported as
warning messages in the \c{qt.modeltest} logging category.
@@ -253,14 +249,45 @@ QAbstractItemModelTester::QAbstractItemModelTester(QAbstractItemModel *model, Fa
connect(model, &QAbstractItemModel::layoutChanged,
this, [d]{ d->layoutChanged(); });
+ // column operations
+ connect(model, &QAbstractItemModel::columnsAboutToBeInserted,
+ this, [d](const QModelIndex &parent, int start, int end) { d->columnsAboutToBeInserted(parent, start, end); });
+ connect(model, &QAbstractItemModel::columnsAboutToBeMoved,
+ this, [d](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) {
+ d->columnsAboutToBeMoved(sourceParent, sourceStart, sourceEnd, destinationParent, destinationColumn); });
+ connect(model, &QAbstractItemModel::columnsAboutToBeRemoved,
+ this, [d](const QModelIndex &parent, int start, int end) { d->columnsAboutToBeRemoved(parent, start, end); });
+ connect(model, &QAbstractItemModel::columnsInserted,
+ this, [d](const QModelIndex &parent, int start, int end) { d->columnsInserted(parent, start, end); });
+ connect(model, &QAbstractItemModel::columnsMoved,
+ this, [d](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int col) {
+ d->columnsMoved(parent, start, end, destination, col); });
+ connect(model, &QAbstractItemModel::columnsRemoved,
+ this, [d](const QModelIndex &parent, int start, int end) { d->columnsRemoved(parent, start, end); });
+
+ // row operations
connect(model, &QAbstractItemModel::rowsAboutToBeInserted,
this, [d](const QModelIndex &parent, int start, int end) { d->rowsAboutToBeInserted(parent, start, end); });
+ connect(model, &QAbstractItemModel::rowsAboutToBeMoved,
+ this, [d](const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) {
+ d->rowsAboutToBeMoved(sourceParent, sourceStart, sourceEnd, destinationParent, destinationRow); });
connect(model, &QAbstractItemModel::rowsAboutToBeRemoved,
this, [d](const QModelIndex &parent, int start, int end) { d->rowsAboutToBeRemoved(parent, start, end); });
connect(model, &QAbstractItemModel::rowsInserted,
this, [d](const QModelIndex &parent, int start, int end) { d->rowsInserted(parent, start, end); });
+ connect(model, &QAbstractItemModel::rowsMoved,
+ this, [d](const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) {
+ d->rowsMoved(parent, start, end, destination, row); });
connect(model, &QAbstractItemModel::rowsRemoved,
this, [d](const QModelIndex &parent, int start, int end) { d->rowsRemoved(parent, start, end); });
+
+ // reset
+ connect(model, &QAbstractItemModel::modelAboutToBeReset,
+ this, [d]() { d->modelAboutToBeReset(); });
+ connect(model, &QAbstractItemModel::modelReset,
+ this, [d]() { d->modelReset(); });
+
+ // data
connect(model, &QAbstractItemModel::dataChanged,
this, [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) { d->dataChanged(topLeft, bottomRight); });
connect(model, &QAbstractItemModel::headerDataChanged,
@@ -289,6 +316,20 @@ QAbstractItemModelTester::FailureReportingMode QAbstractItemModelTester::failure
return d->failureReportingMode;
}
+/*!
+ If \a value is true, enables dynamic population of the
+ tested model, which is the default.
+ If \a value is false, it disables it.
+
+ \since 6.4
+ \sa QAbstractItemModel::fetchMore()
+*/
+void QAbstractItemModelTester::setUseFetchMore(bool value)
+{
+ Q_D(QAbstractItemModelTester);
+ d->useFetchMore = value;
+}
+
bool QAbstractItemModelTester::verify(bool statement, const char *statementStr, const char *description, const char *file, int line)
{
Q_D(QAbstractItemModelTester);
@@ -323,9 +364,11 @@ void QAbstractItemModelTesterPrivate::nonDestructiveBasicTest()
MODELTESTER_VERIFY(!model->buddy(QModelIndex()).isValid());
model->canFetchMore(QModelIndex());
MODELTESTER_VERIFY(model->columnCount(QModelIndex()) >= 0);
- fetchingMore = true;
- model->fetchMore(QModelIndex());
- fetchingMore = false;
+ if (useFetchMore) {
+ fetchingMore = true;
+ model->fetchMore(QModelIndex());
+ fetchingMore = false;
+ }
Qt::ItemFlags flags = model->flags(QModelIndex());
MODELTESTER_VERIFY(flags == Qt::ItemIsDropEnabled || flags == 0);
model->hasChildren(QModelIndex());
@@ -503,7 +546,7 @@ void QAbstractItemModelTesterPrivate::checkChildren(const QModelIndex &parent, i
p = p.parent();
// For models that are dynamically populated
- if (model->canFetchMore(parent)) {
+ if (model->canFetchMore(parent) && useFetchMore) {
fetchingMore = true;
model->fetchMore(parent);
fetchingMore = false;
@@ -571,7 +614,7 @@ void QAbstractItemModelTesterPrivate::checkChildren(const QModelIndex &parent, i
// recursively go down the children
if (model->hasChildren(index) && currentDepth < 10)
- checkChildren(index, ++currentDepth);
+ checkChildren(index, currentDepth + 1);
// make sure that after testing the children that the index doesn't change.
QModelIndex newerIndex = model->index(r, c, parent);
@@ -613,14 +656,14 @@ void QAbstractItemModelTesterPrivate::data()
// Check that the alignment is one we know about
QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole);
if (textAlignmentVariant.isValid()) {
- Qt::Alignment alignment = qvariant_cast<Qt::Alignment>(textAlignmentVariant);
+ Qt::Alignment alignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(textAlignmentVariant);
MODELTESTER_COMPARE(alignment, (alignment & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask)));
}
// Check that the "check state" is one we know about.
QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole);
if (checkStateVariant.isValid()) {
- int state = checkStateVariant.toInt();
+ Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(checkStateVariant);
MODELTESTER_VERIFY(state == Qt::Unchecked
|| state == Qt::PartiallyChecked
|| state == Qt::Checked);
@@ -632,6 +675,89 @@ void QAbstractItemModelTesterPrivate::data()
return;
}
+void QAbstractItemModelTesterPrivate::columnsAboutToBeInserted(const QModelIndex &parent, int start,
+ int end)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::ColumnsInserted;
+
+ qCDebug(lcModelTest) << "columnsAboutToBeInserted"
+ << "start=" << start << "end=" << end << "parent=" << parent
+ << "parent data=" << model->data(parent).toString()
+ << "current count of parent=" << model->rowCount(parent)
+ << "last before insertion=" << model->index(start - 1, 0, parent)
+ << model->data(model->index(start - 1, 0, parent));
+}
+
+void QAbstractItemModelTesterPrivate::columnsInserted(const QModelIndex &parent, int first,
+ int last)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsInserted);
+ changeInFlight = ChangeInFlight::None;
+
+ qCDebug(lcModelTest) << "columnsInserted"
+ << "start=" << first << "end=" << last << "parent=" << parent
+ << "parent data=" << model->data(parent).toString()
+ << "current count of parent=" << model->rowCount(parent);
+}
+
+void QAbstractItemModelTesterPrivate::columnsAboutToBeMoved(const QModelIndex &sourceParent,
+ int sourceStart, int sourceEnd,
+ const QModelIndex &destinationParent,
+ int destinationColumn)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::ColumnsMoved;
+
+ qCDebug(lcModelTest) << "columnsAboutToBeMoved"
+ << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
+ << "sourceParent=" << sourceParent
+ << "sourceParent data=" << model->data(sourceParent).toString()
+ << "destinationParent=" << destinationParent
+ << "destinationColumn=" << destinationColumn;
+}
+
+void QAbstractItemModelTesterPrivate::columnsMoved(const QModelIndex &sourceParent, int sourceStart,
+ int sourceEnd,
+ const QModelIndex &destinationParent,
+ int destinationColumn)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsMoved);
+ changeInFlight = ChangeInFlight::None;
+
+ qCDebug(lcModelTest) << "columnsMoved"
+ << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
+ << "sourceParent=" << sourceParent
+ << "sourceParent data=" << model->data(sourceParent).toString()
+ << "destinationParent=" << destinationParent
+ << "destinationColumn=" << destinationColumn;
+}
+
+void QAbstractItemModelTesterPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, int first,
+ int last)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::ColumnsRemoved;
+
+ qCDebug(lcModelTest) << "columnsAboutToBeRemoved"
+ << "start=" << first << "end=" << last << "parent=" << parent
+ << "parent data=" << model->data(parent).toString()
+ << "current count of parent=" << model->rowCount(parent)
+ << "last before removal=" << model->index(first - 1, 0, parent)
+ << model->data(model->index(first - 1, 0, parent));
+}
+
+void QAbstractItemModelTesterPrivate::columnsRemoved(const QModelIndex &parent, int first, int last)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ColumnsRemoved);
+ changeInFlight = ChangeInFlight::None;
+
+ qCDebug(lcModelTest) << "columnsRemoved"
+ << "start=" << first << "end=" << last << "parent=" << parent
+ << "parent data=" << model->data(parent).toString()
+ << "current count of parent=" << model->rowCount(parent);
+}
+
/*
Store what is about to be inserted to make sure it actually happens
@@ -639,6 +765,9 @@ void QAbstractItemModelTesterPrivate::data()
*/
void QAbstractItemModelTesterPrivate::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::RowsInserted;
+
qCDebug(lcModelTest) << "rowsAboutToBeInserted"
<< "start=" << start << "end=" << end << "parent=" << parent
<< "parent data=" << model->data(parent).toString()
@@ -660,6 +789,9 @@ void QAbstractItemModelTesterPrivate::rowsAboutToBeInserted(const QModelIndex &p
*/
void QAbstractItemModelTesterPrivate::rowsInserted(const QModelIndex &parent, int start, int end)
{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsInserted);
+ changeInFlight = ChangeInFlight::None;
+
qCDebug(lcModelTest) << "rowsInserted"
<< "start=" << start << "end=" << end << "parent=" << parent
<< "parent data=" << model->data(parent).toString()
@@ -690,21 +822,70 @@ void QAbstractItemModelTesterPrivate::rowsInserted(const QModelIndex &parent, in
}
}
+void QAbstractItemModelTesterPrivate::rowsAboutToBeMoved(const QModelIndex &sourceParent,
+ int sourceStart, int sourceEnd,
+ const QModelIndex &destinationParent,
+ int destinationRow)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::RowsMoved;
+
+ qCDebug(lcModelTest) << "rowsAboutToBeMoved"
+ << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
+ << "sourceParent=" << sourceParent
+ << "sourceParent data=" << model->data(sourceParent).toString()
+ << "destinationParent=" << destinationParent
+ << "destinationRow=" << destinationRow;
+}
+
+void QAbstractItemModelTesterPrivate::rowsMoved(const QModelIndex &sourceParent, int sourceStart,
+ int sourceEnd, const QModelIndex &destinationParent,
+ int destinationRow)
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsMoved);
+ changeInFlight = ChangeInFlight::None;
+
+ qCDebug(lcModelTest) << "rowsMoved"
+ << "sourceStart=" << sourceStart << "sourceEnd=" << sourceEnd
+ << "sourceParent=" << sourceParent
+ << "sourceParent data=" << model->data(sourceParent).toString()
+ << "destinationParent=" << destinationParent
+ << "destinationRow=" << destinationRow;
+}
+
void QAbstractItemModelTesterPrivate::layoutAboutToBeChanged()
{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::LayoutChanged;
+
for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i)
changing.append(QPersistentModelIndex(model->index(i, 0)));
}
void QAbstractItemModelTesterPrivate::layoutChanged()
{
- for (int i = 0; i < changing.count(); ++i) {
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::LayoutChanged);
+ changeInFlight = ChangeInFlight::None;
+
+ for (int i = 0; i < changing.size(); ++i) {
QPersistentModelIndex p = changing[i];
MODELTESTER_COMPARE(model->index(p.row(), p.column(), p.parent()), QModelIndex(p));
}
changing.clear();
}
+void QAbstractItemModelTesterPrivate::modelAboutToBeReset()
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::ModelReset;
+}
+
+void QAbstractItemModelTesterPrivate::modelReset()
+{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::ModelReset);
+ changeInFlight = ChangeInFlight::None;
+}
+
/*
Store what is about to be inserted to make sure it actually happens
@@ -712,6 +893,9 @@ void QAbstractItemModelTesterPrivate::layoutChanged()
*/
void QAbstractItemModelTesterPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::None);
+ changeInFlight = ChangeInFlight::RowsRemoved;
+
qCDebug(lcModelTest) << "rowsAboutToBeRemoved"
<< "start=" << start << "end=" << end << "parent=" << parent
<< "parent data=" << model->data(parent).toString()
@@ -742,6 +926,9 @@ void QAbstractItemModelTesterPrivate::rowsAboutToBeRemoved(const QModelIndex &pa
*/
void QAbstractItemModelTesterPrivate::rowsRemoved(const QModelIndex &parent, int start, int end)
{
+ MODELTESTER_COMPARE(changeInFlight, ChangeInFlight::RowsRemoved);
+ changeInFlight = ChangeInFlight::None;
+
qCDebug(lcModelTest) << "rowsRemoved"
<< "start=" << start << "end=" << end << "parent=" << parent
<< "parent data=" << model->data(parent).toString()
@@ -850,3 +1037,5 @@ bool QAbstractItemModelTesterPrivate::compare(const T1 &t1, const T2 &t2,
QT_END_NAMESPACE
+
+#include "moc_qabstractitemmodeltester.cpp"
diff --git a/src/testlib/qabstractitemmodeltester.h b/src/testlib/qabstractitemmodeltester.h
index 57b8f283bc..c4d94be261 100644
--- a/src/testlib/qabstractitemmodeltester.h
+++ b/src/testlib/qabstractitemmodeltester.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTITEMMODELTESTER_H
#define QABSTRACTITEMMODELTESTER_H
@@ -83,6 +47,7 @@ public:
QAbstractItemModel *model() const;
FailureReportingMode failureReportingMode() const;
+ void setUseFetchMore(bool value);
private:
friend inline bool QTestPrivate::testDataGuiRoles(QAbstractItemModelTester *tester);
diff --git a/src/testlib/qabstracttestlogger.cpp b/src/testlib/qabstracttestlogger.cpp
index c7a0784da7..de6fb63560 100644
--- a/src/testlib/qabstracttestlogger.cpp
+++ b/src/testlib/qabstracttestlogger.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qabstracttestlogger_p.h>
#include <QtTest/qtestassert.h>
+#include <qbenchmark_p.h>
#include <qtestresult_p.h>
#include <QtCore/qbytearray.h>
@@ -57,7 +22,94 @@
#endif
QT_BEGIN_NAMESPACE
+/*!
+ \internal
+ \class QAbstractTestLogger
+ \inmodule QtTest
+ \brief Base class for test loggers
+
+ Implementations of logging for QtTest should implement all pure virtual
+ methods of this class and may implement the other virtual methods. This
+ class's documentation of each virtual method sets out how those
+ implementations are invoked by the QtTest code and offers guidance on how
+ the logging class should use the data. Actual implementations may have
+ different requirements - such as a file format with a defined schema, or a
+ target audience to serve - that affect how it interprets that guidance.
+*/
+/*!
+ \enum QAbstractTestLogger::IncidentTypes
+
+ \value Pass The test ran to completion successfully.
+ \value XFail The test failed a check that is known to fail; this failure
+ does not prevent successful completion of the test and may be
+ followed by further checks.
+ \value Fail The test fails.
+ \value XPass A check which was expected to fail actually passed. This is
+ counted as a failure, as it means whatever caused the known failure
+ no longer does, so the test needs an update.
+ \value Skip The current test ended prematurely, skipping some checks.
+ \value BlacklistedPass As Pass but the test was blacklisted.
+ \value BlacklistedXFail As XFail but the test was blacklisted.
+ \value BlacklistedFail As Fail but the test was blacklisted.
+ \value BlacklistedXPass As XPass but the test was blacklisted.
+
+ A test may also skip (see \l {QAbstractTestLogger::}{MessageTypes}). The
+ first of skip, Fail, XPass or the blacklisted equivalents of the last two to
+ arise is decisive for the outcome of the test: loggers which should only
+ report one outcome should thus record that as the outcome and either ignore
+ later incidents (or skips) in the same run of the test function or map them
+ to some form of message.
+
+ \note tests can be "blacklisted" when they are known to fail
+ unreliably. When testing is used to decide whether a change to the code
+ under test is acceptable, such failures are not automatic grounds for
+ rejecting the change, if the unreliable failure was known before the
+ change. QTest::qExec(), as a result, only returns a failing status code if
+ some non-blacklisted test failed. Logging backends may reasonably report a
+ blacklisted result just as they would report the non-blacklisted equivalent,
+ optionally with some annotation to indicate that the result should not be
+ taken as damning evidence against recent changes to the code under test.
+
+ \sa QAbstractTestLogger::addIncident()
+*/
+
+/*!
+ \enum QAbstractTestLogger::MessageTypes
+
+ The members whose names begin with \c Q describe messages that originate in
+ calls, by the test or code under test, to Qt logging functions (implemented
+ as macros) whose names are similar, with a \c q in place of the leading \c
+ Q. The other members describe messages generated internally by QtTest.
+
+ \value QInfo An informational message from qInfo().
+ \value QWarning A warning from qWarning().
+ \value QDebug A debug message from qDebug().
+ \value QCritical A critical error from qCritical().
+ \value QFatal A fatal error from qFatal(), or an unrecognised message from
+ the Qt logging functions.
+ \value Info Messages QtTest generates as requested by the \c{-v1} or \c{-v2}
+ command-line option being specified when running the test.
+ \value Warn A warning generated internally by QtTest
+
+ \note For these purposes, some utilities provided by QtTestlib as helper
+ functions to facilitate testing - such as \l QSignalSpy, \l
+ QTestAccessibility, \l QTest::qExtractTestData(), and the facilities to
+ deliver artificial mouse and keyboard events - are treated as test code,
+ rather than internal to QtTest; they call \l qWarning() and friends rather
+ than using the internal logging infrastructure, so that \l
+ QTest::ignoreMessage() can be used to anticipate the messages.
+
+ \sa QAbstractTestLogger::addMessage()
+*/
+
+/*!
+ Constructs the base-class parts of the logger.
+
+ Derived classes should pass this base-constructor the \a filename of the
+ file to which they shall log test results, or \nullptr to write to standard
+ output. The protected member \c stream is set to the open file descriptor.
+*/
QAbstractTestLogger::QAbstractTestLogger(const char *filename)
{
if (!filename) {
@@ -81,20 +133,47 @@ QAbstractTestLogger::QAbstractTestLogger(const char *filename)
#endif
}
+/*!
+ Destroys the logger object.
+
+ If the protected \c stream is not standard output, it is closed. In any
+ case it is cleared.
+*/
QAbstractTestLogger::~QAbstractTestLogger()
{
QTEST_ASSERT(stream);
- if (stream != stdout) {
+ if (stream != stdout)
fclose(stream);
- }
stream = nullptr;
}
+/*!
+ Returns true if the logger supports repeated test runs.
+
+ Repetition of test runs is disabled by default, and can be enabled only for
+ test loggers that support it. Even if the logger may create syntactically
+ correct test reports, log-file analyzers may assume that test names are
+ unique within one report file.
+*/
+bool QAbstractTestLogger::isRepeatSupported() const
+{
+ return false;
+}
+
+/*!
+ Returns true if the \c output stream is standard output.
+*/
bool QAbstractTestLogger::isLoggingToStdout() const
{
return stream == stdout;
}
+/*!
+ Helper utility to blot out unprintable characters in \a str.
+
+ Takes a \c{'\0'}-terminated mutable string and changes any characters of it
+ that are not suitable for printing to \c{'?'} characters.
+*/
void QAbstractTestLogger::filterUnprintable(char *str) const
{
unsigned char *idx = reinterpret_cast<unsigned char *>(str);
@@ -105,6 +184,13 @@ void QAbstractTestLogger::filterUnprintable(char *str) const
}
}
+/*!
+ Convenience method to write \a msg to the output stream.
+
+ The output \a msg must be a \c{'\0'}-terminated string (and not \nullptr).
+ A copy of it is passed to \l filterUnprintable() and the result written to
+ the output \c stream, which is then flushed.
+*/
void QAbstractTestLogger::outputString(const char *msg)
{
QTEST_ASSERT(stream);
@@ -120,14 +206,160 @@ void QAbstractTestLogger::outputString(const char *msg)
delete [] filtered;
}
+/*!
+ Called before the start of a test run.
+
+ This virtual method is called before the first tests are run. A logging
+ implementation might open a file, write some preamble, or prepare in other
+ ways, such as setting up initial values of variables. It can use the usual
+ Qt logging infrastucture, since it is also called before QtTest installs its
+ own custom message handler.
+
+ \sa stopLogging()
+*/
void QAbstractTestLogger::startLogging()
{
}
+/*!
+ Called after the end of a test run.
+
+ This virtual method is called after all tests have run. A logging
+ implementation might collate information gathered from the run, write a
+ summary, or close a file. It can use the usual Qt logging infrastucture,
+ since it is also called after QtTest has restored the default message
+ handler it replaced with its own custom message handler.
+
+ \sa startLogging()
+*/
void QAbstractTestLogger::stopLogging()
{
}
+void QAbstractTestLogger::addBenchmarkResults(const QList<QBenchmarkResult> &result)
+{
+ for (const auto &m : result)
+ addBenchmarkResult(m);
+}
+
+/*!
+ \fn void QAbstractTestLogger::enterTestFunction(const char *function)
+
+ This virtual method is called before each test function is invoked. It is
+ passed the name of the test function (without its class prefix) as \a
+ function. It is likewise called for \c{initTestCase()} at the start of
+ testing, after \l startLogging(), and for \c{cleanupTestCase()} at the end
+ of testing, in each case passing the name of the function. It is also called
+ with \nullptr as \a function after the last of these functions, or in the
+ event of an early end to testing, before \l stopLogging().
+
+ For data-driven test functions, this is called only once, before the data
+ function is called to set up the table of datasets and the test is run with
+ its first dataset.
+
+ Every logging implementation must implement this method. It shall typically
+ need to record the name of the function for later use in log messages.
+
+ \sa leaveTestFunction(), enterTestData()
+*/
+/*!
+ \fn void QAbstractTestLogger::leaveTestFunction()
+
+ This virtual method is called after a test function has completed, to match
+ \l enterTestFunction(). For data-driven test functions, this is called only
+ once, after the test is run with its last dataset.
+
+ Every logging implementation must implement this method. In some cases it
+ may be called more than once without an intervening call to \l
+ enterTestFunction(). In such cases, the implementation should ignore these
+ later calls, until the next call to enterTestFunction().
+
+ \sa enterTestFunction(), enterTestData()
+*/
+/*!
+ \fn void QAbstractTestLogger::enterTestData(QTestData *)
+
+ This virtual method is called before and after each call to a test
+ function. For a data-driven test, the call before is passed the name of the
+ test data row. This may combine a global data row name with a local data row
+ name. For non-data-driven tests and for the call after a test function,
+ \nullptr is passed
+
+ A logging implementation might chose to record the data row name for
+ reporting of results from the test for that data row. It should, in such a
+ case, clear its record of the name when called with \nullptr.
+
+ \sa enterTestFunction(), leaveTestFunction()
+*/
+/*!
+ \fn void QAbstractTestLogger::addIncident(IncidentTypes type, const char *description, const char *file, int line)
+
+ This virtual method is called when an event occurs that relates to the
+ resolution of the test. The \a type indicates whether this was a pass, a
+ fail or a skip, whether a failure was expected, and whether the test being
+ run is blacklisted. The \a description may be empty (for a pass) or a
+ message describing the nature of the incident. Where the location in code of
+ the incident is known, it is indicated by \a file and \a line; otherwise,
+ these are \a nullptr and 0, respectively.
+
+ Every logging implementation must implement this method. Note that there are
+ circumstances where more than one incident may be reported, in this way, for
+ a single run of a test on a single dataset. It is the implementation's
+ responsibility to recognize such cases and decide what to do about them. For
+ purposes of counting resolutions of tests in the "Totals" report at the end
+ of a test run, QtTest considers the first incident (excluding XFail and its
+ blacklisted variant) decisive.
+
+ \sa addMessage(), addBenchmarkResult()
+*/
+/*!
+ \fn void QAbstractTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
+
+ This virtual method is called after a benchmark has been run enough times to
+ produce usable data. It is passed the median \a result from all cycles of
+ the code controlled by the test's QBENCHMARK loop.
+
+ Every logging implementation must implement this method.
+
+ \sa addIncident(), addMessage()
+*/
+/*!
+ \overload
+ \fn void QAbstractTestLogger::addMessage(MessageTypes type, const QString &message, const char *file, int line)
+
+ This virtual method is called, via its \c QtMsgType overload, from the
+ custom message handler QtTest installs. It is also used to
+ warn about various situations detected by QtTest itself, such
+ as \e failure to see a message anticipated by QTest::ignoreMessage() and,
+ particularly when verbosity options have been enabled via the command-line,
+ to log some extra information.
+
+ Every logging implementation must implement this method. The \a type
+ indicates the category of message and the \a message is the content to be
+ reported. When the message is associated with specific code, the name of the
+ \a file and \a line number within it are also supplied; otherwise, these are
+ \nullptr and 0, respectively.
+
+ \sa QTest::ignoreMessage(), addIncident()
+*/
+
+/*!
+ \overload
+
+ This virtual method is called from the custom message handler QtTest
+ installs in place of Qt's default message handler for the duration of
+ testing, unless QTest::ignoreMessage() was used to ignore it, or too many
+ messages have previously been processed. (The limiting number of messages is
+ controlled by the -maxwarnings option to a test and defaults to 2002.)
+
+ Logging implementations should not normally need to override this method.
+ The base implementation converts \a type to the matching \l MessageType,
+ formats the given \a message suitably for the specified \a context, and
+ forwards the converted type and formatted message to the overload that takes
+ MessageType and QString.
+
+ \sa QTest::ignoreMessage(), addIncident()
+*/
void QAbstractTestLogger::addMessage(QtMsgType type, const QMessageLogContext &context,
const QString &message)
{
@@ -135,12 +367,11 @@ void QAbstractTestLogger::addMessage(QtMsgType type, const QMessageLogContext &c
switch (type) {
case QtDebugMsg: return QAbstractTestLogger::QDebug;
case QtInfoMsg: return QAbstractTestLogger::QInfo;
- case QtCriticalMsg: return QAbstractTestLogger::QSystem;
+ case QtCriticalMsg: return QAbstractTestLogger::QCritical;
case QtWarningMsg: return QAbstractTestLogger::QWarning;
case QtFatalMsg: return QAbstractTestLogger::QFatal;
}
- Q_UNREACHABLE();
- return QAbstractTestLogger::QFatal;
+ Q_UNREACHABLE_RETURN(QAbstractTestLogger::QFatal);
}();
QString formattedMessage = qFormatLogMessage(type, context, message);
@@ -151,10 +382,13 @@ void QAbstractTestLogger::addMessage(QtMsgType type, const QMessageLogContext &c
addMessage(messageType, formattedMessage);
}
-namespace QTest
+namespace
{
+ constexpr int MAXSIZE = 1024 * 1024 * 2;
+}
-extern void filter_unprintable(char *str);
+namespace QTest
+{
/*!
\fn int QTest::qt_asprintf(QTestCharBuffer *buf, const char *format, ...);
@@ -162,33 +396,27 @@ extern void filter_unprintable(char *str);
*/
int qt_asprintf(QTestCharBuffer *str, const char *format, ...)
{
- static const int MAXSIZE = 1024*1024*2;
-
Q_ASSERT(str);
-
int size = str->size();
+ Q_ASSERT(size > 0);
va_list ap;
int res = 0;
- for (;;) {
+ do {
va_start(ap, format);
res = qvsnprintf(str->data(), size, format, ap);
va_end(ap);
- str->data()[size - 1] = '\0';
- if (res >= 0 && res < size) {
- // We succeeded
- break;
- }
- // buffer wasn't big enough, try again.
+ // vsnprintf() reliably '\0'-terminates
+ Q_ASSERT(res < 0 || str->data()[res < size ? res : size - 1] == '\0');
// Note, we're assuming that a result of -1 is always due to running out of space.
- size *= 2;
- if (size > MAXSIZE) {
+ if (res >= 0 && res < size) // Success
break;
- }
- if (!str->reset(size))
- break; // out of memory - take what we have
- }
+
+ // Buffer wasn't big enough, try again:
+ size *= 2;
+ // If too large or out of memory, take what we have:
+ } while (size <= MAXSIZE && str->reset(size));
return res;
}
@@ -215,6 +443,28 @@ void generateTestIdentifier(QTestCharBuffer *identifier, int parts)
globalDataTag, tagFiller, dataTag, testFuctionEnd);
}
+// strcat() for QTestCharBuffer objects:
+bool appendCharBuffer(QTestCharBuffer *accumulator, const QTestCharBuffer &more)
+{
+ const auto bufsize = [](const QTestCharBuffer &buf) -> int {
+ const int max = buf.size();
+ return max > 0 ? int(qstrnlen(buf.constData(), max)) : 0;
+ };
+ const int extra = bufsize(more);
+ if (extra <= 0)
+ return true; // Nothing to do, fatuous success
+
+ const int oldsize = bufsize(*accumulator);
+ const int newsize = oldsize + extra + 1; // 1 for final '\0'
+ if (newsize > MAXSIZE || !accumulator->resize(newsize))
+ return false; // too big or unable to grow
+
+ char *tail = accumulator->data() + oldsize;
+ memcpy(tail, more.constData(), extra);
+ tail[extra] = '\0';
+ return true;
+}
+
}
QT_END_NAMESPACE
diff --git a/src/testlib/qabstracttestlogger_p.h b/src/testlib/qabstracttestlogger_p.h
index 84803b45c1..b4a66cd12a 100644
--- a/src/testlib/qabstracttestlogger_p.h
+++ b/src/testlib/qabstracttestlogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTTESTLOGGER_P_H
#define QABSTRACTTESTLOGGER_P_H
@@ -52,6 +16,8 @@
//
#include <QtTest/qttestglobal.h>
+#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qbytearrayalgorithms.h>
#include <stdio.h>
#include <stdlib.h>
@@ -63,8 +29,10 @@ class QTestData;
class Q_TESTLIB_EXPORT QAbstractTestLogger
{
+ Q_DISABLE_COPY_MOVE(QAbstractTestLogger)
public:
enum IncidentTypes {
+ Skip,
Pass,
XFail,
Fail,
@@ -76,14 +44,14 @@ public:
};
enum MessageTypes {
- Warn,
- QWarning,
QDebug,
- QSystem,
+ QInfo,
+ QWarning,
+ QCritical,
QFatal,
- Skip,
+ // testlib's internal messages:
Info,
- QInfo
+ Warn
};
QAbstractTestLogger(const char *filename);
@@ -100,6 +68,7 @@ public:
virtual void addIncident(IncidentTypes type, const char *description,
const char *file = nullptr, int line = 0) = 0;
virtual void addBenchmarkResult(const QBenchmarkResult &result) = 0;
+ virtual void addBenchmarkResults(const QList<QBenchmarkResult> &result);
virtual void addMessage(QtMsgType, const QMessageLogContext &,
const QString &);
@@ -107,6 +76,8 @@ public:
virtual void addMessage(MessageTypes type, const QString &message,
const char *file = nullptr, int line = 0) = 0;
+ virtual bool isRepeatSupported() const;
+
bool isLoggingToStdout() const;
void outputString(const char *msg);
@@ -153,12 +124,14 @@ struct QTestCharBuffer
return _size;
}
- inline bool reset(int newSize)
+ bool reset(int newSize, bool copy = false)
{
char *newBuf = nullptr;
if (buf == staticBuf) {
// if we point to our internal buffer, we need to malloc first
newBuf = reinterpret_cast<char *>(malloc(newSize));
+ if (copy && newBuf)
+ qstrncpy(newBuf, buf, _size);
} else {
// if we already malloc'ed, just realloc
newBuf = reinterpret_cast<char *>(realloc(buf, newSize));
@@ -173,6 +146,13 @@ struct QTestCharBuffer
return true;
}
+ bool resize(int newSize) {
+ return newSize <= _size || reset(newSize, true);
+ }
+
+ void clear() { buf[0] = '\0'; }
+ bool isEmpty() { return buf[0] == '\0'; }
+
private:
int _size = InitialSize;
char* buf;
@@ -188,6 +168,7 @@ namespace QTestPrivate
{
enum IdentifierPart { TestObject = 0x1, TestFunction = 0x2, TestDataTag = 0x4, AllParts = 0xFFFF };
void Q_TESTLIB_EXPORT generateTestIdentifier(QTestCharBuffer *identifier, int parts = AllParts);
+ bool appendCharBuffer(QTestCharBuffer *accumulator, const QTestCharBuffer &more);
}
QT_END_NAMESPACE
diff --git a/src/testlib/qappletestlogger.cpp b/src/testlib/qappletestlogger.cpp
index dfeadebdef..7517a95344 100644
--- a/src/testlib/qappletestlogger.cpp
+++ b/src/testlib/qappletestlogger.cpp
@@ -1,52 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qappletestlogger_p.h"
-#include <QPair>
-
QT_BEGIN_NAMESPACE
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
using namespace QTestPrivate;
+/*! \internal
+ \class QAppleTestLogger
+ \inmodule QtTest
+
+ QAppleTestLogger reports test results through Apple's unified system logging.
+ Results can be viewed in the Console app.
+*/
+
bool QAppleTestLogger::debugLoggingEnabled()
{
// Debug-level messages are only captured in memory when debug logging is
@@ -103,6 +73,8 @@ void QAppleTestLogger::addIncident(IncidentTypes type, const char *description,
{
MessageData messageData = [=]() {
switch (type) {
+ case QAbstractTestLogger::Skip:
+ return MessageData{QtInfoMsg, "skip"};
case QAbstractTestLogger::Pass:
return MessageData{QtInfoMsg, "pass"};
case QAbstractTestLogger::XFail:
@@ -130,7 +102,15 @@ void QAppleTestLogger::addIncident(IncidentTypes type, const char *description,
QString message = testIdentifier();
if (qstrlen(description))
- message += QLatin1Char('\n') % QString::fromLatin1(description);
+ message += u'\n' % QString::fromLatin1(description);
+
+ // As long as the Apple logger doesn't propagate the context's file and
+ // line number we need to manually print it.
+ if (context.line && context.file) {
+ QTestCharBuffer line;
+ QTest::qt_asprintf(&line, "\n [Loc: %s:%d]", context.file, context.line);
+ message += QLatin1String(line.data());
+ }
AppleUnifiedLogger::messageHandler(messageData.messageType, context, message, subsystem());
}
@@ -149,12 +129,10 @@ void QAppleTestLogger::addMessage(MessageTypes type, const QString &message, con
return MessageData{QtWarningMsg, nullptr};
case QAbstractTestLogger::QDebug:
return MessageData{QtDebugMsg, nullptr};
- case QAbstractTestLogger::QSystem:
- return MessageData{QtWarningMsg, "system"};
+ case QAbstractTestLogger::QCritical:
+ return MessageData{QtWarningMsg, "critical"};
case QAbstractTestLogger::QFatal:
return MessageData{QtFatalMsg, nullptr};
- case QAbstractTestLogger::Skip:
- return MessageData{QtInfoMsg, "skip"};
case QAbstractTestLogger::Info:
case QAbstractTestLogger::QInfo:
return MessageData{QtInfoMsg, nullptr};
@@ -166,16 +144,8 @@ void QAppleTestLogger::addMessage(MessageTypes type, const QString &message, con
messageData.generateCategory(&category);
QMessageLogContext context(file, line, /* function = */ nullptr, category.data());
- QString msg = message;
-
- if (type == Skip) {
- if (!message.isNull())
- msg.prepend(testIdentifier() + QLatin1Char('\n'));
- else
- msg = testIdentifier();
- }
- AppleUnifiedLogger::messageHandler(messageData.messageType, context, msg, subsystem());
+ AppleUnifiedLogger::messageHandler(messageData.messageType, context, message, subsystem());
}
QString QAppleTestLogger::subsystem() const
diff --git a/src/testlib/qappletestlogger_p.h b/src/testlib/qappletestlogger_p.h
index 6075f9fccb..e77ae6cf10 100644
--- a/src/testlib/qappletestlogger_p.h
+++ b/src/testlib/qappletestlogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QAPPLETESTLOGGER_P_H
#define QAPPLETESTLOGGER_P_H
diff --git a/src/testlib/qasciikey.cpp b/src/testlib/qasciikey.cpp
index 28404ec37d..114b757dc4 100644
--- a/src/testlib/qasciikey.cpp
+++ b/src/testlib/qasciikey.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/qtestcase.h>
#include <QtTest/qtestassert.h>
@@ -177,7 +141,7 @@ Qt::Key QTest::asciiToKey(char ascii)
case 0xb2: return Qt::Key_twosuperior;
case 0xb3: return Qt::Key_threesuperior;
case 0xb4: return Qt::Key_acute;
- case 0xb5: return Qt::Key_mu;
+ case 0xb5: return Qt::Key_micro;
case 0xb6: return Qt::Key_paragraph;
case 0xb7: return Qt::Key_periodcentered;
case 0xb8: return Qt::Key_cedilla;
@@ -405,7 +369,7 @@ char QTest::keyToAscii(Qt::Key key)
case Qt::Key_twosuperior: return char(0xb2);
case Qt::Key_threesuperior: return char(0xb3);
case Qt::Key_acute: return char(0xb4);
- case Qt::Key_mu: return char(0xb5);
+ case Qt::Key_micro: return char(0xb5);
case Qt::Key_paragraph: return char(0xb6);
case Qt::Key_periodcentered: return char(0xb7);
case Qt::Key_cedilla: return char(0xb8);
@@ -498,6 +462,11 @@ char QTest::keyToAscii(Qt::Key key)
case Qt::Key_LaunchE : return 0; // = 0x10b0,
case Qt::Key_LaunchF : return 0; // = 0x10b1,
+ // Keypad navigation keys
+ case Qt::Key_Select : return 0; // = 0x01010000
+ case Qt::Key_Yes : return 0; // = 0x01010001
+ case Qt::Key_No : return 0; // = 0x01010002
+
default: QTEST_ASSERT(false); return 0;
}
}
diff --git a/src/testlib/qbenchmark.cpp b/src/testlib/qbenchmark.cpp
index aafdc64831..af6ee5f7c6 100644
--- a/src/testlib/qbenchmark.cpp
+++ b/src/testlib/qbenchmark.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/qbenchmark.h>
#include <QtTest/private/qbenchmark_p.h>
@@ -132,10 +96,13 @@ int QBenchmarkTestMethodData::adjustIterationCount(int suggestion)
return iterationCount;
}
-void QBenchmarkTestMethodData::setResult(
- qreal value, QTest::QBenchmarkMetric metric, bool setByMacro)
+void QBenchmarkTestMethodData::setResults(const QList<QBenchmarkMeasurerBase::Measurement> &list,
+ bool setByMacro)
{
bool accepted = false;
+ QBenchmarkMeasurerBase::Measurement firstMeasurement = {};
+ if (!list.isEmpty())
+ firstMeasurement = list.constFirst();
// Always accept the result if the iteration count has been
// specified on the command line with -iterations.
@@ -150,9 +117,9 @@ void QBenchmarkTestMethodData::setResult(
// Test the result directly without calling the measurer if the minimum time
// has been specified on the command line with -minimumvalue.
else if (QBenchmarkGlobalData::current->walltimeMinimum != -1)
- accepted = (value > QBenchmarkGlobalData::current->walltimeMinimum);
+ accepted = (firstMeasurement.value > QBenchmarkGlobalData::current->walltimeMinimum);
else
- accepted = QBenchmarkGlobalData::current->measurer->isMeasurementAccepted(value);
+ accepted = QBenchmarkGlobalData::current->measurer->isMeasurementAccepted(firstMeasurement);
// Accept the result or double the number of iterations.
if (accepted)
@@ -160,8 +127,10 @@ void QBenchmarkTestMethodData::setResult(
else
iterationCount *= 2;
- this->result = QBenchmarkResult(
- QBenchmarkGlobalData::current->context, value, iterationCount, metric, setByMacro);
+ valid = true;
+ results.reserve(list.size());
+ for (auto m : list)
+ results.emplaceBack(QBenchmarkGlobalData::current->context, m, iterationCount, setByMacro);
}
/*!
@@ -169,7 +138,7 @@ void QBenchmarkTestMethodData::setResult(
\internal
The QBenchmarkIterationController class is used by the QBENCHMARK macro to
- drive the benchmarking loop. It is repsonsible for starting and stopping
+ drive the benchmarking loop. It is responsible for starting and stopping
the timing measurements as well as calling the result reporting functions.
*/
@@ -193,13 +162,12 @@ QTest::QBenchmarkIterationController::QBenchmarkIterationController()
*/
QTest::QBenchmarkIterationController::~QBenchmarkIterationController()
{
- const qreal result = QTest::endBenchmarkMeasurement();
- QBenchmarkTestMethodData::current->setResult(result, QBenchmarkGlobalData::current->measurer->metricType());
+ QBenchmarkTestMethodData::current->setResults(QTest::endBenchmarkMeasurement());
}
/*! \internal
*/
-bool QTest::QBenchmarkIterationController::isDone()
+bool QTest::QBenchmarkIterationController::isDone() const noexcept
{
if (QBenchmarkTestMethodData::current->runOnce)
return i > 0;
@@ -208,14 +176,14 @@ bool QTest::QBenchmarkIterationController::isDone()
/*! \internal
*/
-void QTest::QBenchmarkIterationController::next()
+void QTest::QBenchmarkIterationController::next() noexcept
{
++i;
}
/*! \internal
*/
-int QTest::iterationCount()
+int QTest::iterationCount() noexcept
{
return QBenchmarkTestMethodData::current->iterationCount;
}
@@ -245,7 +213,7 @@ void QTest::beginBenchmarkMeasurement()
/*! \internal
*/
-quint64 QTest::endBenchmarkMeasurement()
+QList<QBenchmarkMeasurerBase::Measurement> QTest::endBenchmarkMeasurement()
{
// the clock is ticking before the line below, don't add code here.
return QBenchmarkGlobalData::current->measurer->stop();
@@ -270,7 +238,7 @@ quint64 QTest::endBenchmarkMeasurement()
*/
void QTest::setBenchmarkResult(qreal result, QTest::QBenchmarkMetric metric)
{
- QBenchmarkTestMethodData::current->setResult(result, metric, false);
+ QBenchmarkTestMethodData::current->setResult({ result, metric }, false);
}
template <typename T>
diff --git a/src/testlib/qbenchmark.h b/src/testlib/qbenchmark.h
index 4a55fcc23e..d0e8c78567 100644
--- a/src/testlib/qbenchmark.h
+++ b/src/testlib/qbenchmark.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARK_H
#define QBENCHMARK_H
@@ -64,8 +28,8 @@ public:
QBenchmarkIterationController();
QBenchmarkIterationController(RunMode runMode);
~QBenchmarkIterationController();
- bool isDone();
- void next();
+ bool isDone() const noexcept;
+ void next() noexcept;
int i;
};
@@ -74,12 +38,12 @@ public:
// --- BEGIN public API ---
#define QBENCHMARK \
- for (QTest::QBenchmarkIterationController __iteration_controller; \
- __iteration_controller.isDone() == false; __iteration_controller.next())
+ for (QTest::QBenchmarkIterationController _q_iteration_controller; \
+ _q_iteration_controller.isDone() == false; _q_iteration_controller.next())
#define QBENCHMARK_ONCE \
- for (QTest::QBenchmarkIterationController __iteration_controller(QTest::QBenchmarkIterationController::RunOnce); \
- __iteration_controller.isDone() == false; __iteration_controller.next())
+ for (QTest::QBenchmarkIterationController _q_iteration_controller(QTest::QBenchmarkIterationController::RunOnce); \
+ _q_iteration_controller.isDone() == false; _q_iteration_controller.next())
namespace QTest
{
diff --git a/src/testlib/qbenchmark_p.h b/src/testlib/qbenchmark_p.h
index 32e77366e7..902b092b16 100644
--- a/src/testlib/qbenchmark_p.h
+++ b/src/testlib/qbenchmark_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARK_P_H
#define QBENCHMARK_P_H
@@ -97,28 +61,24 @@ class QBenchmarkResult
{
public:
QBenchmarkContext context;
- qreal value = -1;
+ QBenchmarkMeasurerBase::Measurement measurement = { -1, QTest::FramesPerSecond };
int iterations = -1;
- QTest::QBenchmarkMetric metric = QTest::FramesPerSecond;
bool setByMacro = true;
- bool valid = false;
QBenchmarkResult() = default;
QBenchmarkResult(
- const QBenchmarkContext &context, const qreal value, const int iterations,
- QTest::QBenchmarkMetric metric, bool setByMacro)
+ const QBenchmarkContext &context, QBenchmarkMeasurerBase::Measurement m,
+ const int iterations, bool setByMacro)
: context(context)
- , value(value)
+ , measurement(m)
, iterations(iterations)
- , metric(metric)
, setByMacro(setByMacro)
- , valid(true)
{ }
bool operator<(const QBenchmarkResult &other) const
{
- return (value / iterations) < (other.value / other.iterations);
+ return (measurement.value / iterations) < (other.measurement.value / other.iterations);
}
};
Q_DECLARE_TYPEINFO(QBenchmarkResult, Q_RELOCATABLE_TYPE);
@@ -155,10 +115,10 @@ private:
};
/*
- The QBenchmarkTestMethodData class stores all benchmark-related data
- for the current test case. QBenchmarkTestMethodData:current is
- created at the beginning of qInvokeTestMethod() and cleared at
- the end.
+ The QBenchmarkTestMethodData class stores all benchmark-related data for the
+ current test case. QBenchmarkTestMethodData:current is set to a local
+ instance at the beginning of TestMethods::invokeTest() and cleared by its
+ destructor when that instance drops out of scope.
*/
class Q_TESTLIB_EXPORT QBenchmarkTestMethodData
{
@@ -172,12 +132,15 @@ public:
void beginDataRun();
void endDataRun();
- bool isBenchmark() const { return result.valid; }
+ bool isBenchmark() const { return valid; }
bool resultsAccepted() const { return resultAccepted; }
int adjustIterationCount(int suggestion);
- void setResult(qreal value, QTest::QBenchmarkMetric metric, bool setByMacro = true);
+ void setResults(const QList<QBenchmarkMeasurerBase::Measurement> &m, bool setByMacro = true);
+ void setResult(QBenchmarkMeasurerBase::Measurement m, bool setByMacro = true)
+ { setResults({ m }, setByMacro); }
- QBenchmarkResult result;
+ QList<QBenchmarkResult> results;
+ bool valid = false;
bool resultAccepted = false;
bool runOnce = false;
int iterationCount = -1;
@@ -186,12 +149,12 @@ public:
// low-level API:
namespace QTest
{
- int iterationCount();
+ int iterationCount() noexcept;
void setIterationCountHint(int count);
void setIterationCount(int count);
- Q_TESTLIB_EXPORT void beginBenchmarkMeasurement();
- Q_TESTLIB_EXPORT quint64 endBenchmarkMeasurement();
+ void beginBenchmarkMeasurement();
+ QList<QBenchmarkMeasurerBase::Measurement> endBenchmarkMeasurement();
}
QT_END_NAMESPACE
diff --git a/src/testlib/qbenchmarkevent.cpp b/src/testlib/qbenchmarkevent.cpp
index bcae9325cd..5a895106a2 100644
--- a/src/testlib/qbenchmarkevent.cpp
+++ b/src/testlib/qbenchmarkevent.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qbenchmarkevent_p.h>
#include <QtTest/private/qbenchmark_p.h>
@@ -54,21 +18,16 @@ void QBenchmarkEvent::start()
QAbstractEventDispatcher::instance()->installNativeEventFilter(this);
}
-qint64 QBenchmarkEvent::checkpoint()
-{
- return eventCounter;
-}
-
-qint64 QBenchmarkEvent::stop()
+QList<QBenchmarkMeasurerBase::Measurement> QBenchmarkEvent::stop()
{
QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
- return eventCounter;
+ return { { qreal(eventCounter), QTest::Events } };
}
// It's very tempting to simply reject a measurement if 0 events
// where counted, however that is a possible situation and returning
// false here will create a infinite loop. Do not change this.
-bool QBenchmarkEvent::isMeasurementAccepted(qint64 measurement)
+bool QBenchmarkEvent::isMeasurementAccepted(QBenchmarkMeasurerBase::Measurement measurement)
{
Q_UNUSED(measurement);
return true;
@@ -85,11 +44,6 @@ int QBenchmarkEvent::adjustMedianCount(int suggestion)
return 1;
}
-QTest::QBenchmarkMetric QBenchmarkEvent::metricType()
-{
- return QTest::Events;
-}
-
// This could be done in a much better way, this is just the beginning.
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool QBenchmarkEvent::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
diff --git a/src/testlib/qbenchmarkevent_p.h b/src/testlib/qbenchmarkevent_p.h
index 9fe3daa33b..7e997e7779 100644
--- a/src/testlib/qbenchmarkevent_p.h
+++ b/src/testlib/qbenchmarkevent_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARKEVENT_P_H
#define QBENCHMARKEVENT_P_H
@@ -64,13 +28,10 @@ public:
QBenchmarkEvent();
~QBenchmarkEvent();
void start() override;
- qint64 checkpoint() override;
- qint64 stop() override;
- bool isMeasurementAccepted(qint64 measurement) override;
+ QList<Measurement> stop() override;
+ bool isMeasurementAccepted(Measurement measurement) override;
int adjustIterationCount(int suggestion) override;
int adjustMedianCount(int suggestion) override;
- bool repeatCount() override { return 1; }
- QTest::QBenchmarkMetric metricType() override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
#else
diff --git a/src/testlib/qbenchmarkmeasurement.cpp b/src/testlib/qbenchmarkmeasurement.cpp
index 228ab15f99..99d5b1dd4f 100644
--- a/src/testlib/qbenchmarkmeasurement.cpp
+++ b/src/testlib/qbenchmarkmeasurement.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qbenchmarktimemeasurers_p.h>
#include <QtTest/private/qbenchmark_p.h>
@@ -52,19 +16,14 @@ void QBenchmarkTimeMeasurer::start()
time.start();
}
-qint64 QBenchmarkTimeMeasurer::checkpoint()
+QList<QBenchmarkMeasurerBase::Measurement> QBenchmarkTimeMeasurer::stop()
{
- return time.elapsed();
+ return { { qreal(time.elapsed()), QTest::WalltimeMilliseconds } };
}
-qint64 QBenchmarkTimeMeasurer::stop()
+bool QBenchmarkTimeMeasurer::isMeasurementAccepted(Measurement measurement)
{
- return time.elapsed();
-}
-
-bool QBenchmarkTimeMeasurer::isMeasurementAccepted(qint64 measurement)
-{
- return (measurement > 50);
+ return (measurement.value > 50);
}
int QBenchmarkTimeMeasurer::adjustIterationCount(int suggestion)
@@ -82,11 +41,6 @@ int QBenchmarkTimeMeasurer::adjustMedianCount(int)
return 1;
}
-QTest::QBenchmarkMetric QBenchmarkTimeMeasurer::metricType()
-{
- return QTest::WalltimeMilliseconds;
-}
-
#ifdef HAVE_TICK_COUNTER // defined in 3rdparty/cycle_p.h
void QBenchmarkTickMeasurer::start()
@@ -94,19 +48,13 @@ void QBenchmarkTickMeasurer::start()
startTicks = getticks();
}
-qint64 QBenchmarkTickMeasurer::checkpoint()
+QList<QBenchmarkMeasurerBase::Measurement> QBenchmarkTickMeasurer::stop()
{
CycleCounterTicks now = getticks();
- return qRound64(elapsed(now, startTicks));
+ return { { elapsed(now, startTicks), QTest::CPUTicks } };
}
-qint64 QBenchmarkTickMeasurer::stop()
-{
- CycleCounterTicks now = getticks();
- return qRound64(elapsed(now, startTicks));
-}
-
-bool QBenchmarkTickMeasurer::isMeasurementAccepted(qint64)
+bool QBenchmarkTickMeasurer::isMeasurementAccepted(QBenchmarkMeasurerBase::Measurement)
{
return true;
}
@@ -126,11 +74,6 @@ bool QBenchmarkTickMeasurer::needsWarmupIteration()
return true;
}
-QTest::QBenchmarkMetric QBenchmarkTickMeasurer::metricType()
-{
- return QTest::CPUTicks;
-}
-
#endif
diff --git a/src/testlib/qbenchmarkmeasurement_p.h b/src/testlib/qbenchmarkmeasurement_p.h
index 1e2b82c25d..56b978228b 100644
--- a/src/testlib/qbenchmarkmeasurement_p.h
+++ b/src/testlib/qbenchmarkmeasurement_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARKMEASUREMENT_P_H
#define QBENCHMARKMEASUREMENT_P_H
@@ -52,23 +16,27 @@
//
#include <QtTest/qbenchmark.h>
+#include <QtCore/qlist.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
class QBenchmarkMeasurerBase
{
public:
+ struct Measurement
+ {
+ qreal value;
+ QTest::QBenchmarkMetric metric;
+ };
virtual ~QBenchmarkMeasurerBase() = default;
virtual void init() {}
virtual void start() = 0;
- virtual qint64 checkpoint() = 0;
- virtual qint64 stop() = 0;
- virtual bool isMeasurementAccepted(qint64 measurement) = 0;
+ virtual QList<Measurement> stop() = 0;
+ virtual bool isMeasurementAccepted(Measurement m) = 0;
virtual int adjustIterationCount(int suggestion) = 0;
virtual int adjustMedianCount(int suggestion) = 0;
- virtual bool repeatCount() { return true; }
virtual bool needsWarmupIteration() { return false; }
- virtual QTest::QBenchmarkMetric metricType() = 0;
};
QT_END_NAMESPACE
diff --git a/src/testlib/qbenchmarkmetric.cpp b/src/testlib/qbenchmarkmetric.cpp
index 88f4235d46..7f5206982a 100644
--- a/src/testlib/qbenchmarkmetric.cpp
+++ b/src/testlib/qbenchmarkmetric.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qbenchmarkmetric_p.h>
@@ -147,7 +111,7 @@ const char * QTest::benchmarkMetricName(QBenchmarkMetric metric)
/*!
\since 4.7
- Retuns the units of measure for the specified \a metric.
+ Returns the units of measure for the specified \a metric.
*/
const char * QTest::benchmarkMetricUnit(QBenchmarkMetric metric)
{
diff --git a/src/testlib/qbenchmarkmetric.h b/src/testlib/qbenchmarkmetric.h
index 6605606402..57cf2cdbe0 100644
--- a/src/testlib/qbenchmarkmetric.h
+++ b/src/testlib/qbenchmarkmetric.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARKMETRIC_H
#define QBENCHMARKMETRIC_H
diff --git a/src/testlib/qbenchmarkmetric_p.h b/src/testlib/qbenchmarkmetric_p.h
index 049b9711e2..0ea9d753c4 100644
--- a/src/testlib/qbenchmarkmetric_p.h
+++ b/src/testlib/qbenchmarkmetric_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARKMETRIC_P_H
#define QBENCHMARKMETRIC_P_H
@@ -53,6 +17,7 @@
#include <QtTest/qttestglobal.h>
#include <QtTest/qbenchmarkmetric.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/testlib/qbenchmarkperfevents.cpp b/src/testlib/qbenchmarkperfevents.cpp
index e8c6969d4a..c161879a7d 100644
--- a/src/testlib/qbenchmarkperfevents.cpp
+++ b/src/testlib/qbenchmarkperfevents.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qbenchmarkperfevents_p.h"
#include "qbenchmarkmetric.h"
@@ -53,8 +17,9 @@
#include <string.h>
#include <stdio.h>
-#include <sys/syscall.h>
#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
#include "3rdparty/linux_perf_event_p.h"
@@ -83,7 +48,13 @@
QT_BEGIN_NAMESPACE
+struct PerfEvent
+{
+ quint32 type;
+ quint64 config;
+};
static perf_event_attr attr;
+Q_GLOBAL_STATIC(QList<PerfEvent>, eventTypes);
static void initPerf()
{
@@ -98,14 +69,20 @@ static void initPerf()
attr.inherit_stat = true; // aggregate all the info from child processes
attr.task = true; // trace fork/exits
- // set a default performance counter: CPU cycles
- attr.type = PERF_TYPE_HARDWARE;
- attr.config = PERF_COUNT_HW_CPU_CYCLES; // default
-
done = true;
}
}
+static QList<PerfEvent> defaultCounters()
+{
+ return {
+ { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS },
+ { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
+ };
+}
+
// This class does not exist in the API so it's qdoc comment marker was removed.
/*
@@ -131,7 +108,8 @@ static void initPerf()
static int perf_event_open(perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags)
{
#ifdef SYS_perf_event_open
- return syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags);
+ // syscall() returns long, but perf_event_open() is used to get a file descriptor
+ return int(syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags));
#else
Q_UNUSED(attr);
Q_UNUSED(pid);
@@ -164,8 +142,8 @@ bool QBenchmarkPerfEventsMeasurer::isAvailable()
HARDWARE BUS_CYCLES BusCycles bus-cycles
HARDWARE STALLED_CYCLES_FRONTEND StalledCycles stalled-cycles-frontend idle-cycles-frontend
HARDWARE STALLED_CYCLES_BACKEND StalledCycles stalled-cycles-backend idle-cycles-backend
- SOFTWARE CPU_CLOCK WalltimeMilliseconds cpu-clock
- SOFTWARE TASK_CLOCK WalltimeMilliseconds task-clock
+ SOFTWARE CPU_CLOCK WalltimeNanoseconds cpu-clock
+ SOFTWARE TASK_CLOCK WalltimeNanoseconds task-clock
SOFTWARE PAGE_FAULTS PageFaults page-faults faults
SOFTWARE PAGE_FAULTS_MAJ MajorPageFaults major-faults
SOFTWARE PAGE_FAULTS_MIN MinorPageFaults minor-faults
@@ -231,7 +209,7 @@ for $entry (sort @strings) {
$map{$entry}[2],
$map{$entry}[3];
}
-print " { 0, PERF_TYPE_MAX, 0, QTest::Events }\n};\n";
+print "};\n";
=== cut perl ===
*/
@@ -345,7 +323,7 @@ static const Events eventlist[] = {
{ 170, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, QTest::CacheMisses },
{ 183, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, QTest::CacheReferences },
{ 200, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES, QTest::ContextSwitches },
- { 217, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, QTest::WalltimeMilliseconds },
+ { 217, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, QTest::WalltimeNanoseconds },
{ 227, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, QTest::CPUCycles },
{ 238, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS, QTest::CPUMigrations },
{ 253, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES, QTest::ContextSwitches },
@@ -414,17 +392,15 @@ static const Events eventlist[] = {
{ 1292, PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, QTest::RefCPUCycles },
{ 1303, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND, QTest::StalledCycles },
{ 1326, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, QTest::StalledCycles },
- { 1350, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK, QTest::WalltimeMilliseconds },
- { 0, PERF_TYPE_MAX, 0, QTest::Events }
+ { 1350, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK, QTest::WalltimeNanoseconds },
};
/* -- END GENERATED CODE -- */
-QTest::QBenchmarkMetric QBenchmarkPerfEventsMeasurer::metricForEvent(quint32 type, quint64 event_id)
+static QTest::QBenchmarkMetric metricForEvent(PerfEvent counter)
{
- const Events *ptr = eventlist;
- for ( ; ptr->type != PERF_TYPE_MAX; ++ptr) {
- if (ptr->type == type && ptr->event_id == event_id)
- return ptr->metric;
+ for (const Events &ev : eventlist) {
+ if (ev.type == counter.type && ev.event_id == counter.config)
+ return ev.metric;
}
return QTest::Events;
}
@@ -432,47 +408,39 @@ QTest::QBenchmarkMetric QBenchmarkPerfEventsMeasurer::metricForEvent(quint32 typ
void QBenchmarkPerfEventsMeasurer::setCounter(const char *name)
{
initPerf();
- const char *colon = strchr(name, ':');
- int n = colon ? colon - name : strlen(name);
- const Events *ptr = eventlist;
- for ( ; ptr->type != PERF_TYPE_MAX; ++ptr) {
- int c = strncmp(name, eventlist_strings + ptr->offset, n);
- if (c == 0)
+ eventTypes->clear();
+ std::string_view input = name;
+ if (qsizetype idx = input.find(':'); idx >= 0)
+ input = input.substr(0, idx);
+
+ while (!input.empty()) {
+ std::string_view countername = input;
+ if (qsizetype idx = countername.find(','); idx >= 0)
+ countername = countername.substr(0, idx);
+
+ for (const Events &ev : eventlist) {
+ int c = countername.compare(eventlist_strings + ev.offset);
+ if (c > 0)
+ continue;
+ if (c < 0) {
+ fprintf(stderr, "ERROR: Performance counter type '%.*s' is unknown\n",
+ int(countername.size()), countername.data());
+ exit(1);
+ }
+ eventTypes->append({ ev.type, ev.event_id });
break;
- if (c < 0) {
- fprintf(stderr, "ERROR: Performance counter type '%s' is unknown\n", name);
- exit(1);
}
- }
-
- attr.type = ptr->type;
- attr.config = ptr->event_id;
- // now parse the attributes
- if (!colon)
- return;
- while (*++colon) {
- switch (*colon) {
- case 'u':
- attr.exclude_user = true;
- break;
- case 'k':
- attr.exclude_kernel = true;
- break;
- case 'h':
- attr.exclude_hv = true;
- break;
- case 'G':
- attr.exclude_guest = true;
- break;
- case 'H':
- attr.exclude_host = true;
- break;
- default:
- fprintf(stderr, "ERROR: Unknown attribute '%c'\n", *colon);
- exit(1);
- }
+ if (countername.size() == input.size())
+ input = {};
+ else
+ input.remove_prefix(countername.size() + 1);
}
+
+ // We used to support attributes, but our code was the opposite of what
+ // perf(1) does, plus QBenchlib isn't exactly expected to be used to
+ // profile Linux kernel code or launch guest VMs as part of the workload.
+ // So we keep accepting the colon as a delimiter but ignore it.
}
void QBenchmarkPerfEventsMeasurer::listCounters()
@@ -483,28 +451,20 @@ void QBenchmarkPerfEventsMeasurer::listCounters()
}
printf("The following performance counters are available:\n");
- const Events *ptr = eventlist;
- for ( ; ptr->type != PERF_TYPE_MAX; ++ptr) {
- printf(" %-30s [%s]\n", eventlist_strings + ptr->offset,
- ptr->type == PERF_TYPE_HARDWARE ? "hardware" :
- ptr->type == PERF_TYPE_SOFTWARE ? "software" :
- ptr->type == PERF_TYPE_HW_CACHE ? "cache" : "other");
+ for (const Events &ev : eventlist) {
+ printf(" %-30s [%s]\n", eventlist_strings + ev.offset,
+ ev.type == PERF_TYPE_HARDWARE ? "hardware" :
+ ev.type == PERF_TYPE_SOFTWARE ? "software" :
+ ev.type == PERF_TYPE_HW_CACHE ? "cache" : "other");
}
-
- printf("\nAttributes can be specified by adding a colon and the following:\n"
- " u - exclude measuring in the userspace\n"
- " k - exclude measuring in kernel mode\n"
- " h - exclude measuring in the hypervisor\n"
- " G - exclude measuring when running virtualized (guest VM)\n"
- " H - exclude measuring when running non-virtualized (host system)\n"
- "Attributes can be combined, for example: -perfcounter branch-mispredicts:kh\n");
}
QBenchmarkPerfEventsMeasurer::QBenchmarkPerfEventsMeasurer() = default;
QBenchmarkPerfEventsMeasurer::~QBenchmarkPerfEventsMeasurer()
{
- qt_safe_close(fd);
+ for (int fd : std::as_const(fds))
+ qt_safe_close(fd);
}
void QBenchmarkPerfEventsMeasurer::init()
@@ -513,43 +473,56 @@ void QBenchmarkPerfEventsMeasurer::init()
void QBenchmarkPerfEventsMeasurer::start()
{
-
initPerf();
- if (fd == -1) {
- // pid == 0 -> attach to the current process
- // cpu == -1 -> monitor on all CPUs
- // group_fd == -1 -> this is the group leader
- // flags == 0 -> reserved, must be zero
- fd = perf_event_open(&attr, 0, -1, -1, 0);
- if (fd == -1) {
- perror("QBenchmarkPerfEventsMeasurer::start: perf_event_open");
- exit(1);
- } else {
- ::fcntl(fd, F_SETFD, FD_CLOEXEC);
+ QList<PerfEvent> &counters = *eventTypes;
+ if (counters.isEmpty())
+ counters = defaultCounters();
+ if (fds.isEmpty()) {
+ pid_t pid = 0; // attach to the current process only
+ int cpu = -1; // on any CPU
+ int group_fd = -1;
+ int flags = PERF_FLAG_FD_CLOEXEC;
+
+ fds.reserve(counters.size());
+ for (PerfEvent counter : std::as_const(counters)) {
+ attr.type = counter.type;
+ attr.config = counter.config;
+ int fd = perf_event_open(&attr, pid, cpu, group_fd, flags);
+ if (fd == -1) {
+ // probably a paranoid kernel (/proc/sys/kernel/perf_event_paranoid)
+ attr.exclude_kernel = true;
+ attr.exclude_hv = true;
+ fd = perf_event_open(&attr, pid, cpu, group_fd, flags);
+ }
+ if (fd == -1) {
+ perror("QBenchmarkPerfEventsMeasurer::start: perf_event_open");
+ exit(1);
+ }
+
+ fds.append(fd);
}
}
- // enable the counter
- ::ioctl(fd, PERF_EVENT_IOC_RESET);
- ::ioctl(fd, PERF_EVENT_IOC_ENABLE);
+ // enable the counters
+ for (int fd : std::as_const(fds))
+ ::ioctl(fd, PERF_EVENT_IOC_RESET);
+ prctl(PR_TASK_PERF_EVENTS_ENABLE);
}
-qint64 QBenchmarkPerfEventsMeasurer::checkpoint()
+QList<QBenchmarkMeasurerBase::Measurement> QBenchmarkPerfEventsMeasurer::stop()
{
- ::ioctl(fd, PERF_EVENT_IOC_DISABLE);
- qint64 value = readValue();
- ::ioctl(fd, PERF_EVENT_IOC_ENABLE);
- return value;
-}
+ // disable the counters
+ prctl(PR_TASK_PERF_EVENTS_DISABLE);
-qint64 QBenchmarkPerfEventsMeasurer::stop()
-{
- // disable the counter
- ::ioctl(fd, PERF_EVENT_IOC_DISABLE);
- return readValue();
+ const QList<PerfEvent> &counters = *eventTypes;
+ QList<Measurement> result(counters.size(), {});
+ for (qsizetype i = 0; i < counters.size(); ++i) {
+ result[i] = readValue(i);
+ }
+ return result;
}
-bool QBenchmarkPerfEventsMeasurer::isMeasurementAccepted(qint64)
+bool QBenchmarkPerfEventsMeasurer::isMeasurementAccepted(Measurement)
{
return true;
}
@@ -564,11 +537,6 @@ int QBenchmarkPerfEventsMeasurer::adjustMedianCount(int)
return 1;
}
-QTest::QBenchmarkMetric QBenchmarkPerfEventsMeasurer::metricType()
-{
- return metricForEvent(attr.type, attr.config);
-}
-
static quint64 rawReadValue(int fd)
{
/* from the kernel docs:
@@ -604,14 +572,10 @@ static quint64 rawReadValue(int fd)
return results.value * (double(results.time_running) / double(results.time_enabled));
}
-qint64 QBenchmarkPerfEventsMeasurer::readValue()
+QBenchmarkMeasurerBase::Measurement QBenchmarkPerfEventsMeasurer::readValue(qsizetype idx)
{
- quint64 raw = rawReadValue(fd);
- if (metricType() == QTest::WalltimeMilliseconds) {
- // perf returns nanoseconds
- return raw / 1000000;
- }
- return raw;
+ quint64 raw = rawReadValue(fds.at(idx));
+ return { qreal(qint64(raw)), metricForEvent(eventTypes->at(idx)) };
}
QT_END_NAMESPACE
diff --git a/src/testlib/qbenchmarkperfevents_p.h b/src/testlib/qbenchmarkperfevents_p.h
index 3f27161ef5..5f94ea7794 100644
--- a/src/testlib/qbenchmarkperfevents_p.h
+++ b/src/testlib/qbenchmarkperfevents_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARKPERFEVENTS_P_H
#define QBENCHMARKPERFEVENTS_P_H
@@ -62,23 +26,19 @@ public:
~QBenchmarkPerfEventsMeasurer();
void init() override;
void start() override;
- qint64 checkpoint() override;
- qint64 stop() override;
- bool isMeasurementAccepted(qint64 measurement) override;
+ QList<Measurement> stop() override;
+ bool isMeasurementAccepted(Measurement measurement) override;
int adjustIterationCount(int suggestion) override;
int adjustMedianCount(int suggestion) override;
- bool repeatCount() override { return true; }
bool needsWarmupIteration() override { return true; }
- QTest::QBenchmarkMetric metricType() override;
static bool isAvailable();
- static QTest::QBenchmarkMetric metricForEvent(quint32 type, quint64 event_id);
static void setCounter(const char *name);
static void listCounters();
private:
- int fd = -1;
+ QList<int> fds;
- qint64 readValue();
+ Measurement readValue(qsizetype idx = 0);
};
QT_END_NAMESPACE
diff --git a/src/testlib/qbenchmarktimemeasurers_p.h b/src/testlib/qbenchmarktimemeasurers_p.h
index f268669e2f..2f1364db02 100644
--- a/src/testlib/qbenchmarktimemeasurers_p.h
+++ b/src/testlib/qbenchmarktimemeasurers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARKTIMEMEASURERS_P_H
#define QBENCHMARKTIMEMEASURERS_P_H
@@ -61,13 +25,11 @@ class QBenchmarkTimeMeasurer : public QBenchmarkMeasurerBase
{
public:
void start() override;
- qint64 checkpoint() override;
- qint64 stop() override;
- bool isMeasurementAccepted(qint64 measurement) override;
+ QList<Measurement> stop() override;
+ bool isMeasurementAccepted(Measurement measurement) override;
int adjustIterationCount(int sugestion) override;
int adjustMedianCount(int suggestion) override;
bool needsWarmupIteration() override;
- QTest::QBenchmarkMetric metricType() override;
private:
QElapsedTimer time;
};
@@ -78,13 +40,11 @@ class QBenchmarkTickMeasurer : public QBenchmarkMeasurerBase
{
public:
void start() override;
- qint64 checkpoint() override;
- qint64 stop() override;
- bool isMeasurementAccepted(qint64 measurement) override;
+ QList<Measurement> stop() override;
+ bool isMeasurementAccepted(Measurement measurement) override;
int adjustIterationCount(int) override;
int adjustMedianCount(int suggestion) override;
bool needsWarmupIteration() override;
- QTest::QBenchmarkMetric metricType() override;
private:
CycleCounterTicks startTicks;
};
diff --git a/src/testlib/qbenchmarkvalgrind.cpp b/src/testlib/qbenchmarkvalgrind.cpp
index 29cbcd8e3c..bea3066e66 100644
--- a/src/testlib/qbenchmarkvalgrind.cpp
+++ b/src/testlib/qbenchmarkvalgrind.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qbenchmark_p.h>
@@ -48,8 +12,13 @@
#include <QtCore/qset.h>
#include <QtTest/private/callgrind_p.h>
+#include <charconv>
+#include <optional>
+
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
// Returns \c true if valgrind is available.
bool QBenchmarkValgrindUtils::haveValgrind()
{
@@ -57,7 +26,7 @@ bool QBenchmarkValgrindUtils::haveValgrind()
return false;
#else
QProcess process;
- process.start(QLatin1String("valgrind"), QStringList(QLatin1String("--version")));
+ process.start(u"valgrind"_s, QStringList(u"--version"_s));
return process.waitForStarted() && process.waitForFinished(-1);
#endif
}
@@ -76,7 +45,10 @@ bool QBenchmarkValgrindUtils::rerunThroughCallgrind(const QStringList &origAppAr
static void dumpOutput(const QByteArray &data, FILE *fh)
{
QFile file;
- file.open(fh, QIODevice::WriteOnly);
+ if (!file.open(fh, QIODevice::WriteOnly)) {
+ qFatal("Could not open filehandle for dumping output: %s",
+ qPrintable(file.errorString()));
+ }
file.write(data);
}
@@ -87,23 +59,24 @@ qint64 QBenchmarkValgrindUtils::extractResult(const QString &fileName)
Q_ASSERT(openOk);
Q_UNUSED(openOk);
- qint64 val = -1;
- bool valSeen = false;
- QRegularExpression rxValue(QLatin1String("^summary: (\\d+)"));
+ std::optional<qint64> val = std::nullopt;
while (!file.atEnd()) {
- const QString line(QLatin1String(file.readLine()));
- QRegularExpressionMatch match = rxValue.match(line);
- if (match.hasMatch()) {
- bool ok;
- val = match.captured(1).toLongLong(&ok);
- Q_ASSERT(ok);
- valSeen = true;
- break;
+ const QByteArray line = file.readLine();
+ constexpr QByteArrayView tag = "summary: ";
+ if (line.startsWith(tag)) {
+ const auto maybeNumber = line.data() + tag.size();
+ const auto end = line.data() + line.size();
+ qint64 v;
+ const auto r = std::from_chars(maybeNumber, end, v);
+ if (r.ec == std::errc{}) {
+ val = v;
+ break;
+ }
}
}
- if (Q_UNLIKELY(!valSeen))
+ if (Q_UNLIKELY(!val))
qFatal("Failed to extract result");
- return val;
+ return *val;
}
// Gets the newest file name (i.e. the one with the highest integer suffix).
@@ -169,22 +142,20 @@ QString QBenchmarkValgrindUtils::outFileBase(qint64 pid)
bool QBenchmarkValgrindUtils::runCallgrindSubProcess(const QStringList &origAppArgs, int &exitCode)
{
const QString &execFile = origAppArgs.at(0);
- QStringList args;
- args << QLatin1String("--tool=callgrind") << QLatin1String("--instr-atstart=yes")
- << QLatin1String("--quiet")
- << execFile << QLatin1String("-callgrindchild");
+ QStringList args{ u"--tool=callgrind"_s, u"--instr-atstart=yes"_s,
+ u"--quiet"_s, execFile, u"-callgrindchild"_s };
// pass on original arguments that make sense (e.g. avoid wasting time producing output
// that will be ignored anyway) ...
for (int i = 1; i < origAppArgs.size(); ++i) {
const QString &arg = origAppArgs.at(i);
- if (arg == QLatin1String("-callgrind"))
+ if (arg == "-callgrind"_L1)
continue;
args << arg; // ok to pass on
}
QProcess process;
- process.start(QLatin1String("valgrind"), args);
+ process.start(u"valgrind"_s, args);
process.waitForStarted(-1);
QBenchmarkGlobalData::current->callgrindOutFileBase =
QBenchmarkValgrindUtils::outFileBase(process.processId());
@@ -202,19 +173,14 @@ void QBenchmarkCallgrindMeasurer::start()
CALLGRIND_ZERO_STATS;
}
-qint64 QBenchmarkCallgrindMeasurer::checkpoint()
+QList<QBenchmarkMeasurerBase::Measurement> QBenchmarkCallgrindMeasurer::stop()
{
CALLGRIND_DUMP_STATS;
const qint64 result = QBenchmarkValgrindUtils::extractLastResult();
- return result;
+ return { { qreal(result), QTest::InstructionReads } };
}
-qint64 QBenchmarkCallgrindMeasurer::stop()
-{
- return checkpoint();
-}
-
-bool QBenchmarkCallgrindMeasurer::isMeasurementAccepted(qint64 measurement)
+bool QBenchmarkCallgrindMeasurer::isMeasurementAccepted(Measurement measurement)
{
Q_UNUSED(measurement);
return true;
@@ -235,9 +201,4 @@ bool QBenchmarkCallgrindMeasurer::needsWarmupIteration()
return true;
}
-QTest::QBenchmarkMetric QBenchmarkCallgrindMeasurer::metricType()
-{
- return QTest::InstructionReads;
-}
-
QT_END_NAMESPACE
diff --git a/src/testlib/qbenchmarkvalgrind_p.h b/src/testlib/qbenchmarkvalgrind_p.h
index 0cbb8bb103..f9925c9211 100644
--- a/src/testlib/qbenchmarkvalgrind_p.h
+++ b/src/testlib/qbenchmarkvalgrind_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QBENCHMARKVALGRIND_P_H
#define QBENCHMARKVALGRIND_P_H
@@ -77,13 +41,11 @@ class QBenchmarkCallgrindMeasurer : public QBenchmarkMeasurerBase
{
public:
void start() override;
- qint64 checkpoint() override;
- qint64 stop() override;
- bool isMeasurementAccepted(qint64 measurement) override;
+ QList<Measurement> stop() override;
+ bool isMeasurementAccepted(Measurement measurement) override;
int adjustIterationCount(int) override;
int adjustMedianCount(int) override;
bool needsWarmupIteration() override;
- QTest::QBenchmarkMetric metricType() override;
};
QT_END_NAMESPACE
diff --git a/src/testlib/qcomparisontesthelper.cpp b/src/testlib/qcomparisontesthelper.cpp
new file mode 100644
index 0000000000..b71267b625
--- /dev/null
+++ b/src/testlib/qcomparisontesthelper.cpp
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qcomparisontesthelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QTestPrivate {
+
+QByteArray formatTypeWithCRefImpl(QMetaType type, bool isConst, bool isRef, bool isRvalueRef)
+{
+ QByteArray res(type.name());
+ if (isConst)
+ res.append(" const");
+ if (isRef)
+ res.append(isRvalueRef ? " &&" : " &");
+ return res;
+}
+
+} // namespace QTestPrivate
+
+QT_END_NAMESPACE
diff --git a/src/testlib/qcomparisontesthelper_p.h b/src/testlib/qcomparisontesthelper_p.h
new file mode 100644
index 0000000000..afeb1088c4
--- /dev/null
+++ b/src/testlib/qcomparisontesthelper_p.h
@@ -0,0 +1,373 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCOMPARISONTESTHELPER_P_H
+#define QCOMPARISONTESTHELPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/q20type_traits.h>
+#include <QtCore/qxptype_traits.h>
+#include <QtTest/qtest.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTestPrivate {
+
+#ifdef __cpp_lib_three_way_comparison
+template <typename LT, typename RT>
+using HasThreeWayComparisonOp = decltype(std::declval<LT>() <=> std::declval<RT>());
+
+template <typename LT, typename RT>
+constexpr bool implementsThreeWayComparisonOp_v = qxp::is_detected_v<HasThreeWayComparisonOp,
+ LT, RT>;
+#endif
+
+Q_TESTLIB_EXPORT QByteArray formatTypeWithCRefImpl(QMetaType type, bool isConst,
+ bool isRef, bool isRvalueRef);
+
+template <typename T>
+QByteArray formatTypeWithCRef()
+{
+ return formatTypeWithCRefImpl(QMetaType::fromType<q20::remove_cvref_t<T>>(),
+ std::is_const_v<std::remove_reference_t<T>>,
+ std::is_reference_v<T>,
+ std::is_rvalue_reference_v<T>);
+}
+
+#define FOR_EACH_CREF(Func, Left, Right, Op, Result) \
+ Func(Left &, Right &, Op, Result) \
+ Func(Left &, Right const &, Op, Result) \
+ Func(Left &, Right &&, Op, Result) \
+ Func(Left &, Right const &&, Op, Result) \
+ Func(Left const &, Right &, Op, Result) \
+ Func(Left const &, Right const &, Op, Result) \
+ Func(Left const &, Right &&, Op, Result) \
+ Func(Left const &, Right const &&, Op, Result) \
+ Func(Left &&, Right &, Op, Result) \
+ Func(Left &&, Right const &, Op, Result) \
+ Func(Left &&, Right &&, Op, Result) \
+ Func(Left &&, Right const &&, Op, Result) \
+ Func(Left const &&, Right &, Op, Result) \
+ Func(Left const &&, Right const &, Op, Result) \
+ Func(Left const &&, Right &&, Op, Result) \
+ Func(Left const &&, Right const &&, Op, Result) \
+ /* END */
+
+#define CHECK_SINGLE_OPERATOR(Left, Right, Op, Result) \
+ do { \
+ constexpr bool qtest_op_check_isImplNoexcept \
+ = noexcept(std::declval<Left>() Op std::declval<Right>()); \
+ if constexpr (!qtest_op_check_isImplNoexcept) { \
+ QEXPECT_FAIL("", QByteArray("(" + formatTypeWithCRef<Left>() \
+ + " " #Op " " + formatTypeWithCRef<Right>() \
+ + ") is not noexcept").constData(), \
+ Continue); \
+ /* Ideally, operators should be noexcept, so warn if they are not. */ \
+ /* Do not make it a hard error, because the fix is not always trivial. */ \
+ QVERIFY(qtest_op_check_isImplNoexcept); \
+ } \
+ static_assert(std::is_convertible_v<decltype( \
+ std::declval<Left>() Op std::declval<Right>()), Result>); \
+ if constexpr (!std::is_same_v<Left, Right>) { \
+ static_assert(std::is_convertible_v<decltype( \
+ std::declval<Right>() Op std::declval<Left>()), Result>); \
+ } \
+ } while (false); \
+ /* END */
+
+/*!
+ \internal
+
+ This function checks that the types \c LeftType and \c RightType properly
+ define {in}equality operators (== and !=). The checks are performed for
+ all combinations of cvref-qualified lvalues and rvalues.
+*/
+template <typename LeftType, typename RightType = LeftType>
+void testEqualityOperatorsCompile()
+{
+ FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, ==, bool)
+ FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, !=, bool)
+}
+
+/*!
+ \internal
+
+ This function checks that the types \c LeftType and \c RightType properly
+ define all comparison operators (==, !=, <, >, <=, >=). The checks are
+ performed for all combinations of cvref-qualified lvalues and rvalues.
+
+ If compiled in C++20 mode, also checks \c {operator<=>()} if that is
+ implemented.
+*/
+template <typename LeftType, typename RightType = LeftType>
+void testAllComparisonOperatorsCompile()
+{
+ testEqualityOperatorsCompile<LeftType, RightType>();
+ if (QTest::currentTestFailed())
+ return;
+ FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, >, bool)
+ FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, <, bool)
+ FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, >=, bool)
+ FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, <=, bool)
+#ifdef __cpp_lib_three_way_comparison
+ if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
+ FOR_EACH_CREF(CHECK_SINGLE_OPERATOR, LeftType, RightType, <=>, std::partial_ordering)
+ }
+#endif
+}
+
+#undef CHECK_SINGLE_OPERATOR
+#undef FOR_EACH_CREF
+
+#define CHECK_RUNTIME_CREF(Func, Left, Right, Op, Expected) \
+ do { \
+ Func(Left, Right, Op, Expected); \
+ Func(std::as_const(Left), Right, Op, Expected); \
+ Func(Left, std::as_const(Right), Op, Expected); \
+ Func(std::as_const(Left), std::as_const(Right), Op, Expected); \
+ } while (false) \
+ /* END */
+
+#define CHECK_RUNTIME_LR(Left, Right, Op, Expected) \
+ do { \
+ QCOMPARE_EQ(Left Op Right, Expected); \
+ QCOMPARE_EQ(std::move(Left) Op Right, Expected); \
+ QCOMPARE_EQ(Left Op std::move(Right), Expected); \
+ QCOMPARE_EQ(std::move(Left) Op std::move(Right), Expected); \
+ } while (false) \
+ /* END */
+
+#ifdef __cpp_lib_three_way_comparison
+
+// Hide the macro under an ifdef, because it otherwise triggers a warning
+// in Clang C++17 build.
+#define CHECK_RUNTIME_3WAY(Left, Right, Op, Expected) \
+ do { \
+ QCOMPARE_EQ((Left <=> Right) Op 0, Expected); \
+ QCOMPARE_EQ((std::move(Left) <=> Right) Op 0, Expected); \
+ QCOMPARE_EQ((Left <=> std::move(Right)) Op 0, Expected); \
+ QCOMPARE_EQ((std::move(Left) <=> std::move(Right)) Op 0, Expected); \
+ } while (false) \
+ /* END */
+
+#endif // __cpp_lib_three_way_comparison
+
+/*!
+ \internal
+ Basic testing of equality operators.
+
+ The helper function tests {in}equality operators (== and !=) for the \a lhs
+ operand of type \c {LeftType} and the \a rhs operand of type \c {RightType},
+ plus the reverse order of the operands.
+
+ The \a expectedEqual parameter is an expected result for \c {operator==()}.
+
+ \note Any test calling this method will need to check the test state after
+ doing so, if there is any later code in the test.
+
+ \code
+ QTime early(12, 34, 56, 00);
+ QTime later(12, 34, 56, 01);
+ QTestPrivate::testEqualityOperators(early, later, false);
+ if (QTest:currentTestFailed())
+ return;
+ \endcode
+*/
+template <typename LeftType, typename RightType>
+void testEqualityOperators(LeftType lhs, RightType rhs, bool expectedEqual)
+{
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, ==, expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, !=, !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==, expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=, !expectedEqual);
+}
+
+/*!
+ \internal
+ Basic testing of equality and relation operators.
+
+ The helper function tests all six relation and equality operators
+ (==, !=, <, >, <=, >=) for the \a lhs operand of type \c {LeftType} and
+ the \a rhs operand of type \c {RightType} and all six for the reverse
+ order of the operands.
+
+ If compiled in C++20 mode, also checks \c {operator<=>()} if that is
+ implemented.
+
+ When compiled in C++17 mode, the \c OrderingType must be one of
+ Qt::partial_ordering, Qt::strong_ordering, or Qt::weak_ordering.
+ In C++20 mode, also the \c {std::*_ordering} types can be used.
+
+ The \a expectedOrdering parameter provides the expected
+ relation between \a lhs and \a rhs.
+
+ \note Any test calling this method will need to check the test state after
+ doing so, if there is any later code in the test.
+
+ \code
+ QDateTime now = QDateTime::currentDateTime();
+ QDateTime later = now.addMSec(1);
+ QTestPrivate::testComparisonOperators(now, later, Qt::weak_ordering::less);
+ if (QTest:currentTestFailed())
+ return;
+ \endcode
+*/
+template <typename LeftType, typename RightType, typename OrderingType>
+void testAllComparisonOperators(LeftType lhs, RightType rhs, OrderingType expectedOrdering)
+{
+ constexpr bool isQOrderingType = std::is_same_v<OrderingType, Qt::partial_ordering>
+ || std::is_same_v<OrderingType, Qt::weak_ordering>
+ || std::is_same_v<OrderingType, Qt::strong_ordering>;
+#ifdef __cpp_lib_three_way_comparison
+ constexpr bool isStdOrderingType = std::is_same_v<OrderingType, std::partial_ordering>
+ || std::is_same_v<OrderingType, std::weak_ordering>
+ || std::is_same_v<OrderingType, std::strong_ordering>;
+#else
+ constexpr bool isStdOrderingType = false;
+#endif
+
+ static_assert(isQOrderingType || isStdOrderingType,
+ "Please provide, as the expectedOrdering parameter, a value "
+ "of one of the Qt::{partial,weak,strong}_ordering or "
+ "std::{partial,weak,strong}_ordering types.");
+
+ // We have all sorts of operator==() between Q*Ordering and std::*_ordering
+ // types, so we can just compare to Qt::partial_ordering.
+ const bool expectedEqual = expectedOrdering == Qt::partial_ordering::equivalent;
+ const bool expectedLess = expectedOrdering == Qt::partial_ordering::less;
+ const bool expectedUnordered = expectedOrdering == Qt::partial_ordering::unordered;
+
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, ==,
+ !expectedUnordered && expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, !=,
+ expectedUnordered || !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, <,
+ !expectedUnordered && expectedLess);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, >,
+ !expectedUnordered && !expectedLess && !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, <=,
+ !expectedUnordered && (expectedEqual || expectedLess));
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, >=,
+ !expectedUnordered && !expectedLess);
+#ifdef __cpp_lib_three_way_comparison
+ if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
+ if constexpr (std::is_convertible_v<OrderingType, std::strong_ordering>)
+ static_assert(std::is_same_v<decltype(lhs <=> rhs), std::strong_ordering>);
+ else if constexpr (std::is_convertible_v<OrderingType, std::weak_ordering>)
+ static_assert(std::is_same_v<decltype(lhs <=> rhs), std::weak_ordering>);
+ else
+ static_assert(std::is_same_v<decltype(lhs <=> rhs), std::partial_ordering>);
+
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, ==,
+ !expectedUnordered && expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, !=,
+ expectedUnordered || !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, <,
+ !expectedUnordered && expectedLess);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, >,
+ !expectedUnordered && !expectedLess && !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, <=,
+ !expectedUnordered && (expectedEqual || expectedLess));
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, lhs, rhs, >=,
+ !expectedUnordered && !expectedLess);
+ }
+#endif
+
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==,
+ !expectedUnordered && expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=,
+ expectedUnordered || !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, <,
+ !expectedUnordered && !expectedLess && !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, >,
+ !expectedUnordered && expectedLess);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, <=,
+ !expectedUnordered && !expectedLess);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, >=,
+ !expectedUnordered && (expectedEqual || expectedLess));
+#ifdef __cpp_lib_three_way_comparison
+ if constexpr (implementsThreeWayComparisonOp_v<LeftType, RightType>) {
+ if constexpr (std::is_convertible_v<OrderingType, std::strong_ordering>)
+ static_assert(std::is_same_v<decltype(rhs <=> lhs), std::strong_ordering>);
+ else if constexpr (std::is_convertible_v<OrderingType, std::weak_ordering>)
+ static_assert(std::is_same_v<decltype(rhs <=> lhs), std::weak_ordering>);
+ else
+ static_assert(std::is_same_v<decltype(rhs <=> lhs), std::partial_ordering>);
+
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, ==,
+ !expectedUnordered && expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, !=,
+ expectedUnordered || !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, <,
+ !expectedUnordered && !expectedLess && !expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, >,
+ !expectedUnordered && expectedLess);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, <=,
+ !expectedUnordered && !expectedLess);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_3WAY, rhs, lhs, >=,
+ !expectedUnordered && (expectedEqual || expectedLess));
+ }
+#endif
+}
+
+#ifdef __cpp_lib_three_way_comparison
+#undef CHECK_RUNTIME_3WAY
+#endif
+#undef CHECK_RUNTIME_LR
+#undef CHECK_RUNTIME_CREF
+
+} // namespace QTestPrivate
+
+/*!
+ \internal
+
+ A helper macro that calls QTestPrivate::testEqualityOperators(), checks the
+ test's state after the function is executed, and generates a meaningful
+ debug message with the original file and line numbers if the test has
+ failed.
+*/
+#define QT_TEST_EQUALITY_OPS(Left, Right, Expected) \
+ do { \
+ auto report = qScopeGuard([] { \
+ qDebug("testEqualityOperators(" #Left ", " #Right ", " #Expected ") " \
+ "failed in " __FILE__ " on line %d", __LINE__); \
+ }); \
+ QTestPrivate::testEqualityOperators(Left, Right, Expected); \
+ if (QTest::currentTestFailed()) \
+ return; \
+ report.dismiss(); \
+ } while (false)
+
+/*!
+ \internal
+
+ A helper macro that calls QTestPrivate::testAllComparisonOperators(), checks
+ the test's state after the function is executed, and generates a meaningful
+ debug message with the original file and line numbers if the test has
+ failed.
+*/
+#define QT_TEST_ALL_COMPARISON_OPS(Left, Right, Expected) \
+ do { \
+ auto report = qScopeGuard([] { \
+ qDebug("testAllComparisonOperators(" #Left ", " #Right ", " #Expected ") " \
+ "failed in " __FILE__ " on line %d", __LINE__); \
+ }); \
+ QTestPrivate::testAllComparisonOperators(Left, Right, Expected); \
+ if (QTest::currentTestFailed()) \
+ return; \
+ report.dismiss(); \
+ } while (false)
+
+QT_END_NAMESPACE
+
+#endif // QCOMPARISONTESTHELPER_P_H
diff --git a/src/testlib/qcsvbenchmarklogger.cpp b/src/testlib/qcsvbenchmarklogger.cpp
index f410ec6e3d..d4de8f08b7 100644
--- a/src/testlib/qcsvbenchmarklogger.cpp
+++ b/src/testlib/qcsvbenchmarklogger.cpp
@@ -1,46 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcsvbenchmarklogger_p.h"
#include "qtestresult_p.h"
#include "qbenchmark_p.h"
+/*! \internal
+ \class QCsvBenchmarkLogger
+ \inmodule QtTest
+
+ QCsvBenchmarkLogger implements a comma-separated value format for benchmarks.
+
+ This is intended to be suitable for import into spreadsheets.
+ It does not print test failures, debug messages, warnings or any other details.
+*/
+
QCsvBenchmarkLogger::QCsvBenchmarkLogger(const char *filename)
: QAbstractTestLogger(filename)
{
@@ -83,13 +57,14 @@ void QCsvBenchmarkLogger::addBenchmarkResult(const QBenchmarkResult &result)
: "";
const char *filler = (tag[0] && gtag[0]) ? ":" : "";
- const char *metric = QTest::benchmarkMetricName(result.metric);
+ const char *metric = QTest::benchmarkMetricName(result.measurement.metric);
char buf[1024];
// "function","[globaltag:]tag","metric",value_per_iteration,total,iterations
qsnprintf(buf, sizeof(buf), "\"%s\",\"%s%s%s\",\"%s\",%.13g,%.13g,%u\n",
fn, gtag, filler, tag, metric,
- result.value / result.iterations, result.value, result.iterations);
+ result.measurement.value / result.iterations,
+ result.measurement.value, result.iterations);
outputString(buf);
}
diff --git a/src/testlib/qcsvbenchmarklogger_p.h b/src/testlib/qcsvbenchmarklogger_p.h
index 83e465c859..afbc264bf8 100644
--- a/src/testlib/qcsvbenchmarklogger_p.h
+++ b/src/testlib/qcsvbenchmarklogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCSVBENCHMARKLOGGER_P_H
#define QCSVBENCHMARKLOGGER_P_H
diff --git a/src/testlib/qemulationdetector_p.h b/src/testlib/qemulationdetector_p.h
index 3ae3d07839..a4b62818af 100644
--- a/src/testlib/qemulationdetector_p.h
+++ b/src/testlib/qemulationdetector_p.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QEMULATIONDETECTOR_P_H
#define QEMULATIONDETECTOR_P_H
@@ -40,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#if defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM)
#define SHOULD_CHECK_ARM_ON_X86
diff --git a/src/testlib/qjunittestlogger.cpp b/src/testlib/qjunittestlogger.cpp
index 147777f77c..4ee5788bee 100644
--- a/src/testlib/qjunittestlogger.cpp
+++ b/src/testlib/qjunittestlogger.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qjunittestlogger_p.h>
#include <QtTest/private/qtestelement_p.h>
@@ -45,18 +9,21 @@
#include <QtTest/private/qbenchmark_p.h>
#include <QtTest/private/qtestlog_p.h>
-#ifdef min // windows.h without NOMINMAX is included by the benchmark headers.
-# undef min
-#endif
-#ifdef max
-# undef max
-#endif
-
#include <QtCore/qlibraryinfo.h>
#include <string.h>
QT_BEGIN_NAMESPACE
+/*! \internal
+ \class QJUnitTestLogger
+ \inmodule QtTest
+
+ QJUnitTestLogger implements logging in a JUnit-compatible XML format.
+
+ The \l{JUnit XML} format was originally developed for Java testing.
+ It is supported by \l{Test Center}.
+*/
+// QTBUG-95424 links to further useful documentation.
QJUnitTestLogger::QJUnitTestLogger(const char *filename)
: QAbstractTestLogger(filename)
@@ -69,24 +36,35 @@ QJUnitTestLogger::~QJUnitTestLogger()
delete logFormatter;
}
+// We track test timing per test case, so we
+// need to maintain our own elapsed timer.
+Q_CONSTINIT static QElapsedTimer elapsedTestcaseTime;
+static qreal elapsedTestCaseSeconds()
+{
+ return elapsedTestcaseTime.nsecsElapsed() / 1e9;
+}
+
+static QByteArray toSecondsFormat(qreal ms)
+{
+ return QByteArray::number(ms / 1000, 'f', 3);
+}
+
void QJUnitTestLogger::startLogging()
{
QAbstractTestLogger::startLogging();
logFormatter = new QTestJUnitStreamer(this);
- delete systemOutputElement;
- systemOutputElement = new QTestElement(QTest::LET_SystemOutput);
- delete systemErrorElement;
- systemErrorElement = new QTestElement(QTest::LET_SystemError);
Q_ASSERT(!currentTestSuite);
currentTestSuite = new QTestElement(QTest::LET_TestSuite);
currentTestSuite->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName());
auto localTime = QDateTime::currentDateTime();
- auto localTimeWithUtcOffset = localTime.toOffsetFromUtc(localTime.offsetFromUtc());
currentTestSuite->addAttribute(QTest::AI_Timestamp,
- localTimeWithUtcOffset.toString(Qt::ISODate).toUtf8().constData());
+ localTime.toString(Qt::ISODate).toUtf8().constData());
+
+ currentTestSuite->addAttribute(QTest::AI_Hostname,
+ QSysInfo::machineHostName().toUtf8().constData());
QTestElement *property;
QTestElement *properties = new QTestElement(QTest::LET_Properties);
@@ -94,19 +72,21 @@ void QJUnitTestLogger::startLogging()
property = new QTestElement(QTest::LET_Property);
property->addAttribute(QTest::AI_Name, "QTestVersion");
property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR);
- properties->addLogElement(property);
+ properties->addChild(property);
property = new QTestElement(QTest::LET_Property);
property->addAttribute(QTest::AI_Name, "QtVersion");
property->addAttribute(QTest::AI_PropertyValue, qVersion());
- properties->addLogElement(property);
+ properties->addChild(property);
property = new QTestElement(QTest::LET_Property);
property->addAttribute(QTest::AI_Name, "QtBuild");
property->addAttribute(QTest::AI_PropertyValue, QLibraryInfo::build());
- properties->addLogElement(property);
+ properties->addChild(property);
- currentTestSuite->addLogElement(properties);
+ currentTestSuite->addChild(properties);
+
+ elapsedTestcaseTime.start();
}
void QJUnitTestLogger::stopLogging()
@@ -122,21 +102,15 @@ void QJUnitTestLogger::stopLogging()
qsnprintf(buf, sizeof(buf), "%i", errorCounter);
currentTestSuite->addAttribute(QTest::AI_Errors, buf);
- currentTestSuite->addAttribute(QTest::AI_Time,
- QByteArray::number(QTestLog::msecsTotalTime() / 1000, 'f').constData());
-
- currentTestSuite->addLogElement(listOfTestcases);
+ qsnprintf(buf, sizeof(buf), "%i", QTestLog::skipCount());
+ currentTestSuite->addAttribute(QTest::AI_Skipped, buf);
- // For correct indenting, make sure every testcase knows its parent
- QTestElement *testcase = listOfTestcases;
- while (testcase) {
- testcase->setParent(currentTestSuite);
- testcase = testcase->nextElement();
- }
+ currentTestSuite->addAttribute(QTest::AI_Time,
+ toSecondsFormat(QTestLog::msecsTotalTime()).constData());
- if (systemOutputElement->childElements())
- currentTestSuite->addLogElement(systemOutputElement);
- currentTestSuite->addLogElement(systemErrorElement);
+ for (auto *testCase : listOfTestcases)
+ currentTestSuite->addChild(testCase);
+ listOfTestcases.clear();
logFormatter->output(currentTestSuite);
@@ -148,226 +122,164 @@ void QJUnitTestLogger::stopLogging()
void QJUnitTestLogger::enterTestFunction(const char *function)
{
- currentLogElement = new QTestElement(QTest::LET_TestCase);
- currentLogElement->addAttribute(QTest::AI_Name, function);
- currentLogElement->addToList(&listOfTestcases);
+ enterTestCase(function);
+}
+
+void QJUnitTestLogger::enterTestCase(const char *name)
+{
+ currentTestCase = new QTestElement(QTest::LET_TestCase);
+ currentTestCase->addAttribute(QTest::AI_Name, name);
+ currentTestCase->addAttribute(QTest::AI_Classname, QTestResult::currentTestObjectName());
+ listOfTestcases.push_back(currentTestCase);
+
+ Q_ASSERT(!systemOutputElement && !systemErrorElement);
+ systemOutputElement = new QTestElement(QTest::LET_SystemOutput);
+ systemErrorElement = new QTestElement(QTest::LET_SystemError);
// The element will be deleted when the suite is deleted
++testCounter;
+
+ elapsedTestcaseTime.restart();
}
-void QJUnitTestLogger::leaveTestFunction()
+void QJUnitTestLogger::enterTestData(QTestData *)
{
- currentLogElement->addAttribute(QTest::AI_Time,
- QByteArray::number(QTestLog::msecsFunctionTime() / 1000, 'f').constData());
+ QTestCharBuffer testIdentifier;
+ QTestPrivate::generateTestIdentifier(&testIdentifier,
+ QTestPrivate::TestFunction | QTestPrivate::TestDataTag);
+
+ static const char *lastTestFunction = nullptr;
+ if (QTestResult::currentTestFunction() != lastTestFunction) {
+ // Adopt existing testcase for the initial test data
+ auto *name = const_cast<QTestElementAttribute*>(
+ currentTestCase->attribute(QTest::AI_Name));
+ name->setPair(QTest::AI_Name, testIdentifier.data());
+ lastTestFunction = QTestResult::currentTestFunction();
+ elapsedTestcaseTime.restart();
+ } else {
+ // Create new test cases for remaining test data
+ leaveTestCase();
+ enterTestCase(testIdentifier.data());
+ }
}
-void QJUnitTestLogger::addIncident(IncidentTypes type, const char *description,
- const char *file, int line)
+void QJUnitTestLogger::leaveTestFunction()
{
- const char *typeBuf = nullptr;
- char buf[100];
-
- switch (type) {
- case QAbstractTestLogger::XPass:
- ++failureCounter;
- typeBuf = "xpass";
- break;
- case QAbstractTestLogger::Pass:
- typeBuf = "pass";
- break;
- case QAbstractTestLogger::XFail:
- typeBuf = "xfail";
- break;
- case QAbstractTestLogger::Fail:
- ++failureCounter;
- typeBuf = "fail";
- break;
- case QAbstractTestLogger::BlacklistedPass:
- typeBuf = "bpass";
- break;
- case QAbstractTestLogger::BlacklistedFail:
- ++failureCounter;
- typeBuf = "bfail";
- break;
- case QAbstractTestLogger::BlacklistedXPass:
- typeBuf = "bxpass";
- break;
- case QAbstractTestLogger::BlacklistedXFail:
- ++failureCounter;
- typeBuf = "bxfail";
- break;
- default:
- typeBuf = "??????";
- break;
- }
+ leaveTestCase();
+}
- if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XPass) {
- QTestElement *failureElement = new QTestElement(QTest::LET_Failure);
- failureElement->addAttribute(QTest::AI_Result, typeBuf);
- if (file)
- failureElement->addAttribute(QTest::AI_File, file);
- else
- failureElement->addAttribute(QTest::AI_File, "");
- qsnprintf(buf, sizeof(buf), "%i", line);
- failureElement->addAttribute(QTest::AI_Line, buf);
- failureElement->addAttribute(QTest::AI_Description, description);
- addTag(failureElement);
- currentLogElement->addLogElement(failureElement);
- }
+void QJUnitTestLogger::leaveTestCase()
+{
+ currentTestCase->addAttribute(QTest::AI_Time,
+ toSecondsFormat(elapsedTestCaseSeconds() * 1000).constData());
- /*
- Only one result can be shown for the whole testfunction.
- Check if we currently have a result, and if so, overwrite it
- iff the new result is worse.
- */
- QTestElementAttribute* resultAttr =
- const_cast<QTestElementAttribute*>(currentLogElement->attribute(QTest::AI_Result));
- if (resultAttr) {
- const char* oldResult = resultAttr->value();
- bool overwrite = false;
- if (!strcmp(oldResult, "pass")) {
- overwrite = true;
- }
- else if (!strcmp(oldResult, "bpass") || !strcmp(oldResult, "bxfail")) {
- overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail) || (type == QAbstractTestLogger::XFail)
- || (type == QAbstractTestLogger::BlacklistedFail) || (type == QAbstractTestLogger::BlacklistedXPass);
- }
- else if (!strcmp(oldResult, "bfail") || !strcmp(oldResult, "bxpass")) {
- overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail) || (type == QAbstractTestLogger::XFail);
- }
- else if (!strcmp(oldResult, "xfail")) {
- overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail);
- }
- else if (!strcmp(oldResult, "xpass")) {
- overwrite = (type == QAbstractTestLogger::Fail);
- }
- if (overwrite) {
- resultAttr->setPair(QTest::AI_Result, typeBuf);
- }
- }
- else {
- currentLogElement->addAttribute(QTest::AI_Result, typeBuf);
- }
+ if (!systemOutputElement->childElements().empty())
+ currentTestCase->addChild(systemOutputElement);
+ else
+ delete systemOutputElement;
- if (file)
- currentLogElement->addAttribute(QTest::AI_File, file);
+ if (!systemErrorElement->childElements().empty())
+ currentTestCase->addChild(systemErrorElement);
else
- currentLogElement->addAttribute(QTest::AI_File, "");
+ delete systemErrorElement;
- qsnprintf(buf, sizeof(buf), "%i", line);
- currentLogElement->addAttribute(QTest::AI_Line, buf);
+ systemOutputElement = nullptr;
+ systemErrorElement = nullptr;
+}
- /*
- Since XFAIL does not add a failure to the testlog in junitxml, add a message, so we still
- have some information about the expected failure.
- */
- if (type == QAbstractTestLogger::XFail) {
- QJUnitTestLogger::addMessage(QAbstractTestLogger::Info, QString::fromUtf8(description), file, line);
+void QJUnitTestLogger::addIncident(IncidentTypes type, const char *description,
+ const char *file, int line)
+{
+ if (type == Fail || type == XPass) {
+ auto failureType = [&]() {
+ switch (type) {
+ case QAbstractTestLogger::Fail: return "fail";
+ case QAbstractTestLogger::XPass: return "xpass";
+ default: Q_UNREACHABLE();
+ }
+ }();
+
+ addFailure(QTest::LET_Failure, failureType, QString::fromUtf8(description));
+ } else if (type == XFail) {
+ // Since XFAIL does not add a failure to the testlog in JUnit XML we add a
+ // message, so we still have some information about the expected failure.
+ addMessage(Info, QString::fromUtf8(description), file, line);
+ } else if (type == Skip) {
+ auto skippedElement = new QTestElement(QTest::LET_Skipped);
+ skippedElement->addAttribute(QTest::AI_Message, description);
+ currentTestCase->addChild(skippedElement);
}
}
-void QJUnitTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
+void QJUnitTestLogger::addFailure(QTest::LogElementType elementType,
+ const char *failureType, const QString &failureDescription)
{
- QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark);
+ if (elementType == QTest::LET_Failure) {
+ // Make sure we're not adding failure when we already have error,
+ // or adding additional failures when we already have a failure.
+ for (auto *childElement : currentTestCase->childElements()) {
+ if (childElement->elementType() == QTest::LET_Error ||
+ childElement->elementType() == QTest::LET_Failure)
+ return;
+ }
+ }
- benchmarkElement->addAttribute(
- QTest::AI_Metric,
- QTest::benchmarkMetricName(result.metric));
- benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toUtf8().data());
+ QTestElement *failureElement = new QTestElement(elementType);
+ failureElement->addAttribute(QTest::AI_Type, failureType);
- const qreal valuePerIteration = qreal(result.value) / qreal(result.iterations);
- benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(valuePerIteration).constData());
+ // Assume the first line is the message, and the remainder are details
+ QString message = failureDescription.section(u'\n', 0, 0);
+ QString details = failureDescription.section(u'\n', 1);
- char buf[100];
- qsnprintf(buf, sizeof(buf), "%i", result.iterations);
- benchmarkElement->addAttribute(QTest::AI_Iterations, buf);
- currentLogElement->addLogElement(benchmarkElement);
-}
+ failureElement->addAttribute(QTest::AI_Message, message.toUtf8().constData());
-void QJUnitTestLogger::addTag(QTestElement* element)
-{
- const char *tag = QTestResult::currentDataTag();
- const char *gtag = QTestResult::currentGlobalDataTag();
- const char *filler = (tag && gtag) ? ":" : "";
- if ((!tag || !tag[0]) && (!gtag || !gtag[0])) {
- return;
+ if (!details.isEmpty()) {
+ auto textNode = new QTestElement(QTest::LET_Text);
+ textNode->addAttribute(QTest::AI_Value, details.toUtf8().constData());
+ failureElement->addChild(textNode);
}
- if (!tag) {
- tag = "";
- }
- if (!gtag) {
- gtag = "";
- }
+ currentTestCase->addChild(failureElement);
- QTestCharBuffer buf;
- QTest::qt_asprintf(&buf, "%s%s%s", gtag, filler, tag);
- element->addAttribute(QTest::AI_Tag, buf.constData());
+ switch (elementType) {
+ case QTest::LET_Failure: ++failureCounter; break;
+ case QTest::LET_Error: ++errorCounter; break;
+ default: Q_UNREACHABLE();
+ }
}
void QJUnitTestLogger::addMessage(MessageTypes type, const QString &message, const char *file, int line)
{
- auto messageElement = new QTestElement(QTest::LET_Message);
- auto systemLogElement = systemOutputElement;
- const char *typeBuf = nullptr;
-
- switch (type) {
- case QAbstractTestLogger::Warn:
- systemLogElement = systemErrorElement;
- typeBuf = "warn";
- break;
- case QAbstractTestLogger::QSystem:
- typeBuf = "system";
- break;
- case QAbstractTestLogger::QDebug:
- typeBuf = "qdebug";
- break;
- case QAbstractTestLogger::QInfo:
- typeBuf = "qinfo";
- break;
- case QAbstractTestLogger::QWarning:
- systemLogElement = systemErrorElement;
- typeBuf = "qwarn";
- break;
- case QAbstractTestLogger::QFatal:
- systemLogElement = systemErrorElement;
- typeBuf = "qfatal";
- break;
- case QAbstractTestLogger::Skip:
- typeBuf = "skip";
- break;
- case QAbstractTestLogger::Info:
- typeBuf = "info";
- break;
- default:
- typeBuf = "??????";
- break;
- }
-
- messageElement->addAttribute(QTest::AI_Type, typeBuf);
- messageElement->addAttribute(QTest::AI_Description, message.toUtf8().constData());
- addTag(messageElement);
+ Q_UNUSED(file);
+ Q_UNUSED(line);
- if (file)
- messageElement->addAttribute(QTest::AI_File, file);
- else
- messageElement->addAttribute(QTest::AI_File, "");
+ if (type == QFatal) {
+ addFailure(QTest::LET_Error, "qfatal", message);
+ return;
+ }
- char buf[100];
- qsnprintf(buf, sizeof(buf), "%i", line);
- messageElement->addAttribute(QTest::AI_Line, buf);
+ auto systemLogElement = [&]() {
+ switch (type) {
+ case QAbstractTestLogger::QDebug:
+ case QAbstractTestLogger::Info:
+ case QAbstractTestLogger::QInfo:
+ return systemOutputElement;
+ case QAbstractTestLogger::Warn:
+ case QAbstractTestLogger::QWarning:
+ case QAbstractTestLogger::QCritical:
+ return systemErrorElement;
+ default:
+ Q_UNREACHABLE();
+ }
+ }();
- currentLogElement->addLogElement(messageElement);
- ++errorCounter;
+ if (!systemLogElement)
+ return; // FIXME: Handle messages outside of test functions
- // Also add the message to the system log (stdout/stderr), if one exists
- if (systemLogElement) {
- auto messageElement = new QTestElement(QTest::LET_Message);
- messageElement->addAttribute(QTest::AI_Description, message.toUtf8().constData());
- systemLogElement->addLogElement(messageElement);
- }
+ auto textNode = new QTestElement(QTest::LET_Text);
+ textNode->addAttribute(QTest::AI_Value, message.toUtf8().constData());
+ systemLogElement->addChild(textNode);
}
QT_END_NAMESPACE
diff --git a/src/testlib/qjunittestlogger_p.h b/src/testlib/qjunittestlogger_p.h
index 0be9e8aeb0..6a7ff31615 100644
--- a/src/testlib/qjunittestlogger_p.h
+++ b/src/testlib/qjunittestlogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QJUNITTESTLOGGER_P_H
#define QJUNITTESTLOGGER_P_H
@@ -51,7 +15,12 @@
// We mean it.
//
+#include <QtTest/qttestglobal.h>
+
#include <QtTest/private/qabstracttestlogger_p.h>
+#include <QtTest/private/qtestelementattribute_p.h>
+
+#include <vector>
QT_BEGIN_NAMESPACE
@@ -70,18 +39,25 @@ class QJUnitTestLogger : public QAbstractTestLogger
void enterTestFunction(const char *function) override;
void leaveTestFunction() override;
+ void enterTestData(QTestData *) override;
+
void addIncident(IncidentTypes type, const char *description,
const char *file = nullptr, int line = 0) override;
- void addBenchmarkResult(const QBenchmarkResult &result) override;
- void addTag(QTestElement* element);
-
void addMessage(MessageTypes type, const QString &message,
const char *file = nullptr, int line = 0) override;
+ void addBenchmarkResult(const QBenchmarkResult &) override {}
+
private:
+ void enterTestCase(const char *name);
+ void leaveTestCase();
+
+ void addFailure(QTest::LogElementType elementType,
+ const char *failureType, const QString &failureDescription);
+
QTestElement *currentTestSuite = nullptr;
- QTestElement *listOfTestcases = nullptr;
- QTestElement *currentLogElement = nullptr;
+ std::vector<QTestElement*> listOfTestcases;
+ QTestElement *currentTestCase = nullptr;
QTestElement *systemOutputElement = nullptr;
QTestElement *systemErrorElement = nullptr;
QTestJUnitStreamer *logFormatter = nullptr;
diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp
index 5d9283d8e5..58c7f6bf5b 100644
--- a/src/testlib/qplaintestlogger.cpp
+++ b/src/testlib/qplaintestlogger.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qtestresult_p.h>
#include <QtTest/qtestassert.h>
@@ -46,18 +10,12 @@
#include <QtCore/private/qlogging_p.h>
-#include <stdarg.h>
+#include <array>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifdef min // windows.h without NOMINMAX is included by the benchmark headers.
-# undef min
-#endif
-#ifdef max
-# undef max
-#endif
-
#include <QtCore/QByteArray>
#include <QtCore/qmath.h>
#include <QtCore/QLibraryInfo>
@@ -72,11 +30,84 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+namespace {
+static const char multiplePrefixes[] = "\0kMGTPE"; // kilo, mega, giga, tera, peta, exa
+static const char submultiplePrefixes[] = "afpnum"; // atto, femto, pico, nano, micro, milli
+
+template <int N> struct FixedBufString
+{
+ static constexpr size_t MaxSize = N;
+ size_t used = 0;
+ std::array<char, N + 2> buf; // for the newline and terminating null
+ FixedBufString()
+ {
+ clear();
+ }
+ void clear()
+ {
+ used = 0;
+ buf[0] = '\0';
+ }
+
+ operator const char *() const
+ {
+ return buf.data();
+ }
+
+ void append(const char *text)
+ {
+ size_t len = qMin(strlen(text), MaxSize - used);
+ memcpy(buf.data() + used, text, len);
+ used += len;
+ buf[used] = '\0';
+ }
+
+ template <typename... Args> void appendf(const char *format, Args &&... args)
+ {
+ // vsnprintf includes the terminating null
+ used += qsnprintf(buf.data() + used, MaxSize - used + 1, format,
+ std::forward<Args>(args)...);
+ }
+
+ template <int Power = 1000> void appendScaled(qreal value, const char *unit)
+ {
+ char prefix[2] = {};
+ qreal v = qAbs(value);
+ qint64 ratio;
+ if (v < 1 && Power == 1000) {
+ const char *prefixes = submultiplePrefixes;
+ ratio = qreal(std::atto::num) / std::atto::den;
+ while (value * ratio > 1000 && *prefixes) {
+ ++prefixes;
+ ratio *= 1000;
+ }
+ prefix[0] = *prefixes;
+ } else {
+ const char *prefixes = multiplePrefixes;
+ ratio = 1;
+ while (value > 1000 * ratio) { // yes, even for binary
+ ++prefixes;
+ ratio *= Power;
+ }
+ prefix[0] = *prefixes;
+ }
+
+ // adjust the value by the ratio
+ value /= ratio;
+ appendf(", %.3g %s%s", value, prefix, unit);
+ }
+};
+} // unnamed namespace
+
namespace QTest {
- static const char *incidentType2String(QAbstractTestLogger::IncidentTypes type)
+ static const char *ptIncidentType2String(QAbstractTestLogger::IncidentTypes type)
{
switch (type) {
+ case QAbstractTestLogger::Skip:
+ return "SKIP ";
case QAbstractTestLogger::Pass:
return "PASS ";
case QAbstractTestLogger::XFail:
@@ -94,7 +125,7 @@ namespace QTest {
case QAbstractTestLogger::BlacklistedXFail:
return "BXFAIL ";
}
- return "??????";
+ Q_UNREACHABLE_RETURN(nullptr);
}
static const char *benchmarkResult2String()
@@ -102,27 +133,25 @@ namespace QTest {
return "RESULT ";
}
- static const char *messageType2String(QAbstractTestLogger::MessageTypes type)
+ static const char *ptMessageType2String(QAbstractTestLogger::MessageTypes type)
{
switch (type) {
- case QAbstractTestLogger::Skip:
- return "SKIP ";
- case QAbstractTestLogger::Warn:
- return "WARNING";
- case QAbstractTestLogger::QWarning:
- return "QWARN ";
case QAbstractTestLogger::QDebug:
return "QDEBUG ";
case QAbstractTestLogger::QInfo:
return "QINFO ";
- case QAbstractTestLogger::QSystem:
- return "QSYSTEM";
+ case QAbstractTestLogger::QWarning:
+ return "QWARN ";
+ case QAbstractTestLogger::QCritical:
+ return "QCRITICAL";
case QAbstractTestLogger::QFatal:
return "QFATAL ";
case QAbstractTestLogger::Info:
return "INFO ";
+ case QAbstractTestLogger::Warn:
+ return "WARNING";
}
- return "??????";
+ Q_UNREACHABLE_RETURN(nullptr);
}
template <typename T>
@@ -143,54 +172,53 @@ namespace QTest {
}
// Pretty-prints a benchmark result using the given number of digits.
- template <typename T> QString formatResult(T number, int significantDigits)
+ template <typename T> QByteArray formatResult(T number, int significantDigits)
{
if (number < T(0))
- return QLatin1String("NAN");
+ return "NAN";
if (number == T(0))
- return QLatin1String("0");
+ return "0";
- QString beforeDecimalPoint = QString::number(qint64(number), 'f', 0);
- QString afterDecimalPoint = QString::number(number, 'f', 20);
- afterDecimalPoint.remove(0, beforeDecimalPoint.count() + 1);
+ QByteArray beforeDecimalPoint = QByteArray::number(qint64(number), 'f', 0);
+ QByteArray afterDecimalPoint = QByteArray::number(number, 'f', 20);
+ afterDecimalPoint.remove(0, beforeDecimalPoint.size() + 1);
- int beforeUse = qMin(beforeDecimalPoint.count(), significantDigits);
- int beforeRemove = beforeDecimalPoint.count() - beforeUse;
+ int beforeUse = qMin(beforeDecimalPoint.size(), significantDigits);
+ int beforeRemove = beforeDecimalPoint.size() - beforeUse;
// Replace insignificant digits before the decimal point with zeros.
beforeDecimalPoint.chop(beforeRemove);
for (int i = 0; i < beforeRemove; ++i) {
- beforeDecimalPoint.append(QLatin1Char('0'));
+ beforeDecimalPoint.append(u'0');
}
int afterUse = significantDigits - beforeUse;
// leading zeroes after the decimal point does not count towards the digit use.
- if (beforeDecimalPoint == QLatin1String("0") && afterDecimalPoint.isEmpty() == false) {
+ if (beforeDecimalPoint == "0" && !afterDecimalPoint.isEmpty()) {
++afterUse;
int i = 0;
- while (i < afterDecimalPoint.count() && afterDecimalPoint.at(i) == QLatin1Char('0')) {
+ while (i < afterDecimalPoint.size() && afterDecimalPoint.at(i) == '0')
++i;
- }
afterUse += i;
}
- int afterRemove = afterDecimalPoint.count() - afterUse;
+ int afterRemove = afterDecimalPoint.size() - afterUse;
afterDecimalPoint.chop(afterRemove);
- QChar separator = QLatin1Char(',');
- QChar decimalPoint = QLatin1Char('.');
+ char separator = ',';
+ char decimalPoint = '.';
// insert thousands separators
- int length = beforeDecimalPoint.length();
- for (int i = beforeDecimalPoint.length() -1; i >= 1; --i) {
+ int length = beforeDecimalPoint.size();
+ for (int i = beforeDecimalPoint.size() -1; i >= 1; --i) {
if ((length - i) % 3 == 0)
beforeDecimalPoint.insert(i, separator);
}
- QString print;
+ QByteArray print;
print = beforeDecimalPoint;
if (afterUse > 0)
print.append(decimalPoint);
@@ -200,17 +228,17 @@ namespace QTest {
return print;
}
-
- template <typename T>
- int formatResult(char * buffer, int bufferSize, T number, int significantDigits)
- {
- QString result = formatResult(number, significantDigits);
- int size = result.count();
- qstrncpy(buffer, std::move(result).toLatin1().constData(), bufferSize);
- return size;
- }
}
+/*! \internal
+ \class QPlainTestLogger
+ \inmodule QtTest
+
+ QPlainTestLogger implements basic logging of test results.
+
+ The format is Qt-specific and aims to be easy to read.
+*/
+
void QPlainTestLogger::outputMessage(const char *str)
{
#if defined(Q_OS_WIN)
@@ -225,28 +253,39 @@ void QPlainTestLogger::outputMessage(const char *str)
outputString(str);
}
-void QPlainTestLogger::printMessage(const char *type, const char *msg, const char *file, int line)
+void QPlainTestLogger::printMessage(MessageSource source, const char *type, const char *msg,
+ const char *file, int line)
{
QTEST_ASSERT(type);
QTEST_ASSERT(msg);
QTestCharBuffer messagePrefix;
- QTestCharBuffer failureLocation;
- if (file) {
+ QTestCharBuffer messageLocation;
#ifdef Q_OS_WIN
-#define FAILURE_LOCATION_STR "\n%s(%d) : failure location"
+ constexpr const char *INCIDENT_LOCATION_STR = "\n%s(%d) : failure location";
+ constexpr const char *OTHER_LOCATION_STR = "\n%s(%d) : message location";
#else
-#define FAILURE_LOCATION_STR "\n Loc: [%s(%d)]"
+ constexpr const char *INCIDENT_LOCATION_STR = "\n Loc: [%s(%d)]";
+ constexpr const char *OTHER_LOCATION_STR = INCIDENT_LOCATION_STR;
#endif
- QTest::qt_asprintf(&failureLocation, FAILURE_LOCATION_STR, file, line);
+
+ if (file) {
+ switch (source) {
+ case MessageSource::Incident:
+ QTest::qt_asprintf(&messageLocation, INCIDENT_LOCATION_STR, file, line);
+ break;
+ case MessageSource::Other:
+ QTest::qt_asprintf(&messageLocation, OTHER_LOCATION_STR, file, line);
+ break;
+ }
}
const char *msgFiller = msg[0] ? " " : "";
QTestCharBuffer testIdentifier;
QTestPrivate::generateTestIdentifier(&testIdentifier);
QTest::qt_asprintf(&messagePrefix, "%s: %s%s%s%s\n",
- type, testIdentifier.data(), msgFiller, msg, failureLocation.data());
+ type, testIdentifier.data(), msgFiller, msg, messageLocation.data());
// In colored mode, printf above stripped our nonprintable control characters.
// Put them back.
@@ -255,60 +294,110 @@ void QPlainTestLogger::printMessage(const char *type, const char *msg, const cha
outputMessage(messagePrefix.data());
}
-void QPlainTestLogger::printBenchmarkResult(const QBenchmarkResult &result)
+void QPlainTestLogger::printBenchmarkResultsHeader(const QBenchmarkResult &result)
{
- const char *bmtag = QTest::benchmarkResult2String();
-
- char buf1[1024];
- qsnprintf(
- buf1, sizeof(buf1), "%s: %s::%s",
- bmtag,
- QTestResult::currentTestObjectName(),
- result.context.slotName.toLatin1().data());
-
- char bufTag[1024];
- bufTag[0] = 0;
- QByteArray tag = result.context.tag.toLocal8Bit();
- if (tag.isEmpty() == false) {
- qsnprintf(bufTag, sizeof(bufTag), ":\"%s\"", tag.data());
- }
-
-
- char fillFormat[8];
- int fillLength = 5;
- qsnprintf(fillFormat, sizeof(fillFormat), ":\n%%%ds", fillLength);
- char fill[1024];
- qsnprintf(fill, sizeof(fill), fillFormat, "");
-
- const char * unitText = QTest::benchmarkMetricUnit(result.metric);
-
- qreal valuePerIteration = qreal(result.value) / qreal(result.iterations);
- char resultBuffer[100] = "";
- QTest::formatResult(resultBuffer, 100, valuePerIteration, QTest::countSignificantDigits(result.value));
-
- char buf2[1024];
- qsnprintf(buf2, sizeof(buf2), "%s %s", resultBuffer, unitText);
-
- char buf2_[1024];
- QByteArray iterationText = " per iteration";
- Q_ASSERT(result.iterations > 0);
- qsnprintf(buf2_, sizeof(buf2_), "%s", iterationText.data());
+ FixedBufString<1022> buf;
+ buf.appendf("%s: %s::%s", QTest::benchmarkResult2String(),
+ QTestResult::currentTestObjectName(), result.context.slotName.toLatin1().data());
+
+ if (QByteArray tag = result.context.tag.toLocal8Bit(); !tag.isEmpty())
+ buf.appendf(":\"%s\":\n", tag.data());
+ else
+ buf.append(":\n");
+ outputMessage(buf);
+}
- char buf3[1024];
- Q_ASSERT(result.iterations > 0);
- QTest::formatResult(resultBuffer, 100, result.value, QTest::countSignificantDigits(result.value));
- qsnprintf(buf3, sizeof(buf3), " (total: %s, iterations: %d)", resultBuffer, result.iterations);
+void QPlainTestLogger::printBenchmarkResults(const QList<QBenchmarkResult> &results)
+{
+ using namespace std::chrono;
+ FixedBufString<1022> buf;
+ auto findResultFor = [&results](QTest::QBenchmarkMetric metric) -> std::optional<qreal> {
+ for (const QBenchmarkResult &result : results) {
+ if (result.measurement.metric == metric)
+ return result.measurement.value;
+ }
+ return std::nullopt;
+ };
+
+ // we need the execution time quite often, so find it first
+ qreal executionTime = 0;
+ if (auto ns = findResultFor(QTest::WalltimeNanoseconds))
+ executionTime = *ns / (1000 * 1000 * 1000);
+ else if (auto ms = findResultFor(QTest::WalltimeMilliseconds))
+ executionTime = *ms / 1000;
+
+ for (const QBenchmarkResult &result : results) {
+ buf.clear();
+
+ const char * unitText = QTest::benchmarkMetricUnit(result.measurement.metric);
+ int significantDigits = QTest::countSignificantDigits(result.measurement.value);
+ qreal valuePerIteration = qreal(result.measurement.value) / qreal(result.iterations);
+ buf.appendf(" %s %s%s", QTest::formatResult(valuePerIteration, significantDigits).constData(),
+ unitText, result.setByMacro ? " per iteration" : "");
+
+ switch (result.measurement.metric) {
+ case QTest::BitsPerSecond:
+ // for bits/s, we'll use powers of 10 (1 Mbit/s = 1000 kbit/s = 1000000 bit/s)
+ buf.appendScaled<1000>(result.measurement.value, "bit/s");
+ break;
+ case QTest::BytesPerSecond:
+ // for B/s, we'll use powers of 2 (1 MB/s = 1024 kB/s = 1048576 B/s)
+ buf.appendScaled<1024>(result.measurement.value, "B/s");
+ break;
+
+ case QTest::CPUCycles:
+ case QTest::RefCPUCycles:
+ if (!qIsNull(executionTime))
+ buf.appendScaled(result.measurement.value / executionTime, "Hz");
+ break;
+
+ case QTest::Instructions:
+ if (auto cycles = findResultFor(QTest::CPUCycles)) {
+ buf.appendf(", %.3f instr/cycle", result.measurement.value / *cycles);
+ break;
+ }
+ Q_FALLTHROUGH();
+
+ case QTest::InstructionReads:
+ case QTest::Events:
+ case QTest::BytesAllocated:
+ case QTest::CPUMigrations:
+ case QTest::BusCycles:
+ case QTest::StalledCycles:
+ case QTest::BranchInstructions:
+ case QTest::BranchMisses:
+ case QTest::CacheReferences:
+ case QTest::CacheReads:
+ case QTest::CacheWrites:
+ case QTest::CachePrefetches:
+ case QTest::CacheMisses:
+ case QTest::CacheReadMisses:
+ case QTest::CacheWriteMisses:
+ case QTest::CachePrefetchMisses:
+ case QTest::ContextSwitches:
+ case QTest::PageFaults:
+ case QTest::MinorPageFaults:
+ case QTest::MajorPageFaults:
+ case QTest::AlignmentFaults:
+ case QTest::EmulationFaults:
+ if (!qIsNull(executionTime))
+ buf.appendScaled(result.measurement.value / executionTime, "/sec");
+ break;
+
+ case QTest::FramesPerSecond:
+ case QTest::CPUTicks:
+ case QTest::WalltimeMilliseconds:
+ case QTest::WalltimeNanoseconds:
+ break; // no additional information
+ }
- char buf[1024];
+ Q_ASSERT(result.iterations > 0);
+ buf.appendf(" (total: %s, iterations: %d)\n",
+ QTest::formatResult(result.measurement.value, significantDigits).constData(),
+ result.iterations);
- if (result.setByMacro) {
- qsnprintf(buf, sizeof(buf), "%s%s%s%s%s%s\n", buf1, bufTag, fill, buf2, buf2_, buf3);
- } else {
- qsnprintf(buf, sizeof(buf), "%s%s%s%s\n", buf1, bufTag, fill, buf2);
+ outputMessage(buf);
}
-
- memcpy(buf, bmtag, strlen(bmtag));
- outputMessage(buf);
}
QPlainTestLogger::QPlainTestLogger(const char *filename)
@@ -360,7 +449,7 @@ void QPlainTestLogger::stopLogging()
void QPlainTestLogger::enterTestFunction(const char * /*function*/)
{
if (QTestLog::verboseLevel() >= 1)
- printMessage(QTest::messageType2String(Info), "entering");
+ printMessage(MessageSource::Other, QTest::ptMessageType2String(Info), "entering");
}
void QPlainTestLogger::leaveTestFunction()
@@ -370,21 +459,22 @@ void QPlainTestLogger::leaveTestFunction()
void QPlainTestLogger::addIncident(IncidentTypes type, const char *description,
const char *file, int line)
{
- // suppress PASS and XFAIL in silent mode
- if ((type == QAbstractTestLogger::Pass || type == QAbstractTestLogger::XFail)
+ // suppress B?PASS and B?XFAIL in silent mode
+ if ((type == Pass || type == BlacklistedPass || type == XFail || type == BlacklistedXFail)
&& QTestLog::verboseLevel() < 0)
return;
- printMessage(QTest::incidentType2String(type), description, file, line);
+ printMessage(MessageSource::Incident, QTest::ptIncidentType2String(type), description, file, line);
}
-void QPlainTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
+void QPlainTestLogger::addBenchmarkResults(const QList<QBenchmarkResult> &results)
{
// suppress benchmark results in silent mode
- if (QTestLog::verboseLevel() < 0)
+ if (QTestLog::verboseLevel() < 0 || results.isEmpty())
return;
- printBenchmarkResult(result);
+ printBenchmarkResultsHeader(results.first());
+ printBenchmarkResults(results);
}
void QPlainTestLogger::addMessage(QtMsgType type, const QMessageLogContext &context, const QString &message)
@@ -396,10 +486,19 @@ void QPlainTestLogger::addMessage(MessageTypes type, const QString &message,
const char *file, int line)
{
// suppress non-fatal messages in silent mode
- if (type != QAbstractTestLogger::QFatal && QTestLog::verboseLevel() < 0)
+ if (type != QFatal && QTestLog::verboseLevel() < 0)
return;
- printMessage(QTest::messageType2String(type), qPrintable(message), file, line);
+ printMessage(MessageSource::Other, QTest::ptMessageType2String(type), qPrintable(message), file, line);
+}
+
+bool QPlainTestLogger::isRepeatSupported() const
+{
+ // The plain text logger creates unstructured reports. Such reports are not
+ // parser friendly, and are unlikely to be parsed by any test reporting
+ // tools. We can therefore allow repeated test runs with minimum risk that
+ // any parsers fails to handle repeated test names.
+ return true;
}
QT_END_NAMESPACE
diff --git a/src/testlib/qplaintestlogger_p.h b/src/testlib/qplaintestlogger_p.h
index 80ef4864c1..819a54fd50 100644
--- a/src/testlib/qplaintestlogger_p.h
+++ b/src/testlib/qplaintestlogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLAINTESTLOGGER_P_H
#define QPLAINTESTLOGGER_P_H
@@ -69,7 +33,9 @@ public:
void addIncident(IncidentTypes type, const char *description,
const char *file = nullptr, int line = 0) override;
- void addBenchmarkResult(const QBenchmarkResult &result) override;
+ void addBenchmarkResult(const QBenchmarkResult &) final override
+ { Q_UNREACHABLE(); }
+ void addBenchmarkResults(const QList<QBenchmarkResult> &results) override;
void addMessage(QtMsgType, const QMessageLogContext &,
const QString &) override;
@@ -77,10 +43,18 @@ public:
void addMessage(MessageTypes type, const QString &message,
const char *file = nullptr, int line = 0) override;
+ bool isRepeatSupported() const override;
+
private:
- void printMessage(const char *type, const char *msg, const char *file = nullptr, int line = 0);
+ enum class MessageSource {
+ Incident,
+ Other,
+ };
+ void printMessage(MessageSource source, const char *type, const char *msg,
+ const char *file = nullptr, int line = 0);
void outputMessage(const char *str);
- void printBenchmarkResult(const QBenchmarkResult &result);
+ void printBenchmarkResultsHeader(const QBenchmarkResult &result);
+ void printBenchmarkResults(const QList<QBenchmarkResult> &result);
};
QT_END_NAMESPACE
diff --git a/src/testlib/qpropertytesthelper_p.h b/src/testlib/qpropertytesthelper_p.h
index e81b145887..c691802a39 100644
--- a/src/testlib/qpropertytesthelper_p.h
+++ b/src/testlib/qpropertytesthelper_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPROPERTYTESTHELPER_P_H
#define QPROPERTYTESTHELPER_P_H
@@ -52,8 +16,10 @@
//
#include <QtCore/QObject>
+#include <QtCore/QProperty>
#include <QtTest/QSignalSpy>
#include <QTest>
+#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -118,6 +84,14 @@ namespace QTestPrivate {
allocate its returned string using \c {new char[]}, so that it can be used
in place of \l {QTest::toString()}.
+ The \a helperConstructor method is used to create another instance of
+ \c TestedClass. This instance is used to test for binding loops. By default,
+ the method returns a default-constructed \c TestedClass. A custom
+ \a helperConstructor should be provided if \c TestedClass is not
+ default-constructible. Some very specific properties cannot be tested for
+ binding loops. Pass a lambda that returns an \c {std::nullptr} as
+ \a helperConstructor in such case.
+
\note Any test calling this method will need to call
\code
if (QTest::currentTestFailed())
@@ -134,7 +108,9 @@ void testReadWritePropertyBasics(
std::function<bool(const PropertyType &, const PropertyType &)> comparator =
[](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
std::function<char *(const PropertyType &)> represent =
- [](const PropertyType &val) { return QTest::toString(val); })
+ [](const PropertyType &val) { return QTest::toString(val); },
+ std::function<std::unique_ptr<TestedClass>(void)> helperConstructor =
+ []() { return std::make_unique<TestedClass>(); })
{
// get the property
const QMetaObject *metaObject = instance.metaObject();
@@ -163,7 +139,7 @@ void testReadWritePropertyBasics(
testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
represent);
if (spy)
- QCOMPARE(spy->count(), 1);
+ QCOMPARE(spy->size(), 1);
QUntypedBindable bindable = metaProperty.bindable(&instance);
@@ -182,7 +158,7 @@ void testReadWritePropertyBasics(
QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), changed, comparator, represent);
if (spy)
- QCOMPARE(spy->count(), 2);
+ QCOMPARE(spy->size(), 2);
// Bind object's property to other property
QProperty<PropertyType> propSetter(initial);
@@ -196,7 +172,7 @@ void testReadWritePropertyBasics(
QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), initial, comparator, represent);
QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), initial, comparator, represent);
if (spy)
- QCOMPARE(spy->count(), 3);
+ QCOMPARE(spy->size(), 3);
// Count notifications triggered; should only happen on actual change.
int updateCount = 0;
@@ -211,7 +187,7 @@ void testReadWritePropertyBasics(
QPROPERTY_TEST_COMPARISON_HELPER(propObserverLambda.value(), changed, comparator, represent);
QCOMPARE(updateCount, 1);
if (spy)
- QCOMPARE(spy->count(), 4);
+ QCOMPARE(spy->size(), 4);
// Test that manually setting the value (even the same one) breaks the
// binding.
@@ -222,7 +198,46 @@ void testReadWritePropertyBasics(
// value didn't change -> the signal should not be emitted
if (spy)
- QCOMPARE(spy->count(), 4);
+ QCOMPARE(spy->size(), 4);
+
+ // test binding loop
+ if (std::unique_ptr<TestedClass> helperObj = helperConstructor()) {
+ // Reset to 'initial', so that the binding loop test could check the
+ // 'changed' value, because some tests already rely on the 'instance' to
+ // have the 'changed' value once this test passes
+ testedObj.setProperty(propertyName, QVariant::fromValue(initial));
+ const QPropertyBinding<PropertyType> binding([&]() {
+ QObject *obj = static_cast<QObject *>(helperObj.get());
+ obj->setProperty(propertyName, QVariant::fromValue(changed));
+ return obj->property(propertyName).template value<PropertyType>();
+ }, {});
+ bindable.setBinding(binding);
+ QPROPERTY_TEST_COMPARISON_HELPER(
+ testedObj.property(propertyName).template value<PropertyType>(), changed,
+ comparator, represent);
+ QVERIFY2(!binding.error().hasError(), qPrintable(binding.error().description()));
+ }
+}
+
+/*!
+ \internal
+ \overload
+
+ This overload supports the case where the caller only needs to override
+ the default for \a helperConstructor. It uses the defaults for all the other
+ parameters.
+*/
+template<typename TestedClass, typename PropertyType>
+void testReadWritePropertyBasics(
+ TestedClass &instance, const PropertyType &initial, const PropertyType &changed,
+ const char *propertyName,
+ std::function<std::unique_ptr<TestedClass>(void)> helperConstructor)
+{
+ testReadWritePropertyBasics<TestedClass, PropertyType>(
+ instance, initial, changed, propertyName,
+ [](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
+ [](const PropertyType &val) { return QTest::toString(val); },
+ helperConstructor);
}
/*!
@@ -258,6 +273,14 @@ void testReadWritePropertyBasics(
allocate its returned string using \c {new char[]}, so that it can be used
in place of \l {QTest::toString()}.
+ The \a helperConstructor method is used to create another instance of
+ \c TestedClass. This instance is used to test for binding loops. By default,
+ the method returns a default-constructed \c TestedClass. A custom
+ \a helperConstructor should be provided if \c TestedClass is not
+ default-constructible. Some very specific properties cannot be tested for
+ binding loops. Pass a lambda that returns an \c {std::nullptr} as
+ \a helperConstructor in such case.
+
\note Any test calling this method will need to call
\code
if (QTest::currentTestFailed())
@@ -276,7 +299,9 @@ void testWriteOncePropertyBasics(
std::function<bool(const PropertyType &, const PropertyType &)> comparator =
[](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
std::function<char *(const PropertyType &)> represent =
- [](const PropertyType &val) { return QTest::toString(val); })
+ [](const PropertyType &val) { return QTest::toString(val); },
+ std::function<std::unique_ptr<TestedClass>(void)> helperConstructor =
+ []() { return std::make_unique<TestedClass>(); })
{
// get the property
const QMetaObject *metaObject = instance.metaObject();
@@ -310,10 +335,19 @@ void testWriteOncePropertyBasics(
propObserver.setBinding(bindable.makeBinding());
QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), prior, comparator, represent);
- // Create a binding that sets the 'changed' value to the property
- QProperty<PropertyType> propSetter(changed);
+ // Create a binding that sets the 'changed' value to the property.
+ // This also tests binding loops.
QVERIFY(!bindable.hasBinding());
- bindable.setBinding(Qt::makePropertyBinding(propSetter));
+ std::unique_ptr<TestedClass> helperObj = helperConstructor();
+ QProperty<PropertyType> propSetter(changed); // if the helperConstructor() returns nullptr
+ const QPropertyBinding<PropertyType> binding = helperObj
+ ? Qt::makePropertyBinding([&]() {
+ QObject *obj = static_cast<QObject *>(helperObj.get());
+ obj->setProperty(propertyName, QVariant::fromValue(changed));
+ return obj->property(propertyName).template value<PropertyType>();
+ })
+ : Qt::makePropertyBinding(propSetter);
+ bindable.setBinding(binding);
QVERIFY(bindable.hasBinding());
QPROPERTY_TEST_COMPARISON_HELPER(
@@ -321,7 +355,7 @@ void testWriteOncePropertyBasics(
represent);
QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
if (spy)
- QCOMPARE(spy->count(), 1);
+ QCOMPARE(spy->size(), 1);
// Attempt to set back the 'prior' value and verify that it has no effect
testedObj.setProperty(propertyName, QVariant::fromValue(prior));
@@ -330,13 +364,34 @@ void testWriteOncePropertyBasics(
represent);
QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
if (spy)
- QCOMPARE(spy->count(), 1);
+ QCOMPARE(spy->size(), 1);
if (bindingPreservedOnWrite)
QVERIFY(bindable.hasBinding());
else
QVERIFY(!bindable.hasBinding());
}
+/*!
+ \internal
+ \overload
+
+ This overload supports the case where the caller only needs to override
+ the default for \a helperConstructor. It uses the defaults for all the other
+ parameters.
+*/
+template<typename TestedClass, typename PropertyType>
+void testWriteOncePropertyBasics(
+ TestedClass &instance, const PropertyType &prior, const PropertyType &changed,
+ const char *propertyName,
+ bool bindingPreservedOnWrite,
+ std::function<std::unique_ptr<TestedClass>(void)> helperConstructor)
+{
+ testWriteOncePropertyBasics<TestedClass, PropertyType>(
+ instance, prior, changed, propertyName, bindingPreservedOnWrite,
+ [](const PropertyType &lhs, const PropertyType &rhs) { return lhs == rhs; },
+ [](const PropertyType &val) { return QTest::toString(val); },
+ helperConstructor);
+}
/*!
\internal
@@ -420,7 +475,7 @@ void testReadOnlyPropertyBasics(
testedObj.property(propertyName).template value<PropertyType>(), initial, comparator,
represent);
if (spy)
- QCOMPARE(spy->count(), 0);
+ QCOMPARE(spy->size(), 0);
QProperty<PropertyType> propObserver;
propObserver.setBinding(bindable.makeBinding());
@@ -436,7 +491,7 @@ void testReadOnlyPropertyBasics(
QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), changed, comparator, represent);
if (spy)
- QCOMPARE(spy->count(), 1);
+ QCOMPARE(spy->size(), 1);
}
} // namespace QTestPrivate
diff --git a/src/testlib/qsignaldumper.cpp b/src/testlib/qsignaldumper.cpp
index a0080c2a37..9a6412ffc2 100644
--- a/src/testlib/qsignaldumper.cpp
+++ b/src/testlib/qsignaldumper.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qsignaldumper_p.h>
@@ -60,10 +24,21 @@ inline static void qPrintMessage(const QByteArray &ba)
}
Q_GLOBAL_STATIC(QList<QByteArray>, ignoreClasses)
-static int iLevel = 0;
-static int ignoreLevel = 0;
+Q_CONSTINIT static QBasicMutex ignoreClassesMutex;
+Q_CONSTINIT thread_local int iLevel = 0;
+Q_CONSTINIT thread_local int ignoreLevel = 0;
enum { IndentSpacesCount = 4 };
+static bool classIsIgnored(const char *className)
+{
+ if (Q_LIKELY(!ignoreClasses.exists()))
+ return false;
+ QMutexLocker locker(&ignoreClassesMutex);
+ if (ignoreClasses()->isEmpty())
+ return false;
+ return ignoreClasses()->contains(QByteArrayView(className));
+}
+
static void qSignalDumperCallback(QObject *caller, int signal_index, void **argv)
{
Q_ASSERT(caller);
@@ -74,7 +49,7 @@ static void qSignalDumperCallback(QObject *caller, int signal_index, void **argv
QMetaMethod member = QMetaObjectPrivate::signal(mo, signal_index);
Q_ASSERT(member.isValid());
- if (QTest::ignoreClasses() && QTest::ignoreClasses()->contains(mo->className())) {
+ if (classIsIgnored(mo->className())) {
++QTest::ignoreLevel;
return;
}
@@ -96,7 +71,7 @@ static void qSignalDumperCallback(QObject *caller, int signal_index, void **argv
str += " (";
QList<QByteArray> args = member.parameterTypes();
- for (int i = 0; i < args.count(); ++i) {
+ for (int i = 0; i < args.size(); ++i) {
const QByteArray &arg = args.at(i);
int typeId = QMetaType::fromName(args.at(i).constData()).id();
if (arg.endsWith('*') || arg.endsWith('&')) {
@@ -134,8 +109,7 @@ static void qSignalDumperCallbackSlot(QObject *caller, int method_index, void **
if (!member.isValid())
return;
- if (QTest::ignoreLevel ||
- (QTest::ignoreClasses() && QTest::ignoreClasses()->contains(mo->className())))
+ if (QTest::ignoreLevel || classIsIgnored(mo->className()))
return;
QByteArray str;
@@ -158,8 +132,7 @@ static void qSignalDumperCallbackSlot(QObject *caller, int method_index, void **
static void qSignalDumperCallbackEndSignal(QObject *caller, int /*signal_index*/)
{
Q_ASSERT(caller); Q_ASSERT(caller->metaObject());
- if (QTest::ignoreClasses()
- && QTest::ignoreClasses()->contains(caller->metaObject()->className())) {
+ if (classIsIgnored(caller->metaObject()->className())) {
--QTest::ignoreLevel;
Q_ASSERT(QTest::ignoreLevel >= 0);
return;
@@ -192,13 +165,15 @@ void QSignalDumper::endDump()
void QSignalDumper::ignoreClass(const QByteArray &klass)
{
+ QMutexLocker locker(&QTest::ignoreClassesMutex);
if (QTest::ignoreClasses())
QTest::ignoreClasses()->append(klass);
}
void QSignalDumper::clearIgnoredClasses()
{
- if (QTest::ignoreClasses())
+ QMutexLocker locker(&QTest::ignoreClassesMutex);
+ if (QTest::ignoreClasses.exists())
QTest::ignoreClasses()->clear();
}
diff --git a/src/testlib/qsignaldumper_p.h b/src/testlib/qsignaldumper_p.h
index e211f5c059..66959ab53f 100644
--- a/src/testlib/qsignaldumper_p.h
+++ b/src/testlib/qsignaldumper_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSIGNALDUMPER_P_H
#define QSIGNALDUMPER_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <qglobal.h>
+#include <private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/testlib/qsignalspy.cpp b/src/testlib/qsignalspy.cpp
new file mode 100644
index 0000000000..a45ca59378
--- /dev/null
+++ b/src/testlib/qsignalspy.cpp
@@ -0,0 +1,318 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsignalspy.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSignalSpy
+ \inmodule QtTest
+
+ \brief The QSignalSpy class enables introspection of signal emission.
+
+ QSignalSpy can connect to any signal of any object and records its emission.
+ QSignalSpy itself is a list of QVariant lists. Each emission of the signal
+ will append one item to the list, containing the arguments of the signal.
+
+ The following example records all signal emissions for the \c clicked() signal
+ of a QCheckBox:
+
+ \snippet code/doc_src_qsignalspy.cpp 0
+
+ \c{spy.takeFirst()} returns the arguments for the first emitted signal, as a
+ list of QVariant objects. The \c clicked() signal has a single bool argument,
+ which is stored as the first entry in the list of arguments.
+
+ The example below catches a signal from a custom object:
+
+ \snippet code/doc_src_qsignalspy.cpp 1
+
+ \note Non-standard data types need to be registered, using
+ the qRegisterMetaType() function, before you can create a
+ QSignalSpy. For example:
+
+ \snippet code/doc_src_qsignalspy.cpp 2
+
+ To retrieve the instance, you can use qvariant_cast:
+
+ \snippet code/doc_src_qsignalspy.cpp 3
+
+ \section1 Verifying Signal Emissions
+
+ The QSignalSpy class provides an elegant mechanism for capturing the list
+ of signals emitted by an object. However, you should verify its validity
+ after construction. The constructor does a number of sanity checks, such as
+ verifying that the signal to be spied upon actually exists. To make the
+ diagnosis of test failures easier, the results of these checks should be
+ checked by calling \c QVERIFY(spy.isValid()) before proceeding further with
+ a test.
+
+ \sa QVERIFY()
+ */
+
+/*! \fn QSignalSpy::QSignalSpy(const QObject *object, const char *signal)
+
+ Constructs a new QSignalSpy that listens for emissions of the \a signal
+ from the QObject \a object. If QSignalSpy is not able to listen for a
+ valid signal (for example, because \a object is \nullptr or \a signal does
+ not denote a valid signal of \a object), an explanatory warning message
+ will be output using qWarning() and subsequent calls to \c isValid() will
+ return false.
+
+ Example:
+ \snippet code/doc_src_qsignalspy.cpp 4
+*/
+
+/*! \fn template <typename PointerToMemberFunction> QSignalSpy::QSignalSpy(const QObject *object, PointerToMemberFunction signal)
+ \since 5.4
+
+ Constructs a new QSignalSpy that listens for emissions of the \a signal
+ from the QObject \a object. If QSignalSpy is not able to listen for a
+ valid signal (for example, because \a object is \nullptr or \a signal does
+ not denote a valid signal of \a object), an explanatory warning message
+ will be output using qWarning() and subsequent calls to \c isValid() will
+ return false.
+
+ Example:
+ \snippet code/doc_src_qsignalspy.cpp 6
+*/
+
+/*! \fn QSignalSpy::QSignalSpy(const QObject *obj, QMetaMethod signal)
+ \since 5.14
+
+ Constructs a new QSignalSpy that listens for emissions of the \a signal
+ from the QObject \a obj. If QSignalSpy is not able to listen for a
+ valid signal (for example, because \a obj is \nullptr or \a signal does
+ not denote a valid signal of \a obj), an explanatory warning message
+ will be output using qWarning() and subsequent calls to \c isValid() will
+ return false.
+
+ This constructor is convenient to use when Qt's meta-object system is
+ heavily used in a test.
+
+ Basic usage example:
+ \snippet code/doc_src_qsignalspy.cpp 7
+
+ Imagine we need to check whether all properties of the QWindow class
+ that represent minimum and maximum dimensions are properly writable.
+ The following example demonstrates one of the approaches:
+ \snippet code/doc_src_qsignalspy.cpp 8
+*/
+
+/*! \fn QSignalSpy::isValid() const
+
+ Returns \c true if the signal spy listens to a valid signal, otherwise false.
+*/
+
+/*! \fn QSignalSpy::signal() const
+
+ Returns the normalized signal the spy is currently listening to.
+*/
+
+/*! \fn bool QSignalSpy::wait(int timeout)
+ \since 5.0
+
+ This is an overloaded function, equivalent passing \a timeout to the
+ chrono overload:
+ \code
+ wait(std::chrono::milliseconds{timeout});
+ \endcode
+
+ Returns \c true if the signal was emitted at least once in \a timeout,
+ otherwise returns \c false.
+*/
+
+/*!
+ \since 6.6
+
+ Starts an event loop that runs until the given signal is received
+ or \a timeout has passed, whichever happens first.
+
+ \a timeout is any valid std::chrono::duration (std::chrono::seconds,
+ std::chrono::milliseconds ...etc).
+
+ Returns \c true if the signal was emitted at least once in \a timeout,
+ otherwise returns \c false.
+
+ Example:
+ \code
+ using namespace std::chrono_literals;
+ QSignalSpy spy(object, signal);
+ spy.wait(2s);
+ \endcode
+*/
+bool QSignalSpy::wait(std::chrono::milliseconds timeout)
+{
+ QMutexLocker locker(&m_mutex);
+ Q_ASSERT(!m_waiting);
+ const qsizetype origCount = size();
+ m_waiting = true;
+ locker.unlock();
+
+ m_loop.enterLoop(timeout);
+
+ locker.relock();
+ m_waiting = false;
+ return size() > origCount;
+}
+
+static bool isSignalMetaMethodValid(QMetaMethod signal)
+{
+ if (!signal.isValid()) {
+ qWarning("QSignalSpy: Null signal is not valid");
+ return false;
+ }
+
+ if (signal.methodType() != QMetaMethod::Signal) {
+ qWarning("QSignalSpy: Not a signal: '%s'", signal.methodSignature().constData());
+ return false;
+ }
+
+ return true;
+}
+
+static bool isObjectValid(const QObject *object)
+{
+ const bool valid = !!object;
+
+ if (!valid)
+ qWarning("QSignalSpy: Cannot spy on a null object");
+
+ return valid;
+}
+
+QSignalSpy::ObjectSignal QSignalSpy::verify(const QObject *obj, const char *aSignal)
+{
+ if (!isObjectValid(obj))
+ return {};
+
+ if (!aSignal) {
+ qWarning("QSignalSpy: Null signal name is not valid");
+ return {};
+ }
+
+ if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
+ qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
+ return {};
+ }
+
+ const QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
+ const QMetaObject * const mo = obj->metaObject();
+ const int sigIndex = mo->indexOfMethod(ba.constData());
+ if (sigIndex < 0) {
+ qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
+ return {};
+ }
+
+ return verify(obj, mo->method(sigIndex));
+}
+
+QSignalSpy::ObjectSignal QSignalSpy::verify(const QObject *obj, QMetaMethod signal)
+{
+ if (isObjectValid(obj) && isSignalMetaMethodValid(signal))
+ return {obj, signal};
+ else
+ return {};
+}
+
+QList<int> QSignalSpy::makeArgs(const QMetaMethod &member, const QObject *obj)
+{
+ QList<int> result;
+ result.reserve(member.parameterCount());
+ for (int i = 0; i < member.parameterCount(); ++i) {
+ QMetaType tp = member.parameterMetaType(i);
+ if (!tp.isValid() && obj) {
+ void *argv[] = { &tp, &i };
+ QMetaObject::metacall(const_cast<QObject*>(obj),
+ QMetaObject::RegisterMethodArgumentMetaType,
+ member.methodIndex(), argv);
+ }
+ if (!tp.isValid()) {
+ qWarning("QSignalSpy: Unable to handle parameter '%s' of type '%s' of method '%s',"
+ " use qRegisterMetaType to register it.",
+ member.parameterNames().at(i).constData(),
+ member.parameterTypes().at(i).constData(),
+ member.name().constData());
+ }
+ result.append(tp.id());
+ }
+ return result;
+}
+
+class QSignalSpyPrivate : public QObject
+{
+ QSignalSpy * const q;
+public:
+ explicit QSignalSpyPrivate(QSignalSpy *qq) : q(qq) {}
+
+ int qt_metacall(QMetaObject::Call call, int methodId, void **a) override;
+};
+
+QSignalSpy::QSignalSpy(ObjectSignal os)
+ : args(os.obj ? makeArgs(os.sig, os.obj) : QList<int>{})
+{
+ if (!os.obj)
+ return;
+
+ auto i = std::make_unique<QSignalSpyPrivate>(this);
+
+ const auto signalIndex = os.sig.methodIndex();
+ const auto slotIndex = QObject::staticMetaObject.methodCount();
+ if (!QMetaObject::connect(os.obj, signalIndex,
+ i.get(), slotIndex, Qt::DirectConnection)) {
+ qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
+ return;
+ }
+
+ d_ptr = std::move(i);
+
+ sig = os.sig.methodSignature();
+}
+
+/*!
+ Destructor.
+*/
+QSignalSpy::~QSignalSpy()
+ = default;
+
+void QSignalSpy::appendArgs(void **a)
+{
+ QList<QVariant> list;
+ list.reserve(args.size());
+ for (qsizetype i = 0; i < args.size(); ++i) {
+ const QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
+ if (type == QMetaType::QVariant)
+ list << *reinterpret_cast<QVariant *>(a[i + 1]);
+ else
+ list << QVariant(QMetaType(type), a[i + 1]);
+ }
+ QMutexLocker locker(&m_mutex);
+ append(std::move(list));
+
+ if (m_waiting) {
+ locker.unlock();
+ m_loop.exitLoop();
+ }
+}
+
+/*!
+ \reimp
+ \internal
+*/
+int QSignalSpyPrivate::qt_metacall(QMetaObject::Call call, int methodId, void **a)
+{
+ methodId = QObject::qt_metacall(call, methodId, a);
+ if (methodId < 0)
+ return methodId;
+
+ if (call == QMetaObject::InvokeMetaMethod) {
+ if (methodId == 0) {
+ q->appendArgs(a);
+ }
+ --methodId;
+ }
+ return methodId;
+}
+
+QT_END_NAMESPACE
diff --git a/src/testlib/qsignalspy.h b/src/testlib/qsignalspy.h
index 3ba56749ea..591545b4d5 100644
--- a/src/testlib/qsignalspy.h
+++ b/src/testlib/qsignalspy.h
@@ -1,239 +1,71 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSIGNALSPY_H
#define QSIGNALSPY_H
#include <QtCore/qbytearray.h>
#include <QtCore/qlist.h>
-#include <QtCore/qobject.h>
#include <QtCore/qmetaobject.h>
#include <QtTest/qtesteventloop.h>
#include <QtCore/qvariant.h>
+#include <QtCore/qmutex.h>
+
+#include <memory>
QT_BEGIN_NAMESPACE
class QVariant;
-
-class QSignalSpy: public QObject, public QList<QList<QVariant> >
+class QSignalSpyPrivate;
+class QSignalSpy : public QList<QList<QVariant> >
{
+ struct ObjectSignal {
+ const QObject *obj;
+ QMetaMethod sig;
+ };
+ friend class QSignalSpyPrivate;
+ std::unique_ptr<QSignalSpyPrivate> d_ptr;
public:
explicit QSignalSpy(const QObject *obj, const char *aSignal)
- : m_waiting(false)
- {
- if (!isObjectValid(obj))
- return;
-
- if (!aSignal) {
- qWarning("QSignalSpy: Null signal name is not valid");
- return;
- }
-
- if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) {
- qWarning("QSignalSpy: Not a valid signal, use the SIGNAL macro");
- return;
- }
-
- const QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
- const QMetaObject * const mo = obj->metaObject();
- const int sigIndex = mo->indexOfMethod(ba.constData());
- if (sigIndex < 0) {
- qWarning("QSignalSpy: No such signal: '%s'", ba.constData());
- return;
- }
-
- if (!connectToSignal(obj, sigIndex))
- return;
-
- sig = ba;
- initArgs(mo->method(sigIndex), obj);
- }
-
-#ifdef Q_CLANG_QDOC
+ : QSignalSpy(verify(obj, aSignal)) {}
+#ifdef Q_QDOC
template <typename PointerToMemberFunction>
QSignalSpy(const QObject *object, PointerToMemberFunction signal);
#else
template <typename Func>
QSignalSpy(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func signal0)
- : m_waiting(false)
- {
- if (!isObjectValid(obj))
- return;
-
- if (!signal0) {
- qWarning("QSignalSpy: Null signal name is not valid");
- return;
- }
-
- const QMetaObject * const mo = obj->metaObject();
- const QMetaMethod signalMetaMethod = QMetaMethod::fromSignal(signal0);
- const int sigIndex = signalMetaMethod.methodIndex();
-
- if (!isSignalMetaMethodValid(signalMetaMethod))
- return;
-
- if (!connectToSignal(obj, sigIndex))
- return;
-
- sig = signalMetaMethod.methodSignature();
- initArgs(mo->method(sigIndex), obj);
- }
-#endif // Q_CLANG_QDOC
-
- QSignalSpy(const QObject *obj, const QMetaMethod &signal)
- : m_waiting(false)
- {
- if (isObjectValid(obj) && isSignalMetaMethodValid(signal) &&
- connectToSignal(obj, signal.methodIndex())) {
- sig = signal.methodSignature();
- initArgs(signal, obj);
- }
- }
+ : QSignalSpy(verify(obj, QMetaMethod::fromSignal(signal0))) {}
+#endif // Q_QDOC
+ QSignalSpy(const QObject *obj, QMetaMethod signal)
+ : QSignalSpy(verify(obj, signal)) {}
+ Q_TESTLIB_EXPORT ~QSignalSpy();
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
- bool wait(int timeout = 5000)
- {
- Q_ASSERT(!m_waiting);
- const int origCount = count();
- m_waiting = true;
- m_loop.enterLoopMSecs(timeout);
- m_waiting = false;
- return count() > origCount;
- }
-
- int qt_metacall(QMetaObject::Call call, int methodId, void **a) override
- {
- methodId = QObject::qt_metacall(call, methodId, a);
- if (methodId < 0)
- return methodId;
-
- if (call == QMetaObject::InvokeMetaMethod) {
- if (methodId == 0) {
- appendArgs(a);
- }
- --methodId;
- }
- return methodId;
- }
+ bool wait(int timeout)
+ { return wait(std::chrono::milliseconds{timeout}); }
+
+ Q_TESTLIB_EXPORT bool wait(std::chrono::milliseconds timeout = std::chrono::seconds{5});
private:
- bool connectToSignal(const QObject *sender, int sigIndex)
- {
- static const int memberOffset = QObject::staticMetaObject.methodCount();
- const bool connected = QMetaObject::connect(
- sender, sigIndex, this, memberOffset, Qt::DirectConnection, nullptr);
-
- if (!connected)
- qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
-
- return connected;
- }
-
- static bool isSignalMetaMethodValid(const QMetaMethod &signal)
- {
- const bool valid = signal.isValid() && signal.methodType() == QMetaMethod::Signal;
-
- if (!valid)
- qWarning("QSignalSpy: Not a valid signal: '%s'", signal.methodSignature().constData());
-
- return valid;
- }
-
- static bool isObjectValid(const QObject *object)
- {
- const bool valid = !!object;
-
- if (!valid)
- qWarning("QSignalSpy: Cannot spy on a null object");
-
- return valid;
- }
-
- void initArgs(const QMetaMethod &member, const QObject *obj)
- {
- args.reserve(member.parameterCount());
- for (int i = 0; i < member.parameterCount(); ++i) {
- QMetaType tp = member.parameterMetaType(i);
- if (!tp.isValid() && obj) {
- void *argv[] = { &tp, &i };
- QMetaObject::metacall(const_cast<QObject*>(obj),
- QMetaObject::RegisterMethodArgumentMetaType,
- member.methodIndex(), argv);
- }
- if (!tp.isValid()) {
- qWarning("QSignalSpy: Unable to handle parameter '%s' of type '%s' of method '%s',"
- " use qRegisterMetaType to register it.",
- member.parameterNames().at(i).constData(),
- member.parameterTypes().at(i).constData(),
- member.name().constData());
- }
- args << tp.id();
- }
- }
-
- void appendArgs(void **a)
- {
- QList<QVariant> list;
- list.reserve(args.count());
- for (int i = 0; i < args.count(); ++i) {
- const QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
- if (type == QMetaType::QVariant)
- list << *reinterpret_cast<QVariant *>(a[i + 1]);
- else
- list << QVariant(QMetaType(type), a[i + 1]);
- }
- append(list);
-
- if (m_waiting)
- m_loop.exitLoop();
- }
+ Q_TESTLIB_EXPORT explicit QSignalSpy(ObjectSignal os);
+
+ Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, QMetaMethod signal);
+ Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, const char *aSignal);
+
+ Q_TESTLIB_EXPORT static QList<int> makeArgs(const QMetaMethod &member, const QObject *obj);
+ Q_TESTLIB_EXPORT void appendArgs(void **a);
// the full, normalized signal name
QByteArray sig;
// holds the QMetaType types for the argument list of the signal
- QList<int> args;
+ const QList<int> args;
QTestEventLoop m_loop;
- bool m_waiting;
+ bool m_waiting = false;
+ QMutex m_mutex; // protects m_waiting and the QList base class, between appendArgs() and wait()
};
QT_END_NAMESPACE
diff --git a/src/testlib/qsignalspy.qdoc b/src/testlib/qsignalspy.qdoc
deleted file mode 100644
index 42e02ed601..0000000000
--- a/src/testlib/qsignalspy.qdoc
+++ /dev/null
@@ -1,148 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \class QSignalSpy
- \inmodule QtTest
-
- \brief The QSignalSpy class enables introspection of signal emission.
-
- QSignalSpy can connect to any signal of any object and records its emission.
- QSignalSpy itself is a list of QVariant lists. Each emission of the signal
- will append one item to the list, containing the arguments of the signal.
-
- The following example records all signal emissions for the \c clicked() signal
- of a QCheckBox:
-
- \snippet code/doc_src_qsignalspy.cpp 0
-
- \c{spy.takeFirst()} returns the arguments for the first emitted signal, as a
- list of QVariant objects. The \c clicked() signal has a single bool argument,
- which is stored as the first entry in the list of arguments.
-
- The example below catches a signal from a custom object:
-
- \snippet code/doc_src_qsignalspy.cpp 1
-
- \note Non-standard data types need to be registered, using
- the qRegisterMetaType() function, before you can create a
- QSignalSpy. For example:
-
- \snippet code/doc_src_qsignalspy.cpp 2
-
- To retrieve the instance, you can use qvariant_cast:
-
- \snippet code/doc_src_qsignalspy.cpp 3
-
- \section1 Verifying Signal Emissions
-
- The QSignalSpy class provides an elegant mechanism for capturing the list
- of signals emitted by an object. However, you should verify its validity
- after construction. The constructor does a number of sanity checks, such as
- verifying that the signal to be spied upon actually exists. To make the
- diagnosis of test failures easier, the results of these checks should be
- checked by calling \c QVERIFY(spy.isValid()) before proceeding further with
- a test.
-
- \sa QVERIFY()
- */
-
-/*! \fn QSignalSpy::QSignalSpy(const QObject *object, const char *signal)
-
- Constructs a new QSignalSpy that listens for emissions of the \a signal
- from the QObject \a object. If QSignalSpy is not able to listen for a
- valid signal (for example, because \a object is \nullptr or \a signal does
- not denote a valid signal of \a object), an explanatory warning message
- will be output using qWarning() and subsequent calls to \c isValid() will
- return false.
-
- Example:
- \snippet code/doc_src_qsignalspy.cpp 4
-*/
-
-/*! \fn template <typename PointerToMemberFunction> QSignalSpy::QSignalSpy(const QObject *object, PointerToMemberFunction signal)
- \since 5.4
-
- Constructs a new QSignalSpy that listens for emissions of the \a signal
- from the QObject \a object. If QSignalSpy is not able to listen for a
- valid signal (for example, because \a object is \nullptr or \a signal does
- not denote a valid signal of \a object), an explanatory warning message
- will be output using qWarning() and subsequent calls to \c isValid() will
- return false.
-
- Example:
- \snippet code/doc_src_qsignalspy.cpp 6
-*/
-
-/*! \fn QSignalSpy::QSignalSpy(const QObject *obj, const QMetaMethod &signal)
- \since 5.14
-
- Constructs a new QSignalSpy that listens for emissions of the \a signal
- from the QObject \a obj. If QSignalSpy is not able to listen for a
- valid signal (for example, because \a obj is \nullptr or \a signal does
- not denote a valid signal of \a obj), an explanatory warning message
- will be output using qWarning() and subsequent calls to \c isValid() will
- return false.
-
- This constructor is convenient to use when Qt's meta-object system is
- heavily used in a test.
-
- Basic usage example:
- \snippet code/doc_src_qsignalspy.cpp 7
-
- Imagine we need to check whether all properties of the QWindow class
- that represent minimum and maximum dimensions are properly writable.
- The following example demonstrates one of the approaches:
- \snippet code/doc_src_qsignalspy.cpp 8
-*/
-
-/*! \fn QSignalSpy::isValid() const
-
- Returns \c true if the signal spy listens to a valid signal, otherwise false.
-*/
-
-/*! \fn QSignalSpy::signal() const
-
- Returns the normalized signal the spy is currently listening to.
-*/
-
-/*! \fn int QSignalSpy::qt_metacall(QMetaObject::Call call, int id, void **a)
- \internal
-*/
-
-/*! \fn QSignalSpy::wait(int timeout)
-
- \since 5.0
-
- Starts an event loop that runs until the given signal is received.
- Optionally the event loop can return earlier on a \a timeout (in milliseconds).
-
- Returns \c true if the signal was emitted at least once in \a timeout milliseconds, otherwise returns \c false.
-
- Example:
- \snippet code/doc_src_qsignalspy.cpp 5
-*/
diff --git a/src/testlib/qt_cmdline.cmake b/src/testlib/qt_cmdline.cmake
index e69de29bb2..d3601c1e39 100644
--- a/src/testlib/qt_cmdline.cmake
+++ b/src/testlib/qt_cmdline.cmake
@@ -0,0 +1 @@
+qt_commandline_option(batch-tests TYPE boolean NAME batch_tests)
diff --git a/src/testlib/qtaptestlogger.cpp b/src/testlib/qtaptestlogger.cpp
index 5b5a3c4875..76f0ba0e8b 100644
--- a/src/testlib/qtaptestlogger.cpp
+++ b/src/testlib/qtaptestlogger.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qtaptestlogger_p.h"
@@ -49,9 +13,87 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+/*! \internal
+ \class QTapTestLogger
+ \inmodule QtTest
+
+ QTapTestLogger implements the Test Anything Protocol v13.
+
+ The \l{Test Anything Protocol} (TAP) is a simple plain-text interface
+ between testing code and systems for reporting and analyzing test results.
+ Since QtTest doesn't build the table for a data-driven test until the test
+ is about to be run, we don't typically know how many tests we'll run until
+ we've run them, so we put The Plan at the end, rather than the beginning.
+ We summarise the results in comments following The Plan.
+
+ \section1 YAMLish
+
+ The TAP protocol supports inclusion, after a Test Line, of a "diagnostic
+ block" in YAML, containing supporting information. We use this to package
+ other information from the test, in combination with comments to record
+ information we're unable to deliver at the same time as a test line. By
+ convention, TAP producers limit themselves to a restricted subset of YAML,
+ known as YAMLish, for maximal compatibility with TAP consumers.
+
+ YAML (see \c yaml.org for details) supports three data-types: mapping (hash
+ or dictionary), sequence (list, array or set) and scalar (string or number),
+ much as JSON does. It uses indentation to indicate levels of nesting of
+ mappings and sequences within one another. A line starting with a dash (at
+ the same level of indent as its context) introduces a list entry. A line
+ starting with a key (more indented than its context, aside from any earlier
+ keys at its level) followed by a colon introduces a mapping entry; the key
+ may be either a simple word or a quoted string (within which numeric escapes
+ may be used to indicate unicode characters). The value associated with a
+ given key, or the entry in a list, can appar after the key-colon or dasy
+ either on the same line or on a succession of subsequent lines at higher
+ indent. Thus
+
+ \code
+ - first list item
+ -
+ second: list item is a mapping
+ with:
+ - two keys
+ - the second of which is a list
+ - back in the outer list, a third item
+ \endcode
+
+ In YAMLish, the top-level structure should be a hash. The keys supported for
+ this hash, with the meanings for their values, are:
+
+ \list
+ \li \c message: free text supplying supporting details
+ \li \c severity: how bad is it ?
+ \li \c source: source describing the test, as an URL (compare file, line)
+ \li \c at: identifies the function (with file and line) performing the test
+ \li \c datetime: when the test was run (ISO 8601 or HTTP format)
+ \li \c file: source of the test as a local file-name, when appropriate
+ \li \c line: line number within the source
+ \li \c name: test name
+ \li \c extensions: sub-hash in which to store random other stuff
+ \li \c actual: what happened (a.k.a. found; contrast expected)
+ \li \c expected: what was expected (a.k.a. wanted; contrast actual)
+ \li \c display: description of the result, suitable for display
+ \li \c dump: a sub-hash of variable values when the result arose
+ \li \c error: describes the error
+ \li \c backtrace: describes the call-stack of the error
+ \endlist
+
+ In practice, only \c at, \c expected and \c actual appear to be generally
+ supported.
+
+ We can also have several messages produced within a single test, so the
+ simple \c message / \c severity pair of top-level keys does not serve us
+ well. We therefore use \c extensions with a sub-tag \c messages in which to
+ package a list of messages.
+
+ \sa QAbstractTestLogger
+*/
+
QTapTestLogger::QTapTestLogger(const char *filename)
: QAbstractTestLogger(filename)
- , m_wasExpectedFail(false)
{
}
@@ -76,7 +118,7 @@ void QTapTestLogger::stopLogging()
QTestCharBuffer testPlanAndStats;
QTest::qt_asprintf(&testPlanAndStats,
- "1..%d\n"
+ "1..%d\n" // The plan (last non-diagnostic line)
"# tests %d\n"
"# pass %d\n"
"# fail %d\n",
@@ -88,176 +130,339 @@ void QTapTestLogger::stopLogging()
void QTapTestLogger::enterTestFunction(const char *function)
{
- Q_UNUSED(function);
- m_wasExpectedFail = false;
+ m_firstExpectedFail.clear();
+ Q_ASSERT(!m_gatherMessages);
+ Q_ASSERT(m_comments.isEmpty());
+ Q_ASSERT(m_messages.isEmpty());
+ m_gatherMessages = function != nullptr;
}
void QTapTestLogger::enterTestData(QTestData *data)
{
- Q_UNUSED(data);
- m_wasExpectedFail = false;
+ m_firstExpectedFail.clear();
+ if (!m_messages.isEmpty() || !m_comments.isEmpty())
+ flushMessages();
+ m_gatherMessages = data != nullptr;
}
using namespace QTestPrivate;
-void QTapTestLogger::outputTestLine(bool ok, int testNumber, QTestCharBuffer &directive)
+void QTapTestLogger::outputTestLine(bool ok, int testNumber, const QTestCharBuffer &directive)
{
QTestCharBuffer testIdentifier;
QTestPrivate::generateTestIdentifier(&testIdentifier, TestFunction | TestDataTag);
QTestCharBuffer testLine;
- QTest::qt_asprintf(&testLine, "%s %d - %s%s\n",
- ok ? "ok" : "not ok", testNumber, testIdentifier.data(), directive.data());
+ QTest::qt_asprintf(&testLine, "%s %d - %s%s\n", ok ? "ok" : "not ok",
+ testNumber, testIdentifier.data(), directive.constData());
outputString(testLine.data());
}
-void QTapTestLogger::addIncident(IncidentTypes type, const char *description,
- const char *file, int line)
+// The indent needs to be two spaces for maximum compatibility.
+// This matches the width of the "- " prefix on a list item's first line.
+#define YAML_INDENT " "
+
+void QTapTestLogger::outputBuffer(const QTestCharBuffer &buffer)
{
- if (m_wasExpectedFail && type == Pass) {
- // XFail comes with a corresponding Pass incident, but we only want
- // to emit a single test point for it, so skip the this pass.
- return;
- }
+ auto isComment = [&buffer]() {
+ return buffer.constData()[strlen(YAML_INDENT)] == '#';
+ };
+ if (!m_gatherMessages)
+ outputString(buffer.constData());
+ else
+ QTestPrivate::appendCharBuffer(isComment() ? &m_comments : &m_messages, buffer);
+}
- bool ok = type == Pass || type == XPass || type == BlacklistedPass || type == BlacklistedXPass;
+void QTapTestLogger::beginYamlish()
+{
+ outputString(YAML_INDENT "---\n");
+}
- QTestCharBuffer directive;
- if (type == XFail || type == XPass || type == BlacklistedFail || type == BlacklistedPass
- || type == BlacklistedXFail || type == BlacklistedXPass) {
- // We treat expected or blacklisted failures/passes as TODO-failures/passes,
- // which should be treated as soft issues by consumers. Not all do though :/
- QTest::qt_asprintf(&directive, " # TODO %s", description);
+void QTapTestLogger::endYamlish()
+{
+ // Flush any accumulated messages:
+ if (!m_messages.isEmpty()) {
+ outputString(YAML_INDENT "extensions:\n");
+ outputString(YAML_INDENT YAML_INDENT "messages:\n");
+ outputString(m_messages.constData());
+ m_messages.clear();
}
+ outputString(YAML_INDENT "...\n");
+}
+
+void QTapTestLogger::flushComments()
+{
+ if (!m_comments.isEmpty()) {
+ outputString(m_comments.constData());
+ m_comments.clear();
+ }
+}
- int testNumber = QTestLog::totalCount();
- if (type == XFail) {
- // The global test counter hasn't been updated yet for XFail
- testNumber += 1;
+void QTapTestLogger::flushMessages()
+{
+ /* A _data() function's messages show up here. */
+ QTestCharBuffer dataLine;
+ QTest::qt_asprintf(&dataLine, "ok %d - %s() # Data prepared\n",
+ QTestLog::totalCount(), QTestResult::currentTestFunction());
+ outputString(dataLine.constData());
+ flushComments();
+ if (!m_messages.isEmpty()) {
+ beginYamlish();
+ endYamlish();
}
+}
- outputTestLine(ok, testNumber, directive);
+void QTapTestLogger::addIncident(IncidentTypes type, const char *description,
+ const char *file, int line)
+{
+ const bool isExpectedFail = type == XFail || type == BlacklistedXFail;
+ const bool ok = (m_firstExpectedFail.isEmpty()
+ && (type == Pass || type == BlacklistedPass || type == Skip
+ || type == XPass || type == BlacklistedXPass));
- if (!ok) {
- // All failures need a diagnostics sections to not confuse consumers
+ const char *const incident = [type](const char *priorXFail) {
+ switch (type) {
+ // We treat expected or blacklisted failures/passes as TODO-failures/passes,
+ // which should be treated as soft issues by consumers. Not all do though :/
+ case BlacklistedPass:
+ if (priorXFail[0] != '\0')
+ return priorXFail;
+ Q_FALLTHROUGH();
+ case XFail: case BlacklistedXFail:
+ case XPass: case BlacklistedXPass:
+ case BlacklistedFail:
+ return "TODO";
+ case Skip:
+ return "SKIP";
+ case Pass:
+ if (priorXFail[0] != '\0')
+ return priorXFail;
+ Q_FALLTHROUGH();
+ case Fail:
+ break;
+ }
+ return static_cast<const char *>(nullptr);
+ }(m_firstExpectedFail.constData());
- // The indent needs to be two spaces for maximum compatibility
- #define YAML_INDENT " "
+ QTestCharBuffer directive;
+ if (incident) {
+ QTest::qt_asprintf(&directive, "%s%s%s%s",
+ isExpectedFail ? "" : " # ", incident,
+ description && description[0] ? " " : "", description);
+ }
- outputString(YAML_INDENT "---\n");
+ if (!isExpectedFail) {
+ m_gatherMessages = false;
+ outputTestLine(ok, QTestLog::totalCount(), directive);
+ } else if (m_gatherMessages && m_firstExpectedFail.isEmpty()) {
+ QTestPrivate::appendCharBuffer(&m_firstExpectedFail, directive);
+ }
+ flushComments();
+
+ if (!ok || !m_messages.isEmpty()) {
+ // All failures need a diagnostics section to not confuse consumers.
+ // We also need a diagnostics section when we have messages to report.
+ if (isExpectedFail) {
+ QTestCharBuffer message;
+ if (m_gatherMessages) {
+ QTest::qt_asprintf(&message, YAML_INDENT YAML_INDENT "- severity: xfail\n"
+ YAML_INDENT YAML_INDENT YAML_INDENT "message:%s\n",
+ directive.constData() + 4);
+ } else {
+ QTest::qt_asprintf(&message, YAML_INDENT "# xfail:%s\n", directive.constData() + 4);
+ }
+ outputBuffer(message);
+ } else {
+ beginYamlish();
+ }
- if (type != XFail) {
+ if (!isExpectedFail || m_gatherMessages) {
+ const char *indent = isExpectedFail ? YAML_INDENT YAML_INDENT YAML_INDENT : YAML_INDENT;
+ if (!ok) {
#if QT_CONFIG(regularexpression)
- // This is fragile, but unfortunately testlib doesn't plumb
- // the expected and actual values to the loggers (yet).
- static QRegularExpression verifyRegex(
- QLatin1String("^'(?<actualexpression>.*)' returned (?<actual>\\w+).+\\((?<message>.*)\\)$"));
-
- static QRegularExpression comparRegex(
- QLatin1String("^(?<message>.*)\n"
- "\\s*Actual\\s+\\((?<actualexpression>.*)\\)\\s*: (?<actual>.*)\n"
- "\\s*Expected\\s+\\((?<expectedexpresssion>.*)\\)\\s*: (?<expected>.*)$"));
-
- QString descriptionString = QString::fromUtf8(description);
- QRegularExpressionMatch match = verifyRegex.match(descriptionString);
- if (!match.hasMatch())
- match = comparRegex.match(descriptionString);
-
- if (match.hasMatch()) {
- bool isVerify = match.regularExpression() == verifyRegex;
- QString message = match.captured(QLatin1String("message"));
- QString expected;
- QString actual;
-
- if (isVerify) {
- QString expression = QLatin1String(" (")
- % match.captured(QLatin1String("actualexpression")) % QLatin1Char(')') ;
- actual = match.captured(QLatin1String("actual")).toLower() % expression;
- expected = (actual.startsWith(QLatin1String("true")) ? QLatin1String("false") : QLatin1String("true")) % expression;
- if (message.isEmpty())
- message = QLatin1String("Verification failed");
- } else {
- expected = match.captured(QLatin1String("expected"))
- % QLatin1String(" (") % match.captured(QLatin1String("expectedexpresssion")) % QLatin1Char(')');
- actual = match.captured(QLatin1String("actual"))
- % QLatin1String(" (") % match.captured(QLatin1String("actualexpression")) % QLatin1Char(')');
+ enum class OperationType {
+ Unknown,
+ Compare, /* Plain old QCOMPARE */
+ Verify, /* QVERIFY */
+ CompareOp, /* QCOMPARE_OP */
+ };
+
+ // This is fragile, but unfortunately testlib doesn't plumb
+ // the expected and actual values to the loggers (yet).
+ static const QRegularExpression verifyRegex(
+ u"^'(?<actualexpression>.*)' returned "
+ "(?<actual>\\w+)\\. \\((?<message>.*)\\)$"_s);
+
+ static const QRegularExpression compareRegex(
+ u"^(?<message>.*)\n"
+ "\\s*Actual\\s+\\((?<actualexpression>.*)\\)\\s*: (?<actual>.*)\n"
+ "\\s*Expected\\s+\\((?<expectedexpresssion>.*)\\)\\s*: "
+ "(?<expected>.*)$"_s);
+
+ static const QRegularExpression compareOpRegex(
+ u"^(?<message>.*)\n"
+ "\\s*Computed\\s+\\((?<actualexpression>.*)\\)\\s*: (?<actual>.*)\n"
+ "\\s*Baseline\\s+\\((?<expectedexpresssion>.*)\\)\\s*: "
+ "(?<expected>.*)$"_s);
+
+ const QString descriptionString = QString::fromUtf8(description);
+ QRegularExpressionMatch match = verifyRegex.match(descriptionString);
+
+ OperationType opType = OperationType::Unknown;
+ if (match.hasMatch())
+ opType = OperationType::Verify;
+
+ if (opType == OperationType::Unknown) {
+ match = compareRegex.match(descriptionString);
+ if (match.hasMatch())
+ opType = OperationType::Compare;
}
- QTestCharBuffer diagnosticsYamlish;
- QTest::qt_asprintf(&diagnosticsYamlish,
- YAML_INDENT "type: %s\n"
- YAML_INDENT "message: %s\n"
-
- // Some consumers understand 'wanted/found', while others need
- // 'expected/actual', so we do both for maximum compatibility.
- YAML_INDENT "wanted: %s\n"
- YAML_INDENT "found: %s\n"
- YAML_INDENT "expected: %s\n"
- YAML_INDENT "actual: %s\n",
-
- isVerify ? "QVERIFY" : "QCOMPARE",
- qPrintable(message),
- qPrintable(expected), qPrintable(actual),
- qPrintable(expected), qPrintable(actual)
- );
-
- outputString(diagnosticsYamlish.data());
- } else {
- QTestCharBuffer unparsableDescription;
- QTest::qt_asprintf(&unparsableDescription,
- YAML_INDENT "# %s\n", description);
- outputString(unparsableDescription.data());
- }
-#else
- QTestCharBuffer unparsableDescription;
- QTest::qt_asprintf(&unparsableDescription,
- YAML_INDENT "# %s\n", description);
- outputString(unparsableDescription.data());
+ if (opType == OperationType::Unknown) {
+ match = compareOpRegex.match(descriptionString);
+ if (match.hasMatch())
+ opType = OperationType::CompareOp;
+ }
+
+ if (opType != OperationType::Unknown) {
+ QString message = match.captured(u"message");
+ QLatin1StringView comparisonType;
+ QString expected;
+ QString actual;
+ const auto parenthesize = [&match](QLatin1StringView key) -> QString {
+ return " ("_L1 % match.captured(key) % u')';
+ };
+ const QString actualExpression = parenthesize("actualexpression"_L1);
+
+ if (opType == OperationType::Verify) {
+ comparisonType = "QVERIFY"_L1;
+ actual = match.captured(u"actual").toLower() % actualExpression;
+ expected = (actual.startsWith("true "_L1) ? "false"_L1 : "true"_L1)
+ % actualExpression;
+ if (message.isEmpty())
+ message = u"Verification failed"_s;
+ } else if (opType == OperationType::Compare) {
+ comparisonType = "QCOMPARE"_L1;
+ expected = match.captured(u"expected")
+ % parenthesize("expectedexpresssion"_L1);
+ actual = match.captured(u"actual") % actualExpression;
+ } else {
+ struct ComparisonInfo {
+ const char *comparisonType;
+ const char *comparisonStringOp;
+ };
+ // get a proper comparison type based on the error message
+ const auto info = [](const QString &err) -> ComparisonInfo {
+ if (err.contains("different"_L1))
+ return { "QCOMPARE_NE", "!= " };
+ else if (err.contains("less than or equal to"_L1))
+ return { "QCOMPARE_LE", "<= " };
+ else if (err.contains("greater than or equal to"_L1))
+ return { "QCOMPARE_GE", ">= " };
+ else if (err.contains("less than"_L1))
+ return { "QCOMPARE_LT", "< " };
+ else if (err.contains("greater than"_L1))
+ return { "QCOMPARE_GT", "> " };
+ else if (err.contains("to be equal to"_L1))
+ return { "QCOMPARE_EQ", "== " };
+ else
+ return { "Unknown", "" };
+ }(message);
+ comparisonType = QLatin1StringView(info.comparisonType);
+ expected = QLatin1StringView(info.comparisonStringOp)
+ % match.captured(u"expected")
+ % parenthesize("expectedexpresssion"_L1);
+ actual = match.captured(u"actual") % actualExpression;
+ }
+
+ QTestCharBuffer diagnosticsYamlish;
+ QTest::qt_asprintf(&diagnosticsYamlish,
+ "%stype: %s\n"
+ "%smessage: %s\n"
+ // Some consumers understand 'wanted/found', others need
+ // 'expected/actual', so be compatible with both.
+ "%swanted: %s\n"
+ "%sfound: %s\n"
+ "%sexpected: %s\n"
+ "%sactual: %s\n",
+ indent, comparisonType.latin1(),
+ indent, qPrintable(message),
+ indent, qPrintable(expected), indent, qPrintable(actual),
+ indent, qPrintable(expected), indent, qPrintable(actual)
+ );
+
+ outputBuffer(diagnosticsYamlish);
+ } else
#endif
- }
+ if (description && !incident) {
+ QTestCharBuffer unparsableDescription;
+ QTest::qt_asprintf(&unparsableDescription, YAML_INDENT "# %s\n", description);
+ outputBuffer(unparsableDescription);
+ }
+ }
- if (file) {
- QTestCharBuffer location;
- QTest::qt_asprintf(&location,
- // The generic 'at' key is understood by most consumers.
- YAML_INDENT "at: %s::%s() (%s:%d)\n"
-
- // The file and line keys are for consumers that are able
- // to read more granular location info.
- YAML_INDENT "file: %s\n"
- YAML_INDENT "line: %d\n",
-
- QTestResult::currentTestObjectName(),
- QTestResult::currentTestFunction(),
- file, line, file, line
- );
- outputString(location.data());
+ if (file) {
+ QTestCharBuffer location;
+ QTest::qt_asprintf(&location,
+ // The generic 'at' key is understood by most consumers.
+ "%sat: %s::%s() (%s:%d)\n"
+
+ // The file and line keys are for consumers that are able
+ // to read more granular location info.
+ "%sfile: %s\n"
+ "%sline: %d\n",
+
+ indent, QTestResult::currentTestObjectName(),
+ QTestResult::currentTestFunction(),
+ file, line, indent, file, indent, line
+ );
+ outputBuffer(location);
+ }
}
- outputString(YAML_INDENT "...\n");
+ if (!isExpectedFail)
+ endYamlish();
}
-
- m_wasExpectedFail = type == XFail;
}
void QTapTestLogger::addMessage(MessageTypes type, const QString &message,
- const char *file, int line)
+ const char *file, int line)
{
Q_UNUSED(file);
Q_UNUSED(line);
-
- if (type == Skip) {
- QTestCharBuffer directive;
- QTest::qt_asprintf(&directive, " # SKIP %s", message.toUtf8().constData());
- outputTestLine(/* ok = */ true, QTestLog::totalCount(), directive);
- return;
+ const char *const flavor = [type]() {
+ switch (type) {
+ case QDebug: return "debug";
+ case QInfo: return "info";
+ case QWarning: return "warning";
+ case QCritical: return "critical";
+ case QFatal: return "fatal";
+ // Handle internal messages as comments
+ case Info: return "# inform";
+ case Warn: return "# warn";
+ }
+ return "unrecognised message";
+ }();
+
+ QTestCharBuffer diagnostic;
+ if (!m_gatherMessages) {
+ QTest::qt_asprintf(&diagnostic, "%s%s: %s\n",
+ flavor[0] == '#' ? "" : "# ",
+ flavor, qPrintable(message));
+ outputString(diagnostic.constData());
+ } else if (flavor[0] == '#') {
+ QTest::qt_asprintf(&diagnostic, YAML_INDENT "%s: %s\n",
+ flavor, qPrintable(message));
+ QTestPrivate::appendCharBuffer(&m_comments, diagnostic);
+ } else {
+ // These shall appear in a messages: sub-block of the extensions: block,
+ // so triple-indent.
+ QTest::qt_asprintf(&diagnostic, YAML_INDENT YAML_INDENT "- severity: %s\n"
+ YAML_INDENT YAML_INDENT YAML_INDENT "message: %s\n",
+ flavor, qPrintable(message));
+ QTestPrivate::appendCharBuffer(&m_messages, diagnostic);
}
-
- QTestCharBuffer diagnostics;
- QTest::qt_asprintf(&diagnostics, "# %s\n", qPrintable(message));
- outputString(diagnostics.data());
}
QT_END_NAMESPACE
-
diff --git a/src/testlib/qtaptestlogger_p.h b/src/testlib/qtaptestlogger_p.h
index e14035d79c..fa4f338bc0 100644
--- a/src/testlib/qtaptestlogger_p.h
+++ b/src/testlib/qtaptestlogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTAPTESTLOGGER_P_H
#define QTAPTESTLOGGER_P_H
@@ -70,14 +34,22 @@ public:
void enterTestData(QTestData *data) override;
void addIncident(IncidentTypes type, const char *description,
- const char *file = nullptr, int line = 0) override;
+ const char *file = nullptr, int line = 0) override;
void addMessage(MessageTypes type, const QString &message,
- const char *file = nullptr, int line = 0) override;
+ const char *file = nullptr, int line = 0) override;
void addBenchmarkResult(const QBenchmarkResult &) override {}
private:
- void outputTestLine(bool ok, int testNumber, QTestCharBuffer &directive);
- bool m_wasExpectedFail;
+ void outputTestLine(bool ok, int testNumber, const QTestCharBuffer &directive);
+ void outputBuffer(const QTestCharBuffer &buffer);
+ void flushComments();
+ void flushMessages();
+ void beginYamlish();
+ void endYamlish();
+ QTestCharBuffer m_firstExpectedFail;
+ QTestCharBuffer m_comments;
+ QTestCharBuffer m_messages;
+ bool m_gatherMessages = false;
};
QT_END_NAMESPACE
diff --git a/src/testlib/qteamcitylogger.cpp b/src/testlib/qteamcitylogger.cpp
index 8a77143454..840a0334f6 100644
--- a/src/testlib/qteamcitylogger.cpp
+++ b/src/testlib/qteamcitylogger.cpp
@@ -1,47 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Borgar Ovsthus
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2017 Borgar Ovsthus
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qtestresult_p.h>
#include <QtTest/qtestassert.h>
#include <QtTest/private/qtestlog_p.h>
#include <QtTest/private/qteamcitylogger_p.h>
#include <QtCore/qbytearray.h>
+#include <private/qlocale_p.h>
+
+#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -49,11 +17,15 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace QTest {
- static const char *incidentType2String(QAbstractTestLogger::IncidentTypes type)
+ static const char *tcIncidentType2String(QAbstractTestLogger::IncidentTypes type)
{
switch (type) {
+ case QAbstractTestLogger::Skip:
+ return "SKIP";
case QAbstractTestLogger::Pass:
return "PASS";
case QAbstractTestLogger::XFail:
@@ -74,30 +46,35 @@ namespace QTest {
return "??????";
}
- static const char *messageType2String(QAbstractTestLogger::MessageTypes type)
+ static const char *tcMessageType2String(QAbstractTestLogger::MessageTypes type)
{
switch (type) {
- case QAbstractTestLogger::Skip:
- return "SKIP";
- case QAbstractTestLogger::Warn:
- return "WARNING";
- case QAbstractTestLogger::QWarning:
- return "QWARN";
case QAbstractTestLogger::QDebug:
return "QDEBUG";
case QAbstractTestLogger::QInfo:
return "QINFO";
- case QAbstractTestLogger::QSystem:
- return "QSYSTEM";
+ case QAbstractTestLogger::QWarning:
+ return "QWARN";
+ case QAbstractTestLogger::QCritical:
+ return "QCRITICAL";
case QAbstractTestLogger::QFatal:
return "QFATAL";
case QAbstractTestLogger::Info:
return "INFO";
+ case QAbstractTestLogger::Warn:
+ return "WARNING";
}
return "??????";
}
}
+/*! \internal
+ \class QTeamCityLogger
+ \inmodule QtTest
+
+ QTeamCityLogger implements logging in the \l{TeamCity} format.
+*/
+
QTeamCityLogger::QTeamCityLogger(const char *filename)
: QAbstractTestLogger(filename)
{
@@ -109,16 +86,20 @@ void QTeamCityLogger::startLogging()
{
QAbstractTestLogger::startLogging();
- flowID = tcEscapedString(QString::fromUtf8(QTestResult::currentTestObjectName()));
+ tcEscapedString(&flowID, QTestResult::currentTestObjectName());
- QString str = QString(QLatin1String("##teamcity[testSuiteStarted name='%1' flowId='%1']\n")).arg(flowID);
- outputString(qPrintable(str));
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf, "##teamcity[testSuiteStarted name='%s' flowId='%s']\n",
+ flowID.constData(), flowID.constData());
+ outputString(buf.constData());
}
void QTeamCityLogger::stopLogging()
{
- QString str = QString(QLatin1String("##teamcity[testSuiteFinished name='%1' flowId='%1']\n")).arg(flowID);
- outputString(qPrintable(str));
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf, "##teamcity[testSuiteFinished name='%s' flowId='%s']\n",
+ flowID.constData(), flowID.constData());
+ outputString(buf.constData());
QAbstractTestLogger::stopLogging();
}
@@ -136,56 +117,70 @@ void QTeamCityLogger::leaveTestFunction()
void QTeamCityLogger::addIncident(IncidentTypes type, const char *description,
const char *file, int line)
{
- // suppress PASS and XFAIL in silent mode
- if ((type == QAbstractTestLogger::Pass || type == QAbstractTestLogger::XFail) && QTestLog::verboseLevel() < 0)
+ // suppress B?PASS and B?XFAIL in silent mode
+ if ((type == Pass || type == XFail || type == BlacklistedPass || type == BlacklistedXFail) && QTestLog::verboseLevel() < 0)
return;
- QString buf;
+ QTestCharBuffer buf;
+ QTestCharBuffer tmpFuncName;
+ escapedTestFuncName(&tmpFuncName);
- QString tmpFuncName = escapedTestFuncName();
+ if (qstrcmp(tmpFuncName.constData(), currTestFuncName.constData()) != 0) {
+ QTest::qt_asprintf(&buf, "##teamcity[testStarted name='%s' flowId='%s']\n",
+ tmpFuncName.constData(), flowID.constData());
+ outputString(buf.constData());
- if (tmpFuncName != currTestFuncName) {
- buf = QString(QLatin1String("##teamcity[testStarted name='%1' flowId='%2']\n")).arg(tmpFuncName, flowID);
- outputString(qPrintable(buf));
+ currTestFuncName.clear();
+ QTestPrivate::appendCharBuffer(&currTestFuncName, tmpFuncName);
}
- currTestFuncName = tmpFuncName;
-
if (type == QAbstractTestLogger::XFail) {
- addPendingMessage(QTest::incidentType2String(type), QString::fromUtf8(description), file, line);
+ addPendingMessage(QTest::tcIncidentType2String(type), description, file, line);
return;
}
- QString detailedText = QString::fromUtf8(description);
- detailedText = tcEscapedString(detailedText);
+ QTestCharBuffer detailedText;
+ tcEscapedString(&detailedText, description);
// Test failed
- if ((type == QAbstractTestLogger::Fail) || (type == QAbstractTestLogger::XPass)) {
- QString messageText(QLatin1String("Failure!"));
-
+ if (type == Fail || type == XPass) {
+ QTestCharBuffer messageText;
if (file)
- messageText += QString(QLatin1String(" |[Loc: %1(%2)|]")).arg(QString::fromUtf8(file)).arg(line);
+ QTest::qt_asprintf(&messageText, "Failure! |[Loc: %s(%d)|]", file, line);
+ else
+ QTest::qt_asprintf(&messageText, "Failure!");
+
+ QTest::qt_asprintf(&buf, "##teamcity[testFailed name='%s' message='%s' details='%s'"
+ " flowId='%s']\n", tmpFuncName.constData(), messageText.constData(),
+ detailedText.constData(), flowID.constData());
+
+ outputString(buf.constData());
+ } else if (type == Skip) {
+ if (file) {
+ QTestCharBuffer detail;
+ QTest::qt_asprintf(&detail, " |[Loc: %s(%d)|]", file, line);
+ QTestPrivate::appendCharBuffer(&detailedText, detail);
+ }
- buf = QString(QLatin1String("##teamcity[testFailed name='%1' message='%2' details='%3' flowId='%4']\n"))
- .arg(tmpFuncName,
- messageText,
- detailedText,
- flowID);
+ QTest::qt_asprintf(&buf, "##teamcity[testIgnored name='%s' message='%s' flowId='%s']\n",
+ currTestFuncName.constData(), detailedText.constData(),
+ flowID.constData());
- outputString(qPrintable(buf));
+ outputString(buf.constData());
}
if (!pendingMessages.isEmpty()) {
- buf = QString(QLatin1String("##teamcity[testStdOut name='%1' out='%2' flowId='%3']\n"))
- .arg(tmpFuncName, pendingMessages, flowID);
-
- outputString(qPrintable(buf));
+ QTest::qt_asprintf(&buf, "##teamcity[testStdOut name='%s' out='%s' flowId='%s']\n",
+ tmpFuncName.constData(), pendingMessages.constData(),
+ flowID.constData());
+ outputString(buf.constData());
pendingMessages.clear();
}
- buf = QString(QLatin1String("##teamcity[testFinished name='%1' flowId='%2']\n")).arg(tmpFuncName, flowID);
- outputString(qPrintable(buf));
+ QTest::qt_asprintf(&buf, "##teamcity[testFinished name='%s' flowId='%s']\n",
+ tmpFuncName.constData(), flowID.constData());
+ outputString(buf.constData());
}
void QTeamCityLogger::addBenchmarkResult(const QBenchmarkResult &)
@@ -197,88 +192,88 @@ void QTeamCityLogger::addMessage(MessageTypes type, const QString &message,
const char *file, int line)
{
// suppress non-fatal messages in silent mode
- if (type != QAbstractTestLogger::QFatal && QTestLog::verboseLevel() < 0)
+ if (type != QFatal && QTestLog::verboseLevel() < 0)
return;
- QString escapedMessage = tcEscapedString(message);
-
- QString buf;
-
- if (type == QAbstractTestLogger::Skip) {
- if (file)
- escapedMessage.append(QString(QLatin1String(" |[Loc: %1(%2)|]")).arg(QString::fromUtf8(file)).arg(line));
-
- buf = QString(QLatin1String("##teamcity[testIgnored name='%1' message='%2' flowId='%3']\n"))
- .arg(escapedTestFuncName(), escapedMessage, flowID);
-
- outputString(qPrintable(buf));
- }
- else {
- addPendingMessage(QTest::messageType2String(type), escapedMessage, file, line);
- }
+ QTestCharBuffer escapedMessage;
+ tcEscapedString(&escapedMessage, qUtf8Printable(message));
+ addPendingMessage(QTest::tcMessageType2String(type), escapedMessage.constData(), file, line);
}
-QString QTeamCityLogger::tcEscapedString(const QString &str) const
+void QTeamCityLogger::tcEscapedString(QTestCharBuffer *buf, const char *str) const
{
- QString formattedString;
+ {
+ size_t size = qstrlen(str) + 1;
+ for (const char *p = str; *p; ++p) {
+ if (strchr("\n\r|[]'", *p))
+ ++size;
+ }
+ Q_ASSERT(size <= size_t(INT_MAX));
+ buf->resize(int(size));
+ }
- for (QChar ch : str) {
- switch (ch.toLatin1()) {
+ bool swallowSpace = true;
+ char *p = buf->data();
+ for (; *str; ++str) {
+ char ch = *str;
+ switch (ch) {
case '\n':
- formattedString.append(QLatin1String("|n"));
+ p++[0] = '|';
+ ch = 'n';
+ swallowSpace = false;
break;
case '\r':
- formattedString.append(QLatin1String("|r"));
+ p++[0] = '|';
+ ch = 'r';
+ swallowSpace = false;
break;
case '|':
- formattedString.append(QLatin1String("||"));
- break;
case '[':
- formattedString.append(QLatin1String("|["));
- break;
case ']':
- formattedString.append(QLatin1String("|]"));
- break;
case '\'':
- formattedString.append(QLatin1String("|'"));
+ p++[0] = '|';
+ swallowSpace = false;
break;
default:
- formattedString.append(ch);
+ if (ascii_isspace(ch)) {
+ if (swallowSpace)
+ continue;
+ swallowSpace = true;
+ ch = ' ';
+ } else {
+ swallowSpace = false;
+ }
+ break;
}
+ p++[0] = ch;
}
-
- return std::move(formattedString).simplified();
+ Q_ASSERT(p < buf->data() + buf->size());
+ if (swallowSpace && p > buf->data()) {
+ Q_ASSERT(p[-1] == ' ');
+ --p;
+ }
+ Q_ASSERT(p == buf->data() || !ascii_isspace(p[-1]));
+ *p = '\0';
}
-QString QTeamCityLogger::escapedTestFuncName() const
+void QTeamCityLogger::escapedTestFuncName(QTestCharBuffer *buf) const
{
- const char *fn = QTestResult::currentTestFunction() ? QTestResult::currentTestFunction()
- : "UnknownTestFunc";
- const char *tag = QTestResult::currentDataTag() ? QTestResult::currentDataTag() : "";
-
- return tcEscapedString(QString::asprintf("%s(%s)", fn, tag));
+ constexpr int TestTag = QTestPrivate::TestFunction | QTestPrivate::TestDataTag;
+ QTestPrivate::generateTestIdentifier(buf, TestTag);
}
-void QTeamCityLogger::addPendingMessage(const char *type, const QString &msg, const char *file, int line)
+void QTeamCityLogger::addPendingMessage(const char *type, const char *msg,
+ const char *file, int line)
{
- QString pendMessage;
-
- if (!pendingMessages.isEmpty())
- pendMessage += QLatin1String("|n");
-
- if (file) {
- pendMessage += QString(QLatin1String("%1 |[Loc: %2(%3)|]: %4"))
- .arg(QString::fromUtf8(type), QString::fromUtf8(file))
- .arg(line)
- .arg(msg);
+ const char *pad = pendingMessages.isEmpty() ? "" : "|n";
- }
- else {
- pendMessage += QString(QLatin1String("%1: %2"))
- .arg(QString::fromUtf8(type), msg);
- }
+ QTestCharBuffer newMessage;
+ if (file)
+ QTest::qt_asprintf(&newMessage, "%s%s |[Loc: %s(%d)|]: %s", pad, type, file, line, msg);
+ else
+ QTest::qt_asprintf(&newMessage, "%s%s: %s", pad, type, msg);
- pendingMessages.append(pendMessage);
+ QTestPrivate::appendCharBuffer(&pendingMessages, newMessage);
}
QT_END_NAMESPACE
diff --git a/src/testlib/qteamcitylogger_p.h b/src/testlib/qteamcitylogger_p.h
index dd7c0cdcf0..406aa690e5 100644
--- a/src/testlib/qteamcitylogger_p.h
+++ b/src/testlib/qteamcitylogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Borgar Ovsthus
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Borgar Ovsthus
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTEAMCITYLOGGER_P_H
#define QTEAMCITYLOGGER_P_H
@@ -77,13 +41,13 @@ public:
const char *file = nullptr, int line = 0) override;
private:
- QString currTestFuncName;
- QString pendingMessages;
- QString flowID;
+ QTestCharBuffer currTestFuncName;
+ QTestCharBuffer pendingMessages;
+ QTestCharBuffer flowID;
- QString tcEscapedString(const QString &str) const;
- QString escapedTestFuncName() const;
- void addPendingMessage(const char *type, const QString &msg, const char *file, int line);
+ void tcEscapedString(QTestCharBuffer *buf, const char *str) const;
+ void escapedTestFuncName(QTestCharBuffer *buf) const;
+ void addPendingMessage(const char *type, const char *msg, const char *file, int line);
};
QT_END_NAMESPACE
diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h
index cee01e7217..ff3744a48e 100644
--- a/src/testlib/qtest.h
+++ b/src/testlib/qtest.h
@@ -1,77 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Copyright (C) 2020 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2020 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTEST_H
#define QTEST_H
+#if 0
+#pragma qt_class(QTest)
+#endif
+
#include <QtTest/qttestglobal.h>
#include <QtTest/qtestcase.h>
#include <QtTest/qtestdata.h>
+#include <QtTest/qtesttostring.h>
#include <QtTest/qbenchmark.h>
-#include <QtCore/qbitarray.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qcborarray.h>
-#include <QtCore/qcborcommon.h>
-#include <QtCore/qcbormap.h>
-#include <QtCore/qcborvalue.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qcborcommon.h>
-#include <QtCore/qdatetime.h>
-#if QT_CONFIG(itemmodel)
-#include <QtCore/qabstractitemmodel.h>
-#endif
-#include <QtCore/qobject.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qurl.h>
-#include <QtCore/quuid.h>
-
#if defined(TESTCASE_LOWDPI)
#include <QtCore/qcoreapplication.h>
#endif
-#include <QtCore/qpoint.h>
-#include <QtCore/qsize.h>
-#include <QtCore/qrect.h>
-
#include <initializer_list>
#include <memory>
@@ -80,342 +27,15 @@ QT_BEGIN_NAMESPACE
namespace QTest
{
-template <> inline char *toString(const QStringView &str)
-{
- return QTest::toPrettyUnicode(str);
-}
-
-template<> inline char *toString(const QString &str)
-{
- return toString(QStringView(str));
-}
-
-template<> inline char *toString(const QLatin1String &str)
-{
- return toString(QString(str));
-}
-
-template<> inline char *toString(const QByteArray &ba)
-{
- return QTest::toPrettyCString(ba.constData(), ba.length());
-}
-
-template<> inline char *toString(const QBitArray &ba)
-{
- qsizetype size = ba.size();
- char *str = new char[size + 1];
- for (qsizetype i = 0; i < size; ++i)
- str[i] = "01"[ba.testBit(i)];
- str[size] = '\0';
- return str;
-}
-
-#if QT_CONFIG(datestring)
-template<> inline char *toString(const QTime &time)
-{
- return time.isValid()
- ? qstrdup(qPrintable(time.toString(u"hh:mm:ss.zzz")))
- : qstrdup("Invalid QTime");
-}
-
-template<> inline char *toString(const QDate &date)
-{
- return date.isValid()
- ? qstrdup(qPrintable(date.toString(u"yyyy/MM/dd")))
- : qstrdup("Invalid QDate");
-}
-
-template<> inline char *toString(const QDateTime &dateTime)
-{
- return dateTime.isValid()
- ? qstrdup(qPrintable(dateTime.toString(u"yyyy/MM/dd hh:mm:ss.zzz[t]")))
- : qstrdup("Invalid QDateTime");
-}
-#endif // datestring
-
-template<> inline char *toString(const QCborError &c)
-{
- // use the Q_ENUM formatting
- return toString(c.c);
-}
-
-template<> inline char *toString(const QChar &c)
-{
- const ushort uc = c.unicode();
- if (uc < 128) {
- char msg[32] = {'\0'};
- qsnprintf(msg, sizeof(msg), "QChar: '%c' (0x%x)", char(uc), unsigned(uc));
- return qstrdup(msg);
- }
- return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16))));
-}
-
-#if QT_CONFIG(itemmodel)
-template<> inline char *toString(const QModelIndex &idx)
-{
- char msg[128];
- qsnprintf(msg, sizeof(msg), "QModelIndex(%d,%d,%p,%p)", idx.row(), idx.column(), idx.internalPointer(), idx.model());
- return qstrdup(msg);
-}
-#endif
-
-template<> inline char *toString(const QPoint &p)
-{
- char msg[128] = {'\0'};
- qsnprintf(msg, sizeof(msg), "QPoint(%d,%d)", p.x(), p.y());
- return qstrdup(msg);
-}
-
-template<> inline char *toString(const QSize &s)
-{
- char msg[128] = {'\0'};
- qsnprintf(msg, sizeof(msg), "QSize(%dx%d)", s.width(), s.height());
- return qstrdup(msg);
-}
-
-template<> inline char *toString(const QRect &s)
-{
- char msg[256] = {'\0'};
- qsnprintf(msg, sizeof(msg), "QRect(%d,%d %dx%d) (bottomright %d,%d)",
- s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
- return qstrdup(msg);
-}
-
-template<> inline char *toString(const QPointF &p)
-{
- char msg[64] = {'\0'};
- qsnprintf(msg, sizeof(msg), "QPointF(%g,%g)", p.x(), p.y());
- return qstrdup(msg);
-}
-
-template<> inline char *toString(const QSizeF &s)
-{
- char msg[64] = {'\0'};
- qsnprintf(msg, sizeof(msg), "QSizeF(%gx%g)", s.width(), s.height());
- return qstrdup(msg);
-}
-
-template<> inline char *toString(const QRectF &s)
-{
- char msg[256] = {'\0'};
- qsnprintf(msg, sizeof(msg), "QRectF(%g,%g %gx%g) (bottomright %g,%g)",
- s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
- return qstrdup(msg);
-}
-
-template<> inline char *toString(const QUrl &uri)
-{
- if (!uri.isValid())
- return qstrdup(qPrintable(QLatin1String("Invalid URL: ") + uri.errorString()));
- return qstrdup(uri.toEncoded().constData());
-}
-
-template <> inline char *toString(const QUuid &uuid)
-{
- return qstrdup(uuid.toByteArray().constData());
-}
-
-template<> inline char *toString(const QVariant &v)
-{
- QByteArray vstring("QVariant(");
- if (v.isValid()) {
- QByteArray type(v.typeName());
- if (type.isEmpty()) {
- type = QByteArray::number(v.userType());
- }
- vstring.append(type);
- if (!v.isNull()) {
- vstring.append(',');
- if (v.canConvert<QString>()) {
- vstring.append(v.toString().toLocal8Bit());
- }
- else {
- vstring.append("<value not representable as string>");
- }
- }
- }
- vstring.append(')');
-
- return qstrdup(vstring.constData());
-}
-
-namespace Internal {
-struct QCborValueFormatter
-{
- enum { BufferLen = 256 };
- static char *formatSimpleType(QCborSimpleType st)
- {
- char *buf = new char[BufferLen];
- qsnprintf(buf, BufferLen, "QCborValue(QCborSimpleType(%d))", int(st));
- return buf;
- }
-
- static char *formatTag(QCborTag tag, const QCborValue &taggedValue)
- {
- QScopedArrayPointer<char> hold(format(taggedValue));
- char *buf = new char[BufferLen];
- qsnprintf(buf, BufferLen, "QCborValue(QCborTag(%llu), %s)", tag, hold.get());
- return buf;
- }
-
- static char *innerFormat(QCborValue::Type t, const char *str)
- {
- static const QMetaEnum typeEnum = []() {
- int idx = QCborValue::staticMetaObject.indexOfEnumerator("Type");
- return QCborValue::staticMetaObject.enumerator(idx);
- }();
-
- char *buf = new char[BufferLen];
- const char *typeName = typeEnum.valueToKey(t);
- if (typeName)
- qsnprintf(buf, BufferLen, "QCborValue(%s, %s)", typeName, str);
- else
- qsnprintf(buf, BufferLen, "QCborValue(<unknown type 0x%02x>)", t);
- return buf;
- }
-
- template<typename T> static char *format(QCborValue::Type type, const T &t)
- {
- QScopedArrayPointer<char> hold(QTest::toString(t));
- return innerFormat(type, hold.get());
- }
-
- static char *format(const QCborValue &v)
- {
- switch (v.type()) {
- case QCborValue::Integer:
- return format(v.type(), v.toInteger());
- case QCborValue::ByteArray:
- return format(v.type(), v.toByteArray());
- case QCborValue::String:
- return format(v.type(), v.toString());
- case QCborValue::Array:
- return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toArray())).get());
- case QCborValue::Map:
- return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toMap())).get());
- case QCborValue::Tag:
- return formatTag(v.tag(), v.taggedValue());
- case QCborValue::SimpleType:
- break;
- case QCborValue::True:
- return qstrdup("QCborValue(true)");
- case QCborValue::False:
- return qstrdup("QCborValue(false)");
- case QCborValue::Null:
- return qstrdup("QCborValue(nullptr)");
- case QCborValue::Undefined:
- return qstrdup("QCborValue()");
- case QCborValue::Double:
- return format(v.type(), v.toDouble());
- case QCborValue::DateTime:
- case QCborValue::Url:
- case QCborValue::RegularExpression:
- return format(v.type(), v.taggedValue().toString());
- case QCborValue::Uuid:
- return format(v.type(), v.toUuid());
- case QCborValue::Invalid:
- return qstrdup("QCborValue(<invalid>)");
- }
-
- if (v.isSimpleType())
- return formatSimpleType(v.toSimpleType());
- return innerFormat(v.type(), "");
- }
-
- static char *format(const QCborArray &a)
- {
- QByteArray out(1, '[');
- const char *comma = "";
- for (const QCborValueRef v : a) {
- QScopedArrayPointer<char> s(format(v));
- out += comma;
- out += s.get();
- comma = ", ";
- }
- out += ']';
- return qstrdup(out.constData());
- }
-
- static char *format(const QCborMap &m)
- {
- QByteArray out(1, '{');
- const char *comma = "";
- for (auto pair : m) {
- QScopedArrayPointer<char> key(format(pair.first));
- QScopedArrayPointer<char> value(format(pair.second));
- out += comma;
- out += key.get();
- out += ": ";
- out += value.get();
- comma = ", ";
- }
- out += '}';
- return qstrdup(out.constData());
- }
-};
-}
-
-template<> inline char *toString(const QCborValue &v)
-{
- return Internal::QCborValueFormatter::format(v);
-}
-
-template<> inline char *toString(const QCborValueRef &v)
-{
- return toString(QCborValue(v));
-}
-
-template<> inline char *toString(const QCborArray &a)
-{
- return Internal::QCborValueFormatter::format(a);
-}
-
-template<> inline char *toString(const QCborMap &m)
-{
- return Internal::QCborValueFormatter::format(m);
-}
-
-template <typename T1, typename T2>
-inline char *toString(const std::pair<T1, T2> &pair)
-{
- const QScopedArrayPointer<char> first(toString(pair.first));
- const QScopedArrayPointer<char> second(toString(pair.second));
- return formatString("std::pair(", ")", 2, first.data(), second.data());
-}
-
-template <typename Tuple, int... I>
-inline char *toString(const Tuple & tuple, QtPrivate::IndexesList<I...>) {
- using UP = std::unique_ptr<char[]>;
- // Generate a table of N + 1 elements where N is the number of
- // elements in the tuple.
- // The last element is needed to support the empty tuple use case.
- const UP data[] = {
- UP(toString(std::get<I>(tuple)))..., UP{}
- };
- return formatString("std::tuple(", ")", sizeof...(I), data[I].get()...);
-}
-
-template <class... Types>
-inline char *toString(const std::tuple<Types...> &tuple)
-{
- static const std::size_t params_count = sizeof...(Types);
- return toString(tuple, typename QtPrivate::Indexes<params_count>::Value());
-}
-
-inline char *toString(std::nullptr_t)
-{
- return toString(QLatin1String("nullptr"));
-}
-
template<>
-inline bool qCompare(QString const &t1, QLatin1String const &t2, const char *actual,
- const char *expected, const char *file, int line)
+inline bool qCompare(QString const &t1, QLatin1StringView const &t2, const char *actual,
+ const char *expected, const char *file, int line)
{
return qCompare(t1, QString(t2), actual, expected, file, line);
}
template<>
-inline bool qCompare(QLatin1String const &t1, QString const &t2, const char *actual,
- const char *expected, const char *file, int line)
+inline bool qCompare(QLatin1StringView const &t1, QString const &t2, const char *actual,
+ const char *expected, const char *file, int line)
{
return qCompare(QString(t1), t2, actual, expected, file, line);
}
@@ -457,7 +77,7 @@ bool _q_compareSequence(ActualIterator actualIt, ActualIterator actualEnd,
delete [] val2;
}
}
- return compare_helper(isOk, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(isOk, msg, actual, expected, file, line);
}
namespace Internal {
@@ -503,14 +123,16 @@ template <typename T>
inline bool qCompare(QFlags<T> const &t1, T const &t2, const char *actual, const char *expected,
const char *file, int line)
{
- return qCompare(int(t1), int(t2), actual, expected, file, line);
+ using Int = typename QFlags<T>::Int;
+ return qCompare(Int(t1), Int(t2), actual, expected, file, line);
}
template <typename T>
inline bool qCompare(QFlags<T> const &t1, int const &t2, const char *actual, const char *expected,
const char *file, int line)
{
- return qCompare(int(t1), t2, actual, expected, file, line);
+ using Int = typename QFlags<T>::Int;
+ return qCompare(Int(t1), Int(t2), actual, expected, file, line);
}
template<>
@@ -612,14 +234,47 @@ struct QtCoverageScanner
#define TESTLIB_SELFCOVERAGE_START(name)
#endif
-#define QTEST_APPLESS_MAIN(TestObject) \
+#if !defined(QTEST_BATCH_TESTS)
+// Internal (but used by some testlib selftests to hack argc and argv).
+// Tests should normally implement initMain() if they have set-up to do before
+// instantiating the test class.
+#define QTEST_MAIN_WRAPPER(TestObject, ...) \
int main(int argc, char *argv[]) \
{ \
- TESTLIB_SELFCOVERAGE_START(TestObject) \
+ TESTLIB_SELFCOVERAGE_START(#TestObject) \
+ QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
+ __VA_ARGS__ \
TestObject tc; \
QTEST_SET_MAIN_SOURCE_PATH \
return QTest::qExec(&tc, argc, argv); \
}
+#else
+// BATCHED_TEST_NAME is defined for each test in a batch in cmake. Some odd
+// targets, like snippets, don't define it though. Play safe by providing a
+// default value.
+#if !defined(BATCHED_TEST_NAME)
+#define BATCHED_TEST_NAME "other"
+#endif
+#define QTEST_MAIN_WRAPPER(TestObject, ...) \
+\
+void qRegister##TestObject() \
+{ \
+ auto runTest = [](int argc, char** argv) -> int { \
+ TESTLIB_SELFCOVERAGE_START(TestObject) \
+ QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
+ __VA_ARGS__ \
+ TestObject tc; \
+ QTEST_SET_MAIN_SOURCE_PATH \
+ return QTest::qExec(&tc, argc, argv); \
+ }; \
+ QTest::qRegisterTestCase(QStringLiteral(BATCHED_TEST_NAME), runTest); \
+} \
+\
+Q_CONSTRUCTOR_FUNCTION(qRegister##TestObject)
+#endif
+
+// For when you don't even want a QApplication:
+#define QTEST_APPLESS_MAIN(TestObject) QTEST_MAIN_WRAPPER(TestObject)
#include <QtTest/qtestsystem.h>
@@ -627,68 +282,34 @@ int main(int argc, char *argv[]) \
# include <QtTest/qtest_network.h>
#endif
-#if defined(QT_WIDGETS_LIB)
-
-#include <QtTest/qtest_widgets.h>
-
-#ifdef QT_KEYPAD_NAVIGATION
-# define QTEST_DISABLE_KEYPAD_NAVIGATION QApplication::setNavigationMode(Qt::NavigationModeNone);
-#else
-# define QTEST_DISABLE_KEYPAD_NAVIGATION
-#endif
-
-#define QTEST_MAIN_IMPL(TestObject) \
- TESTLIB_SELFCOVERAGE_START(#TestObject) \
- QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
- QApplication app(argc, argv); \
- app.setAttribute(Qt::AA_Use96Dpi, true); \
- QTEST_DISABLE_KEYPAD_NAVIGATION \
- TestObject tc; \
- QTEST_SET_MAIN_SOURCE_PATH \
- return QTest::qExec(&tc, argc, argv);
+// Internal
+#define QTEST_QAPP_SETUP(klaz) \
+ klaz app(argc, argv); \
+ app.setAttribute(Qt::AA_Use96Dpi, true);
+#if defined(QT_WIDGETS_LIB)
+# include <QtTest/qtest_widgets.h>
+# ifdef QT_KEYPAD_NAVIGATION
+# define QTEST_DISABLE_KEYPAD_NAVIGATION QApplication::setNavigationMode(Qt::NavigationModeNone);
+# else
+# define QTEST_DISABLE_KEYPAD_NAVIGATION
+# endif
+// Internal
+# define QTEST_MAIN_SETUP() QTEST_QAPP_SETUP(QApplication) QTEST_DISABLE_KEYPAD_NAVIGATION
#elif defined(QT_GUI_LIB)
-
-#include <QtTest/qtest_gui.h>
-
-#define QTEST_MAIN_IMPL(TestObject) \
- TESTLIB_SELFCOVERAGE_START(#TestObject) \
- QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
- QGuiApplication app(argc, argv); \
- app.setAttribute(Qt::AA_Use96Dpi, true); \
- TestObject tc; \
- QTEST_SET_MAIN_SOURCE_PATH \
- return QTest::qExec(&tc, argc, argv);
-
+# include <QtTest/qtest_gui.h>
+// Internal
+# define QTEST_MAIN_SETUP() QTEST_QAPP_SETUP(QGuiApplication)
#else
-
-#define QTEST_MAIN_IMPL(TestObject) \
- TESTLIB_SELFCOVERAGE_START(#TestObject) \
- QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
- QCoreApplication app(argc, argv); \
- app.setAttribute(Qt::AA_Use96Dpi, true); \
- TestObject tc; \
- QTEST_SET_MAIN_SOURCE_PATH \
- return QTest::qExec(&tc, argc, argv);
-
+// Internal
+# define QTEST_MAIN_SETUP() QTEST_QAPP_SETUP(QCoreApplication)
#endif // QT_GUI_LIB
-#define QTEST_MAIN(TestObject) \
-int main(int argc, char *argv[]) \
-{ \
- QTEST_MAIN_IMPL(TestObject) \
-}
+// For most tests:
+#define QTEST_MAIN(TestObject) QTEST_MAIN_WRAPPER(TestObject, QTEST_MAIN_SETUP())
+// For command-line tests
#define QTEST_GUILESS_MAIN(TestObject) \
-int main(int argc, char *argv[]) \
-{ \
- TESTLIB_SELFCOVERAGE_START(#TestObject) \
- QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
- QCoreApplication app(argc, argv); \
- app.setAttribute(Qt::AA_Use96Dpi, true); \
- TestObject tc; \
- QTEST_SET_MAIN_SOURCE_PATH \
- return QTest::qExec(&tc, argc, argv); \
-}
+ QTEST_MAIN_WRAPPER(TestObject, QTEST_QAPP_SETUP(QCoreApplication))
#endif
diff --git a/src/testlib/qtest_gui.h b/src/testlib/qtest_gui.h
index eeec036437..667debb3c4 100644
--- a/src/testlib/qtest_gui.h
+++ b/src/testlib/qtest_gui.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTEST_GUI_H
#define QTEST_GUI_H
@@ -53,11 +17,15 @@
#include <QtTest/qtestevent.h>
#include <QtTest/qtestmouse.h>
#include <QtTest/qtesttouch.h>
+#include <QtTest/qtestwheel.h>
#include <QtTest/qtestkeyboard.h>
#include <QtGui/qcolor.h>
#include <QtGui/qpixmap.h>
#include <QtGui/qimage.h>
+#if QT_CONFIG(shortcut)
+#include <QtGui/qkeysequence.h>
+#endif
#include <QtGui/qregion.h>
#include <QtGui/qvector2d.h>
#include <QtGui/qvector3d.h>
@@ -113,7 +81,7 @@ template<> inline char *toString(const QRegion &region)
return qstrdup(result.constData());
}
-#if !defined(QT_NO_VECTOR2D) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_VECTOR2D) || defined(Q_QDOC)
template<> inline char *toString(const QVector2D &v)
{
QByteArray result = "QVector2D(" + QByteArray::number(double(v.x())) + ", "
@@ -121,7 +89,7 @@ template<> inline char *toString(const QVector2D &v)
return qstrdup(result.constData());
}
#endif // !QT_NO_VECTOR2D
-#if !defined(QT_NO_VECTOR3D) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_VECTOR3D) || defined(Q_QDOC)
template<> inline char *toString(const QVector3D &v)
{
QByteArray result = "QVector3D(" + QByteArray::number(double(v.x())) + ", "
@@ -129,7 +97,7 @@ template<> inline char *toString(const QVector3D &v)
return qstrdup(result.constData());
}
#endif // !QT_NO_VECTOR3D
-#if !defined(QT_NO_VECTOR4D) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_VECTOR4D) || defined(Q_QDOC)
template<> inline char *toString(const QVector4D &v)
{
QByteArray result = "QVector4D(" + QByteArray::number(double(v.x())) + ", "
@@ -139,6 +107,13 @@ template<> inline char *toString(const QVector4D &v)
}
#endif // !QT_NO_VECTOR4D
+#if QT_CONFIG(shortcut)
+template<> inline char *toString(const QKeySequence &keySequence)
+{
+ return toString(keySequence.toString());
+}
+#endif
+
inline bool qCompare(QIcon const &t1, QIcon const &t2, const char *actual, const char *expected,
const char *file, int line)
{
@@ -158,17 +133,17 @@ inline bool qCompare(QImage const &t1, QImage const &t2,
qsnprintf(msg, 1024, "Compared QImages differ.\n"
" Actual (%s).isNull(): %d\n"
" Expected (%s).isNull(): %d", actual, t1Null, expected, t2Null);
- return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(false, msg, actual, expected, file, line);
}
if (t1Null && t2Null)
- return compare_helper(true, nullptr, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(true, nullptr, actual, expected, file, line);
if (!qFuzzyCompare(t1.devicePixelRatio(), t2.devicePixelRatio())) {
qsnprintf(msg, 1024, "Compared QImages differ in device pixel ratio.\n"
" Actual (%s): %g\n"
" Expected (%s): %g",
actual, t1.devicePixelRatio(),
expected, t2.devicePixelRatio());
- return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(false, msg, actual, expected, file, line);
}
if (t1.width() != t2.width() || t1.height() != t2.height()) {
qsnprintf(msg, 1024, "Compared QImages differ in size.\n"
@@ -176,17 +151,17 @@ inline bool qCompare(QImage const &t1, QImage const &t2,
" Expected (%s): %dx%d",
actual, t1.width(), t1.height(),
expected, t2.width(), t2.height());
- return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(false, msg, actual, expected, file, line);
}
if (t1.format() != t2.format()) {
qsnprintf(msg, 1024, "Compared QImages differ in format.\n"
" Actual (%s): %d\n"
" Expected (%s): %d",
actual, t1.format(), expected, t2.format());
- return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(false, msg, actual, expected, file, line);
}
return compare_helper(t1 == t2, "Compared values are not the same",
- nullptr, nullptr, actual, expected, file, line);
+ actual, expected, file, line);
}
inline bool qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, const char *expected,
@@ -200,17 +175,17 @@ inline bool qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, c
qsnprintf(msg, 1024, "Compared QPixmaps differ.\n"
" Actual (%s).isNull(): %d\n"
" Expected (%s).isNull(): %d", actual, t1Null, expected, t2Null);
- return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(false, msg, actual, expected, file, line);
}
if (t1Null && t2Null)
- return compare_helper(true, nullptr, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(true, nullptr, actual, expected, file, line);
if (!qFuzzyCompare(t1.devicePixelRatio(), t2.devicePixelRatio())) {
qsnprintf(msg, 1024, "Compared QPixmaps differ in device pixel ratio.\n"
" Actual (%s): %g\n"
" Expected (%s): %g",
actual, t1.devicePixelRatio(),
expected, t2.devicePixelRatio());
- return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(false, msg, actual, expected, file, line);
}
if (t1.width() != t2.width() || t1.height() != t2.height()) {
qsnprintf(msg, 1024, "Compared QPixmaps differ in size.\n"
@@ -218,7 +193,7 @@ inline bool qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, c
" Expected (%s): %dx%d",
actual, t1.width(), t1.height(),
expected, t2.width(), t2.height());
- return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line);
+ return compare_helper(false, msg, actual, expected, file, line);
}
return qCompare(t1.toImage(), t2.toImage(), actual, expected, file, line);
}
diff --git a/src/testlib/qtest_network.h b/src/testlib/qtest_network.h
index b417f03a7f..403a663ae2 100644
--- a/src/testlib/qtest_network.h
+++ b/src/testlib/qtest_network.h
@@ -1,46 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTEST_NETWORK_H
#define QTEST_NETWORK_H
-#include <QtTest/qtest.h>
+#include <QtTest/qtesttostring.h>
// enable NETWORK features
#ifndef QT_NETWORK_LIB
@@ -79,6 +43,7 @@ inline char *toString<QHostAddress>(const QHostAddress &addr)
return toString(addr.toString());
}
+} // namespace QTest
inline char *toString(QNetworkReply::NetworkError code)
{
@@ -93,7 +58,7 @@ inline char *toString(QNetworkReply::NetworkError code)
inline char *toString(const QNetworkCookie &cookie)
{
- return toString(cookie.toRawForm());
+ return QTest::toString(cookie.toRawForm());
}
inline char *toString(const QList<QNetworkCookie> &list)
@@ -105,11 +70,9 @@ inline char *toString(const QList<QNetworkCookie> &list)
result.chop(2); // remove trailing ", "
}
result.append(')');
- return toString(result);
+ return QTest::toString(result);
}
-} // namespace QTest
-
QT_END_NAMESPACE
#endif
diff --git a/src/testlib/qtest_widgets.h b/src/testlib/qtest_widgets.h
index f31cbe7dc7..90ee3aa6aa 100644
--- a/src/testlib/qtest_widgets.h
+++ b/src/testlib/qtest_widgets.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTEST_WIDGETS_H
#define QTEST_WIDGETS_H
@@ -79,7 +43,7 @@ inline const char *toString(QSizePolicy::Policy p)
inline QByteArray toString(QSizePolicy::ControlTypes ct)
{
static const QMetaEnum me = QSizePolicy::staticMetaObject.enumerator(QSizePolicy::staticMetaObject.indexOfEnumerator("ControlTypes"));
- return me.valueToKeys(int(ct));
+ return me.valueToKeys(int(ct.toInt()));
}
inline QByteArray toString(QSizePolicy sp)
@@ -98,15 +62,16 @@ inline QByteArray toString(QSizePolicy sp)
}
} // namespace Internal
+} // namespace QTest
inline char *toString(QSizePolicy::Policy p)
{
- return qstrdup(Internal::toString(p));
+ return qstrdup(QTest::Internal::toString(p));
}
inline char *toString(QSizePolicy::ControlTypes ct)
{
- return qstrdup(Internal::toString(ct).constData());
+ return qstrdup(QTest::Internal::toString(ct).constData());
}
inline char *toString(QSizePolicy::ControlType ct)
@@ -116,11 +81,9 @@ inline char *toString(QSizePolicy::ControlType ct)
inline char *toString(QSizePolicy sp)
{
- return qstrdup(Internal::toString(sp).constData());
+ return qstrdup(QTest::Internal::toString(sp).constData());
}
-} // namespace QTest
-
QT_END_NAMESPACE
#endif
diff --git a/src/testlib/qtestaccessible.h b/src/testlib/qtestaccessible.h
index 8fe5fba338..a4236aa5f3 100644
--- a/src/testlib/qtestaccessible.h
+++ b/src/testlib/qtestaccessible.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTACCESSIBLE_H
#define QTESTACCESSIBLE_H
@@ -144,14 +108,14 @@ public:
qWarning("Timeout waiting for accessibility event.");
return false;
}
- const bool res = *eventList().first() == *ev;
+ const bool res = *eventList().constFirst() == *ev;
if (!res)
qWarning("%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
delete eventList().takeFirst();
return res;
}
static bool containsEvent(QAccessibleEvent *event) {
- for (const QAccessibleEvent *ev : qAsConst(eventList())) {
+ for (const QAccessibleEvent *ev : std::as_const(eventList())) {
if (*ev == *event)
return true;
}
@@ -186,8 +150,8 @@ private:
static void updateHandler(QAccessibleEvent *event)
{
auto ev = copyEvent(event);
- if (ev->object()) {
- QObject::connect(ev->object(), &QObject::destroyed, [&, ev](){
+ if (auto obj = ev->object()) {
+ QObject::connect(obj, &QObject::destroyed, obj, [&, ev](){
auto index= eventList().indexOf(ev);
if (index == -1)
return;
diff --git a/src/testlib/qtestassert.h b/src/testlib/qtestassert.h
index 6498ea84ef..63ebbed71c 100644
--- a/src/testlib/qtestassert.h
+++ b/src/testlib/qtestassert.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTASSERT_H
#define QTESTASSERT_H
@@ -44,10 +8,9 @@
QT_BEGIN_NAMESPACE
+#define QTEST_ASSERT(cond) ((cond) ? static_cast<void>(0) : qt_assert(#cond, __FILE__, __LINE__))
-#define QTEST_ASSERT(cond) do { if (!(cond)) qt_assert(#cond,__FILE__,__LINE__); } while (false)
-
-#define QTEST_ASSERT_X(cond, where, what) do { if (!(cond)) qt_assert_x(where, what,__FILE__,__LINE__); } while (false)
+#define QTEST_ASSERT_X(cond, where, what) ((cond) ? static_cast<void>(0) : qt_assert_x(where, what, __FILE__, __LINE__))
QT_END_NAMESPACE
diff --git a/src/testlib/qtestblacklist.cpp b/src/testlib/qtestblacklist.cpp
index d220e9f6eb..4154f8f2a6 100644
--- a/src/testlib/qtestblacklist.cpp
+++ b/src/testlib/qtestblacklist.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qtestblacklist_p.h"
#include "qtestresult_p.h"
@@ -52,6 +16,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*
The BLACKLIST file format is a grouped listing of keywords.
@@ -59,18 +25,29 @@ QT_BEGIN_NAMESPACE
referring to this documentation is kind to readers. Comments can also be used
to indicate the reasons for ignoring particular cases.
- The key "ci" applies only when run by COIN.
- The key "cmake" applies when Qt is built using CMake. Other keys name platforms,
- operating systems, distributions, tool-chains or architectures; a ! prefix
- reverses what it checks. A version, joined to a key (at present, only for
- distributions and for msvc) with a hyphen, limits the key to the specific
- version. A keyword line matches if every key on it applies to the present
- run. Successive lines are alternate conditions for ignoring a test.
-
- Ungrouped lines at the beginning of a file apply to the whole testcase.
- A group starts with a [square-bracketed] identification of a test function,
- optionally with (after a colon, the name of) a specific data set, to ignore.
- Subsequent lines give conditions for ignoring this test.
+ Each blacklist line is interpreted as a list of keywords in an AND-relationship.
+ To blacklist a test for multiple platforms (OR-relationship), use separate lines.
+
+ The key "ci" applies only when run by COIN. Other keys name platforms, operating systems,
+ distributions, tool-chains or architectures; a ! prefix reverses what it
+ checks. A version, joined to a key (at present, only for distributions and
+ for msvc) with a hyphen, limits the key to the specific version. A keyword
+ line matches if every key on it applies to the present run. Successive lines
+ are alternate conditions for ignoring a test.
+
+ Ungrouped lines at the beginning of a file apply to the whole testcase. A
+ group starts with a [square-bracketed] identification of a test function to
+ ignore. For data-driven tests, this identification can be narrowed by the
+ inclusion of global and local data row tags, separated from the function name
+ and each other by colons. If both global and function-specific data rows tags
+ are supplied, the global one comes first (as in the tag reported in test
+ output, albeit in parentheses after the function name). Even when a test does
+ have global and local data tags, you can omit either or both. (If a global
+ data row's name coincides with that of a local data row, some unintended
+ matches may result; try to keep your data-row tags distinct.)
+
+ Subsequent lines give conditions for ignoring this test. You need at least
+ one or the group has no effect.
# See qtbase/src/testlib/qtestblacklist.cpp for format
# Test doesn't work on QNX at all
@@ -83,12 +60,15 @@ QT_BEGIN_NAMESPACE
# Flaky in COIN on macOS, not reproducible by developers
[testSlowly]
- ci osx
+ macos ci
# Needs basic C++11 support
[testfunction2:testData]
msvc-2010
+ [getFile:withProxy SSL:localhost]
+ android
+
QML test functions are identified using the following format:
<TestCase name>::<function name>:<data tag>
@@ -138,12 +118,18 @@ static QSet<QByteArray> keywords()
#ifdef Q_OS_WATCHOS
<< "watchos"
#endif
+#ifdef Q_OS_VISIONOS
+ << "visionos"
+#endif
#ifdef Q_OS_ANDROID
<< "android"
#endif
#ifdef Q_OS_QNX
<< "qnx"
#endif
+#ifdef Q_OS_WEBOS
+ << "webos"
+#endif
#if QT_POINTER_SIZE == 8
<< "64bit"
@@ -169,8 +155,10 @@ static QSet<QByteArray> keywords()
<< "msvc-2015"
# elif _MSC_VER <= 1916
<< "msvc-2017"
-# else
+# elif _MSC_VER <= 1929
<< "msvc-2019"
+# else
+ << "msvc-2022"
# endif
#endif
@@ -184,18 +172,14 @@ static QSet<QByteArray> keywords()
#ifdef QT_BUILD_INTERNAL
<< "developer-build"
#endif
-
- << "cmake"
;
-#if QT_CONFIG(properties)
QCoreApplication *app = QCoreApplication::instance();
if (app) {
const QVariant platformName = app->property("platformName");
if (platformName.isValid())
set << platformName.toByteArray();
}
-#endif
return set;
}
@@ -279,7 +263,7 @@ void parseBlackList()
if (line.isEmpty())
continue;
if (line.startsWith('[')) {
- function = line.mid(1, line.length() - 2);
+ function = line.mid(1, line.size() - 2);
continue;
}
bool condition = checkCondition(line);
@@ -295,21 +279,30 @@ void parseBlackList()
}
}
-void checkBlackLists(const char *slot, const char *data)
+// Returns \c true if this test-case is blacklisted.
+bool checkBlackLists(const char *slot, const char *data, const char *global)
{
bool ignore = ignoreAll;
if (!ignore && ignoredTests) {
QByteArray s = slot;
- ignore = (ignoredTests->find(s) != ignoredTests->end());
+ ignore = ignoredTests->find(s) != ignoredTests->end();
if (!ignore && data) {
- s += ':';
- s += data;
- ignore = (ignoredTests->find(s) != ignoredTests->end());
+ s = (s + ':') + data;
+ ignore = ignoredTests->find(s) != ignoredTests->end();
+ }
+
+ if (!ignore && global) {
+ s = slot + ":"_ba + global;
+ ignore = ignoredTests->find(s) != ignoredTests->end();
+ if (!ignore && data) {
+ s = (s + ':') + data;
+ ignore = ignoredTests->find(s) != ignoredTests->end();
+ }
}
}
- QTestResult::setBlacklistCurrentTest(ignore);
+ return ignore;
}
} // QTestPrivate
diff --git a/src/testlib/qtestblacklist_p.h b/src/testlib/qtestblacklist_p.h
index 4522c64992..3bba0e7672 100644
--- a/src/testlib/qtestblacklist_p.h
+++ b/src/testlib/qtestblacklist_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTBLACKLIST_P_H
#define QTESTBLACKLIST_P_H
@@ -52,13 +16,15 @@
//
#include <QtTest/qttestglobal.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
namespace QTestPrivate {
// Export functions so they can also be used by QQuickTest
Q_TESTLIB_EXPORT void parseBlackList();
- Q_TESTLIB_EXPORT void checkBlackLists(const char *slot, const char *data);
+ Q_TESTLIB_EXPORT bool checkBlackLists(const char *slot, const char *data,
+ const char *global = nullptr);
}
QT_END_NAMESPACE
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp
index 631ca9afb6..2ab55cac36 100644
--- a/src/testlib/qtestcase.cpp
+++ b/src/testlib/qtestcase.cpp
@@ -1,51 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/qtestcase.h>
+#include <QtTest/private/qtestcase_p.h>
#include <QtTest/qtestassert.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
-#include <QtCore/qdiriterator.h>
+#include <QtCore/qdirlisting.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qfloat16.h>
@@ -69,8 +34,12 @@
#include <QtTest/private/qtestresult_p.h>
#include <QtTest/private/qsignaldumper_p.h>
#include <QtTest/private/qbenchmark_p.h>
+#if QT_CONFIG(batch_test_support)
+#include <QtTest/private/qtestregistry_p.h>
+#endif // QT_CONFIG(batch_test_support)
#include <QtTest/private/cycle_p.h>
#include <QtTest/private/qtestblacklist_p.h>
+#include <QtTest/private/qtestcrashhandler_p.h>
#if defined(HAVE_XCTEST)
#include <QtTest/private/qxctestlogger_p.h>
#endif
@@ -82,11 +51,21 @@
#include <QtTest/private/qappletestlogger_p.h>
#endif
-#include <cmath>
-#include <numeric>
#include <algorithm>
-#include <mutex>
+#include <array>
+#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
+# include <charconv>
+#else
+// Broken implementation, causes link failures just by #include'ing!
+# undef __cpp_lib_to_chars // in case <version> was included
+#endif
#include <chrono>
+#include <cmath>
+#include <limits>
+#include <memory>
+#include <mutex>
+#include <numeric>
+#include <optional>
#include <stdarg.h>
#include <stdio.h>
@@ -98,19 +77,37 @@
#endif
#ifdef Q_OS_WIN
+# include <iostream>
# if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
# include <crtdbg.h>
# endif
#include <qt_windows.h> // for Sleep
#endif
#ifdef Q_OS_UNIX
+#include <QtCore/private/qcore_unix_p.h>
+
#include <errno.h>
+#if __has_include(<paths.h>)
+# include <paths.h>
+#endif
#include <signal.h>
#include <time.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
#include <unistd.h>
# if !defined(Q_OS_INTEGRITY)
# include <sys/resource.h>
# endif
+# ifndef _PATH_DEFPATH
+# define _PATH_DEFPATH "/usr/bin:/bin"
+# endif
+# ifndef SIGSTKSZ
+# define SIGSTKSZ 0 /* we have code to set the minimum */
+# endif
+# ifndef SA_RESETHAND
+# define SA_RESETHAND 0
+# endif
#endif
#if defined(Q_OS_MACOS)
@@ -120,134 +117,19 @@
#include <CoreFoundation/CFPreferences.h>
#endif
+#if defined(Q_OS_WASM)
+#include <emscripten.h>
+#endif
+
#include <vector>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
using QtMiscUtils::toHexUpper;
using QtMiscUtils::fromHex;
-static bool debuggerPresent()
-{
-#if defined(Q_OS_LINUX)
- int fd = open("/proc/self/status", O_RDONLY);
- if (fd == -1)
- return false;
- char buffer[2048];
- ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
- if (size == -1) {
- close(fd);
- return false;
- }
- buffer[size] = 0;
- const char tracerPidToken[] = "\nTracerPid:";
- char *tracerPid = strstr(buffer, tracerPidToken);
- if (!tracerPid) {
- close(fd);
- return false;
- }
- tracerPid += sizeof(tracerPidToken);
- long int pid = strtol(tracerPid, &tracerPid, 10);
- close(fd);
- return pid != 0;
-#elif defined(Q_OS_WIN)
- return IsDebuggerPresent();
-#elif defined(Q_OS_MACOS)
- // Check if there is an exception handler for the process:
- mach_msg_type_number_t portCount = 0;
- exception_mask_t masks[EXC_TYPES_COUNT];
- mach_port_t ports[EXC_TYPES_COUNT];
- exception_behavior_t behaviors[EXC_TYPES_COUNT];
- thread_state_flavor_t flavors[EXC_TYPES_COUNT];
- exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
- kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
- ports, behaviors, flavors);
- if (result == KERN_SUCCESS) {
- for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
- if (MACH_PORT_VALID(ports[portIndex])) {
- return true;
- }
- }
- }
- return false;
-#else
- // TODO
- return false;
-#endif
-}
-
-#if !defined(Q_OS_WASM)
-static bool hasSystemCrashReporter()
-{
-#if defined(Q_OS_MACOS)
- return QTestPrivate::macCrashReporterWillShowDialog();
-#else
- return false;
-#endif
-}
-
-static void disableCoreDump()
-{
- bool ok = false;
- const int disableCoreDump = qEnvironmentVariableIntValue("QTEST_DISABLE_CORE_DUMP", &ok);
- if (ok && disableCoreDump) {
-#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
- struct rlimit limit;
- limit.rlim_cur = 0;
- limit.rlim_max = 0;
- if (setrlimit(RLIMIT_CORE, &limit) != 0)
- qWarning("Failed to disable core dumps: %d", errno);
-#endif
- }
-}
-Q_CONSTRUCTOR_FUNCTION(disableCoreDump);
-
-static void stackTrace()
-{
- bool ok = false;
- const int disableStackDump = qEnvironmentVariableIntValue("QTEST_DISABLE_STACK_DUMP", &ok);
- if (ok && disableStackDump)
- return;
-
- if (debuggerPresent() || hasSystemCrashReporter())
- return;
-
-#if defined(Q_OS_LINUX) || (defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM_64))
-
- const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
- const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
- fprintf(stderr, "\n=== Received signal at function time: %dms, total time: %dms, dumping stack ===\n",
- msecsFunctionTime, msecsTotalTime);
-
-# ifdef Q_OS_LINUX
- char cmd[512];
- qsnprintf(cmd, 512, "gdb --pid %d 1>&2 2>/dev/null <<EOF\n"
- "set prompt\n"
- "set height 0\n"
- "thread apply all where full\n"
- "detach\n"
- "quit\n"
- "EOF\n",
- static_cast<int>(getpid()));
- if (system(cmd) == -1)
- fprintf(stderr, "calling gdb failed\n");
- fprintf(stderr, "=== End of stack trace ===\n");
-# elif defined(Q_OS_MACOS)
- char cmd[512];
- qsnprintf(cmd, 512, "lldb -p %d 1>&2 2>/dev/null <<EOF\n"
- "bt all\n"
- "quit\n"
- "EOF\n",
- static_cast<int>(getpid()));
- if (system(cmd) == -1)
- fprintf(stderr, "calling lldb failed\n");
- fprintf(stderr, "=== End of stack trace ===\n");
-# endif
-
-#endif
-}
-#endif // !Q_OS_WASM
-
static bool installCoverageTool(const char * appname, const char * testname)
{
#if defined(__COVERAGESCANNER__) && !QT_CONFIG(testlib_selfcover)
@@ -285,12 +167,197 @@ namespace QTestPrivate
Q_TESTLIB_EXPORT Qt::MouseButtons qtestMouseButtons = Qt::NoButton;
}
+namespace {
+
+class TestFailedException : public std::exception // clazy:exclude=copyable-polymorphic
+{
+public:
+ TestFailedException() = default;
+ ~TestFailedException() override = default;
+
+ const char *what() const noexcept override { return "QtTest: test failed"; }
+};
+
+class TestSkippedException : public std::exception // clazy:exclude=copyable-polymorphic
+{
+public:
+ TestSkippedException() = default;
+ ~TestSkippedException() override = default;
+
+ const char *what() const noexcept override { return "QtTest: test was skipped"; }
+};
+
+} // unnamed namespace
+
namespace QTest
{
+void Internal::throwOnFail() { throw TestFailedException(); }
+void Internal::throwOnSkip() { throw TestSkippedException(); }
+
+Q_CONSTINIT static QBasicAtomicInt g_throwOnFail = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT static QBasicAtomicInt g_throwOnSkip = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+void Internal::maybeThrowOnFail()
+{
+ if (g_throwOnFail.loadRelaxed() > 0)
+ Internal::throwOnFail();
+}
+
+void Internal::maybeThrowOnSkip()
+{
+ if (g_throwOnSkip.loadRelaxed() > 0)
+ Internal::throwOnSkip();
+}
+
+/*!
+ \since 6.8
+ \macro QTEST_THROW_ON_FAIL
+ \relates <QTest>
+
+ When defined, QCOMPARE()/QVERIFY() etc always throw on failure.
+ QTest::throwOnFail() then no longer has any effect.
+*/
+
+/*!
+ \since 6.8
+ \macro QTEST_THROW_ON_SKIP
+ \relates <QTest>
+
+ When defined, QSKIP() always throws. QTest::throwOnSkip() then no longer
+ has any effect.
+*/
+
+/*!
+ \since 6.8
+ \class QTest::ThrowOnFailEnabler
+ \inmodule QtTestLib
+
+ RAII class around setThrowOnFail().
+*/
+/*!
+ \fn QTest::ThrowOnFailEnabler::ThrowOnFailEnabler()
+
+ Constructor. Calls \c{setThrowOnFail(true)}.
+*/
+/*!
+ \fn QTest::ThrowOnFailEnabler::~ThrowOnFailEnabler()
+
+ Destructor. Calls \c{setThrowOnFail(false)}.
+*/
+
+/*!
+ \since 6.8
+ \class QTest::ThrowOnFailDisabler
+ \inmodule QtTestLib
+
+ RAII class around setThrowOnFail().
+*/
+/*!
+ \fn QTest::ThrowOnFailDisabler::ThrowOnFailDisabler()
+
+ Constructor. Calls \c{setThrowOnFail(false)}.
+*/
+/*!
+ \fn QTest::ThrowOnFailDisabler::~ThrowOnFailDisabler()
+
+ Destructor. Calls \c{setThrowOnFail(true)}.
+*/
+
+/*!
+ \since 6.8
+ \class QTest::ThrowOnSkipEnabler
+ \inmodule QtTestLib
+
+ RAII class around setThrowOnSkip().
+*/
+/*!
+ \fn QTest::ThrowOnSkipEnabler::ThrowOnSkipEnabler()
+
+ Constructor. Calls \c{setThrowOnSkip(true)}.
+*/
+/*!
+ \fn QTest::ThrowOnSkipEnabler::~ThrowOnSkipEnabler()
+
+ Destructor. Calls \c{setThrowOnSkip(false)}.
+*/
+
+/*!
+ \since 6.8
+ \class QTest::ThrowOnSkipDisabler
+ \inmodule QtTestLib
+
+ RAII class around setThrowOnSkip().
+*/
+/*!
+ \fn QTest::ThrowOnSkipDisabler::ThrowOnSkipDisabler()
+
+ Constructor. Calls \c{setThrowOnSkip(false)}.
+*/
+/*!
+ \fn QTest::ThrowOnSkipDisabler::~ThrowOnSkipDisabler()
+
+ Destructor. Calls \c{setThrowOnSkip(true)}.
+*/
+
+/*!
+ \since 6.8
+
+ Enables (\a enable = \c true) or disables (\ enable = \c false) throwing on
+ QCOMPARE()/QVERIFY() failures (as opposed to just returning from the
+ immediately-surrounding function context).
+
+ The feature is reference-counted: If you call this function \e{N} times
+ with \c{true}, you need to call it \e{N} times with \c{false} to get back
+ to where you started.
+
+ The default is \c{false}, unless the \l{Qt Test Environment Variables}
+ {QTEST_THROW_ON_FAIL environment variable} is set.
+
+ This call has no effect when the \l{QTEST_THROW_ON_FAIL} C++ macro is
+ defined.
+
+ \note You must compile your tests with exceptions enabled to use this
+ feature.
+
+ \sa setThrowOnSkip(), ThrowOnFailEnabler, ThrowOnFailDisabler, QTEST_THROW_ON_FAIL
+*/
+void setThrowOnFail(bool enable) noexcept
+{
+ g_throwOnFail.fetchAndAddRelaxed(enable ? 1 : -1);
+}
+
+/*!
+ \since 6.8
+
+ Enables (\a enable = \c true) or disables (\ enable = \c false) throwing on
+ QSKIP() (as opposed to just returning from the immediately-surrounding
+ function context).
+
+ The feature is reference-counted: If you call this function \e{N} times
+ with \c{true}, you need to call it \e{N} times with \c{false} to get back
+ to where you started.
+
+ The default is \c{false}, unless the \l{Qt Test Environment Variables}
+ {QTEST_THROW_ON_SKIP environment variable} is set.
+
+ This call has no effect when the \l{QTEST_THROW_ON_SKIP} C++ macro is
+ defined.
+
+ \note You must compile your tests with exceptions enabled to use this
+ feature.
+
+ \sa setThrowOnFail(), ThrowOnSkipEnabler, ThrowOnSkipDisabler, QTEST_THROW_ON_SKIP
+*/
+void setThrowOnSkip(bool enable) noexcept
+{
+ g_throwOnSkip.fetchAndAddRelaxed(enable ? 1 : -1);
+}
+
QString Internal::formatTryTimeoutDebugMessage(q_no_char8_t::QUtf8StringView expr, int timeout, int actual)
{
- return QLatin1String("QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) was too short, %3 ms would have been sufficient this time.")
+ return "QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) "
+ "was too short, %3 ms would have been sufficient this time."_L1
// ### Qt 7: remove the toString() (or earlier, when arg() can handle QUtf8StringView), passing the view directly
.arg(expr.toString(), QString::number(timeout), QString::number(actual));
}
@@ -301,6 +368,7 @@ class WatchDog;
static QObject *currentTestObject = nullptr;
static QString mainSourcePath;
+static bool inTestFunction = false;
#if defined(Q_OS_MACOS)
static IOPMAssertionID macPowerSavingDisabled = 0;
@@ -319,7 +387,7 @@ public:
static QMetaMethod findMethod(const QObject *obj, const char *signature);
private:
- bool invokeTest(int index, const char *data, WatchDog *watchDog) const;
+ bool invokeTest(int index, QLatin1StringView tag, std::optional<WatchDog> &watchDog) const;
void invokeTestOnData(int index) const;
QMetaMethod m_initTestCaseMethod; // might not exist, check isValid().
@@ -364,19 +432,32 @@ static int eventDelay = -1;
#if QT_CONFIG(thread)
static int timeout = -1;
#endif
-static bool noCrashHandler = false;
+static int repetitions = 1;
+static bool repeatForever = false;
+static bool skipBlacklisted = false;
-/*! \internal
- Invoke a method of the object without generating warning if the method does not exist
- */
-static void invokeMethod(QObject *obj, const char *methodName)
+namespace Internal {
+bool noCrashHandler = false;
+}
+
+static bool invokeTestMethodIfValid(QMetaMethod m, QObject *obj = QTest::currentTestObject)
+{
+ if (!m.isValid())
+ return false;
+ bool ok = true;
+ try { ok = m.invoke(obj, Qt ::DirectConnection); }
+ catch (const TestFailedException &) {} // ignore (used for control flow)
+ catch (const TestSkippedException &) {} // ditto
+ // every other exception is someone else's problem
+ return ok;
+}
+
+static void invokeTestMethodIfExists(const char *methodName, QObject *obj = QTest::currentTestObject)
{
const QMetaObject *metaObject = obj->metaObject();
int funcIndex = metaObject->indexOfMethod(methodName);
- if (funcIndex >= 0) {
- QMetaMethod method = metaObject->method(funcIndex);
- method.invoke(obj, Qt::DirectConnection);
- }
+ // doesn't generate a warning if it doesn't exist:
+ invokeTestMethodIfValid(metaObject->method(funcIndex), obj);
}
int defaultEventDelay()
@@ -432,16 +513,25 @@ Q_TESTLIB_EXPORT bool printAvailableFunctions = false;
Q_TESTLIB_EXPORT QStringList testFunctions;
Q_TESTLIB_EXPORT QStringList testTags;
-static void qPrintTestSlots(FILE *stream, const char *filter = nullptr)
+static bool qPrintTestSlots(FILE *stream, const char *filter = nullptr, const char *preamble = "")
{
+ const auto matches = [filter](const QByteArray &s) {
+ return !filter || QLatin1StringView(s).contains(QLatin1StringView(filter),
+ Qt::CaseInsensitive);
+ };
+ bool matched = false;
for (int i = 0; i < QTest::currentTestObject->metaObject()->methodCount(); ++i) {
QMetaMethod sl = QTest::currentTestObject->metaObject()->method(i);
if (isValidSlot(sl)) {
const QByteArray signature = sl.methodSignature();
- if (!filter || QString::fromLatin1(signature).contains(QLatin1String(filter), Qt::CaseInsensitive))
- fprintf(stream, "%s\n", signature.constData());
+ if (matches(signature)) {
+ fprintf(stream, "%s%s\n", preamble, signature.constData());
+ preamble = "";
+ matched = true;
+ }
}
}
+ return matched;
}
static void qPrintDataTags(FILE *stream)
@@ -451,7 +541,7 @@ static void qPrintDataTags(FILE *stream)
// Get global data tags:
QTestTable::globalTestTable();
- invokeMethod(QTest::currentTestObject, "initTestCase_data()");
+ invokeTestMethodIfExists("initTestCase_data()");
const QTestTable *gTable = QTestTable::globalTestTable();
const QMetaObject *currTestMetaObj = QTest::currentTestObject->metaObject();
@@ -470,15 +560,15 @@ static void qPrintDataTags(FILE *stream)
QByteArray member;
member.resize(qstrlen(slot) + qstrlen("_data()") + 1);
qsnprintf(member.data(), member.size(), "%s_data()", slot);
- invokeMethod(QTest::currentTestObject, member.constData());
+ invokeTestMethodIfExists(member.constData());
const int dataCount = table.dataCount();
localTags.reserve(dataCount);
for (int j = 0; j < dataCount; ++j)
- localTags << QLatin1String(table.testData(j)->dataTag());
+ localTags << QLatin1StringView(table.testData(j)->dataTag());
// Print all tag combinations:
if (gTable->dataCount() == 0) {
- if (localTags.count() == 0) {
+ if (localTags.size() == 0) {
// No tags at all, so just print the test function:
fprintf(stream, "%s %s\n", currTestMetaObj->className(), slot);
} else {
@@ -490,7 +580,7 @@ static void qPrintDataTags(FILE *stream)
}
} else {
for (int j = 0; j < gTable->dataCount(); ++j) {
- if (localTags.count() == 0) {
+ if (localTags.size() == 0) {
// Only global tags, so print the current one:
fprintf(
stream, "%s %s __global__ %s\n",
@@ -527,10 +617,18 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
int logFormat = -1; // Not set
const char *logFilename = nullptr;
+ repetitions = 1;
+ repeatForever = false;
+
QTest::testFunctions.clear();
QTest::testTags.clear();
-#if defined(Q_OS_MAC) && defined(HAVE_XCTEST)
+ if (qEnvironmentVariableIsSet("QTEST_THROW_ON_FAIL"))
+ QTest::setThrowOnFail(true);
+ if (qEnvironmentVariableIsSet("QTEST_THROW_ON_SKIP"))
+ QTest::setThrowOnSkip(true);
+
+#if defined(Q_OS_DARWIN) && defined(HAVE_XCTEST)
if (QXcodeTestLogger::canLogTestProgress())
logFormat = QTestLog::XCTest;
#endif
@@ -581,6 +679,15 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
" -maxwarnings n : Sets the maximum amount of messages to output.\n"
" 0 means unlimited, default: 2000\n"
" -nocrashhandler : Disables the crash handler. Useful for debugging crashes.\n"
+ " -repeat n : Run the testsuite n times or until the test fails.\n"
+ " Useful for finding flaky tests. If negative, the tests are\n"
+ " repeated forever. This is intended as a developer tool, and\n"
+ " is only supported with the plain text logger.\n"
+ " -skipblacklisted : Skip blacklisted tests. Useful for measuring test coverage.\n"
+ " -[no]throwonfail : Enables/disables throwing on QCOMPARE()/QVERIFY()/etc.\n"
+ " Default: off, unless QTEST_THROW_ON_FAIL is set."
+ " -[no]throwonskip : Enables/disables throwing on QSKIP().\n"
+ " Default: off, unless QTEST_THROW_ON_SKIP is set."
"\n"
" Benchmarking options:\n"
#if QT_CONFIG(valgrind)
@@ -609,14 +716,14 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
"%s", argv[0], testOptions);
if (qml) {
- printf ("\n"
- " QmlTest options:\n"
- " -import dir : Specify an import directory.\n"
- " -plugins dir : Specify a directory where to search for plugins.\n"
- " -input dir/file : Specify the root directory for test cases or a single test case file.\n"
- " -translation file : Specify the translation file.\n"
- " -file-selector dir : Specify a file selector for the QML engine.\n"
- );
+ printf("\n"
+ " QmlTest options:\n"
+ " -import dir : Specify an import directory.\n"
+ " -plugins dir : Specify a directory where to search for plugins.\n"
+ " -input dir/file : Specify the root directory for test cases or a single test case file.\n"
+ " -translation file : Specify the translation file.\n"
+ " -file-selector dir : Specify a file selector for the QML engine.\n"
+ );
}
printf("\n"
@@ -638,7 +745,10 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
logFormat = QTestLog::Plain;
} else if (strcmp(argv[i], "-csv") == 0) {
logFormat = QTestLog::CSV;
- } else if (strcmp(argv[i], "-junitxml") == 0 || strcmp(argv[i], "-xunitxml") == 0) {
+ } else if (strcmp(argv[i], "-junitxml") == 0) {
+ logFormat = QTestLog::JUnitXML;
+ } else if (strcmp(argv[i], "-xunitxml") == 0) {
+ fprintf(stderr, "WARNING: xunitxml is deprecated. Please use junitxml.\n");
logFormat = QTestLog::JUnitXML;
} else if (strcmp(argv[i], "-xml") == 0) {
logFormat = QTestLog::XML;
@@ -678,9 +788,12 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
logFormat = QTestLog::LightXML;
else if (strcmp(format, "xml") == 0)
logFormat = QTestLog::XML;
- else if (strcmp(format, "junitxml") == 0 || strcmp(format, "xunitxml") == 0)
+ else if (strcmp(format, "junitxml") == 0)
logFormat = QTestLog::JUnitXML;
- else if (strcmp(format, "teamcity") == 0)
+ else if (strcmp(format, "xunitxml") == 0) {
+ fprintf(stderr, "WARNING: xunitxml is deprecated. Please use junitxml.\n");
+ logFormat = QTestLog::JUnitXML;
+ } else if (strcmp(format, "teamcity") == 0)
logFormat = QTestLog::TeamCity;
else if (strcmp(format, "tap") == 0)
logFormat = QTestLog::TAP;
@@ -724,19 +837,40 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
} else {
QTestLog::setMaxWarnings(qToInt(argv[++i]));
}
+ } else if (strcmp(argv[i], "-repeat") == 0) {
+ if (i + 1 >= argc) {
+ fprintf(stderr, "-repeat needs an extra parameter for the number of repetitions\n");
+ exit(1);
+ } else {
+ repetitions = qToInt(argv[++i]);
+ repeatForever = repetitions < 0;
+ }
} else if (strcmp(argv[i], "-nocrashhandler") == 0) {
- QTest::noCrashHandler = true;
+ QTest::Internal::noCrashHandler = true;
+ } else if (strcmp(argv[i], "-skipblacklisted") == 0) {
+ QTest::skipBlacklisted = true;
+ } else if (strcmp(argv[i], "-throwonfail") == 0) {
+ QTest::setThrowOnFail(true);
+ } else if (strcmp(argv[i], "-nothrowonfail") == 0) {
+ QTest::setThrowOnFail(false);
+ } else if (strcmp(argv[i], "-throwonskip") == 0) {
+ QTest::setThrowOnSkip(true);
+ } else if (strcmp(argv[i], "-nothrowonskip") == 0) {
+ QTest::setThrowOnSkip(false);
#if QT_CONFIG(valgrind)
} else if (strcmp(argv[i], "-callgrind") == 0) {
- if (QBenchmarkValgrindUtils::haveValgrind())
- if (QFileInfo(QDir::currentPath()).isWritable()) {
- QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::CallgrindParentProcess);
- } else {
- fprintf(stderr, "WARNING: Current directory not writable. Using the walltime measurer.\n");
- }
- else {
- fprintf(stderr, "WARNING: Valgrind not found or too old. Make sure it is installed and in your path. "
- "Using the walltime measurer.\n");
+ if (!QBenchmarkValgrindUtils::haveValgrind()) {
+ fprintf(stderr,
+ "WARNING: Valgrind not found or too old. "
+ "Make sure it is installed and in your path. "
+ "Using the walltime measurer.\n");
+ } else if (QFileInfo(QDir::currentPath()).isWritable()) {
+ QBenchmarkGlobalData::current->setMode(
+ QBenchmarkGlobalData::CallgrindParentProcess);
+ } else {
+ fprintf(stderr,
+ "WARNING: Current directory not writable. "
+ "Using the walltime measurer.\n");
}
} else if (strcmp(argv[i], "-callgrindchild") == 0) { // "private" option
QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::CallgrindChildProcess);
@@ -857,23 +991,28 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool
// If no loggers were created by the long version of the -o command-line
// option, but a logger was requested via the old-style option, add it.
const bool explicitLoggerRequested = logFormat != -1;
- if (QTestLog::loggerCount() == 0 && explicitLoggerRequested)
+ if (!QTestLog::hasLoggers() && explicitLoggerRequested)
QTestLog::addLogger(QTestLog::LogMode(logFormat), logFilename);
bool addFallbackLogger = !explicitLoggerRequested;
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
// Any explicitly requested loggers will be added by now, so we can check if they use stdout
- const bool safeToAddAppleLogger = !AppleUnifiedLogger::willMirrorToStderr() || !QTestLog::loggerUsingStdout();
+ const bool safeToAddAppleLogger = !AppleUnifiedLogger::preventsStderrLogging() || !QTestLog::loggerUsingStdout();
if (safeToAddAppleLogger && QAppleTestLogger::debugLoggingEnabled()) {
QTestLog::addLogger(QTestLog::Apple, nullptr);
- if (AppleUnifiedLogger::willMirrorToStderr() && !logFilename)
+ if (AppleUnifiedLogger::preventsStderrLogging() && !logFilename)
addFallbackLogger = false; // Prevent plain test logger fallback below
}
#endif
if (addFallbackLogger)
QTestLog::addLogger(QTestLog::Plain, logFilename);
+
+ if (repetitions != 1 && !QTestLog::isRepeatSupported()) {
+ fprintf(stderr, "-repeat is only supported with plain text logger\n");
+ exit(1);
+ }
}
// Temporary, backwards compatibility, until qtdeclarative's use of it is converted
@@ -881,21 +1020,24 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) {
qtest_qParseArgs(argc, const_cast<const char *const *>(argv), qml);
}
-QBenchmarkResult qMedian(const QList<QBenchmarkResult> &container)
+static QList<QBenchmarkResult> qMedian(const QList<QList<QBenchmarkResult>> &container)
{
- const int count = container.count();
+ const int count = container.size();
if (count == 0)
- return QBenchmarkResult();
+ return {};
if (count == 1)
return container.front();
- QList<QBenchmarkResult> containerCopy = container;
- std::sort(containerCopy.begin(), containerCopy.end());
+ QList<QList<QBenchmarkResult>> containerCopy = container;
+ std::sort(containerCopy.begin(), containerCopy.end(),
+ [](const QList<QBenchmarkResult> &a, const QList<QBenchmarkResult> &b) {
+ return a.first() < b.first();
+ });
const int middle = count / 2;
- // ### handle even-sized containers here by doing an aritmetic mean of the two middle items.
+ // ### handle even-sized containers here by doing an arithmetic mean of the two middle items.
return containerCopy.at(middle);
}
@@ -911,15 +1053,6 @@ struct QTestDataSetter
}
};
-namespace {
-
-qreal addResult(qreal current, const QBenchmarkResult& r)
-{
- return current + r.value;
-}
-
-}
-
void TestMethods::invokeTestOnData(int index) const
{
/* Benchmarking: for each median iteration*/
@@ -927,43 +1060,51 @@ void TestMethods::invokeTestOnData(int index) const
bool isBenchmark = false;
int i = (QBenchmarkGlobalData::current->measurer->needsWarmupIteration()) ? -1 : 0;
- QList<QBenchmarkResult> results;
+ QList<QList<QBenchmarkResult>> resultsList;
bool minimumTotalReached = false;
do {
QBenchmarkTestMethodData::current->beginDataRun();
+ if (i < 0)
+ QBenchmarkTestMethodData::current->iterationCount = 1;
/* Benchmarking: for each accumulation iteration*/
bool invokeOk;
do {
- if (m_initMethod.isValid())
- m_initMethod.invoke(QTest::currentTestObject, Qt::DirectConnection);
- if (QTestResult::skipCurrentTest() || QTestResult::currentTestFailed())
- break;
+ QTest::inTestFunction = true;
+ invokeTestMethodIfValid(m_initMethod);
- QBenchmarkTestMethodData::current->result = QBenchmarkResult();
- QBenchmarkTestMethodData::current->resultAccepted = false;
+ const bool initQuit =
+ QTestResult::skipCurrentTest() || QTestResult::currentTestFailed();
+ if (!initQuit) {
+ QBenchmarkTestMethodData::current->results.clear();
+ QBenchmarkTestMethodData::current->resultAccepted = false;
+ QBenchmarkTestMethodData::current->valid = false;
- QBenchmarkGlobalData::current->context.tag =
- QLatin1String(
- QTestResult::currentDataTag()
- ? QTestResult::currentDataTag() : "");
+ QBenchmarkGlobalData::current->context.tag = QLatin1StringView(
+ QTestResult::currentDataTag() ? QTestResult::currentDataTag() : "");
- invokeOk = m_methods[index].invoke(QTest::currentTestObject, Qt::DirectConnection);
- if (!invokeOk)
- QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__);
+ invokeOk = invokeTestMethodIfValid(m_methods[index]);
+ if (!invokeOk)
+ QTestResult::addFailure("Unable to execute slot", __FILE__, __LINE__);
- isBenchmark = QBenchmarkTestMethodData::current->isBenchmark();
+ isBenchmark = QBenchmarkTestMethodData::current->isBenchmark();
+ } else {
+ invokeOk = false;
+ }
+ QTest::inTestFunction = false;
QTestResult::finishedCurrentTestData();
- if (m_cleanupMethod.isValid())
- m_cleanupMethod.invoke(QTest::currentTestObject, Qt::DirectConnection);
+ if (!initQuit) {
+ invokeTestMethodIfValid(m_cleanupMethod);
- // Process any deleteLater(), like event-loop based apps would do. Fixes memleak reports.
- if (QCoreApplication::instance())
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
-
- // If the test isn't a benchmark, finalize the result after cleanup() has finished.
+ // Process any deleteLater(), used by event-loop-based apps.
+ // Fixes memleak reports.
+ if (QCoreApplication::instance())
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ }
+ // If the test isn't a benchmark, finalize the result after
+ // cleanup() has finished (or init has lead us to skip the test).
if (!isBenchmark)
QTestResult::finishedCurrentTestDataCleanup();
@@ -978,26 +1119,29 @@ void TestMethods::invokeTestOnData(int index) const
QBenchmarkTestMethodData::current->endDataRun();
if (!QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed()) {
if (i > -1) // iteration -1 is the warmup iteration.
- results.append(QBenchmarkTestMethodData::current->result);
-
- if (isBenchmark && QBenchmarkGlobalData::current->verboseOutput) {
- if (i == -1) {
- QTestLog::info(qPrintable(
- QString::fromLatin1("warmup stage result : %1")
- .arg(QBenchmarkTestMethodData::current->result.value)), nullptr, 0);
- } else {
- QTestLog::info(qPrintable(
- QString::fromLatin1("accumulation stage result: %1")
- .arg(QBenchmarkTestMethodData::current->result.value)), nullptr, 0);
- }
+ resultsList.append(QBenchmarkTestMethodData::current->results);
+
+ if (isBenchmark && QBenchmarkGlobalData::current->verboseOutput &&
+ !QBenchmarkTestMethodData::current->results.isEmpty()) {
+ // we only print the first result
+ const QBenchmarkResult &first = QBenchmarkTestMethodData::current->results.constFirst();
+ QString pattern = i < 0 ? "warmup stage result : %1"_L1
+ : "accumulation stage result: %1"_L1;
+ QTestLog::info(qPrintable(pattern.arg(first.measurement.value)), nullptr, 0);
}
}
- // Verify if the minimum total measurement is reached, if it was specified:
+ // Verify if the minimum total measurement (for the first measurement)
+ // was reached, if it was specified:
if (QBenchmarkGlobalData::current->minimumTotal == -1) {
minimumTotalReached = true;
} else {
- const qreal total = std::accumulate(results.begin(), results.end(), 0.0, addResult);
+ auto addResult = [](qreal current, const QList<QBenchmarkResult> &r) {
+ if (!r.isEmpty())
+ current += r.first().measurement.value;
+ return current;
+ };
+ const qreal total = std::accumulate(resultsList.begin(), resultsList.end(), 0.0, addResult);
minimumTotalReached = (total >= QBenchmarkGlobalData::current->minimumTotal);
}
} while (isBenchmark
@@ -1010,7 +1154,7 @@ void TestMethods::invokeTestOnData(int index) const
QTestResult::finishedCurrentTestDataCleanup();
// Only report benchmark figures if the test passed
if (testPassed && QBenchmarkTestMethodData::current->resultsAccepted())
- QTestLog::addBenchmarkResult(qMedian(results));
+ QTestLog::addBenchmarkResults(qMedian(resultsList));
}
}
@@ -1018,17 +1162,30 @@ void TestMethods::invokeTestOnData(int index) const
class WatchDog : public QThread
{
- enum Expectation {
+ enum Expectation : std::size_t {
+ // bits 0..1: state
ThreadStart,
TestFunctionStart,
TestFunctionEnd,
ThreadEnd,
- };
- bool waitFor(std::unique_lock<QtPrivate::mutex> &m, Expectation e)
+ // bits 2..: generation
+ };
+ static constexpr auto ExpectationMask = Expectation{ThreadStart | TestFunctionStart | TestFunctionEnd | ThreadEnd};
+ static_assert(size_t(ExpectationMask) == 0x3);
+ static constexpr size_t GenerationShift = 2;
+
+ static constexpr Expectation state(Expectation e) noexcept
+ { return Expectation{e & ExpectationMask}; }
+ static constexpr size_t generation(Expectation e) noexcept
+ { return e >> GenerationShift; }
+ static constexpr Expectation combine(Expectation e, size_t gen) noexcept
+ { return Expectation{e | (gen << GenerationShift)}; }
+
+ bool waitFor(std::unique_lock<std::mutex> &m, Expectation e)
{
auto expectationChanged = [this, e] { return expecting.load(std::memory_order_relaxed) != e; };
- switch (e) {
+ switch (state(e)) {
case TestFunctionEnd:
return waitCondition.wait_for(m, defaultTimeout(), expectationChanged);
case ThreadStart:
@@ -1037,14 +1194,26 @@ class WatchDog : public QThread
waitCondition.wait(m, expectationChanged);
return true;
}
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
+ }
+
+ void setExpectation(Expectation e)
+ {
+ Q_ASSERT(generation(e) == 0); // no embedded generation allowed
+ const auto locker = qt_scoped_lock(mutex);
+ auto cur = expecting.load(std::memory_order_relaxed);
+ auto gen = generation(cur);
+ if (e == TestFunctionStart)
+ ++gen;
+ e = combine(e, gen);
+ expecting.store(e, std::memory_order_relaxed);
+ waitCondition.notify_all();
}
public:
WatchDog()
{
- setObjectName(QLatin1String("QtTest Watchdog"));
+ setObjectName("QtTest Watchdog"_L1);
auto locker = qt_unique_lock(mutex);
expecting.store(ThreadStart, std::memory_order_relaxed);
start();
@@ -1053,36 +1222,29 @@ public:
~WatchDog()
{
- {
- const auto locker = qt_scoped_lock(mutex);
- expecting.store(ThreadEnd, std::memory_order_relaxed);
- waitCondition.notify_all();
- }
+ setExpectation(ThreadEnd);
wait();
}
void beginTest()
{
- const auto locker = qt_scoped_lock(mutex);
- expecting.store(TestFunctionEnd, std::memory_order_relaxed);
- waitCondition.notify_all();
+ setExpectation(TestFunctionEnd);
}
void testFinished()
{
- const auto locker = qt_scoped_lock(mutex);
- expecting.store(TestFunctionStart, std::memory_order_relaxed);
- waitCondition.notify_all();
+ setExpectation(TestFunctionStart);
}
void run() override
{
+ CrashHandler::blockUnixSignals();
auto locker = qt_unique_lock(mutex);
expecting.store(TestFunctionStart, std::memory_order_release);
waitCondition.notify_all();
while (true) {
Expectation e = expecting.load(std::memory_order_acquire);
- switch (e) {
+ switch (state(e)) {
case ThreadEnd:
return;
case ThreadStart:
@@ -1090,9 +1252,9 @@ public:
case TestFunctionStart:
case TestFunctionEnd:
if (Q_UNLIKELY(!waitFor(locker, e))) {
-#ifndef Q_OS_WASM
- stackTrace();
-#endif
+ fflush(stderr);
+ CrashHandler::printTestRunTime();
+ CrashHandler::generateStackTrace();
qFatal("Test function timed out");
}
}
@@ -1100,8 +1262,8 @@ public:
}
private:
- QtPrivate::mutex mutex;
- QtPrivate::condition_variable waitCondition;
+ std::mutex mutex;
+ std::condition_variable waitCondition;
std::atomic<Expectation> expecting;
};
@@ -1114,8 +1276,28 @@ public:
void testFinished() {};
};
-#endif
+#endif // QT_CONFIG(thread)
+
+static void printUnknownDataTagError(QLatin1StringView name, QLatin1StringView tag,
+ const QTestTable &lTable, const QTestTable &gTable)
+{
+ fprintf(stderr, "Unknown testdata for function %s(): '%s'\n", name.constData(), tag.data());
+ const int localDataCount = lTable.dataCount();
+ if (localDataCount) {
+ fputs("Available test-specific data tags:\n", stderr);
+ for (int i = 0; i < localDataCount; ++i)
+ fprintf(stderr, "\t%s\n", lTable.testData(i)->dataTag());
+ }
+ const int globalDataCount = gTable.dataCount();
+ if (globalDataCount) {
+ fputs("Available global data tags:\n", stderr);
+ for (int i = 0; i < globalDataCount; ++i)
+ fprintf(stderr, "\t%s\n", gTable.testData(i)->dataTag());
+ }
+ if (localDataCount == 0 && globalDataCount == 0)
+ fputs("Function has no data tags\n", stderr);
+}
/*!
\internal
@@ -1125,14 +1307,14 @@ public:
If the function was successfully called, true is returned, otherwise
false.
- */
-bool TestMethods::invokeTest(int index, const char *data, WatchDog *watchDog) const
+*/
+bool TestMethods::invokeTest(int index, QLatin1StringView tag, std::optional<WatchDog> &watchDog) const
{
QBenchmarkTestMethodData benchmarkData;
QBenchmarkTestMethodData::current = &benchmarkData;
const QByteArray &name = m_methods[index].name();
- QBenchmarkGlobalData::current->context.slotName = QLatin1String(name) + QLatin1String("()");
+ QBenchmarkGlobalData::current->context.slotName = QLatin1StringView(name) + "()"_L1;
char member[512];
QTestTable table;
@@ -1142,6 +1324,23 @@ bool TestMethods::invokeTest(int index, const char *data, WatchDog *watchDog) co
const QTestTable *gTable = QTestTable::globalTestTable();
const int globalDataCount = gTable->dataCount();
int curGlobalDataIndex = 0;
+ const auto globalDataTag = [gTable, globalDataCount](int index) {
+ return globalDataCount ? gTable->testData(index)->dataTag() : nullptr;
+ };
+
+ const auto dataTagMatches = [](QLatin1StringView tag, QLatin1StringView local,
+ QLatin1StringView global) {
+ if (tag.isEmpty()) // No tag specified => run all data sets for this function
+ return true;
+ if (tag == local || tag == global) // Equal to either => run it
+ return true;
+ // Also allow global:local as a match:
+ return tag.startsWith(global) && tag.endsWith(local) &&
+ tag.size() == global.size() + 1 + local.size() &&
+ tag[global.size()] == ':';
+ };
+ bool foundFunction = false;
+ bool blacklisted = false;
/* For each entry in the global data table, do: */
do {
@@ -1150,68 +1349,64 @@ bool TestMethods::invokeTest(int index, const char *data, WatchDog *watchDog) co
if (curGlobalDataIndex == 0) {
qsnprintf(member, 512, "%s_data()", name.constData());
- invokeMethod(QTest::currentTestObject, member);
+ invokeTestMethodIfExists(member);
if (QTestResult::skipCurrentTest())
break;
}
- bool foundFunction = false;
int curDataIndex = 0;
const int dataCount = table.dataCount();
-
- // Data tag requested but none available?
- if (data && !dataCount) {
- // Let empty data tag through.
- if (!*data)
- data = nullptr;
- else {
- fprintf(stderr, "Unknown testdata for function %s(): '%s'\n", name.constData(), data);
- fprintf(stderr, "Function has no testdata.\n");
- return false;
- }
- }
+ const auto dataTag = [&table, dataCount](int index) {
+ return dataCount ? table.testData(index)->dataTag() : nullptr;
+ };
/* For each entry in this test's data table, do: */
do {
QTestResult::setSkipCurrentTest(false);
QTestResult::setBlacklistCurrentTest(false);
- if (!data || !qstrcmp(data, table.testData(curDataIndex)->dataTag())) {
+ if (dataTagMatches(tag, QLatin1StringView(dataTag(curDataIndex)),
+ QLatin1StringView(globalDataTag(curGlobalDataIndex)))) {
foundFunction = true;
+ blacklisted = QTestPrivate::checkBlackLists(name.constData(), dataTag(curDataIndex),
+ globalDataTag(curGlobalDataIndex));
+ if (blacklisted)
+ QTestResult::setBlacklistCurrentTest(true);
+
+ if (blacklisted && skipBlacklisted) {
+ QTest::qSkip("Skipping blacklisted test since -skipblacklisted option is set.",
+ NULL, 0);
+ QTestResult::finishedCurrentTestData();
+ QTestResult::finishedCurrentTestDataCleanup();
+ } else {
+ QTestDataSetter s(
+ curDataIndex >= dataCount ? nullptr : table.testData(curDataIndex));
+
+ QTestPrivate::qtestMouseButtons = Qt::NoButton;
+ if (watchDog)
+ watchDog->beginTest();
+ QTest::lastMouseTimestamp += 500; // Maintain at least 500ms mouse event timestamps between each test function call
+ invokeTestOnData(index);
+ if (watchDog)
+ watchDog->testFinished();
+ }
- QTestPrivate::checkBlackLists(name.constData(), dataCount ? table.testData(curDataIndex)->dataTag() : nullptr);
-
- QTestDataSetter s(curDataIndex >= dataCount ? nullptr : table.testData(curDataIndex));
-
- QTestPrivate::qtestMouseButtons = Qt::NoButton;
- if (watchDog)
- watchDog->beginTest();
- QTest::lastMouseTimestamp += 500; // Maintain at least 500ms mouse event timestamps between each test function call
- invokeTestOnData(index);
- if (watchDog)
- watchDog->testFinished();
-
- if (data)
+ if (!tag.isEmpty() && !globalDataCount)
break;
}
++curDataIndex;
} while (curDataIndex < dataCount);
- if (data && !foundFunction) {
- fprintf(stderr, "Unknown testdata for function %s: '%s()'\n", name.constData(), data);
- fprintf(stderr, "Available testdata:\n");
- for (int i = 0; i < table.dataCount(); ++i)
- fprintf(stderr, "%s\n", table.testData(i)->dataTag());
- return false;
- }
-
QTestResult::setCurrentGlobalTestData(nullptr);
++curGlobalDataIndex;
} while (curGlobalDataIndex < globalDataCount);
+ if (!tag.isEmpty() && !foundFunction) {
+ printUnknownDataTagError(QLatin1StringView(name), tag, table, *gTable);
+ QTestResult::addFailure(qPrintable("Data tag not found: %1"_L1.arg(tag)));
+ }
QTestResult::finishedCurrentTestFunction();
QTestResult::setSkipCurrentTest(false);
QTestResult::setBlacklistCurrentTest(false);
- QTestResult::setCurrentTestData(nullptr);
return true;
}
@@ -1240,7 +1435,7 @@ void *fetchData(QTestData *data, const char *tagName, int typeId)
/*!
* \internal
- */
+*/
char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...)
{
va_list ap;
@@ -1264,8 +1459,6 @@ char *formatString(const char *prefix, const char *suffix, size_t numArguments,
}
/*!
- \fn char* QTest::toHexRepresentation(const char *ba, int length)
-
Returns a pointer to a string that is the string \a ba represented
as a space-separated sequence of hex characters. If the input is
considered too long, it is truncated. A trucation is indicated in
@@ -1274,8 +1467,8 @@ char *formatString(const char *prefix, const char *suffix, size_t numArguments,
to operator delete[].
\a length is the length of the string \a ba.
- */
-char *toHexRepresentation(const char *ba, int length)
+*/
+char *toHexRepresentation(const char *ba, qsizetype length)
{
if (length == 0)
return qstrdup("");
@@ -1287,12 +1480,12 @@ char *toHexRepresentation(const char *ba, int length)
* maxLen can't be for example 200 because Qt Test is sprinkled with fixed
* size char arrays.
* */
- const int maxLen = 50;
- const int len = qMin(maxLen, length);
+ const qsizetype maxLen = 50;
+ const qsizetype len = qMin(maxLen, length);
char *result = nullptr;
if (length > maxLen) {
- const int size = len * 3 + 4;
+ const qsizetype size = len * 3 + 4;
result = new char[size];
char *const forElipsis = result + size - 5;
@@ -1303,13 +1496,13 @@ char *toHexRepresentation(const char *ba, int length)
result[size - 1] = '\0';
}
else {
- const int size = len * 3;
+ const qsizetype size = len * 3;
result = new char[size];
result[size - 1] = '\0';
}
- int i = 0;
- int o = 0;
+ qsizetype i = 0;
+ qsizetype o = 0;
while (true) {
const char at = ba[i];
@@ -1334,12 +1527,12 @@ char *toHexRepresentation(const char *ba, int length)
Returns the same QByteArray but with only the ASCII characters still shown;
everything else is replaced with \c {\xHH}.
*/
-char *toPrettyCString(const char *p, int length)
+char *toPrettyCString(const char *p, qsizetype length)
{
bool trimmed = false;
- QScopedArrayPointer<char> buffer(new char[256]);
+ auto buffer = std::make_unique<char[]>(256);
const char *end = p + length;
- char *dst = buffer.data();
+ char *dst = buffer.get();
bool lastWasHexEscape = false;
*dst++ = '"';
@@ -1349,7 +1542,7 @@ char *toPrettyCString(const char *p, int length)
// 2 bytes: a simple escape sequence (\n)
// 3 bytes: "" and a character
// 4 bytes: an hex escape sequence (\xHH)
- if (dst - buffer.data() > 246) {
+ if (dst - buffer.get() > 246) {
// plus the quote, the three dots and NUL, it's 255 in the worst case
trimmed = true;
break;
@@ -1410,79 +1603,81 @@ char *toPrettyCString(const char *p, int length)
*dst++ = '.';
}
*dst++ = '\0';
- return buffer.take();
+ return buffer.release();
}
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
-// this used to be the signature up to and including Qt 5.9
-// keep it for BC reasons:
-Q_TESTLIB_EXPORT
-char *toPrettyUnicode(const ushort *p, int length)
-{
- return toPrettyUnicode(QStringView(p, length));
-}
-#endif
-
/*!
+ \fn char *toPrettyUnicode(QStringView string)
\internal
Returns the same QString but with only the ASCII characters still shown;
everything else is replaced with \c {\uXXXX}.
Similar to QDebug::putString().
*/
+
+constexpr qsizetype PrettyUnicodeMaxOutputSize = 256;
+// escape sequence, closing quote, the three dots and NUL
+constexpr qsizetype PrettyUnicodeMaxIncrement = sizeof(R"(\uXXXX"...)"); // includes NUL
+
+static char *writePrettyUnicodeChar(char16_t ch, char * const buffer)
+{
+ auto dst = buffer;
+ auto first = [&](int n) { Q_ASSERT(dst - buffer == n); return dst; };
+ if (ch < 0x7f && ch >= 0x20 && ch != '\\' && ch != '"') {
+ *dst++ = ch;
+ return first(1);
+ }
+
+ // write as an escape sequence
+ *dst++ = '\\';
+ switch (ch) {
+ case 0x22:
+ case 0x5c:
+ *dst++ = uchar(ch);
+ break;
+ case 0x8:
+ *dst++ = 'b';
+ break;
+ case 0xc:
+ *dst++ = 'f';
+ break;
+ case 0xa:
+ *dst++ = 'n';
+ break;
+ case 0xd:
+ *dst++ = 'r';
+ break;
+ case 0x9:
+ *dst++ = 't';
+ break;
+ default:
+ *dst++ = 'u';
+ *dst++ = toHexUpper(ch >> 12);
+ *dst++ = toHexUpper(ch >> 8);
+ *dst++ = toHexUpper(ch >> 4);
+ *dst++ = toHexUpper(ch);
+ return first(6);
+ }
+ return first(2);
+}
+
char *toPrettyUnicode(QStringView string)
{
auto p = string.utf16();
auto length = string.size();
// keep it simple for the vast majority of cases
bool trimmed = false;
- QScopedArrayPointer<char> buffer(new char[256]);
+ auto buffer = std::make_unique<char[]>(PrettyUnicodeMaxOutputSize);
const auto end = p + length;
- char *dst = buffer.data();
+ char *dst = buffer.get();
*dst++ = '"';
for ( ; p != end; ++p) {
- if (dst - buffer.data() > 245) {
- // plus the quote, the three dots and NUL, it's 250, 251 or 255
+ if (dst - buffer.get() > PrettyUnicodeMaxOutputSize - PrettyUnicodeMaxIncrement) {
trimmed = true;
break;
}
-
- if (*p < 0x7f && *p >= 0x20 && *p != '\\' && *p != '"') {
- *dst++ = *p;
- continue;
- }
-
- // write as an escape sequence
- // this means we may advance dst to buffer.data() + 246 or 250
- *dst++ = '\\';
- switch (*p) {
- case 0x22:
- case 0x5c:
- *dst++ = uchar(*p);
- break;
- case 0x8:
- *dst++ = 'b';
- break;
- case 0xc:
- *dst++ = 'f';
- break;
- case 0xa:
- *dst++ = 'n';
- break;
- case 0xd:
- *dst++ = 'r';
- break;
- case 0x9:
- *dst++ = 't';
- break;
- default:
- *dst++ = 'u';
- *dst++ = toHexUpper(*p >> 12);
- *dst++ = toHexUpper(*p >> 8);
- *dst++ = toHexUpper(*p >> 4);
- *dst++ = toHexUpper(*p);
- }
+ dst = writePrettyUnicodeChar(*p, dst);
}
*dst++ = '"';
@@ -1492,7 +1687,7 @@ char *toPrettyUnicode(QStringView string)
*dst++ = '.';
}
*dst++ = '\0';
- return buffer.take();
+ return buffer.release();
}
void TestMethods::invokeTests(QObject *testObject) const
@@ -1500,23 +1695,21 @@ void TestMethods::invokeTests(QObject *testObject) const
const QMetaObject *metaObject = testObject->metaObject();
QTEST_ASSERT(metaObject);
QTestResult::setCurrentTestFunction("initTestCase");
- if (m_initTestCaseDataMethod.isValid())
- m_initTestCaseDataMethod.invoke(testObject, Qt::DirectConnection);
+ invokeTestMethodIfValid(m_initTestCaseDataMethod, testObject);
- QScopedPointer<WatchDog> watchDog;
- if (!debuggerPresent()
+ std::optional<WatchDog> watchDog = std::nullopt;
+ if (!CrashHandler::alreadyDebugging()
#if QT_CONFIG(valgrind)
&& QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindChildProcess
#endif
) {
- watchDog.reset(new WatchDog);
+ watchDog.emplace();
}
QSignalDumper::startDump();
- if (!QTestResult::skipCurrentTest() && !QTest::currentTestFailed()) {
- if (m_initTestCaseMethod.isValid())
- m_initTestCaseMethod.invoke(testObject, Qt::DirectConnection);
+ if (!QTestResult::skipCurrentTest() && !QTestResult::currentTestFailed()) {
+ invokeTestMethodIfValid(m_initTestCaseMethod, testObject);
// finishedCurrentTestDataCleanup() resets QTestResult::currentTestFailed(), so use a local copy.
const bool previousFailed = QTestResult::currentTestFailed();
@@ -1529,19 +1722,21 @@ void TestMethods::invokeTests(QObject *testObject) const
const char *data = nullptr;
if (i < QTest::testTags.size() && !QTest::testTags.at(i).isEmpty())
data = qstrdup(QTest::testTags.at(i).toLatin1().constData());
- const bool ok = invokeTest(i, data, watchDog.data());
+ const bool ok = invokeTest(i, QLatin1StringView(data), watchDog);
delete [] data;
if (!ok)
break;
}
}
+ const bool wasSkipped = QTestResult::skipCurrentTest();
QTestResult::setSkipCurrentTest(false);
QTestResult::setBlacklistCurrentTest(false);
QTestResult::setCurrentTestFunction("cleanupTestCase");
- if (m_cleanupTestCaseMethod.isValid())
- m_cleanupTestCaseMethod.invoke(testObject, Qt::DirectConnection);
+ invokeTestMethodIfValid(m_cleanupTestCaseMethod, testObject);
QTestResult::finishedCurrentTestData();
+ // Restore skip state as it affects decision on whether we passed:
+ QTestResult::setSkipCurrentTest(wasSkipped || QTestResult::skipCurrentTest());
QTestResult::finishedCurrentTestDataCleanup();
}
QTestResult::finishedCurrentTestFunction();
@@ -1550,273 +1745,34 @@ void TestMethods::invokeTests(QObject *testObject) const
QSignalDumper::endDump();
}
-#if defined(Q_OS_WIN)
-
-// Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
-class DebugSymbolResolver
+#if QT_DEPRECATED_SINCE(6, 8)
+static const char *functionRefFormatter(const void *f)
{
- Q_DISABLE_COPY_MOVE(DebugSymbolResolver)
-public:
- struct Symbol {
- Symbol() : name(nullptr), address(0) {}
-
- const char *name; // Must be freed by caller.
- DWORD64 address;
- };
-
- explicit DebugSymbolResolver(HANDLE process);
- ~DebugSymbolResolver() { cleanup(); }
-
- bool isValid() const { return m_symFromAddr; }
-
- Symbol resolveSymbol(DWORD64 address) const;
-
-private:
- // typedefs from DbgHelp.h/.dll
- struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
- ULONG SizeOfStruct;
- ULONG TypeIndex; // Type Index of symbol
- ULONG64 Reserved[2];
- ULONG Index;
- ULONG Size;
- ULONG64 ModBase; // Base Address of module comtaining this symbol
- ULONG Flags;
- ULONG64 Value; // Value of symbol, ValuePresent should be 1
- ULONG64 Address; // Address of symbol including base address of module
- ULONG Register; // register holding value or pointer to value
- ULONG Scope; // scope of the symbol
- ULONG Tag; // pdb classification
- ULONG NameLen; // Actual length of name
- ULONG MaxNameLen;
- CHAR Name[1]; // Name of symbol
- };
-
- typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
- typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
-
- void cleanup();
-
- const HANDLE m_process;
- HMODULE m_dbgHelpLib;
- SymFromAddrType m_symFromAddr;
+ auto formatter = static_cast<const qxp::function_ref<const char *()> *>(f);
+ return (*formatter)();
};
-void DebugSymbolResolver::cleanup()
+bool reportResult(bool success, qxp::function_ref<const char *()> lhs,
+ qxp::function_ref<const char *()> rhs,
+ const char *lhsExpr, const char *rhsExpr,
+ ComparisonOperation op, const char *file, int line)
{
- if (m_dbgHelpLib)
- FreeLibrary(m_dbgHelpLib);
- m_dbgHelpLib = 0;
- m_symFromAddr = nullptr;
-}
-
-DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
- : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
-{
- bool success = false;
- m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
- if (m_dbgHelpLib) {
- SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
- m_symFromAddr = reinterpret_cast<SymFromAddrType>(
- reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
- success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
- }
- if (!success)
- cleanup();
+ return QTestResult::reportResult(success, &lhs, &rhs,
+ functionRefFormatter, functionRefFormatter,
+ lhsExpr, rhsExpr, op, file, line);
}
+#endif // QT_DEPRECATED_SINCE(6, 8)
-DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
+bool reportResult(bool success, const void *lhs, const void *rhs,
+ const char *(*lhsFormatter)(const void*),
+ const char *(*rhsFormatter)(const void*),
+ const char *lhsExpr, const char *rhsExpr,
+ ComparisonOperation op, const char *file, int line)
{
- // reserve additional buffer where SymFromAddr() will store the name
- struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
- enum { symbolNameLength = 255 };
-
- char name[symbolNameLength + 1];
- };
-
- Symbol result;
- if (!isValid())
- return result;
- NamedSymbolInfo symbolBuffer;
- memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
- symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
- symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
- if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
- return result;
- result.name = qstrdup(symbolBuffer.Name);
- result.address = symbolBuffer.Address;
- return result;
+ return QTestResult::reportResult(success, lhs, rhs, lhsFormatter, rhsFormatter,
+ lhsExpr, rhsExpr, op, file, line);
}
-
-#endif // Q_OS_WIN
-
-class FatalSignalHandler
-{
-public:
- FatalSignalHandler()
- {
-#if defined(Q_OS_WIN)
-# if !defined(Q_CC_MINGW)
- _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
-# endif
- SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
- SetUnhandledExceptionFilter(windowsFaultHandler);
-#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
- sigemptyset(&handledSignals);
-
- const int fatalSignals[] = {
- SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 };
-
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- act.sa_handler = FatalSignalHandler::signal;
-
- // Remove the handler after it is invoked.
-# if !defined(Q_OS_INTEGRITY)
- act.sa_flags = SA_RESETHAND;
-# endif
-
- // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
- // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
-# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
- // Let the signal handlers use an alternate stack
- // This is necessary if SIGSEGV is to catch a stack overflow
-# if defined(Q_CC_GNU) && defined(Q_OF_ELF)
- // Put the alternate stack in the .lbss (large BSS) section so that it doesn't
- // interfere with normal .bss symbols
- __attribute__((section(".lbss.altstack"), aligned(4096)))
-# endif
- static char alternate_stack[16 * 1024];
- stack_t stack;
- stack.ss_flags = 0;
- stack.ss_size = sizeof alternate_stack;
- stack.ss_sp = alternate_stack;
- sigaltstack(&stack, nullptr);
- act.sa_flags |= SA_ONSTACK;
-# endif
-
- // Block all fatal signals in our signal handler so we don't try to close
- // the testlog twice.
- sigemptyset(&act.sa_mask);
- for (int i = 0; fatalSignals[i]; ++i)
- sigaddset(&act.sa_mask, fatalSignals[i]);
-
- struct sigaction oldact;
-
- for (int i = 0; fatalSignals[i]; ++i) {
- sigaction(fatalSignals[i], &act, &oldact);
- if (
-# ifdef SA_SIGINFO
- oldact.sa_flags & SA_SIGINFO ||
-# endif
- oldact.sa_handler != SIG_DFL) {
- sigaction(fatalSignals[i], &oldact, nullptr);
- } else
- {
- sigaddset(&handledSignals, fatalSignals[i]);
- }
- }
-#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
- }
-
- ~FatalSignalHandler()
- {
-#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
- // Unregister any of our remaining signal handlers
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- act.sa_handler = SIG_DFL;
-
- struct sigaction oldact;
-
- for (int i = 1; i < 32; ++i) {
- if (!sigismember(&handledSignals, i))
- continue;
- sigaction(i, &act, &oldact);
-
- // If someone overwrote it in the mean time, put it back
- if (oldact.sa_handler != FatalSignalHandler::signal)
- sigaction(i, &oldact, nullptr);
- }
-#endif
- }
-
-private:
-#if defined(Q_OS_WIN)
- static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
- {
- enum { maxStackFrames = 100 };
- char appName[MAX_PATH];
- if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
- appName[0] = 0;
- const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
- const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
- const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
- printf("A crash occurred in %s.\n"
- "Function time: %dms Total time: %dms\n\n"
- "Exception address: 0x%p\n"
- "Exception code : 0x%lx\n",
- appName, msecsFunctionTime, msecsTotalTime,
- exceptionAddress, exInfo->ExceptionRecord->ExceptionCode);
-
- DebugSymbolResolver resolver(GetCurrentProcess());
- if (resolver.isValid()) {
- DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
- if (exceptionSymbol.name) {
- printf("Nearby symbol : %s\n", exceptionSymbol.name);
- delete [] exceptionSymbol.name;
- }
- void *stack[maxStackFrames];
- fputs("\nStack:\n", stdout);
- const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
- for (unsigned f = 0; f < frameCount; ++f) {
- DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
- if (symbol.name) {
- printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
- delete [] symbol.name;
- } else {
- printf("#%3u: Unable to obtain symbol\n", f + 1);
- }
- }
- }
-
- fputc('\n', stdout);
- fflush(stdout);
-
- return EXCEPTION_EXECUTE_HANDLER;
- }
-#endif // defined(Q_OS_WIN)
-
-#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
- static void signal(int signum)
- {
- const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
- const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
- if (signum != SIGINT) {
- stackTrace();
- if (qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH")) {
- fprintf(stderr, "Pausing process %d for debugging\n", getpid());
- raise(SIGSTOP);
- }
- }
- qFatal("Received signal %d\n"
- " Function time: %dms Total time: %dms",
- signum, msecsFunctionTime, msecsTotalTime);
-# if defined(Q_OS_INTEGRITY)
- {
- struct sigaction act;
- memset(&act, 0, sizeof(struct sigaction));
- act.sa_handler = SIG_DFL;
- sigaction(signum, &act, NULL);
- }
-# endif
- }
-
- sigset_t handledSignals;
-#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
-};
-
-} // namespace
+} // namespace QTest
static void initEnvironment()
{
@@ -1850,30 +1806,43 @@ static void initEnvironment()
than once, as command-line options for logging test output to files and
executing individual test functions will not behave correctly.
- Note: This function is not reentrant, only one test can run at a time. A
+ \note This function is not reentrant, only one test can run at a time. A
test that was executed with qExec() can't run another test via qExec() and
threads are not allowed to call qExec() simultaneously.
- If you have programatically created the arguments, as opposed to getting them
+ If you have programmatically created the arguments, as opposed to getting them
from the arguments in \c main(), it is likely of interest to use
QTest::qExec(QObject *, const QStringList &) since it is Unicode safe.
- \sa QTEST_MAIN()
+ \sa QTEST_MAIN(), QTEST_GUILESS_MAIN(), QTEST_APPLESS_MAIN()
*/
int QTest::qExec(QObject *testObject, int argc, char **argv)
{
+ // NB: QtQuick's testing recombines qInit(), qRun() and qCleanup() to
+ // provide a replacement for qExec() that calls qRun() once for each
+ // built-in style. So think twice about moving parts between these three
+ // functions, as doing so may mess up QtQuick's testing.
qInit(testObject, argc, argv);
int ret = qRun();
qCleanup();
+
+#if defined(Q_OS_WASM)
+ EM_ASM({
+ if (typeof Module != "undefined" && typeof Module.notifyTestFinished != "undefined")
+ Module.notifyTestFinished($0);
+ }, ret);
+#endif // Q_OS_WASM
+
return ret;
}
/*! \internal
- */
+*/
void QTest::qInit(QObject *testObject, int argc, char **argv)
{
initEnvironment();
+ CrashHandler::maybeDisableCoreDump();
QBenchmarkGlobalData::current = new QBenchmarkGlobalData;
#if defined(Q_OS_MACOS)
@@ -1906,12 +1875,14 @@ void QTest::qInit(QObject *testObject, int argc, char **argv)
qtest_qParseArgs(argc, argv, false);
- QTestTable::globalTestTable();
- QTestLog::startLogging();
+#if QT_CONFIG(valgrind)
+ if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess)
+#endif
+ QTestLog::startLogging();
}
/*! \internal
- */
+*/
int QTest::qRun()
{
QTEST_ASSERT(currentTestObject);
@@ -1938,26 +1909,46 @@ int QTest::qRun()
} else
#endif
{
- QScopedPointer<FatalSignalHandler> handler;
- if (!noCrashHandler)
- handler.reset(new FatalSignalHandler);
+ std::optional<CrashHandler::FatalSignalHandler> handler;
+ CrashHandler::prepareStackTrace();
+ if (!Internal::noCrashHandler)
+ handler.emplace();
+ bool seenBad = false;
TestMethods::MetaMethods commandLineMethods;
commandLineMethods.reserve(static_cast<size_t>(QTest::testFunctions.size()));
- for (const QString &tf : qAsConst(QTest::testFunctions)) {
- const QByteArray tfB = tf.toLatin1();
- const QByteArray signature = tfB + QByteArrayLiteral("()");
- QMetaMethod m = TestMethods::findMethod(currentTestObject, signature.constData());
- if (!m.isValid() || !isValidSlot(m)) {
- fprintf(stderr, "Unknown test function: '%s'. Possible matches:\n", tfB.constData());
- qPrintTestSlots(stderr, tfB.constData());
- fprintf(stderr, "\n%s -functions\nlists all available test functions.\n", QTestResult::currentAppName());
- exit(1);
- }
+ for (const QString &tf : std::as_const(QTest::testFunctions)) {
+ const QByteArray tfB = tf.toLatin1();
+ const QByteArray signature = tfB + QByteArrayLiteral("()");
+ QMetaMethod m = TestMethods::findMethod(currentTestObject, signature.constData());
+ if (m.isValid() && isValidSlot(m)) {
commandLineMethods.push_back(m);
+ } else {
+ fprintf(stderr, "Unknown test function: '%s'.", tfB.constData());
+ if (!qPrintTestSlots(stderr, tfB.constData(), " Possible matches:\n"))
+ fputc('\n', stderr);
+ QTestResult::setCurrentTestFunction(tfB.constData());
+ QTestResult::addFailure(qPrintable("Function not found: %1"_L1.arg(tf)));
+ QTestResult::finishedCurrentTestFunction();
+ // Ditch the tag that came with tf as test function:
+ QTest::testTags.remove(commandLineMethods.size());
+ seenBad = true;
+ }
+ }
+ if (seenBad) {
+ // Provide relevant help to do better next time:
+ fprintf(stderr, "\n%s -functions\nlists all available test functions.\n\n",
+ QTestResult::currentAppName());
+ if (commandLineMethods.empty()) // All requested functions missing.
+ return 1;
}
TestMethods test(currentTestObject, std::move(commandLineMethods));
- test.invokeTests(currentTestObject);
+
+ while (QTestLog::failCount() == 0 && (repeatForever || repetitions-- > 0)) {
+ QTestTable::globalTestTable();
+ test.invokeTests(currentTestObject);
+ QTestTable::clearGlobalTestTable();
+ }
}
#ifndef QT_NO_EXCEPTIONS
@@ -1986,13 +1977,15 @@ int QTest::qRun()
}
/*! \internal
- */
+*/
void QTest::qCleanup()
{
currentTestObject = nullptr;
- QTestTable::clearGlobalTestTable();
- QTestLog::stopLogging();
+#if QT_CONFIG(valgrind)
+ if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess)
+#endif
+ QTestLog::stopLogging();
delete QBenchmarkGlobalData::current;
QBenchmarkGlobalData::current = nullptr;
@@ -2002,16 +1995,44 @@ void QTest::qCleanup()
#endif
}
+#if QT_CONFIG(batch_test_support) || defined(Q_QDOC)
+/*!
+ Registers the test \a name, with entry function \a entryFunction, in a
+ central test case registry for the current binary.
+
+ The \a name will be listed when running the batch test binary with no
+ parameters. Running the test binary with the argv[1] of \a name will result
+ in \a entryFunction being called.
+
+ \since 6.5
+*/
+void QTest::qRegisterTestCase(const QString &name, TestEntryFunction entryFunction)
+{
+ QTest::TestRegistry::instance()->registerTest(name, entryFunction);
+}
+
+QList<QString> QTest::qGetTestCaseNames()
+{
+ return QTest::TestRegistry::instance()->getAllTestNames();
+}
+
+QTest::TestEntryFunction QTest::qGetTestCaseEntryFunction(const QString& name)
+{
+ return QTest::TestRegistry::instance()->getTestEntryFunction(name);
+}
+
+#endif // QT_CONFIG(batch_test_support)
+
/*!
\overload
\since 4.4
Behaves identically to qExec(QObject *, int, char**) but takes a
QStringList of \a arguments instead of a \c char** list.
- */
+*/
int QTest::qExec(QObject *testObject, const QStringList &arguments)
{
- const int argc = arguments.count();
+ const int argc = arguments.size();
QVarLengthArray<char *> argv(argc);
QList<QByteArray> args;
@@ -2027,14 +2048,14 @@ int QTest::qExec(QObject *testObject, const QStringList &arguments)
}
/*! \internal
- */
+*/
void QTest::qFail(const char *message, const char *file, int line)
{
QTestResult::fail(message, file, line);
}
/*! \internal
- */
+*/
bool QTest::qVerify(bool statement, const char *statementStr, const char *description,
const char *file, int line)
{
@@ -2042,8 +2063,8 @@ bool QTest::qVerify(bool statement, const char *statementStr, const char *descri
}
/*! \fn void QTest::qSkip(const char *message, const char *file, int line)
-\internal
- */
+ \internal
+*/
void QTest::qSkip(const char *message, const char *file, int line)
{
QTestResult::addSkip(message, file, line);
@@ -2051,20 +2072,91 @@ void QTest::qSkip(const char *message, const char *file, int line)
}
/*! \fn bool QTest::qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode, const char *file, int line)
-\internal
- */
+ \internal
+*/
bool QTest::qExpectFail(const char *dataIndex, const char *comment,
QTest::TestFailMode mode, const char *file, int line)
{
return QTestResult::expectFail(dataIndex, qstrdup(comment), mode, file, line);
}
-/*! \internal
- */
+/*!
+ \internal
+
+ Executes qFail() following a failed QVERIFY_THROWS_EXCEPTION or
+ QVERIFY_THROWS_NO_EXCEPTION, passing a suitable message created from \a expected,
+ \a what, along with \a file and \a line.
+
+ The \a expected parameter contains the type of the exception that is expected to
+ be thrown, or \nullptr, if no exception was expected.
+
+ The \a what parameter contains the result of \c{std::exception::what()}, or nullptr,
+ if a non-\c{std::exception}-derived exception was caught.
+
+ The \a file and \a line parameters hold expansions of the \c{__FILE__} and \c{__LINE__}
+ macros, respectively.
+*/
+void QTest::qCaught(const char *expected, const char *what, const char *file, int line)
+{
+ auto message = [&] {
+ const auto exType = what ? "std::" : "unknown ";
+ const auto ofType = expected ? " of type " : "";
+ const auto no = expected ? "an" : "no";
+ const auto withMsg = what ? " with message " : "";
+ const auto protect = [](const char *s) { return s ? s : ""; };
+
+ return QString::asprintf("Expected %s exception%s%s to be thrown, "
+ "but caught %sexception%s%s",
+ no, ofType, protect(expected),
+ exType, withMsg, protect(what));
+ };
+ qFail(message().toUtf8().constData(), file, line);
+}
+
+/*!
+ \internal
+
+ Contains the implementation of the catch(...) block of
+ QVERIFY_THROWS_EXCEPTION.
+
+ The function inspects std::current_exception() by rethrowing it using
+ std::rethrow_exception().
+
+ The function must be called from a catch handler.
+
+ If the exception inherits std::exception, its what() message is logged and
+ this function returns normally. The caller of this function must then
+ execute a \c{QTEST_FAIL_ACTION} to exit from the test function.
+
+ Otherwise, a message saying an unknown exception was caught is logged and
+ this function rethrows the exception, skipping the \c{QTEST_FAIL_ACTION}
+ that follows this function call in the caller.
+*/
+void QTest::qCaught(const char *expected, const char *file, int line)
+{
+ try {
+ // let's see what the cat brought us:
+ std::rethrow_exception(std::current_exception());
+ } catch (const std::exception &e) {
+ qCaught(expected, e.what(), file, line);
+ } catch (...) {
+ qCaught(expected, nullptr, file, line);
+ throw;
+ }
+ // caller shall invoke `QTEST_FAIL_ACTION` if control reached here
+}
+
+
+#if QT_DEPRECATED_SINCE(6, 3)
+/*!
+ \internal
+ \deprecated [6.3] Use qWarning() instead
+*/
void QTest::qWarn(const char *message, const char *file, int line)
{
QTestLog::warn(message, file, line);
}
+#endif
/*!
Ignores messages created by qDebug(), qInfo() or qWarning(). If the \a message
@@ -2072,9 +2164,9 @@ void QTest::qWarn(const char *message, const char *file, int line)
test log. If the test finished and the \a message was not outputted,
a test failure is appended to the test log.
- \b {Note:} Invoking this function will only ignore one message.
- If the message you want to ignore is outputted twice, you have to
- call ignoreMessage() twice, too.
+ \note Invoking this function will only ignore one message. If the message
+ you want to ignore is output twice, you have to call ignoreMessage() twice,
+ too.
Example:
\snippet code/src_qtestlib_qtestcase.cpp 19
@@ -2097,9 +2189,9 @@ void QTest::ignoreMessage(QtMsgType type, const char *message)
test log. If the test finished and the message was not outputted,
a test failure is appended to the test log.
- \b {Note:} Invoking this function will only ignore one message.
- If the message you want to ignore is outputted twice, you have to
- call ignoreMessage() twice, too.
+ \note Invoking this function will only ignore one message. If the message
+ you want to ignore is output twice, you have to call ignoreMessage() twice,
+ too.
\since 5.3
*/
@@ -2109,14 +2201,76 @@ void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePatte
}
#endif // QT_CONFIG(regularexpression)
+/*!
+ \since 6.3
+ \overload failOnWarning()
+
+ Appends a test failure to the test log if the \a message is output.
+
+ \sa failOnWarning()
+*/
+void QTest::failOnWarning(const char *message)
+{
+ return QTestLog::failOnWarning(message);
+}
+
+#if QT_CONFIG(regularexpression)
+/*!
+ \since 6.3
+
+ Appends a test failure to the test log for each warning that matches
+ \a messagePattern.
+
+ The test function will continue execution when a failure is added. To abort
+ the test instead, you can check \l currentTestFailed() and return early if
+ it's \c true.
+
+ For each warning, the first pattern that matches will cause a failure,
+ and the remaining patterns will be ignored.
+
+ All patterns are cleared at the end of each test function.
+
+ \code
+ void FileTest::loadFiles()
+ {
+ QTest::failOnWarning(QRegularExpression("^Failed to load"));
+
+ // Each of these will cause a test failure:
+ qWarning() << "Failed to load image";
+ qWarning() << "Failed to load video";
+ }
+ \endcode
+
+ To fail every test that triggers a given warning, pass a suitable regular
+ expression to this function in \l {Creating a Test}{init()}:
+
+ \code
+ void FileTest::init()
+ {
+ QTest::failOnWarning(QRegularExpression(".?"));
+ }
+ \endcode
+
+ \note \l ignoreMessage() takes precedence over this function, so any
+ warnings that match a pattern given to both \c ignoreMessage() and
+ \c failOnWarning() will be ignored.
+
+ \sa {Qt Test Environment Variables}{QTEST_FATAL_FAIL}
+*/
+void QTest::failOnWarning(const QRegularExpression &messagePattern)
+{
+ QTestLog::failOnWarning(messagePattern);
+}
+#endif // QT_CONFIG(regularexpression)
+
/*! \internal
- */
+*/
#ifdef Q_OS_WIN
static inline bool isWindowsBuildDirectory(const QString &dirName)
{
- return dirName.compare(QLatin1String("Debug"), Qt::CaseInsensitive) == 0
- || dirName.compare(QLatin1String("Release"), Qt::CaseInsensitive) == 0;
+ return dirName.compare("Debug"_L1, Qt::CaseInsensitive) == 0
+ || dirName.compare("Release"_L1, Qt::CaseInsensitive) == 0;
}
#endif
@@ -2130,7 +2284,7 @@ static inline bool isWindowsBuildDirectory(const QString &dirName)
Returns the temporary directory where the data was extracted or null in case of
errors.
- */
+*/
QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
{
QSharedPointer<QTemporaryDir> result; // null until success, then == tempDir
@@ -2143,7 +2297,7 @@ QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
return result;
const QString dataPath = tempDir->path();
- const QString resourcePath = QLatin1Char(':') + dirName;
+ const QString resourcePath = u':' + dirName;
const QFileInfo fileInfo(resourcePath);
if (!fileInfo.isDir()) {
@@ -2151,30 +2305,36 @@ QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
return result;
}
- QDirIterator it(resourcePath, QDirIterator::Subdirectories);
- if (!it.hasNext()) {
- qWarning("Resource directory '%s' is empty.", qPrintable(resourcePath));
- return result;
- }
-
- while (it.hasNext()) {
- QFileInfo fileInfo = it.nextFileInfo();
-
- if (!fileInfo.isDir()) {
- const QString destination = dataPath + QLatin1Char('/') + QStringView{fileInfo.filePath()}.mid(resourcePath.length());
+ bool isResourceDirEmpty = true;
+ for (const auto &dirEntry : QDirListing(resourcePath, QDirListing::IteratorFlag::Recursive)) {
+ isResourceDirEmpty = false;
+ if (!dirEntry.isDir()) {
+ const QString &filePath = dirEntry.filePath();
+ const QString destination =
+ dataPath + u'/' + QStringView{filePath}.sliced(resourcePath.size());
QFileInfo destinationFileInfo(destination);
QDir().mkpath(destinationFileInfo.path());
- if (!QFile::copy(fileInfo.filePath(), destination)) {
- qWarning("Failed to copy '%s'.", qPrintable(fileInfo.filePath()));
+ QFile file(filePath);
+ if (!file.copy(destination)) {
+ qWarning("Failed to copy '%ls': %ls.", qUtf16Printable(filePath),
+ qUtf16Printable(file.errorString()));
return result;
}
- if (!QFile::setPermissions(destination, QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup)) {
- qWarning("Failed to set permissions on '%s'.", qPrintable(destination));
+
+ file.setFileName(destination);
+ if (!file.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup)) {
+ qWarning("Failed to set permissions on '%ls': %ls.", qUtf16Printable(destination),
+ qUtf16Printable(file.errorString()));
return result;
}
}
}
+ if (isResourceDirEmpty) {
+ qWarning("Resource directory '%s' is empty.", qPrintable(resourcePath));
+ return result;
+ }
+
result = std::move(tempDir);
return result;
@@ -2182,7 +2342,7 @@ QSharedPointer<QTemporaryDir> QTest::qExtractTestData(const QString &dirName)
#endif // QT_CONFIG(temporaryfile)
/*! \internal
- */
+*/
QString QTest::qFindTestData(const QString& base, const char *file, int line, const char *builddir,
const char *sourcedir)
@@ -2206,11 +2366,10 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
}
#endif // Q_OS_WIN
else if (QTestLog::verboseLevel() >= 2) {
- const QString candidate = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QLatin1Char('/') + base);
- QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 not found relative to test binary [%2]; "
- "checking next location").arg(base, candidate)),
- file, line);
+ const QString candidate = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + u'/' + base);
+ QTestLog::info(qPrintable("testdata %1 not found relative to test binary [%2]; "
+ "checking next location"_L1.arg(base, candidate)),
+ file, line);
}
}
@@ -2219,16 +2378,15 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
const char *testObjectName = QTestResult::currentTestObjectName();
if (testObjectName) {
const QString testsPath = QLibraryInfo::path(QLibraryInfo::TestsPath);
- const QString candidate = QString::fromLatin1("%1/%2/%3")
+ const QString candidate = "%1/%2/%3"_L1
.arg(testsPath, QFile::decodeName(testObjectName).toLower(), base);
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
- QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 not found in tests install path [%2]; "
- "checking next location")
- .arg(base, QDir::toNativeSeparators(candidate))),
- file, line);
+ QTestLog::info(qPrintable("testdata %1 not found in tests install path [%2]; "
+ "checking next location"_L1
+ .arg(base, QDir::toNativeSeparators(candidate))),
+ file, line);
}
}
}
@@ -2240,17 +2398,16 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
// If the srcdir is relative, that means it is relative to the current working
// directory of the compiler at compile time, which should be passed in as `builddir'.
- if (!srcdir.isAbsolute() && builddir) {
- srcdir.setFile(QFile::decodeName(builddir) + QLatin1String("/") + srcdir.filePath());
- }
+ if (!srcdir.isAbsolute() && builddir)
+ srcdir.setFile(QFile::decodeName(builddir) + u'/' + srcdir.filePath());
const QString canonicalPath = srcdir.canonicalFilePath();
- const QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base);
+ const QString candidate = "%1/%2"_L1.arg(canonicalPath, base);
if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 not found relative to source path [%2]")
+ "testdata %1 not found relative to source path [%2]"_L1
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
@@ -2258,12 +2415,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
// 4. Try resources
if (found.isEmpty()) {
- const QString candidate = QString::fromLatin1(":/%1").arg(base);
+ const QString candidate = ":/%1"_L1.arg(base);
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 not found in resources [%2]")
+ "testdata %1 not found in resources [%2]"_L1
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
@@ -2271,12 +2428,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
// 5. Try current directory
if (found.isEmpty()) {
- const QString candidate = QDir::currentPath() + QLatin1Char('/') + base;
+ const QString candidate = QDir::currentPath() + u'/' + base;
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 not found in current directory [%2]")
+ "testdata %1 not found in current directory [%2]"_L1
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
@@ -2284,12 +2441,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
// 6. Try main source directory
if (found.isEmpty()) {
- const QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base;
+ const QString candidate = QTest::mainSourcePath % u'/' % base;
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 not found in main source directory [%2]")
+ "testdata %1 not found in main source directory [%2]"_L1
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
@@ -2297,12 +2454,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
// 7. Try the supplied source directory
if (found.isEmpty() && sourcedir) {
- const QString candidate = QFile::decodeName(sourcedir) % QLatin1Char('/') % base;
+ const QString candidate = QFile::decodeName(sourcedir) % u'/' % base;
if (QFileInfo::exists(candidate)) {
found = candidate;
} else if (QTestLog::verboseLevel() >= 2) {
QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 not found in supplied source directory [%2]")
+ "testdata %1 not found in supplied source directory [%2]"_L1
.arg(base, QDir::toNativeSeparators(candidate))),
file, line);
}
@@ -2310,12 +2467,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
if (found.isEmpty()) {
- QTest::qWarn(qPrintable(
- QString::fromLatin1("testdata %1 could not be located!").arg(base)),
+ QTestLog::warn(qPrintable(
+ "testdata %1 could not be located!"_L1.arg(base)),
file, line);
} else if (QTestLog::verboseLevel() >= 1) {
QTestLog::info(qPrintable(
- QString::fromLatin1("testdata %1 was located at %2").arg(base, QDir::toNativeSeparators(found))),
+ "testdata %1 was located at %2"_L1.arg(base, QDir::toNativeSeparators(found))),
file, line);
}
@@ -2323,7 +2480,7 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co
}
/*! \internal
- */
+*/
QString QTest::qFindTestData(const char *base, const char *file, int line, const char *builddir,
const char *sourcedir)
{
@@ -2331,21 +2488,21 @@ QString QTest::qFindTestData(const char *base, const char *file, int line, const
}
/*! \internal
- */
+*/
void *QTest::qData(const char *tagName, int typeId)
{
return fetchData(QTestResult::currentTestData(), tagName, typeId);
}
/*! \internal
- */
+*/
void *QTest::qGlobalData(const char *tagName, int typeId)
{
return fetchData(QTestResult::currentGlobalTestData(), tagName, typeId);
}
/*! \internal
- */
+*/
void *QTest::qElementData(const char *tagName, int metaTypeId)
{
QTEST_ASSERT(tagName);
@@ -2361,7 +2518,7 @@ void *QTest::qElementData(const char *tagName, int metaTypeId)
}
/*! \internal
- */
+*/
void QTest::addColumnInternal(int id, const char *name)
{
QTestTable *tbl = QTestTable::currentTestTable();
@@ -2371,27 +2528,32 @@ void QTest::addColumnInternal(int id, const char *name)
}
/*!
- Appends a new row to the current test data. \a dataTag is the name of
- the testdata that will appear in the test output. Returns a QTestData reference
- that can be used to stream in data.
+ Appends a new row to the current test data.
+
+ The test output will identify the test run with this test data using the
+ name \a dataTag.
+
+ Returns a QTestData reference that can be used to stream in data, one value
+ for each column in the table.
Example:
\snippet code/src_qtestlib_qtestcase.cpp 20
- \b {Note:} This macro can only be used in a test's data function
+ \note This function can only be called as part of a test's data function
that is invoked by the test framework.
See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
a more extensive example.
- \sa addColumn(), QFETCH()
+ \sa addRow(), addColumn(), QFETCH()
*/
QTestData &QTest::newRow(const char *dataTag)
{
QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag cannot be null");
QTestTable *tbl = QTestTable::currentTestTable();
QTEST_ASSERT_X(tbl, "QTest::newRow()", "Cannot add testdata outside of a _data slot.");
- QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()", "Must add columns before attempting to add rows.");
+ QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()",
+ "Must add columns before attempting to add rows.");
return *tbl->newData(dataTag);
}
@@ -2399,31 +2561,36 @@ QTestData &QTest::newRow(const char *dataTag)
/*!
\since 5.9
- Appends a new row to the current test data. The function's arguments are passed
- to qsnprintf() for formatting according to \a format. See the qvsnprintf()
- documentation for caveats and limitations.
+ Appends a new row to the current test data.
+
+ The function's arguments are passed to qsnprintf() for formatting according
+ to \a format. See the qvsnprintf() documentation for caveats and
+ limitations.
- The formatted string will appear as the name of this test data in the test output.
+ The test output will identify the test run with this test data using the
+ name that results from this formatting.
- Returns a QTestData reference that can be used to stream in data.
+ Returns a QTestData reference that can be used to stream in data, one value
+ for each column in the table.
Example:
\snippet code/src_qtestlib_qtestcase.cpp addRow
- \b {Note:} This function can only be used in a test's data function
+ \note This function can only be called as part of a test's data function
that is invoked by the test framework.
See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
a more extensive example.
- \sa addColumn(), QFETCH()
+ \sa newRow(), addColumn(), QFETCH()
*/
QTestData &QTest::addRow(const char *format, ...)
{
QTEST_ASSERT_X(format, "QTest::addRow()", "Format string cannot be null");
QTestTable *tbl = QTestTable::currentTestTable();
QTEST_ASSERT_X(tbl, "QTest::addRow()", "Cannot add testdata outside of a _data slot.");
- QTEST_ASSERT_X(tbl->elementCount(), "QTest::addRow()", "Must add columns before attempting to add rows.");
+ QTEST_ASSERT_X(tbl->elementCount(), "QTest::addRow()",
+ "Must add columns before attempting to add rows.");
char buf[1024];
@@ -2453,8 +2620,8 @@ QTestData &QTest::addRow(const char *format, ...)
To add custom types to the testdata, the type must be registered with
QMetaType via \l Q_DECLARE_METATYPE().
- \b {Note:} This macro can only be used in a test's data function
- that is invoked by the test framework.
+ \note This function can only be used called as part of a test's data
+ function that is invoked by the test framework.
See \l {Chapter 2: Data Driven Testing}{Data Driven Testing} for
a more extensive example.
@@ -2484,7 +2651,7 @@ const char *QTest::currentTestFunction()
/*!
Returns the name of the current test data. If the test doesn't
- have any assigned testdata, the function returns 0.
+ have any assigned testdata, the function returns \nullptr.
*/
const char *QTest::currentDataTag()
{
@@ -2492,22 +2659,55 @@ const char *QTest::currentDataTag()
}
/*!
- Returns \c true if the current test function failed, otherwise false.
+ Returns \c true if the current test function has failed, otherwise false.
+
+ \sa QTest::currentTestResolved()
*/
bool QTest::currentTestFailed()
{
return QTestResult::currentTestFailed();
}
+/*!
+ \since 6.5
+ Returns \c true if the current test function has failed or skipped.
+
+ This applies if the test has failed or exercised a skip. When it is true,
+ the test function should return early. In particular, the \c{QTRY_*} macros
+ and the test event loop terminate their loops early if executed during the
+ test function (but not its cleanup()). After a test has called a helper
+ function that uses this module's macros, it can use this function to test
+ whether to return early.
+
+ \sa QTest::currentTestFailed()
+*/
+bool QTest::currentTestResolved()
+{
+ return QTestResult::currentTestFailed() || QTestResult::skipCurrentTest();
+}
+
+/*!
+ \internal
+ \since 6.4
+ Returns \c true during the run of the test-function and its set-up.
+
+ Used by the \c{QTRY_*} macros and \l QTestEventLoop to check whether to
+ return when QTest::currentTestResolved() is true.
+*/
+bool QTest::runningTest()
+{
+ return QTest::inTestFunction;
+}
+
/*! \internal
- */
+*/
QObject *QTest::testObject()
{
return currentTestObject;
}
/*! \internal
- */
+*/
void QTest::setMainSourcePath(const char *file, const char *builddir)
{
QString mainSourceFile = QFile::decodeName(file);
@@ -2519,7 +2719,9 @@ void QTest::setMainSourcePath(const char *file, const char *builddir)
QTest::mainSourcePath = fi.absolutePath();
}
+#if QT_DEPRECATED_SINCE(6, 4)
/*! \internal
+ \deprecated [6.4]
This function is called by various specializations of QTest::qCompare
to decide whether to report a failure and to produce verbose test output.
@@ -2527,15 +2729,91 @@ void QTest::setMainSourcePath(const char *file, const char *builddir)
will be output if the compare fails. If the compare succeeds, failureMsg
will not be output.
- If the caller has already passed a failure message showing the compared
- values, or if those values cannot be stringified, val1 and val2 can be null.
- */
+ Using this function is not optimal, because it requires the string
+ representations of \a actualVal and \a expectedVal to be pre-calculated,
+ even though they will be used only if the comparison fails. Prefer using the
+ \l compare_helper() overload that takes qxp::function_ref() for such cases.
+
+ If the caller creates a custom failure message showing the compared values,
+ or if those values cannot be stringified, use the overload of the function
+ that takes no \a actualVal and \a expecetedVal parameters.
+*/
bool QTest::compare_helper(bool success, const char *failureMsg,
- char *val1, char *val2,
+ char *actualVal, char *expectedVal,
const char *actual, const char *expected,
const char *file, int line)
{
- return QTestResult::compare(success, failureMsg, val1, val2, actual, expected, file, line);
+ return QTestResult::compare(success, failureMsg, actualVal, expectedVal,
+ actual, expected, file, line);
+}
+#endif // QT_DEPRECATED_SINCE(6, 4)
+
+#if QT_DEPRECATED_SINCE(6, 8)
+/*! \internal
+ \since 6.4
+ This function is called by various specializations of QTest::qCompare
+ to decide whether to report a failure and to produce verbose test output.
+
+ The \a failureMsg parameter can be \c {nullptr}, in which case a default
+ message will be output if the compare fails. If the comparison succeeds,
+ \a failureMsg will not be output.
+
+ This overload of the function uses qxp::function_ref to defer conversion of
+ \a actualVal and \a expectedVal to strings until that is really needed
+ (when the comparison fails). This speeds up test case execution on success.
+*/
+bool QTest::compare_helper(bool success, const char *failureMsg,
+ qxp::function_ref<const char *()> actualVal,
+ qxp::function_ref<const char *()> expectedVal,
+ const char *actual, const char *expected,
+ const char *file, int line)
+{
+ return QTestResult::reportResult(success, &actualVal, &expectedVal,
+ QTest::functionRefFormatter,
+ QTest::functionRefFormatter, actual, expected,
+ QTest::ComparisonOperation::CustomCompare,
+ file, line, failureMsg);
+}
+#endif // QT_DEPRECATED_SINCE(6, 8)
+
+/*! \internal
+ \since 6.8
+ This function is called by various specializations of QTest::qCompare
+ to decide whether to report a failure and to produce verbose test output.
+
+ The \a failureMsg parameter can be \c {nullptr}, in which case a default
+ message will be output if the compare fails. If the comparison succeeds,
+ \a failureMsg will not be output.
+*/
+
+bool QTest::compare_helper(bool success, const char *failureMsg,
+ const void *actualPtr, const void *expectedPtr,
+ const char *(*actualFormatter)(const void *),
+ const char *(*expectedFormatter)(const void *),
+ const char *actual, const char *expected,
+ const char *file, int line)
+{
+ return QTestResult::reportResult(success, actualPtr, expectedPtr,
+ actualFormatter, expectedFormatter,
+ actual, expected,
+ QTest::ComparisonOperation::CustomCompare,
+ file, line, failureMsg);
+}
+
+/*! \internal
+ \since 6.4
+ This function is called by various specializations of QTest::qCompare
+ to decide whether to report a failure and to produce verbose test output.
+
+ This overload should be used when there is no string representation of
+ actual and expected values, so only the \a failureMsg is shown when the
+ comparison fails. Because of that, \a failureMsg can't be \c {nullptr}.
+ If the comparison succeeds, \a failureMsg will not be output.
+*/
+bool QTest::compare_helper(bool success, const char *failureMsg, const char *actual,
+ const char *expected, const char *file, int line)
+{
+ return QTestResult::compare(success, failureMsg, actual, expected, file, line);
}
template <typename T>
@@ -2559,20 +2837,22 @@ static bool floatingCompare(const T &actual, const T &expected)
/*! \fn bool QTest::qCompare(const qfloat16 &t1, const qfloat16 &t2, const char *actual, const char *expected, const char *file, int line)
\internal
- */
+*/
bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected,
const char *file, int line)
{
+ auto formatter = Internal::genericToString<qfloat16>;
return compare_helper(floatingCompare(t1, t2),
"Compared qfloat16s are not the same (fuzzy compare)",
- toString(t1), toString(t2), actual, expected, file, line);
+ &t1, &t2, formatter, formatter,
+ actual, expected, file, line);
}
/*! \fn bool QTest::qCompare(const float &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
\internal
- */
+*/
bool QTest::qCompare(float const &t1, float const &t2, const char *actual, const char *expected,
- const char *file, int line)
+ const char *file, int line)
{
return QTestResult::compare(floatingCompare(t1, t2),
"Compared floats are not the same (fuzzy compare)",
@@ -2581,9 +2861,9 @@ bool QTest::qCompare(float const &t1, float const &t2, const char *actual, const
/*! \fn bool QTest::qCompare(const double &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
\internal
- */
+*/
bool QTest::qCompare(double const &t1, double const &t2, const char *actual, const char *expected,
- const char *file, int line)
+ const char *file, int line)
{
return QTestResult::compare(floatingCompare(t1, t2),
"Compared doubles are not the same (fuzzy compare)",
@@ -2593,7 +2873,7 @@ bool QTest::qCompare(double const &t1, double const &t2, const char *actual, con
/*! \fn bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected, const char *file, int line)
\internal
\since 5.14
- */
+*/
bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected,
const char *file, int line)
{
@@ -2606,7 +2886,7 @@ bool QTest::qCompare(int t1, int t2, const char *actual, const char *expected,
/*! \fn bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char *expected, const char *file, int line)
\internal
\since 6.0
- */
+*/
bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char *expected,
const char *file, int line)
@@ -2620,9 +2900,9 @@ bool QTest::qCompare(qsizetype t1, qsizetype t2, const char *actual, const char
/*! \fn bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected, const char *file, int line)
\internal
\since 5.14
- */
+*/
bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *expected,
- const char *file, int line)
+ const char *file, int line)
{
return QTestResult::compare(t1 == t2,
"Compared values are not the same",
@@ -2632,7 +2912,7 @@ bool QTest::qCompare(unsigned t1, unsigned t2, const char *actual, const char *e
/*! \fn bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
\internal
\since 5.14
- */
+*/
bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const char *expected,
const char *file, int line)
{
@@ -2641,11 +2921,11 @@ bool QTest::qCompare(QStringView t1, QStringView t2, const char *actual, const c
t1, t2, actual, expected, file, line);
}
-/*! \fn bool QTest::qCompare(QStringView t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
+/*!
\internal
\since 5.14
- */
-bool QTest::qCompare(QStringView t1, const QLatin1String &t2, const char *actual, const char *expected,
+*/
+bool QTest::qCompare(QStringView t1, const QLatin1StringView &t2, const char *actual, const char *expected,
const char *file, int line)
{
return QTestResult::compare(t1 == t2,
@@ -2653,11 +2933,11 @@ bool QTest::qCompare(QStringView t1, const QLatin1String &t2, const char *actual
t1, t2, actual, expected, file, line);
}
-/*! \fn bool QTest::qCompare(const QLatin1String &t1, QStringView t2, const char *actual, const char *expected, const char *file, int line)
+/*!
\internal
\since 5.14
- */
-bool QTest::qCompare(const QLatin1String &t1, QStringView t2, const char *actual, const char *expected,
+*/
+bool QTest::qCompare(const QLatin1StringView &t1, QStringView t2, const char *actual, const char *expected,
const char *file, int line)
{
return QTestResult::compare(t1 == t2,
@@ -2668,25 +2948,25 @@ bool QTest::qCompare(const QLatin1String &t1, QStringView t2, const char *actual
/*! \fn bool QTest::qCompare(const QString &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
\internal
\since 5.14
- */
+*/
-/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
+/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1StringView &t2, const char *actual, const char *expected, const char *file, int line)
\internal
\since 5.14
- */
+*/
-/*! \fn bool QTest::qCompare(const QLatin1String &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
+/*! \fn bool QTest::qCompare(const QLatin1StringView &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
\internal
\since 5.14
- */
+*/
/*! \fn bool QTest::qCompare(const double &t1, const float &t2, const char *actual, const char *expected, const char *file, int line)
\internal
- */
+*/
/*! \fn bool QTest::qCompare(const float &t1, const double &t2, const char *actual, const char *expected, const char *file, int line)
\internal
- */
+*/
#define TO_STRING_IMPL(TYPE, FORMAT) \
template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \
@@ -2722,7 +3002,7 @@ TO_STRING_IMPL(unsigned char, %hhu)
the exponent, requiring a leading 0 on single-digit exponents; (at least)
MinGW includes a leading zero also on an already-two-digit exponent,
e.g. 9e-040, which differs from more usual platforms. So massage that away.
- */
+*/
static void massageExponent(char *text)
{
char *p = strchr(text, 'e');
@@ -2813,7 +3093,7 @@ template <> Q_TESTLIB_EXPORT char *QTest::toString<char>(const char &t)
}
/*! \internal
- */
+*/
char *QTest::toString(const char *str)
{
if (!str) {
@@ -2826,21 +3106,16 @@ char *QTest::toString(const char *str)
}
/*! \internal
- */
+*/
char *QTest::toString(const volatile void *p) // Use volatile to match compare_ptr_helper()
{
- return QTest::toString(const_cast<const void *>(p));
-}
-
-char *QTest::toString(const void *p)
-{
char *msg = new char[128];
qsnprintf(msg, 128, "%p", p);
return msg;
}
/*! \internal
- */
+*/
char *QTest::toString(const volatile QObject *vo)
{
if (vo == nullptr)
@@ -2859,35 +3134,37 @@ char *QTest::toString(const volatile QObject *vo)
/*! \fn char *QTest::toString(const QColor &color)
\internal
- */
+*/
/*! \fn char *QTest::toString(const QRegion &region)
\internal
- */
+*/
/*! \fn char *QTest::toString(const QHostAddress &addr)
\internal
- */
+*/
/*! \fn char *QTest::toString(QNetworkReply::NetworkError code)
\internal
- */
+*/
/*! \fn char *QTest::toString(const QNetworkCookie &cookie)
\internal
- */
+*/
/*! \fn char *QTest::toString(const QList<QNetworkCookie> &list)
\internal
- */
+*/
/*! \internal
- */
+*/
bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual,
const char *expected, const char *file, int line)
{
+ auto formatter = Internal::genericToString<const char *>;
return compare_helper(qstrcmp(t1, t2) == 0, "Compared strings are not the same",
- toString(t1), toString(t2), actual, expected, file, line);
+ &t1, &t2, formatter, formatter,
+ actual, expected, file, line);
}
/*!
@@ -2967,11 +3244,11 @@ bool QTest::compare_string_helper(const char *t1, const char *t2, const char *ac
\internal
*/
-/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1String &t2, const char *actual, const char *expected, const char *file, int line)
+/*! \fn bool QTest::qCompare(const QString &t1, const QLatin1StringView &t2, const char *actual, const char *expected, const char *file, int line)
\internal
*/
-/*! \fn bool QTest::qCompare(const QLatin1String &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
+/*! \fn bool QTest::qCompare(const QLatin1StringView &t1, const QString &t2, const char *actual, const char *expected, const char *file, int line)
\internal
*/
diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h
index 158fce4e3d..0ae1a787e2 100644
--- a/src/testlib/qtestcase.h
+++ b/src/testlib/qtestcase.h
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTCASE_H
#define QTESTCASE_H
#include <QtTest/qttestglobal.h>
+#include <QtTest/qtesttostring.h>
#include <QtCore/qstring.h>
#include <QtCore/qnamespace.h>
@@ -49,6 +14,7 @@
#include <QtCore/qsharedpointer.h>
#include <QtCore/qtemporarydir.h>
#include <QtCore/qthread.h>
+#include <QtCore/qxpfunctional.h>
#include <string.h>
@@ -58,134 +24,247 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_EXCEPTIONS
+
+#ifdef QTEST_THROW_ON_FAILURE
+# define QTEST_FAIL_ACTION QTest::Internal::throwOnFail()
+#else
+# define QTEST_FAIL_ACTION do { QTest::Internal::maybeThrowOnFail(); return; } while (false)
+#endif
+
+#ifdef QTEST_THROW_ON_SKIP
+# define QTEST_SKIP_ACTION QTest::Internal::throwOnSkip()
+#else
+# define QTEST_SKIP_ACTION do { QTest::Internal::maybeThrowOnSkip(); return; } while (false)
+#endif
+
+#else
+# if defined(QTEST_THROW_ON_FAILURE) || defined(QTEST_THROW_ON_SKIP)
+# error QTEST_THROW_ON_FAILURE/SKIP require exception support enabled.
+# endif
+#endif // QT_NO_EXCEPTIONS
+
+#ifndef QTEST_FAIL_ACTION
+# define QTEST_FAIL_ACTION return
+#endif
+
+#ifndef QTEST_SKIP_ACTION
+# define QTEST_SKIP_ACTION return
+#endif
+
class qfloat16;
class QRegularExpression;
#define QVERIFY(statement) \
do {\
if (!QTest::qVerify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__))\
- return;\
+ QTEST_FAIL_ACTION; \
} while (false)
#define QFAIL(message) \
do {\
QTest::qFail(static_cast<const char *>(message), __FILE__, __LINE__);\
- return;\
+ QTEST_FAIL_ACTION; \
} while (false)
#define QVERIFY2(statement, description) \
do {\
if (statement) {\
if (!QTest::qVerify(true, #statement, static_cast<const char *>(description), __FILE__, __LINE__))\
- return;\
+ QTEST_FAIL_ACTION; \
} else {\
if (!QTest::qVerify(false, #statement, static_cast<const char *>(description), __FILE__, __LINE__))\
- return;\
+ QTEST_FAIL_ACTION; \
}\
} while (false)
#define QCOMPARE(actual, expected) \
do {\
if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\
- return;\
+ QTEST_FAIL_ACTION; \
+} while (false)
+
+#define QCOMPARE_OP_IMPL(lhs, rhs, op, opId) \
+do { \
+ if (!QTest::qCompareOp<QTest::ComparisonOperation::opId>(lhs, rhs, #lhs, #rhs, __FILE__, __LINE__)) \
+ QTEST_FAIL_ACTION; \
} while (false)
+#define QCOMPARE_EQ(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, ==, Equal)
+#define QCOMPARE_NE(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, !=, NotEqual)
+#define QCOMPARE_LT(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, <, LessThan)
+#define QCOMPARE_LE(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, <=, LessThanOrEqual)
+#define QCOMPARE_GT(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, >, GreaterThan)
+#define QCOMPARE_GE(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, >=, GreaterThanOrEqual)
#ifndef QT_NO_EXCEPTIONS
+# define QVERIFY_THROWS_NO_EXCEPTION(...) \
+ do { \
+ QT_TRY { \
+ __VA_ARGS__; \
+ /* success */ \
+ } QT_CATCH (...) { \
+ QTest::qCaught(nullptr, __FILE__, __LINE__); \
+ QTEST_FAIL_ACTION; \
+ } \
+ } while (false) \
+ /* end */
+
+#if QT_DEPRECATED_SINCE(6, 3)
+namespace QTest {
+QT_DEPRECATED_VERSION_X_6_3("Don't use QVERIFY_EXCEPTION_THROWN(expr, type) anymore, "
+ "use QVERIFY_THROWS_EXCEPTION(type, expr...) instead")
+inline void useVerifyThrowsException() {}
+} // namespace QTest
# define QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) \
+ QVERIFY_THROWS_EXCEPTION(exceptiontype, QTest::useVerifyThrowsException(); expression)
+#endif
+
+# define QVERIFY_THROWS_EXCEPTION(exceptiontype, ...) \
do {\
+ bool qverify_throws_exception_did_not_throw = false; \
QT_TRY {\
- QT_TRY {\
- expression;\
- QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \
- " but no exception caught", __FILE__, __LINE__);\
- return;\
- } QT_CATCH (const exceptiontype &) {\
- }\
- } QT_CATCH (const std::exception &e) {\
- QByteArray msg = QByteArray() + "Expected exception of type " #exceptiontype \
- " to be thrown but std::exception caught with message: " + e.what(); \
- QTest::qFail(msg.constData(), __FILE__, __LINE__);\
- return;\
- } QT_CATCH (...) {\
+ __VA_ARGS__; \
QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \
- " but unknown exception caught", __FILE__, __LINE__);\
- return;\
+ " but no exception caught", __FILE__, __LINE__); \
+ qverify_throws_exception_did_not_throw = true; \
+ } QT_CATCH (const exceptiontype &) { \
+ /* success */ \
+ } QT_CATCH (...) {\
+ QTest::qCaught(#exceptiontype, __FILE__, __LINE__); \
+ QTEST_FAIL_ACTION; \
}\
+ if (qverify_throws_exception_did_not_throw) \
+ QTEST_FAIL_ACTION; \
} while (false)
#else // QT_NO_EXCEPTIONS
/*
- * The expression passed to the macro should throw an exception and we can't
- * catch it because Qt has been compiled without exception support. We can't
+ * These macros check whether the expression passed throws exceptions, but we can't
+ * catch them to check because Qt has been compiled without exception support. We can't
* skip the expression because it may have side effects and must be executed.
* So, users must use Qt with exception support enabled if they use exceptions
* in their code.
*/
-# define QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) \
- static_assert(false, "Support of exceptions is disabled")
+# define QVERIFY_THROWS_EXCEPTION(...) \
+ static_assert(false, "Support for exceptions is disabled")
+# define QVERIFY_THROWS_NO_EXCEPTION(...) \
+ static_assert(false, "Support for exceptions is disabled")
#endif // !QT_NO_EXCEPTIONS
+/* Ideally we would adapt qWaitFor(), or a variant on it, to implement roughly
+ * what the following provides as QTRY_LOOP_IMPL(); however, for now, the
+ * reporting of how much to increase the timeout to (if within a factor of two)
+ * on failure and the check for (QTest::runningTest() &&
+ * QTest::currentTestResolved()) go beyond qWaitFor(). (We no longer care about
+ * the bug in MSVC < 2017 that precluded using qWaitFor() in the implementation
+ * here, see QTBUG-59096.)
+ */
+
// NB: not do {...} while (0) wrapped, as qt_test_i is accessed after it
#define QTRY_LOOP_IMPL(expr, timeoutValue, step) \
if (!(expr)) { \
QTest::qWait(0); \
} \
int qt_test_i = 0; \
- for (; qt_test_i < timeoutValue && !(expr); qt_test_i += step) { \
+ for (; qt_test_i < timeoutValue && !(QTest::runningTest() && QTest::currentTestResolved()) \
+ && !(expr); qt_test_i += step) { \
QTest::qWait(step); \
}
+// Ends in a for-block, so doesn't want a following semicolon.
-#define QTRY_TIMEOUT_DEBUG_IMPL(expr, timeoutValue, step)\
- if (!(expr)) { \
- QTRY_LOOP_IMPL((expr), (2 * timeoutValue), step);\
- if (expr) { \
- QFAIL(qPrintable(QTest::Internal::formatTryTimeoutDebugMessage(u8"" #expr, timeoutValue, timeoutValue + qt_test_i))); \
+#define QTRY_TIMEOUT_DEBUG_IMPL(expr, timeoutValue, step) \
+ if (!(QTest::runningTest() && QTest::currentTestResolved()) && !(expr)) { \
+ QTRY_LOOP_IMPL(expr, 2 * (timeoutValue), step) \
+ if ((expr)) { \
+ QFAIL(qPrintable(QTest::Internal::formatTryTimeoutDebugMessage(\
+ u8"" #expr, timeoutValue, timeoutValue + qt_test_i))); \
} \
}
-// Ideally we'd use qWaitFor instead of QTRY_LOOP_IMPL, but due
-// to a compiler bug on MSVC < 2017 we can't (see QTBUG-59096)
-#define QTRY_IMPL(expr, timeout)\
- const int qt_test_step = timeout < 350 ? timeout / 7 + 1 : 50; \
- const int qt_test_timeoutValue = timeout; \
- { QTRY_LOOP_IMPL(QTest::currentTestFailed() || (expr), qt_test_timeoutValue, qt_test_step); } \
- QTRY_TIMEOUT_DEBUG_IMPL(QTest::currentTestFailed() || (expr), qt_test_timeoutValue, qt_test_step)
+#define QTRY_IMPL(expr, timeoutAsGiven)\
+ const auto qt_test_timeoutAsMs = [&] { \
+ /* make 5s work w/o user action: */ \
+ using namespace std::chrono_literals; \
+ return std::chrono::milliseconds{timeoutAsGiven}; \
+ }(); \
+ const int qt_test_step = qt_test_timeoutAsMs.count() < 350 ? qt_test_timeoutAsMs.count() / 7 + 1 : 50; \
+ const int qt_test_timeoutValue = qt_test_timeoutAsMs.count(); \
+ { QTRY_LOOP_IMPL(expr, qt_test_timeoutValue, qt_test_step) } \
+ QTRY_TIMEOUT_DEBUG_IMPL(expr, qt_test_timeoutValue, qt_test_step)
+// Ends with an if-block, so doesn't want a following semicolon.
// Will try to wait for the expression to become true while allowing event processing
#define QTRY_VERIFY_WITH_TIMEOUT(expr, timeout) \
do { \
- QTRY_IMPL((expr), timeout);\
+ QTRY_IMPL(expr, timeout) \
QVERIFY(expr); \
} while (false)
-#define QTRY_VERIFY(expr) QTRY_VERIFY_WITH_TIMEOUT((expr), 5000)
+#define QTRY_VERIFY(expr) QTRY_VERIFY_WITH_TIMEOUT(expr, 5s)
// Will try to wait for the expression to become true while allowing event processing
#define QTRY_VERIFY2_WITH_TIMEOUT(expr, messageExpression, timeout) \
do { \
- QTRY_IMPL((expr), timeout);\
+ QTRY_IMPL(expr, timeout) \
QVERIFY2(expr, messageExpression); \
} while (false)
-#define QTRY_VERIFY2(expr, messageExpression) QTRY_VERIFY2_WITH_TIMEOUT((expr), (messageExpression), 5000)
+#define QTRY_VERIFY2(expr, messageExpression) QTRY_VERIFY2_WITH_TIMEOUT(expr, messageExpression, 5s)
// Will try to wait for the comparison to become successful while allowing event processing
#define QTRY_COMPARE_WITH_TIMEOUT(expr, expected, timeout) \
do { \
- QTRY_IMPL(((expr) == (expected)), timeout);\
- QCOMPARE((expr), expected); \
+ QTRY_IMPL((expr) == (expected), timeout) \
+ QCOMPARE(expr, expected); \
+} while (false)
+
+#define QTRY_COMPARE(expr, expected) QTRY_COMPARE_WITH_TIMEOUT(expr, expected, 5s)
+
+#define QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, op, opId, timeout) \
+do { \
+ using Q_Cmp = QTest::Internal::Compare<QTest::ComparisonOperation::opId>; \
+ QTRY_IMPL(Q_Cmp::compare((computed), (baseline)), timeout) \
+ QCOMPARE_OP_IMPL(computed, baseline, op, opId); \
} while (false)
-#define QTRY_COMPARE(expr, expected) QTRY_COMPARE_WITH_TIMEOUT((expr), expected, 5000)
+#define QTRY_COMPARE_EQ_WITH_TIMEOUT(computed, baseline, timeout) \
+ QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, ==, Equal, timeout)
+
+#define QTRY_COMPARE_EQ(computed, baseline) QTRY_COMPARE_EQ_WITH_TIMEOUT(computed, baseline, 5s)
+
+#define QTRY_COMPARE_NE_WITH_TIMEOUT(computed, baseline, timeout) \
+ QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, !=, NotEqual, timeout)
+
+#define QTRY_COMPARE_NE(computed, baseline) QTRY_COMPARE_NE_WITH_TIMEOUT(computed, baseline, 5s)
+
+#define QTRY_COMPARE_LT_WITH_TIMEOUT(computed, baseline, timeout) \
+ QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, <, LessThan, timeout)
+
+#define QTRY_COMPARE_LT(computed, baseline) QTRY_COMPARE_LT_WITH_TIMEOUT(computed, baseline, 5s)
+
+#define QTRY_COMPARE_LE_WITH_TIMEOUT(computed, baseline, timeout) \
+ QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, <=, LessThanOrEqual, timeout)
+
+#define QTRY_COMPARE_LE(computed, baseline) QTRY_COMPARE_LE_WITH_TIMEOUT(computed, baseline, 5s)
+
+#define QTRY_COMPARE_GT_WITH_TIMEOUT(computed, baseline, timeout) \
+ QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, >, GreaterThan, timeout)
+
+#define QTRY_COMPARE_GT(computed, baseline) QTRY_COMPARE_GT_WITH_TIMEOUT(computed, baseline, 5s)
+
+#define QTRY_COMPARE_GE_WITH_TIMEOUT(computed, baseline, timeout) \
+ QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, >=, GreaterThanOrEqual, timeout)
+
+#define QTRY_COMPARE_GE(computed, baseline) QTRY_COMPARE_GE_WITH_TIMEOUT(computed, baseline, 5s)
#define QSKIP_INTERNAL(statement) \
do {\
QTest::qSkip(static_cast<const char *>(statement), __FILE__, __LINE__);\
- return;\
+ QTEST_SKIP_ACTION; \
} while (false)
#define QSKIP(statement, ...) QSKIP_INTERNAL(statement)
@@ -193,7 +272,7 @@ do {\
#define QEXPECT_FAIL(dataIndex, comment, mode)\
do {\
if (!QTest::qExpectFail(dataIndex, static_cast<const char *>(comment), QTest::mode, __FILE__, __LINE__))\
- return;\
+ QTEST_FAIL_ACTION; \
} while (false)
#define QFETCH(Type, name)\
@@ -205,12 +284,9 @@ do {\
#define QTEST(actual, testElement)\
do {\
if (!QTest::qTest(actual, testElement, #actual, #testElement, __FILE__, __LINE__))\
- return;\
+ QTEST_FAIL_ACTION; \
} while (false)
-#define QWARN(msg)\
- QTest::qWarn(static_cast<const char *>(msg), __FILE__, __LINE__)
-
#ifdef QT_TESTCASE_BUILDDIR
#ifndef QT_TESTCASE_SOURCEDIR
@@ -230,74 +306,71 @@ do {\
class QObject;
class QTestData;
-#define QTEST_COMPARE_DECL(KLASS)\
- template<> Q_TESTLIB_EXPORT char *toString<KLASS >(const KLASS &);
-
namespace QTest
{
namespace Internal {
+ [[noreturn]] Q_TESTLIB_EXPORT void throwOnFail();
+ [[noreturn]] Q_TESTLIB_EXPORT void throwOnSkip();
+ Q_TESTLIB_EXPORT void maybeThrowOnFail();
+ Q_TESTLIB_EXPORT void maybeThrowOnSkip();
+
Q_TESTLIB_EXPORT QString formatTryTimeoutDebugMessage(q_no_char8_t::QUtf8StringView expr, int timeout, int actual);
- template<typename T> // Output registered enums
- inline typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, char*>::type toString(T e)
+ template <ComparisonOperation> struct Compare;
+ template <> struct Compare<ComparisonOperation::Equal>
{
- QMetaEnum me = QMetaEnum::fromType<T>();
- return qstrdup(me.valueToKey(int(e))); // int cast is necessary to support enum classes
- }
-
- template <typename T>
- inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && std::is_enum_v<T>, char*>::type toString(const T &e)
+ template <typename T1, typename T2> static bool compare(T1 &&lhs, T2 &&rhs)
+ { return std::forward<T1>(lhs) == std::forward<T2>(rhs); }
+ };
+ template <> struct Compare<ComparisonOperation::NotEqual>
{
- return qstrdup(QByteArray::number(static_cast<std::underlying_type_t<T>>(e)).constData());
- }
-
- template <typename T> // Fallback
- inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && !std::is_enum_v<T>, char*>::type toString(const T &)
+ template <typename T1, typename T2> static bool compare(T1 &&lhs, T2 &&rhs)
+ { return std::forward<T1>(lhs) != std::forward<T2>(rhs); }
+ };
+ template <> struct Compare<ComparisonOperation::LessThan>
{
- return nullptr;
- }
+ template <typename T1, typename T2> static bool compare(T1 &&lhs, T2 &&rhs)
+ { return std::forward<T1>(lhs) < std::forward<T2>(rhs); }
+ };
+ template <> struct Compare<ComparisonOperation::LessThanOrEqual>
+ {
+ template <typename T1, typename T2> static bool compare(T1 &&lhs, T2 &&rhs)
+ { return std::forward<T1>(lhs) <= std::forward<T2>(rhs); }
+ };
+ template <> struct Compare<ComparisonOperation::GreaterThan>
+ {
+ template <typename T1, typename T2> static bool compare(T1 &&lhs, T2 &&rhs)
+ { return std::forward<T1>(lhs) > std::forward<T2>(rhs); }
+ };
+ template <> struct Compare<ComparisonOperation::GreaterThanOrEqual>
+ {
+ template <typename T1, typename T2> static bool compare(T1 &&lhs, T2 &&rhs)
+ { return std::forward<T1>(lhs) >= std::forward<T2>(rhs); }
+ };
- template<typename F> // Output QFlags of registered enumerations
- inline typename std::enable_if<QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f)
+ template <typename T1> const char *genericToString(const void *arg)
{
- const QMetaEnum me = QMetaEnum::fromType<F>();
- return qstrdup(me.valueToKeys(int(f)).constData());
+ using QTest::toString;
+ return toString(*static_cast<const T1 *>(arg));
}
- template <typename F> // Fallback: Output hex value
- inline typename std::enable_if<!QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f)
+ template <> inline const char *genericToString<char *>(const void *arg)
{
- const size_t space = 3 + 2 * sizeof(unsigned); // 2 for 0x, two hex digits per byte, 1 for '\0'
- char *msg = new char[space];
- qsnprintf(msg, space, "0x%x", unsigned(f));
- return msg;
+ using QTest::toString;
+ return toString(static_cast<const char *>(arg));
}
- } // namespace Internal
-
- template<typename T>
- inline char *toString(const T &t)
+ template <typename T> const char *pointerToString(const void *arg)
{
- return Internal::toString(t);
+ using QTest::toString;
+ return toString(static_cast<const T *>(arg));
}
- template <typename T1, typename T2>
- inline char *toString(const QPair<T1, T2> &pair);
-
- template <typename T1, typename T2>
- inline char *toString(const std::pair<T1, T2> &pair);
+ // Exported so Qt Quick Test can also use it for generating backtraces upon crashes.
+ Q_TESTLIB_EXPORT extern bool noCrashHandler;
- template <class... Types>
- inline char *toString(const std::tuple<Types...> &tuple);
-
- Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, int length);
- Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, int length);
- Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string);
- Q_TESTLIB_EXPORT char *toString(const char *);
- Q_TESTLIB_EXPORT char *toString(const volatile void *);
- Q_TESTLIB_EXPORT char *toString(const void *); // ### FIXME: Qt 7: Remove
- Q_TESTLIB_EXPORT char *toString(const volatile QObject *);
+ } // namespace Internal
Q_TESTLIB_EXPORT void qInit(QObject *testObject, int argc = 0, char **argv = nullptr);
Q_TESTLIB_EXPORT int qRun();
@@ -306,19 +379,66 @@ namespace QTest
Q_TESTLIB_EXPORT int qExec(QObject *testObject, int argc = 0, char **argv = nullptr);
Q_TESTLIB_EXPORT int qExec(QObject *testObject, const QStringList &arguments);
+#if QT_CONFIG(batch_test_support) || defined(Q_QDOC)
+ using TestEntryFunction = int (*)(int, char **);
+ Q_TESTLIB_EXPORT void qRegisterTestCase(const QString &name, TestEntryFunction entryFunction);
+#endif // QT_CONFIG(batch_test_support)
+
Q_TESTLIB_EXPORT void setMainSourcePath(const char *file, const char *builddir = nullptr);
+ Q_TESTLIB_EXPORT void setThrowOnFail(bool enable) noexcept;
+ Q_TESTLIB_EXPORT void setThrowOnSkip(bool enable) noexcept;
+
+ class ThrowOnFailEnabler {
+ Q_DISABLE_COPY_MOVE(ThrowOnFailEnabler)
+ public:
+ ThrowOnFailEnabler() { setThrowOnFail(true); }
+ ~ThrowOnFailEnabler() { setThrowOnFail(false); }
+ };
+
+ class ThrowOnSkipEnabler {
+ Q_DISABLE_COPY_MOVE(ThrowOnSkipEnabler)
+ public:
+ ThrowOnSkipEnabler() { setThrowOnSkip(true); }
+ ~ThrowOnSkipEnabler() { setThrowOnSkip(false); }
+ };
+
+ class ThrowOnFailDisabler {
+ Q_DISABLE_COPY_MOVE(ThrowOnFailDisabler)
+ public:
+ ThrowOnFailDisabler() { setThrowOnFail(false); }
+ ~ThrowOnFailDisabler() { setThrowOnFail(true); }
+ };
+
+ class ThrowOnSkipDisabler {
+ Q_DISABLE_COPY_MOVE(ThrowOnSkipDisabler)
+ public:
+ ThrowOnSkipDisabler() { setThrowOnSkip(false); }
+ ~ThrowOnSkipDisabler() { setThrowOnSkip(true); }
+ };
Q_TESTLIB_EXPORT bool qVerify(bool statement, const char *statementStr, const char *description,
const char *file, int line);
+ Q_DECL_COLD_FUNCTION
Q_TESTLIB_EXPORT void qFail(const char *message, const char *file, int line);
Q_TESTLIB_EXPORT void qSkip(const char *message, const char *file, int line);
Q_TESTLIB_EXPORT bool qExpectFail(const char *dataIndex, const char *comment, TestFailMode mode,
const char *file, int line);
+ Q_DECL_COLD_FUNCTION
+ Q_TESTLIB_EXPORT void qCaught(const char *expected, const char *what, const char *file, int line);
+ Q_DECL_COLD_FUNCTION
+ Q_TESTLIB_EXPORT void qCaught(const char *expected, const char *file, int line);
+#if QT_DEPRECATED_SINCE(6, 3)
+ QT_DEPRECATED_VERSION_X_6_3("Use qWarning() instead")
Q_TESTLIB_EXPORT void qWarn(const char *message, const char *file = nullptr, int line = 0);
+#endif
Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const char *message);
#if QT_CONFIG(regularexpression)
Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern);
#endif
+ Q_TESTLIB_EXPORT void failOnWarning(const char *message);
+#if QT_CONFIG(regularexpression)
+ Q_TESTLIB_EXPORT void failOnWarning(const QRegularExpression &messagePattern);
+#endif
#if QT_CONFIG(temporaryfile)
Q_TESTLIB_EXPORT QSharedPointer<QTemporaryDir> qExtractTestData(const QString &dirName);
@@ -336,14 +456,41 @@ namespace QTest
Q_TESTLIB_EXPORT const char *currentTestFunction();
Q_TESTLIB_EXPORT const char *currentDataTag();
Q_TESTLIB_EXPORT bool currentTestFailed();
+ Q_TESTLIB_EXPORT bool currentTestResolved();
+ Q_TESTLIB_EXPORT bool runningTest(); // Internal, for use by macros and QTestEventLoop.
Q_TESTLIB_EXPORT Qt::Key asciiToKey(char ascii);
Q_TESTLIB_EXPORT char keyToAscii(Qt::Key key);
+#if QT_DEPRECATED_SINCE(6, 4)
+ QT_DEPRECATED_VERSION_X_6_4("use an overload that takes a formatter callback, "
+ "or an overload that takes only failure message, if you "
+ "do not need to stringify the values")
+ Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg,
+ char *actualVal, char *expectedVal,
+ const char *actual, const char *expected,
+ const char *file, int line);
+#endif // QT_DEPRECATED_SINCE(6, 4)
+#if QT_DEPRECATED_SINCE(6, 8)
+ QT_DEPRECATED_VERSION_X_6_8("use an overload that takes a formatter callback, "
+ "or an overload that takes only failure message, if you "
+ "do not need to stringify the values")
+ Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg,
+ qxp::function_ref<const char*()> actualVal,
+ qxp::function_ref<const char*()> expectedVal,
+ const char *actual, const char *expected,
+ const char *file, int line);
+#endif // QT_DEPRECATED_SINCE(6, 8)
+ Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg,
+ const void *actualPtr, const void *expectedPtr,
+ const char *(*actualFormatter)(const void *),
+ const char *(*expectedFormatter)(const void *),
+ const char *actual, const char *expected,
+ const char *file, int line);
Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg,
- char *val1, char *val2,
const char *actual, const char *expected,
const char *file, int line);
+
Q_TESTLIB_EXPORT void addColumnInternal(int id, const char *name);
template <typename T>
@@ -356,17 +503,6 @@ namespace QTest
Q_TESTLIB_EXPORT QTestData &newRow(const char *dataTag);
Q_TESTLIB_EXPORT QTestData &addRow(const char *format, ...) Q_ATTRIBUTE_FORMAT_PRINTF(1, 2);
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- // kept after adding implementation of <T1, T2> out of paranoia:
- template <typename T>
- inline bool qCompare(T const &t1, T const &t2, const char *actual, const char *expected,
- const char *file, int line)
- {
- return compare_helper(t1 == t2, "Compared values are not the same",
- toString(t1), toString(t2), actual, expected, file, line);
- }
-#endif
-
Q_TESTLIB_EXPORT bool qCompare(qfloat16 const &t1, qfloat16 const &t2,
const char *actual, const char *expected, const char *file, int line);
@@ -390,10 +526,10 @@ namespace QTest
Q_TESTLIB_EXPORT bool qCompare(QStringView t1, QStringView t2,
const char *actual, const char *expected,
const char *file, int line);
- Q_TESTLIB_EXPORT bool qCompare(QStringView t1, const QLatin1String &t2,
+ Q_TESTLIB_EXPORT bool qCompare(QStringView t1, const QLatin1StringView &t2,
const char *actual, const char *expected,
const char *file, int line);
- Q_TESTLIB_EXPORT bool qCompare(const QLatin1String &t1, QStringView t2,
+ Q_TESTLIB_EXPORT bool qCompare(const QLatin1StringView &t1, QStringView t2,
const char *actual, const char *expected,
const char *file, int line);
inline bool qCompare(const QString &t1, const QString &t2,
@@ -402,13 +538,13 @@ namespace QTest
{
return qCompare(QStringView(t1), QStringView(t2), actual, expected, file, line);
}
- inline bool qCompare(const QString &t1, const QLatin1String &t2,
+ inline bool qCompare(const QString &t1, const QLatin1StringView &t2,
const char *actual, const char *expected,
const char *file, int line)
{
return qCompare(QStringView(t1), t2, actual, expected, file, line);
}
- inline bool qCompare(const QLatin1String &t1, const QString &t2,
+ inline bool qCompare(const QLatin1StringView &t1, const QString &t2,
const char *actual, const char *expected,
const char *file, int line)
{
@@ -418,75 +554,72 @@ namespace QTest
inline bool compare_ptr_helper(const volatile void *t1, const volatile void *t2, const char *actual,
const char *expected, const char *file, int line)
{
+ auto formatter = Internal::pointerToString<void>;
return compare_helper(t1 == t2, "Compared pointers are not the same",
- toString(t1), toString(t2), actual, expected, file, line);
+ const_cast<const void *>(t1), const_cast<const void *>(t2),
+ formatter, formatter, actual, expected, file, line);
}
inline bool compare_ptr_helper(const volatile QObject *t1, const volatile QObject *t2, const char *actual,
const char *expected, const char *file, int line)
{
+ auto formatter = Internal::pointerToString<QObject>;
return compare_helper(t1 == t2, "Compared QObject pointers are not the same",
- toString(t1), toString(t2), actual, expected, file, line);
+ const_cast<const QObject *>(t1), const_cast<const QObject *>(t2),
+ formatter, formatter, actual, expected, file, line);
}
inline bool compare_ptr_helper(const volatile QObject *t1, std::nullptr_t, const char *actual,
const char *expected, const char *file, int line)
{
+ auto lhsFormatter = Internal::pointerToString<QObject>;
+ auto rhsFormatter = Internal::genericToString<std::nullptr_t>;
return compare_helper(t1 == nullptr, "Compared QObject pointers are not the same",
- toString(t1), toString(nullptr), actual, expected, file, line);
+ const_cast<const QObject *>(t1), nullptr,
+ lhsFormatter, rhsFormatter, actual, expected, file, line);
}
inline bool compare_ptr_helper(std::nullptr_t, const volatile QObject *t2, const char *actual,
const char *expected, const char *file, int line)
{
+ auto lhsFormatter = Internal::genericToString<std::nullptr_t>;
+ auto rhsFormatter = Internal::pointerToString<QObject>;
return compare_helper(nullptr == t2, "Compared QObject pointers are not the same",
- toString(nullptr), toString(t2), actual, expected, file, line);
+ nullptr, const_cast<const QObject *>(t2),
+ lhsFormatter, rhsFormatter, actual, expected, file, line);
}
inline bool compare_ptr_helper(const volatile void *t1, std::nullptr_t, const char *actual,
const char *expected, const char *file, int line)
{
+ auto lhsFormatter = Internal::pointerToString<void>;
+ auto rhsFormatter = Internal::genericToString<std::nullptr_t>;
return compare_helper(t1 == nullptr, "Compared pointers are not the same",
- toString(t1), toString(nullptr), actual, expected, file, line);
+ const_cast<const void *>(t1), nullptr,
+ lhsFormatter, rhsFormatter, actual, expected, file, line);
}
inline bool compare_ptr_helper(std::nullptr_t, const volatile void *t2, const char *actual,
const char *expected, const char *file, int line)
{
+ auto lhsFormatter = Internal::genericToString<std::nullptr_t>;
+ auto rhsFormatter = Internal::pointerToString<void>;
return compare_helper(nullptr == t2, "Compared pointers are not the same",
- toString(nullptr), toString(t2), actual, expected, file, line);
+ nullptr, const_cast<const void *>(t2),
+ lhsFormatter, rhsFormatter, actual, expected, file, line);
}
- Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual,
- const char *expected, const char *file, int line);
-
- Q_TESTLIB_EXPORT char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...);
-
-#ifndef Q_QDOC
- QTEST_COMPARE_DECL(short)
- QTEST_COMPARE_DECL(ushort)
- QTEST_COMPARE_DECL(int)
- QTEST_COMPARE_DECL(uint)
- QTEST_COMPARE_DECL(long)
- QTEST_COMPARE_DECL(ulong)
- QTEST_COMPARE_DECL(qint64)
- QTEST_COMPARE_DECL(quint64)
-
- QTEST_COMPARE_DECL(float)
- QTEST_COMPARE_DECL(double)
- QTEST_COMPARE_DECL(qfloat16)
- QTEST_COMPARE_DECL(char)
- QTEST_COMPARE_DECL(signed char)
- QTEST_COMPARE_DECL(unsigned char)
- QTEST_COMPARE_DECL(bool)
-#endif
-
- template <typename T1, typename T2>
+ template <typename T1, typename T2 = T1>
inline bool qCompare(const T1 &t1, const T2 &t2, const char *actual, const char *expected,
const char *file, int line)
{
+ using D1 = std::decay_t<T1>;
+ using D2 = std::decay_t<T2>;
+ using Internal::genericToString;
return compare_helper(t1 == t2, "Compared values are not the same",
- toString(t1), toString(t2), actual, expected, file, line);
+ std::addressof(t1), std::addressof(t2),
+ genericToString<D1>, genericToString<D2>,
+ actual, expected, file, line);
}
inline bool qCompare(double const &t1, float const &t2, const char *actual,
@@ -572,9 +705,41 @@ namespace QTest
return qCompare(actual, *static_cast<const T *>(QTest::qElementData(elementName,
qMetaTypeId<T>())), actualStr, expected, file, line);
}
+
+#if QT_DEPRECATED_SINCE(6, 8)
+ QT_DEPRECATED_VERSION_X_6_8("use the overload without qxp::function_ref")
+ Q_TESTLIB_EXPORT bool reportResult(bool success, qxp::function_ref<const char*()> lhs,
+ qxp::function_ref<const char*()> rhs,
+ const char *lhsExpr, const char *rhsExpr,
+ ComparisonOperation op, const char *file, int line);
+#endif // QT_DEPRECATED_SINCE(6, 8)
+
+ Q_TESTLIB_EXPORT bool reportResult(bool success, const void *lhs, const void *rhs,
+ const char *(*lhsFormatter)(const void*),
+ const char *(*rhsFormatter)(const void*),
+ const char *lhsExpr, const char *rhsExpr,
+ ComparisonOperation op, const char *file, int line);
+
+ template <ComparisonOperation op, typename T1, typename T2 = T1>
+ inline bool qCompareOp(T1 &&lhs, T2 &&rhs, const char *lhsExpr, const char *rhsExpr,
+ const char *file, int line)
+ {
+ using D1 = std::decay_t<T1>;
+ using D2 = std::decay_t<T2>;
+ using Internal::genericToString;
+ using Comparator = Internal::Compare<op>;
+
+ /* assumes that op does not actually move from lhs and rhs */
+ bool result = Comparator::compare(std::forward<T1>(lhs), std::forward<T2>(rhs));
+ return reportResult(result, std::addressof(lhs), std::addressof(rhs),
+ genericToString<D1>, genericToString<D2>,
+ lhsExpr, rhsExpr, op, file, line);
+
+ }
}
-#undef QTEST_COMPARE_DECL
+
+#define QWARN(msg) QTest::qWarn(static_cast<const char *>(msg), __FILE__, __LINE__)
QT_END_NAMESPACE
diff --git a/src/testlib/qtestcase.qdoc b/src/testlib/qtestcase.qdoc
index 2a480fb349..6a067c351f 100644
--- a/src/testlib/qtestcase.qdoc
+++ b/src/testlib/qtestcase.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\namespace QTest
@@ -46,8 +22,10 @@
You can use \l QVERIFY2() when it is practical and valuable to put additional
information into the test failure report.
+//! [macro-usage-limitation]
\note This macro can only be used in a test function that is invoked
by the test framework.
+//! [macro-usage-limitation]
For example, the following code shows this macro being used to verify that a
\l QSignalSpy object is valid:
@@ -58,7 +36,8 @@
\c QVERIFY(x == y), because it reports both the expected and actual value
when the comparison fails.
- \sa QCOMPARE(), QTRY_VERIFY(), QSignalSpy, QEXPECT_FAIL()
+ \sa QCOMPARE(), QTRY_VERIFY(), QSignalSpy, QEXPECT_FAIL(), QCOMPARE_EQ(),
+ QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE()
*/
/*! \macro QVERIFY2(condition, message)
@@ -97,7 +76,8 @@
\c {FAIL! : tst_QFile::open_write() 'opened' returned FALSE.
(open /tmp/qt.a3B42Cd: No space left on device)}
- \sa QVERIFY(), QCOMPARE(), QEXPECT_FAIL()
+ \sa QVERIFY(), QCOMPARE(), QEXPECT_FAIL(), QCOMPARE_EQ(), QCOMPARE_NE(),
+ QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE()
*/
/*! \macro QCOMPARE(actual, expected)
@@ -123,25 +103,27 @@
\snippet code/src_qtestlib_qtestcase.cpp 2
When comparing floating-point types (\c float, \c double, and \c qfloat16),
- \l {qFuzzyCompare()} is used for finite values. If \l {qFuzzyIsNull()} is true for
- both values, they are also considered equal. Infinities match if they have
- the same sign, and any NaN as actual value matches with any NaN as expected
- value (even though NaN != NaN, even when they're identical).
+ \l {qFuzzyCompare()} is used for finite values. If \l {<QtNumeric>::}{qFuzzyIsNull()}
+ is true for both values, they are also considered equal. Infinities
+ match if they have the same sign, and any NaN as actual value matches
+ with any NaN as expected value (even though NaN != NaN, even when
+ they're identical).
When comparing QList, arrays and initializer lists of the value type
can be passed as expected value:
\snippet code/src_qtestlib_qtestcase.cpp 34
- Note that using initializer lists requires a defining a helper macro
+ Note that using initializer lists requires defining a helper macro
to prevent the preprocessor from interpreting the commas as macro argument
delimiters:
\snippet code/src_qtestlib_qtestcase.cpp 35
- \note QCOMPARE() can only be used in a test function that is invoked
- by the test framework.
+ \include qtestcase.qdoc macro-usage-limitation
- For your own classes, you can use \l QTest::toString() to format values for
- outputting into the test log.
+//! [to-string-overload-desc]
+ For your own classes, you can overload \l QTest::toString() to format values
+ for output into the test log.
+//! [to-string-overload-desc]
Example:
\snippet code/src_qtestlib_qtestcase_snippet.cpp 34
@@ -150,23 +132,217 @@
be released with \c delete[] (rather than \c free() or plain \c delete) once
the calling code is done with it.
- \sa QVERIFY(), QTRY_COMPARE(), QTest::toString(), QEXPECT_FAIL()
+ \sa QVERIFY(), QTRY_COMPARE(), QTest::toString(), QEXPECT_FAIL(),
+ QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(),
+ QCOMPARE_GT(), QCOMPARE_GE()
+*/
+
+/*! \macro QCOMPARE_EQ(computed, baseline)
+ \since 6.4
+
+ \relates QTest
+
+ The QCOMPARE_EQ() macro checks that \a computed is equal to \a baseline using
+ the equality operator. If that is true, execution continues. If not, a
+ failure is recorded in the test log and the test function returns without
+ attempting any later checks.
+
+ It is generally similar to calling \c {QVERIFY(computed == baseline);}
+ but prints a formatted error message reporting \a computed and \a baseline argument
+ expressions and values in case of failure.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \include qtestcase.qdoc to-string-overload-desc
+
+ \note Unlike QCOMPARE(), this macro does not provide overloads for custom
+ types and pointers. So passing e.g. two \c {const char *} values as
+ parameters will compare \e pointers, while QCOMPARE() does a comparison of
+ C-style strings.
+
+ \sa QCOMPARE(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(),
+ QCOMPARE_GE()
+*/
+
+/*! \macro QCOMPARE_NE(computed, baseline)
+ \since 6.4
+
+ \relates QTest
+
+ The QCOMPARE_NE() macro checks that \a computed is not equal to \a baseline using
+ the inequality operator. If that is true, execution continues. If not, a
+ failure is recorded in the test log and the test function returns without
+ attempting any later checks.
+
+ It is generally similar to calling \c {QVERIFY(computed != baseline);}
+ but prints a formatted error message reporting \a computed and \a baseline argument
+ expressions and values in case of failure.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \include qtestcase.qdoc to-string-overload-desc
+
+ \sa QCOMPARE_EQ(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE()
+*/
+
+/*! \macro QCOMPARE_LT(computed, baseline)
+ \since 6.4
+
+ \relates QTest
+
+ The QCOMPARE_LT() macro checks that \a computed is less than \a baseline using the
+ less-than operator. If that is true, execution continues. If not, a failure
+ is recorded in the test log and the test function returns without attempting
+ any later checks.
+
+ It is generally similar to calling \c {QVERIFY(computed < baseline);}
+ but prints a formatted error message reporting \a computed and \a baseline argument
+ expressions and values in case of failure.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \include qtestcase.qdoc to-string-overload-desc
+
+ \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE()
+*/
+
+/*! \macro QCOMPARE_LE(computed, baseline)
+ \since 6.4
+
+ \relates QTest
+
+ The QCOMPARE_LE() macro checks that \a computed is at most \a baseline using the
+ less-than-or-equal-to operator. If that is true, execution continues. If
+ not, a failure is recorded in the test log and the test function returns
+ without attempting any later checks.
+
+ It is generally similar to calling \c {QVERIFY(computed <= baseline);}
+ but prints a formatted error message reporting \a computed and \a baseline argument
+ expressions and values in case of failure.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \include qtestcase.qdoc to-string-overload-desc
+
+ \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_GT(), QCOMPARE_GE()
+*/
+
+/*! \macro QCOMPARE_GT(computed, baseline)
+ \since 6.4
+
+ \relates QTest
+
+ The QCOMPARE_GT() macro checks that \a computed is greater than \a baseline using
+ the greater-than operator. If that is true, execution continues. If not, a
+ failure is recorded in the test log and the test function returns without
+ attempting any later checks.
+
+ It is generally similar to calling \c {QVERIFY(computed > baseline);}
+ but prints a formatted error message reporting \a computed and \a baseline argument
+ expressions and values in case of failure.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \include qtestcase.qdoc to-string-overload-desc
+
+ \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GE()
+*/
+
+/*! \macro QCOMPARE_GE(computed, baseline)
+ \since 6.4
+
+ \relates QTest
+
+ The QCOMPARE_GE() macro checks that \a computed is at least \a baseline using the
+ greater-than-or-equal-to operator. If that is true, execution continues. If
+ not, a failure is recorded in the test log and the test function returns
+ without attempting any later checks.
+
+ It is generally similar to calling \c {QVERIFY(computed >= baseline);}
+ but prints a formatted error message reporting \a computed and \a baseline argument
+ expressions and values in case of failure.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \include qtestcase.qdoc to-string-overload-desc
+
+ \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT()
*/
/*! \macro QVERIFY_EXCEPTION_THROWN(expression, exceptiontype)
\since 5.3
\relates QTest
+ \deprecated [6.3] Use \c{QVERIFY_THROWS_EXCEPTION(exceptiontype, expression)} instead.
+*/
+
+/*!
+ \macro QVERIFY_THROWS_EXCEPTION(exceptiontype, ...)
+ \relates QTest
+ \since 6.3
+
+ The QVERIFY_THROWS_EXCEPTION macro executes the expression given in the variadic
+ argument and expects to catch an exception thrown from the expression.
+
+ There are several possible outcomes:
+
+ \list
+ \li If the expression throws an exception that is either the same as
+ \a exceptiontype or derived from \a exceptiontype, then execution will continue.
+
+ \li Otherwise, if the expression throws no exception, or the
+ exception thrown derives from \c{std::exception}, then a failure
+ will be recorded in the test log and the macro returns early
+ (from enclosing function).
+
+ \li If the thrown exception derives neither from \c{std::exception} nor from
+ \a exceptiontype, a failure will be recorded in the test log, and the exception is
+ re-thrown. This avoids problems with e.g. pthread cancellation exceptions.
+ \endlist
+
+ The macro uses variadic arguments so the expression can contain commas that the
+ preprocessor considers argument separators, e.g. as in
+ \code
+ QVERIFY_THROWS_EXCEPTION(std::bad_alloc,
+ // macro arguments: ^ exceptiontype
+ std::vector<std::pair<int, long>>{42'000'000'000, {42, 42L}});
+ // macro arguments: \---------- 1 ----------/ \-------- 2 --------/ \3/ \ 4 /
+ // \----------------------- expression -----------------------/
+ \endcode
+
+ \note This macro can only be used in a test function that is invoked
+ by the test framework.
+*/
+
+/*!
+ \macro QVERIFY_THROWS_NO_EXCEPTION(...)
+ \since 6.3
+
+ \relates QTest
+
+ The QVERIFY_THROWS_NO_EXCEPTION macro executes the expression given in its
+ variadic argument and tries to catch any exception thrown from the expression.
+
+ There are several different outcomes:
+
+ \list
+ \li If the expression does not throw an exception, then execution will continue.
- The QVERIFY_EXCEPTION_THROWN macro executes an \a expression and tries
- to catch an exception thrown from the \a expression. If the \a expression
- throws an exception and its type is the same as \a exceptiontype
- or \a exceptiontype is substitutable with the type of thrown exception
- (i.e. usually the type of thrown exception is publicly derived
- from \a exceptiontype) then execution will be continued. If not-substitutable
- type of exception is thrown or the \a expression doesn't throw an exception
- at all, then a failure will be recorded in the test log and
- the test won't be executed further.
+ \li Otherwise, if an exception derived from \c{std::exception} is caught, a failure
+ will be recorded in the test log and the macro returns early (implicit return from
+ enclosing function).
+
+ \li If an exception not derived from \c{std::exception} is caught, a failure will be
+ recorded in the test log and the exception will be re-thrown. This avoids problems
+ with e.g. pthread cancellation exceptions.
+ \endlist
+
+ The macro uses variadic arguments so the expression can contain commas that the
+ preprocessor considers argument separators, e.g. as in
+ \code
+ QVERIFY_THROWS_NO_EXCEPTION(std::pair<int, long>{42, 42L});
+ // macro arguments: \---- 1 ----/ \-- 2 -/ \3 /
+ \endcode
\note This macro can only be used in a test function that is invoked
by the test framework.
@@ -183,6 +359,10 @@
is reached, a failure is recorded in the test log and the test won't be
executed further.
+ //![chrono-timeout]
+ Since Qt 6.8, the \a timeout can also be a \c{std::chrono} literal such as \c{2s}.
+ //![chrono-timeout]
+
\note This macro can only be used in a test function that is invoked
by the test framework.
@@ -214,9 +394,11 @@
except that it outputs a verbose \a message when \a condition is still false
after the specified \a timeout (in milliseconds). The \a message is a plain C string.
+ \include qtestcase.qdoc chrono-timeout
+
Example:
\code
- QTRY_VERIFY2_WITH_TIMEOUT(list.size() > 2, QByteArray::number(list.size()).constData(), 10000);
+ QTRY_VERIFY2_WITH_TIMEOUT(list.size() > 2, QByteArray::number(list.size()).constData(), 10s);
\endcode
\note This macro can only be used in a test function that is invoked
@@ -237,7 +419,7 @@
Example:
\code
- QTRY_VERIFY2_WITH_TIMEOUT(list.size() > 2, QByteArray::number(list.size()).constData());
+ QTRY_VERIFY2(list.size() > 2, QByteArray::number(list.size()).constData());
\endcode
\note This macro can only be used in a test function that is invoked
@@ -258,6 +440,8 @@
will be processed. If the timeout is reached, a failure is recorded in the
test log and the test won't be executed further.
+ \include qtestcase.qdoc chrono-timeout
+
\note This macro can only be used in a test function that is invoked
by the test framework.
@@ -279,6 +463,180 @@
QEXPECT_FAIL()
*/
+/*! \macro QTRY_COMPARE_EQ_WITH_TIMEOUT(computed, baseline, timeout)
+ \since 6.4
+ \relates QTest
+
+ This macro is similar to QCOMPARE_EQ(), but performs the comparison of the
+ \a computed and \a baseline values repeatedly, until either the comparison returns
+ \c true or the \a timeout (in milliseconds) is reached. Between each
+ comparison, events will be processed. If the timeout is reached, a failure
+ is recorded in the test log and the test won't be executed further.
+
+ \include qtestcase.qdoc chrono-timeout
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_EQ(), QTRY_COMPARE_EQ()
+*/
+
+/*! \macro QTRY_COMPARE_EQ(computed, baseline)
+ \since 6.4
+ \relates QTest
+
+ Performs comparison of \a computed and \a baseline values by invoking
+ QTRY_COMPARE_EQ_WITH_TIMEOUT with a timeout of five seconds.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_EQ(), QTRY_COMPARE_EQ_WITH_TIMEOUT()
+*/
+
+/*! \macro QTRY_COMPARE_NE_WITH_TIMEOUT(computed, baseline, timeout)
+ \since 6.4
+ \relates QTest
+
+ This macro is similar to QCOMPARE_NE(), but performs the comparison of the
+ \a computed and \a baseline values repeatedly, until either the comparison returns
+ \c true or the \a timeout (in milliseconds) is reached. Between each
+ comparison, events will be processed. If the timeout is reached, a failure
+ is recorded in the test log and the test won't be executed further.
+
+ \include qtestcase.qdoc chrono-timeout
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_NE(), QTRY_COMPARE_NE()
+*/
+
+/*! \macro QTRY_COMPARE_NE(computed, baseline)
+ \since 6.4
+ \relates QTest
+
+ Performs comparison of \a computed and \a baseline values by invoking
+ QTRY_COMPARE_NE_WITH_TIMEOUT with a timeout of five seconds.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_NE(), QTRY_COMPARE_NE_WITH_TIMEOUT()
+*/
+
+/*! \macro QTRY_COMPARE_LT_WITH_TIMEOUT(computed, baseline, timeout)
+ \since 6.4
+ \relates QTest
+
+ This macro is similar to QCOMPARE_LT(), but performs the comparison of the
+ \a computed and \a baseline values repeatedly, until either the comparison returns
+ \c true or the \a timeout (in milliseconds) is reached. Between each
+ comparison, events will be processed. If the timeout is reached, a failure
+ is recorded in the test log and the test won't be executed further.
+
+ \include qtestcase.qdoc chrono-timeout
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_LT(), QTRY_COMPARE_LT()
+*/
+
+/*! \macro QTRY_COMPARE_LT(computed, baseline)
+ \since 6.4
+ \relates QTest
+
+ Performs comparison of \a computed and \a baseline values by invoking
+ QTRY_COMPARE_LT_WITH_TIMEOUT with a timeout of five seconds.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_LT(), QTRY_COMPARE_LT_WITH_TIMEOUT()
+*/
+
+/*! \macro QTRY_COMPARE_LE_WITH_TIMEOUT(computed, baseline, timeout)
+ \since 6.4
+ \relates QTest
+
+ This macro is similar to QCOMPARE_LE(), but performs the comparison of the
+ \a computed and \a baseline values repeatedly, until either the comparison returns
+ \c true or the \a timeout (in milliseconds) is reached. Between each
+ comparison, events will be processed. If the timeout is reached, a failure
+ is recorded in the test log and the test won't be executed further.
+
+ \include qtestcase.qdoc chrono-timeout
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_LE(), QTRY_COMPARE_LE()
+*/
+
+/*! \macro QTRY_COMPARE_LE(computed, baseline)
+ \since 6.4
+ \relates QTest
+
+ Performs comparison of \a computed and \a baseline values by invoking
+ QTRY_COMPARE_LE_WITH_TIMEOUT with a timeout of five seconds.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_LE(), QTRY_COMPARE_LE_WITH_TIMEOUT()
+*/
+
+/*! \macro QTRY_COMPARE_GT_WITH_TIMEOUT(computed, baseline, timeout)
+ \since 6.4
+ \relates QTest
+
+ This macro is similar to QCOMPARE_GT(), but performs the comparison of the
+ \a computed and \a baseline values repeatedly, until either the comparison returns
+ \c true or the \a timeout (in milliseconds) is reached. Between each
+ comparison, events will be processed. If the timeout is reached, a failure
+ is recorded in the test log and the test won't be executed further.
+
+ \include qtestcase.qdoc chrono-timeout
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_GT(), QTRY_COMPARE_GT()
+*/
+
+/*! \macro QTRY_COMPARE_GT(computed, baseline)
+ \since 6.4
+ \relates QTest
+
+ Performs comparison of \a computed and \a baseline values by invoking
+ QTRY_COMPARE_GT_WITH_TIMEOUT with a timeout of five seconds.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_GT(), QTRY_COMPARE_GT_WITH_TIMEOUT()
+*/
+
+/*! \macro QTRY_COMPARE_GE_WITH_TIMEOUT(computed, baseline, timeout)
+ \since 6.4
+ \relates QTest
+
+ This macro is similar to QCOMPARE_GE(), but performs the comparison of the
+ \a computed and \a baseline values repeatedly, until either the comparison returns
+ \c true or the \a timeout (in milliseconds) is reached. Between each
+ comparison, events will be processed. If the timeout is reached, a failure
+ is recorded in the test log and the test won't be executed further.
+
+ \include qtestcase.qdoc chrono-timeout
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_GE(), QTRY_COMPARE_GE()
+*/
+
+/*! \macro QTRY_COMPARE_GE(computed, baseline)
+ \since 6.4
+ \relates QTest
+
+ Performs comparison of \a computed and \a baseline values by invoking
+ QTRY_COMPARE_GE_WITH_TIMEOUT with a timeout of five seconds.
+
+ \include qtestcase.qdoc macro-usage-limitation
+
+ \sa QCOMPARE_GE(), QTRY_COMPARE_GE_WITH_TIMEOUT()
+*/
+
/*! \macro QFETCH(type, name)
\relates QTest
@@ -336,6 +694,7 @@
\relates QTest
\threadsafe
+ \deprecated Use qWarning() instead.
Appends \a message as a warning to the test log. This macro can be used anywhere
in your tests.
@@ -443,7 +802,8 @@
failure will be reported.
If a \l QVERIFY() or \l QCOMPARE() is marked as an expected failure,
- but passes instead, an unexpected pass (XPASS) is written to the test log.
+ but passes instead, an unexpected pass (XPASS) is written to the test log
+ and will be counted as a test failure.
The parameter \a dataIndex describes for which entry in the test data the
failure is expected. Pass an empty string (\c{""}) if the failure
@@ -452,7 +812,8 @@
\a comment will be appended to the test log for the expected failure.
\a mode is a \l QTest::TestFailMode and sets whether the test should
- continue to execute or not.
+ continue to execute or not. The \a mode is applied regardless of
+ whether the expected test failure occurs.
\note This macro can only be used in a test function that is invoked
by the test framework.
@@ -469,7 +830,7 @@
\snippet code/src_qtestlib_qtestcase.cpp 10
The above testfunction will not continue executing for the test data
- entry \c{data27}.
+ entry \c{data27} (regardless of the value of \c i).
\sa QTest::TestFailMode, QVERIFY(), QCOMPARE()
*/
@@ -621,8 +982,8 @@
this macro.
Unlike QBENCHMARK, the contents of the contained code block is only run
- once. The elapsed time will be reported as "0" if it's to short to
- be measured by the selected backend. (Use)
+ once. The elapsed time will be reported as "0" if it's too short to
+ be measured by the selected backend.
\sa {Qt Test Overview#Creating a Benchmark}{Creating a Benchmark},
{Chapter 5: Writing a Benchmark}{Writing a Benchmark}
@@ -630,14 +991,16 @@
/*! \enum QTest::TestFailMode
- This enum describes the modes for handling an expected failure of the
- \l QVERIFY() or \l QCOMPARE() macros.
+ This enum describes the modes for handling a check, such as by \l
+ QVERIFY() or \l QCOMPARE() macros, that is known to fail. The mode
+ applies regardless of whether the check fails or succeeds.
- \value Abort Aborts the execution of the test. Use this mode when it
- doesn't make sense to execute the test any further after the
- expected failure.
+ \value Abort Aborts the execution of the test. Use this mode when
+ it doesn't make sense to execute the test any further after
+ the problematic check.
- \value Continue Continues execution of the test after the expected failure.
+ \value Continue Continues execution of the test after the
+ problematic check.
\sa QEXPECT_FAIL()
*/
@@ -888,7 +1251,7 @@
\sa QTest::keyClick()
*/
-/*! \fn void QTest::mousePress(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mousePress(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier, QPoint pos = QPoint(), int delay=-1)
Simulates pressing a mouse \a button with an optional \a modifier
on a \a widget. The position is defined by \a pos; the default
@@ -899,7 +1262,7 @@
\sa QTest::mouseRelease(), QTest::mouseClick()
*/
-/*! \fn void QTest::mousePress(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mousePress(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, QPoint pos = QPoint(), int delay=-1)
\overload
\since 5.0
@@ -912,18 +1275,27 @@
\sa QTest::mouseRelease(), QTest::mouseClick()
*/
-/*! \fn void QTest::mouseRelease(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mouseRelease(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier, QPoint pos = QPoint(), int delay=-1)
Simulates releasing a mouse \a button with an optional \a modifier
on a \a widget. The position of the release is defined by \a pos;
the default position is the center of the widget. If \a delay is
specified, the test will wait for the specified amount of
- milliseconds before releasing the button.
+ milliseconds before releasing the button; otherwise, it will wait for a
+ default amount of time (1 ms), which can be overridden via
+ \l {Testing Options}{command-line arguments}.
+
+ \note If you wish to test a double-click by sending events individually,
+ specify a short delay, greater than the default, on both mouse release events.
+ The total of the delays for the press, release, press and release must be
+ less than QStyleHints::mouseDoubleClickInterval(). But if you don't need
+ to check state between events, it's better to use QTest::mouseDClick().
+ \snippet code/src_qtestlib_qtestcase_snippet.cpp 35
\sa QTest::mousePress(), QTest::mouseClick()
*/
-/*! \fn void QTest::mouseRelease(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mouseRelease(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, QPoint pos = QPoint(), int delay=-1)
\overload
\since 5.0
@@ -931,12 +1303,21 @@
on a \a window. The position of the release is defined by \a pos;
the default position is the center of the window. If \a delay is
specified, the test will wait for the specified amount of
- milliseconds before releasing the button.
+ milliseconds before releasing the button; otherwise, it will wait for a
+ default amount of time (1 ms), which can be overridden via
+ \l {Testing Options}{command-line arguments}.
+
+ \note If you wish to test a double-click by sending events individually,
+ specify a short delay, greater than the default, on both mouse release events.
+ The total of the delays for the press, release, press and release must be
+ less than QStyleHints::mouseDoubleClickInterval(). But if you don't need
+ to check state between events, it's better to use QTest::mouseDClick().
+ \snippet code/src_qtestlib_qtestcase_snippet.cpp 35
\sa QTest::mousePress(), QTest::mouseClick()
*/
-/*! \fn void QTest::mouseClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mouseClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier, QPoint pos = QPoint(), int delay=-1)
Simulates clicking a mouse \a button with an optional \a modifier
on a \a widget. The position of the click is defined by \a pos;
@@ -947,7 +1328,7 @@
\sa QTest::mousePress(), QTest::mouseRelease()
*/
-/*! \fn void QTest::mouseClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mouseClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, QPoint pos = QPoint(), int delay=-1)
\overload
\since 5.0
@@ -960,7 +1341,7 @@
\sa QTest::mousePress(), QTest::mouseRelease()
*/
-/*! \fn void QTest::mouseDClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mouseDClick(QWidget *widget, Qt::MouseButton button, Qt::KeyboardModifiers modifier, QPoint pos = QPoint(), int delay=-1)
Simulates double clicking a mouse \a button with an optional \a
modifier on a \a widget. The position of the click is defined by
@@ -971,7 +1352,7 @@
\sa QTest::mouseClick()
*/
-/*! \fn void QTest::mouseDClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTest::mouseDClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey, QPoint pos = QPoint(), int delay=-1)
\overload
\since 5.0
@@ -1002,11 +1383,15 @@
moving the mouse pointer.
*/
-/*!
- \fn template <typename T1, typename T2> char *QTest::toString(const QPair<T1, T2> &pair)
- \overload
- \since 5.11
- Returns a textual representation of the \a pair.
+/*! \fn void QTest::wheelEvent(QWindow *window, QPointF pos, QPoint angleDelta, QPoint pixelDelta = QPoint(0, 0), Qt::KeyboardModifiers stateKey = Qt::NoModifier, Qt::ScrollPhase phase = Qt::NoScrollPhase)
+ \since 6.8
+
+ Simulates a wheel event within \a window at position \a pos in local
+ window coordinates. \a angleDelta contains the wheel rotation angle.
+ A positive value means forward rotation, and a negative one means backward.
+ \a pixelDelta contains the scrolling distance in pixels on screen. This value can be null.
+ The keyboard states at the time of the event are specified by \a stateKey.
+ The scrolling phase of the event is specified by \a phase.
*/
/*!
@@ -1038,7 +1423,7 @@
*/
/*!
- \fn template<typename T> char *QTest::toString(const T &value)
+ \fn template<typename T, QTest::Internal::is_suitable_type_v<T> = true> char *QTest::toString(const T &value)
Returns a textual representation of \a value. This function is used by
\l QCOMPARE() to output verbose information in case of a test failure.
@@ -1074,7 +1459,7 @@
*/
/*!
- \fn char *QTest::toString(const QLatin1String &string)
+ \fn char *QTest::toString(const QLatin1StringView &string)
\overload
Returns a textual representation of the given \a string.
@@ -1223,7 +1608,8 @@
*/
/*!
- \fn char *QTest::toString(QSizePolicy::ControlType ct)
+ \fn char *toString(QSizePolicy::ControlType ct)
+ \relates QTest
\overload
\since 5.5
@@ -1231,7 +1617,8 @@
*/
/*!
- \fn char *QTest::toString(QSizePolicy::ControlTypes cts)
+ \fn char *toString(QSizePolicy::ControlTypes cts)
+ \relates QTest
\overload
\since 5.5
@@ -1239,7 +1626,8 @@
*/
/*!
- \fn char *QTest::toString(QSizePolicy::Policy p)
+ \fn char *toString(QSizePolicy::Policy p)
+ \relates QTest
\overload
\since 5.5
@@ -1247,7 +1635,8 @@
*/
/*!
- \fn char *QTest::toString(QSizePolicy sp)
+ \fn char *toString(QSizePolicy sp)
+ \relates QTest
\overload
\since 5.5
@@ -1255,9 +1644,10 @@
*/
/*!
- \fn template <typename Tuple, int... I> char *QTest::toString(const Tuple &tuple, QtPrivate::IndexesList<I...> )
- \internal
- \since 5.12
+ \fn char *QTest::toString(const QKeySequence &ks)
+ \overload
+ \since 6.5
+ Returns a textual representation of the key sequence \a ks.
*/
/*!
@@ -1267,10 +1657,10 @@
Creates a dummy touch device of type \a devType with capabilities \a caps for
simulation of touch events.
- The touch device will be registered with the QPA window system interface,
- and deleted automatically when the QCoreApplication is deleted. So you
- should typically use createTouchDevice() to initialize a QPointingDevice
- member variable in your test case class, and use the same instance for all tests.
+ The touch device will be registered with the Qt window system interface.
+ You should typically use createTouchDevice() to initialize a QPointingDevice
+ member variable in your test case class, use the same instance for all tests and
+ delete it when no longer needed.
\sa QTest::QTouchEventSequence, touchEvent()
*/
@@ -1298,15 +1688,21 @@
*/
/*!
- \fn void QTest::QTouchEventSequence::commit(bool processEvents)
+ \fn bool QTest::QTouchEventSequence::commit(bool processEvents)
+
+ Commits this touch event to the event system, and returns whether it was
+ accepted after delivery.
+
+ Normally there is no need to call this function because it is called from
+ the destructor. However, if autoCommit is disabled, the events only get
+ committed upon explicitly calling this function. Another reason to call it
+ explicitly is to check the return value.
- Commits this sequence of touch events to the event system. Normally there is no need to call this
- function because it is called from the destructor. However, if autoCommit is disabled, the events
- only get committed upon explicitly calling this function.
+ In special cases, tests may want to disable the processing of the event.
+ This can be achieved by setting \a processEvents to false. This results in
+ merely queuing the event: the event loop will not be forced to process it.
- In special cases tests may want to disable the processing of the events. This can be achieved by
- setting \a processEvents to false. This results in merely queuing the events, the event loop will
- not be forced to process them.
+ Returns whether the event was accepted after delivery.
*/
/*!
diff --git a/src/testlib/qtestcase_p.h b/src/testlib/qtestcase_p.h
new file mode 100644
index 0000000000..ef3e083f88
--- /dev/null
+++ b/src/testlib/qtestcase_p.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTESTCASE_P_H
+#define QTESTCASE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtTest/qtestcase.h>
+#include <QtTest/qttestglobal.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qnamespace.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTest {
+#if QT_CONFIG(batch_test_support)
+ Q_TESTLIB_EXPORT QList<QString> qGetTestCaseNames();
+ Q_TESTLIB_EXPORT TestEntryFunction qGetTestCaseEntryFunction(const QString &name);
+#endif // QT_CONFIG(batch_test_support)
+} // namespace QTest
+
+QT_END_NAMESPACE
+
+#endif // QTESTCASE_P_H
diff --git a/src/testlib/qtestcoreelement_p.h b/src/testlib/qtestcoreelement_p.h
index 4eb7361a5c..7c6bf9dd18 100644
--- a/src/testlib/qtestcoreelement_p.h
+++ b/src/testlib/qtestcoreelement_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTCOREELEMENT_P_H
#define QTESTCOREELEMENT_P_H
@@ -51,21 +15,23 @@
// We mean it.
//
-#include <QtTest/private/qtestcorelist_p.h>
+#include <QtTest/qttestglobal.h>
#include <QtTest/private/qtestelementattribute_p.h>
+#include <vector>
+
QT_BEGIN_NAMESPACE
template <class ElementType>
-class QTestCoreElement: public QTestCoreList<ElementType>
+class QTestCoreElement
{
public:
- QTestCoreElement( int type = -1 );
+ QTestCoreElement(QTest::LogElementType type = QTest::LET_Undefined);
virtual ~QTestCoreElement();
void addAttribute(const QTest::AttributeIndex index, const char *value);
- QTestElementAttribute *attributes() const;
+ const std::vector<QTestElementAttribute*> &attributes() const;
const char *attributeValue(QTest::AttributeIndex index) const;
const char *attributeName(QTest::AttributeIndex index) const;
const QTestElementAttribute *attribute(QTest::AttributeIndex index) const;
@@ -74,20 +40,21 @@ class QTestCoreElement: public QTestCoreList<ElementType>
QTest::LogElementType elementType() const;
private:
- QTestElementAttribute *listOfAttributes = nullptr;
+ std::vector<QTestElementAttribute*> listOfAttributes;
QTest::LogElementType type;
};
template<class ElementType>
-QTestCoreElement<ElementType>::QTestCoreElement(int t)
- : type(QTest::LogElementType(t))
+QTestCoreElement<ElementType>::QTestCoreElement(QTest::LogElementType t)
+ : type(t)
{
}
template<class ElementType>
QTestCoreElement<ElementType>::~QTestCoreElement()
{
- delete listOfAttributes;
+ for (auto *attribute : listOfAttributes)
+ delete attribute;
}
template <class ElementType>
@@ -98,11 +65,11 @@ void QTestCoreElement<ElementType>::addAttribute(const QTest::AttributeIndex att
QTestElementAttribute *testAttribute = new QTestElementAttribute;
testAttribute->setPair(attributeIndex, value);
- testAttribute->addToList(&listOfAttributes);
+ listOfAttributes.push_back(testAttribute);
}
template <class ElementType>
-QTestElementAttribute *QTestCoreElement<ElementType>::attributes() const
+const std::vector<QTestElementAttribute*> &QTestCoreElement<ElementType>::attributes() const
{
return listOfAttributes;
}
@@ -138,9 +105,10 @@ const char *QTestCoreElement<ElementType>::elementName() const
"error",
"testcase",
"testsuite",
- "benchmark",
+ "message",
"system-err",
- "system-out"
+ "system-out",
+ "skipped"
};
if (type != QTest::LET_Undefined)
@@ -158,12 +126,9 @@ QTest::LogElementType QTestCoreElement<ElementType>::elementType() const
template <class ElementType>
const QTestElementAttribute *QTestCoreElement<ElementType>::attribute(QTest::AttributeIndex index) const
{
- QTestElementAttribute *iterator = listOfAttributes;
- while (iterator) {
- if (iterator->index() == index)
- return iterator;
-
- iterator = iterator->nextElement();
+ for (auto *attribute : listOfAttributes) {
+ if (attribute->index() == index)
+ return attribute;
}
return nullptr;
diff --git a/src/testlib/qtestcorelist_p.h b/src/testlib/qtestcorelist_p.h
deleted file mode 100644
index daeb293644..0000000000
--- a/src/testlib/qtestcorelist_p.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QTESTCORELIST_P_H
-#define QTESTCORELIST_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-
-template <class T>
-class QTestCoreList
-{
- public:
- QTestCoreList();
- virtual ~QTestCoreList();
-
- void addToList(T **list);
- T *nextElement();
- T *previousElement();
- private:
- T *next;
- T *prev;
-};
-
-template <class T>
-QTestCoreList<T>::QTestCoreList()
- : next(nullptr)
- , prev(nullptr)
-{
-}
-
-template <class T>
-QTestCoreList<T>::~QTestCoreList()
-{
- if (prev) {
- prev->next = nullptr;
- }
- delete prev;
-
- if (next) {
- next->prev = nullptr;
- }
- delete next;
-}
-
-template <class T>
-void QTestCoreList<T>::addToList(T **list)
-{
- if (next)
- next->addToList(list);
- else {
- next = *list;
- if (next)
- next->prev = static_cast<T*>(this);
- }
-
- *list = static_cast<T*>(this);
-}
-
-template <class T>
-T *QTestCoreList<T>::nextElement()
-{
- return next;
-}
-
-template <class T>
-T *QTestCoreList<T>::previousElement()
-{
- return prev;
-}
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/testlib/qtestcrashhandler.cpp b/src/testlib/qtestcrashhandler.cpp
new file mode 100644
index 0000000000..aabac1c466
--- /dev/null
+++ b/src/testlib/qtestcrashhandler.cpp
@@ -0,0 +1,663 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// Copyright (C) 2024 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtTest/qtestcase.h>
+#include <QtTest/private/qtestcrashhandler_p.h>
+#include <QtTest/qtestassert.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qfloat16.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qtemporarydir.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/private/qlocking_p.h>
+#include <QtCore/private/qtools_p.h>
+#include <QtCore/private/qwaitcondition_p.h>
+
+#include <QtCore/qtestsupport_core.h>
+
+#include <QtTest/private/qtestlog_p.h>
+#include <QtTest/private/qtesttable_p.h>
+#include <QtTest/qtestdata.h>
+#include <QtTest/private/qtestresult_p.h>
+#include <QtTest/private/qsignaldumper_p.h>
+#include <QtTest/private/qbenchmark_p.h>
+#if QT_CONFIG(batch_test_support)
+#include <QtTest/private/qtestregistry_p.h>
+#endif // QT_CONFIG(batch_test_support)
+#include <QtTest/private/cycle_p.h>
+#include <QtTest/private/qtestblacklist_p.h>
+#if defined(HAVE_XCTEST)
+#include <QtTest/private/qxctestlogger_p.h>
+#endif
+#if defined Q_OS_MACOS
+#include <QtTest/private/qtestutil_macos_p.h>
+#endif
+
+#if defined(Q_OS_DARWIN)
+#include <QtTest/private/qappletestlogger_p.h>
+#endif
+
+#if !defined(Q_OS_INTEGRITY) || __GHS_VERSION_NUMBER > 202014
+# include <charconv>
+#else
+// Broken implementation, causes link failures just by #include'ing!
+# undef __cpp_lib_to_chars // in case <version> was included
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(Q_OS_LINUX)
+#include <sys/types.h>
+#include <fcntl.h>
+#endif
+
+#ifdef Q_OS_UNIX
+#include <QtCore/private/qcore_unix_p.h>
+
+#include <errno.h>
+#if __has_include(<paths.h>)
+# include <paths.h>
+#endif
+#include <signal.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <unistd.h>
+# if !defined(Q_OS_INTEGRITY)
+# include <sys/resource.h>
+# endif
+# ifndef _PATH_DEFPATH
+# define _PATH_DEFPATH "/usr/bin:/bin"
+# endif
+# ifndef SIGSTKSZ
+# define SIGSTKSZ 0 /* we have code to set the minimum */
+# endif
+# ifndef SA_RESETHAND
+# define SA_RESETHAND 0
+# endif
+#endif
+
+#if defined(Q_OS_MACOS)
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <mach/task.h>
+#include <mach/mach_init.h>
+#include <CoreFoundation/CFPreferences.h>
+#endif
+
+#if defined(Q_OS_WASM)
+#include <emscripten.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace QTest {
+namespace CrashHandler {
+#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
+struct iovec IoVec(struct iovec vec)
+{
+ return vec;
+}
+struct iovec IoVec(const char *str)
+{
+ struct iovec r = {};
+ r.iov_base = const_cast<char *>(str);
+ r.iov_len = strlen(str);
+ return r;
+}
+
+struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result)
+{
+ char *ptr = result.array.data();
+ if (false) {
+#ifdef __cpp_lib_to_chars
+ } else if (auto r = std::to_chars(ptr, ptr + result.array.size(), n, 10); r.ec == std::errc{}) {
+ ptr = r.ptr;
+#endif
+ } else {
+ // handle the sign
+ if (n < 0) {
+ *ptr++ = '-';
+ n = -n;
+ }
+
+ // find the highest power of the base that is less than this number
+ static constexpr int StartingDivider = ([]() {
+ int divider = 1;
+ for (int i = 0; i < std::numeric_limits<int>::digits10; ++i)
+ divider *= 10;
+ return divider;
+ }());
+ int divider = StartingDivider;
+ while (divider && n < divider)
+ divider /= 10;
+
+ // now convert to string
+ while (divider > 1) {
+ int quot = n / divider;
+ n = n % divider;
+ divider /= 10;
+ *ptr++ = quot + '0';
+ }
+ *ptr++ = n + '0';
+ }
+
+#ifndef QT_NO_DEBUG
+ // this isn't necessary, it just helps in the debugger
+ *ptr = '\0';
+#endif
+ struct iovec r;
+ r.iov_base = result.array.data();
+ r.iov_len = ptr - result.array.data();
+ return r;
+};
+#endif // defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
+
+bool alreadyDebugging()
+{
+#if defined(Q_OS_LINUX)
+ int fd = open("/proc/self/status", O_RDONLY);
+ if (fd == -1)
+ return false;
+ char buffer[2048];
+ ssize_t size = read(fd, buffer, sizeof(buffer) - 1);
+ if (size == -1) {
+ close(fd);
+ return false;
+ }
+ buffer[size] = 0;
+ const char tracerPidToken[] = "\nTracerPid:";
+ char *tracerPid = strstr(buffer, tracerPidToken);
+ if (!tracerPid) {
+ close(fd);
+ return false;
+ }
+ tracerPid += sizeof(tracerPidToken);
+ long int pid = strtol(tracerPid, &tracerPid, 10);
+ close(fd);
+ return pid != 0;
+#elif defined(Q_OS_WIN)
+ return IsDebuggerPresent();
+#elif defined(Q_OS_MACOS)
+ // Check if there is an exception handler for the process:
+ mach_msg_type_number_t portCount = 0;
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+ exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
+ kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &portCount,
+ ports, behaviors, flavors);
+ if (result == KERN_SUCCESS) {
+ for (mach_msg_type_number_t portIndex = 0; portIndex < portCount; ++portIndex) {
+ if (MACH_PORT_VALID(ports[portIndex])) {
+ return true;
+ }
+ }
+ }
+ return false;
+#else
+ // TODO
+ return false;
+#endif
+}
+
+namespace {
+enum DebuggerProgram { None, Gdb, Lldb };
+static bool hasSystemCrashReporter()
+{
+#if defined(Q_OS_MACOS)
+ return QTestPrivate::macCrashReporterWillShowDialog();
+#else
+ return false;
+#endif
+}
+} // unnamed namespaced
+
+void maybeDisableCoreDump()
+{
+#ifdef RLIMIT_CORE
+ bool ok = false;
+ const int disableCoreDump = qEnvironmentVariableIntValue("QTEST_DISABLE_CORE_DUMP", &ok);
+ if (ok && disableCoreDump) {
+ struct rlimit limit;
+ limit.rlim_cur = 0;
+ limit.rlim_max = 0;
+ if (setrlimit(RLIMIT_CORE, &limit) != 0)
+ qWarning("Failed to disable core dumps: %d", errno);
+ }
+#endif
+}
+
+static DebuggerProgram debugger = None;
+void prepareStackTrace()
+{
+
+ bool ok = false;
+ const int disableStackDump = qEnvironmentVariableIntValue("QTEST_DISABLE_STACK_DUMP", &ok);
+ if (ok && disableStackDump)
+ return;
+
+ if (hasSystemCrashReporter())
+ return;
+
+#if defined(Q_OS_MACOS)
+ #define CSR_ALLOW_UNRESTRICTED_FS (1 << 1)
+ std::optional<uint32_t> sipConfiguration = qt_mac_sipConfiguration();
+ if (!sipConfiguration || !(*sipConfiguration & CSR_ALLOW_UNRESTRICTED_FS))
+ return; // LLDB will fail to provide a valid stack trace
+#endif
+
+#ifdef Q_OS_UNIX
+ // like QStandardPaths::findExecutable(), but simpler
+ auto hasExecutable = [](const char *execname) {
+ std::string candidate;
+ std::string path;
+ if (const char *p = getenv("PATH"); p && *p)
+ path = p;
+ else
+ path = _PATH_DEFPATH;
+ for (const char *p = std::strtok(&path[0], ":'"); p; p = std::strtok(nullptr, ":")) {
+ candidate = p;
+ candidate += '/';
+ candidate += execname;
+ if (QT_ACCESS(candidate.data(), X_OK) == 0)
+ return true;
+ }
+ return false;
+ };
+
+ static constexpr DebuggerProgram debuggerSearchOrder[] = {
+# if defined(Q_OS_QNX) || (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
+ Gdb, Lldb
+# else
+ Lldb, Gdb
+# endif
+ };
+ for (DebuggerProgram candidate : debuggerSearchOrder) {
+ switch (candidate) {
+ case None:
+ Q_UNREACHABLE();
+ break;
+ case Gdb:
+ if (hasExecutable("gdb")) {
+ debugger = Gdb;
+ return;
+ }
+ break;
+ case Lldb:
+ if (hasExecutable("lldb")) {
+ debugger = Lldb;
+ return;
+ }
+ break;
+ }
+ }
+#endif // Q_OS_UNIX
+}
+
+#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
+void printTestRunTime()
+{
+ const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
+ const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
+ const char *const name = QTest::currentTestFunction();
+ writeToStderr("\n ", name ? name : "[Non-test]",
+ " function time: ", asyncSafeToString(msecsFunctionTime),
+ "ms, total time: ", asyncSafeToString(msecsTotalTime), "ms\n");
+}
+
+void generateStackTrace()
+{
+ if (debugger == None || alreadyDebugging())
+ return;
+
+# if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
+ writeToStderr("\n=== Stack trace ===\n");
+
+ // execlp() requires null-termination, so call the default constructor
+ AsyncSafeIntBuffer pidbuffer;
+ asyncSafeToString(getpid(), std::move(pidbuffer));
+
+ // Note: POSIX.1-2001 still has fork() in the list of async-safe functions,
+ // but in a future edition, it might be removed. It would be safer to wake
+ // up a babysitter thread to launch the debugger.
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child process
+ (void) dup2(STDERR_FILENO, STDOUT_FILENO); // redirect stdout to stderr
+
+ switch (debugger) {
+ case None:
+ Q_UNREACHABLE();
+ break;
+ case Gdb:
+ execlp("gdb", "gdb", "--nx", "--batch", "-ex", "thread apply all bt",
+ "--pid", pidbuffer.array.data(), nullptr);
+ break;
+ case Lldb:
+ execlp("lldb", "lldb", "--no-lldbinit", "--batch", "-o", "bt all",
+ "--attach-pid", pidbuffer.array.data(), nullptr);
+ break;
+ }
+ _exit(1);
+ } else if (pid < 0) {
+ writeToStderr("Failed to start debugger.\n");
+ } else {
+ int ret;
+ QT_EINTR_LOOP(ret, waitpid(pid, nullptr, 0));
+ }
+
+ writeToStderr("=== End of stack trace ===\n");
+# endif // Q_OS_UNIX && !Q_OS_WASM && !Q_OS_INTEGRITY && !Q_OS_VXWORKS
+}
+#endif // !defined(Q_OS_WASM) || QT_CONFIG(thread)
+
+#if defined(Q_OS_WIN)
+void blockUnixSignals()
+{
+ // Windows does have C signals, but doesn't use them for the purposes we're
+ // talking about here
+}
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
+void blockUnixSignals()
+{
+ // Block most Unix signals so the WatchDog thread won't be called when
+ // external signals are delivered, thus avoiding interfering with the test
+ sigset_t set;
+ sigfillset(&set);
+
+ // we allow the crashing signals, in case we have bugs
+ for (int signo : FatalSignalHandler::fatalSignals)
+ sigdelset(&set, signo);
+
+ pthread_sigmask(SIG_BLOCK, &set, nullptr);
+}
+#endif // Q_OS_* choice
+
+#if defined(Q_OS_WIN)
+void DebugSymbolResolver::cleanup()
+{
+ if (m_dbgHelpLib)
+ FreeLibrary(m_dbgHelpLib);
+ m_dbgHelpLib = 0;
+ m_symFromAddr = nullptr;
+}
+
+DebugSymbolResolver::DebugSymbolResolver(HANDLE process)
+ : m_process(process), m_dbgHelpLib(0), m_symFromAddr(nullptr)
+{
+ bool success = false;
+ m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
+ if (m_dbgHelpLib) {
+ SymInitializeType symInitialize = reinterpret_cast<SymInitializeType>(
+ reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymInitialize")));
+ m_symFromAddr = reinterpret_cast<SymFromAddrType>(
+ reinterpret_cast<QFunctionPointer>(GetProcAddress(m_dbgHelpLib, "SymFromAddr")));
+ success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
+ }
+ if (!success)
+ cleanup();
+}
+
+DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) const
+{
+ // reserve additional buffer where SymFromAddr() will store the name
+ struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
+ enum { symbolNameLength = 255 };
+
+ char name[symbolNameLength + 1];
+ };
+
+ Symbol result;
+ if (!isValid())
+ return result;
+ NamedSymbolInfo symbolBuffer;
+ memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
+ symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
+ symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
+ if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
+ return result;
+ result.name = qstrdup(symbolBuffer.Name);
+ result.address = symbolBuffer.Address;
+ return result;
+}
+
+WindowsFaultHandler::WindowsFaultHandler()
+{
+# if !defined(Q_CC_MINGW)
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
+# endif
+ SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
+ SetUnhandledExceptionFilter(windowsFaultHandler);
+}
+
+LONG WINAPI WindowsFaultHandler::windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
+{
+ enum { maxStackFrames = 100 };
+ char appName[MAX_PATH];
+ if (!GetModuleFileNameA(NULL, appName, MAX_PATH))
+ appName[0] = 0;
+ const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
+ const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
+ const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
+ fprintf(stderr, "A crash occurred in %s.\n", appName);
+ if (const char *name = QTest::currentTestFunction())
+ fprintf(stderr, "While testing %s\n", name);
+ fprintf(stderr, "Function time: %dms Total time: %dms\n\n"
+ "Exception address: 0x%p\n"
+ "Exception code : 0x%lx\n",
+ msecsFunctionTime, msecsTotalTime, exceptionAddress,
+ exInfo->ExceptionRecord->ExceptionCode);
+
+ DebugSymbolResolver resolver(GetCurrentProcess());
+ if (resolver.isValid()) {
+ DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
+ if (exceptionSymbol.name) {
+ fprintf(stderr, "Nearby symbol : %s\n", exceptionSymbol.name);
+ delete [] exceptionSymbol.name;
+ }
+ void *stack[maxStackFrames];
+ fputs("\nStack:\n", stderr);
+ const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
+ for (unsigned f = 0; f < frameCount; ++f) {
+ DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
+ if (symbol.name) {
+ fprintf(stderr, "#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
+ delete [] symbol.name;
+ } else {
+ fprintf(stderr, "#%3u: Unable to obtain symbol\n", f + 1);
+ }
+ }
+ }
+
+ fputc('\n', stderr);
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
+bool FatalSignalHandler::pauseOnCrash = false;
+
+FatalSignalHandler::FatalSignalHandler()
+{
+ pauseOnCrash = qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH");
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_DFL;
+ oldActions().fill(act);
+
+ // Remove the handler after it is invoked.
+ act.sa_flags = SA_RESETHAND | setupAlternateStack();
+
+# ifdef SA_SIGINFO
+ act.sa_flags |= SA_SIGINFO;
+ act.sa_sigaction = FatalSignalHandler::actionHandler;
+# else
+ act.sa_handler = FatalSignalHandler::regularHandler;
+# endif
+
+ // Block all fatal signals in our signal handler so we don't try to close
+ // the testlog twice.
+ sigemptyset(&act.sa_mask);
+ for (int signal : fatalSignals)
+ sigaddset(&act.sa_mask, signal);
+
+ for (size_t i = 0; i < fatalSignals.size(); ++i)
+ sigaction(fatalSignals[i], &act, &oldActions()[i]);
+}
+
+FatalSignalHandler::~FatalSignalHandler()
+{
+ // Restore the default signal handlers in place of ours.
+ // If ours has been replaced, leave the replacement alone.
+ auto isOurs = [](const struct sigaction &old) {
+# ifdef SA_SIGINFO
+ return (old.sa_flags & SA_SIGINFO) && old.sa_sigaction == FatalSignalHandler::actionHandler;
+# else
+ return old.sa_handler == FatalSignalHandler::regularHandler;
+# endif
+ };
+ struct sigaction action;
+
+ for (size_t i = 0; i < fatalSignals.size(); ++i) {
+ struct sigaction &act = oldActions()[i];
+ if (act.sa_flags == 0 && act.sa_handler == SIG_DFL)
+ continue; // Already the default
+ if (sigaction(fatalSignals[i], nullptr, &action))
+ continue; // Failed to query present handler
+ if (isOurs(action))
+ sigaction(fatalSignals[i], &act, nullptr);
+ }
+
+ freeAlternateStack();
+}
+
+FatalSignalHandler::OldActionsArray &FatalSignalHandler::oldActions()
+{
+ Q_CONSTINIT static OldActionsArray oldActions {};
+ return oldActions;
+}
+
+auto FatalSignalHandler::alternateStackSize()
+{
+ struct R { size_t size, pageSize; };
+ static constexpr size_t MinStackSize = 32 * 1024;
+ size_t pageSize = sysconf(_SC_PAGESIZE);
+ size_t size = SIGSTKSZ;
+ if (size < MinStackSize) {
+ size = MinStackSize;
+ } else {
+ // round up to a page
+ size = (size + pageSize - 1) & -pageSize;
+ }
+
+ return R{ size + pageSize, pageSize };
+}
+
+int FatalSignalHandler::setupAlternateStack()
+{
+ // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as
+ // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h)
+# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
+ // Let the signal handlers use an alternate stack
+ // This is necessary if SIGSEGV is to catch a stack overflow
+ auto r = alternateStackSize();
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+# ifdef MAP_STACK
+ flags |= MAP_STACK;
+# endif
+ alternateStackBase = mmap(nullptr, r.size, PROT_READ | PROT_WRITE, flags, -1, 0);
+ if (alternateStackBase == MAP_FAILED)
+ return 0;
+
+ // mark the bottom page inaccessible, to catch a handler stack overflow
+ (void) mprotect(alternateStackBase, r.pageSize, PROT_NONE);
+
+ stack_t stack;
+ stack.ss_flags = 0;
+ stack.ss_size = r.size - r.pageSize;
+ stack.ss_sp = static_cast<char *>(alternateStackBase) + r.pageSize;
+ sigaltstack(&stack, nullptr);
+ return SA_ONSTACK;
+# else
+ return 0;
+# endif
+}
+
+void FatalSignalHandler::freeAlternateStack()
+{
+# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS)
+ if (alternateStackBase != MAP_FAILED) {
+ stack_t stack = {};
+ stack.ss_flags = SS_DISABLE;
+ sigaltstack(&stack, nullptr);
+ munmap(alternateStackBase, alternateStackSize().size);
+ }
+# endif
+}
+
+void FatalSignalHandler::actionHandler(int signum, siginfo_t *info, void *)
+{
+ writeToStderr("Received signal ", asyncSafeToString(signum),
+ " (SIG", signalName(signum), ")");
+
+ bool isCrashingSignal =
+ std::find(crashingSignals.begin(), crashingSignals.end(), signum) != crashingSignals.end();
+ if (isCrashingSignal && (!info || info->si_code <= 0))
+ isCrashingSignal = false; // wasn't sent by the kernel, so it's not really a crash
+ if (isCrashingSignal)
+ printCrashingSignalInfo(info);
+ else if (info && (info->si_code == SI_USER || info->si_code == SI_QUEUE))
+ printSentSignalInfo(info);
+
+ printTestRunTime();
+ if (signum != SIGINT) {
+ generateStackTrace();
+ if (pauseOnCrash) {
+ writeToStderr("Pausing process ", asyncSafeToString(getpid()),
+ " for debugging\n");
+ raise(SIGSTOP);
+ }
+ }
+
+ // chain back to the previous handler, if any
+ for (size_t i = 0; i < fatalSignals.size(); ++i) {
+ struct sigaction &act = oldActions()[i];
+ if (signum != fatalSignals[i])
+ continue;
+
+ // restore the handler (if SA_RESETHAND hasn't done the job for us)
+ if (SA_RESETHAND == 0 || act.sa_handler != SIG_DFL || act.sa_flags)
+ (void) sigaction(signum, &act, nullptr);
+
+ if (!isCrashingSignal)
+ raise(signum);
+
+ // signal is blocked, so it'll be delivered when we return
+ return;
+ }
+
+ // we shouldn't reach here!
+ std::abort();
+}
+#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
+
+} // namespace CrashHandler
+} // namespace QTest
+
+QT_END_NAMESPACE
diff --git a/src/testlib/qtestcrashhandler_p.h b/src/testlib/qtestcrashhandler_p.h
new file mode 100644
index 0000000000..02e19bfaa9
--- /dev/null
+++ b/src/testlib/qtestcrashhandler_p.h
@@ -0,0 +1,251 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QTESTCRASHHANDLER_H
+#define QTESTCRASHHANDLER_H
+
+#include <QtTest/qttestglobal.h>
+
+#include <QtCore/private/qtools_p.h>
+
+#ifdef Q_OS_UNIX
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#endif
+
+#ifdef Q_OS_WIN
+#include <iostream>
+# if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
+# include <crtdbg.h>
+# endif
+#include <qt_windows.h> // for Sleep
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace QTest {
+namespace CrashHandler {
+#if defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
+ struct iovec IoVec(struct iovec vec);
+ struct iovec IoVec(const char *str);
+
+ template <typename... Args> static ssize_t writeToStderr(Args &&... args)
+ {
+ struct iovec vec[] = { IoVec(std::forward<Args>(args))... };
+ return ::writev(STDERR_FILENO, vec, std::size(vec));
+ }
+
+ // async-signal-safe conversion from int to string
+ struct AsyncSafeIntBuffer
+ {
+ // digits10 + 1 for all possible digits
+ // +1 for the sign
+ // +1 for the terminating null
+ static constexpr int Digits10 = std::numeric_limits<int>::digits10 + 3;
+ std::array<char, Digits10> array;
+ constexpr AsyncSafeIntBuffer() : array{} {} // initializes array
+ AsyncSafeIntBuffer(Qt::Initialization) {} // leaves array uninitialized
+ };
+
+ struct iovec asyncSafeToString(int n, AsyncSafeIntBuffer &&result = Qt::Uninitialized);
+#elif defined(Q_OS_WIN)
+ // Windows doesn't need to be async-safe
+ template <typename... Args> static void writeToStderr(Args &&... args)
+ {
+ (std::cerr << ... << args);
+ }
+
+ inline std::string asyncSafeToString(int n)
+ {
+ return std::to_string(n);
+ }
+#endif // defined(Q_OS_UNIX) && (!defined(Q_OS_WASM) || QT_CONFIG(thread))
+
+ bool alreadyDebugging();
+ void blockUnixSignals();
+
+#if !defined(Q_OS_WASM) || QT_CONFIG(thread)
+ void printTestRunTime();
+ void generateStackTrace();
+#endif
+
+ void maybeDisableCoreDump();
+ Q_TESTLIB_EXPORT void prepareStackTrace();
+
+#if defined(Q_OS_WIN)
+ // Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
+ class DebugSymbolResolver
+ {
+ Q_DISABLE_COPY_MOVE(DebugSymbolResolver)
+ public:
+ struct Symbol
+ {
+ Symbol() : name(nullptr), address(0) {}
+
+ const char *name; // Must be freed by caller.
+ DWORD64 address;
+ };
+
+ explicit DebugSymbolResolver(HANDLE process);
+ ~DebugSymbolResolver() { cleanup(); }
+
+ bool isValid() const { return m_symFromAddr; }
+
+ Symbol resolveSymbol(DWORD64 address) const;
+
+ private:
+ // typedefs from DbgHelp.h/.dll
+ struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
+ ULONG SizeOfStruct;
+ ULONG TypeIndex; // Type Index of symbol
+ ULONG64 Reserved[2];
+ ULONG Index;
+ ULONG Size;
+ ULONG64 ModBase; // Base Address of module comtaining this symbol
+ ULONG Flags;
+ ULONG64 Value; // Value of symbol, ValuePresent should be 1
+ ULONG64 Address; // Address of symbol including base address of module
+ ULONG Register; // register holding value or pointer to value
+ ULONG Scope; // scope of the symbol
+ ULONG Tag; // pdb classification
+ ULONG NameLen; // Actual length of name
+ ULONG MaxNameLen;
+ CHAR Name[1]; // Name of symbol
+ };
+
+ typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
+ typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
+
+ void cleanup();
+
+ const HANDLE m_process;
+ HMODULE m_dbgHelpLib;
+ SymFromAddrType m_symFromAddr;
+ };
+
+ class Q_TESTLIB_EXPORT WindowsFaultHandler
+ {
+ public:
+ WindowsFaultHandler();
+
+ private:
+ static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo);
+ };
+ using FatalSignalHandler = WindowsFaultHandler;
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
+ class Q_TESTLIB_EXPORT FatalSignalHandler
+ {
+ public:
+ # define OUR_SIGNALS(F) \
+ F(HUP) \
+ F(INT) \
+ F(QUIT) \
+ F(ABRT) \
+ F(ILL) \
+ F(BUS) \
+ F(FPE) \
+ F(SEGV) \
+ F(PIPE) \
+ F(TERM) \
+ /**/
+ # define CASE_LABEL(S) case SIG ## S: return QT_STRINGIFY(S);
+ # define ENUMERATE_SIGNALS(S) SIG ## S,
+ static const char *signalName(int signum) noexcept
+ {
+ switch (signum) {
+ OUR_SIGNALS(CASE_LABEL)
+ }
+
+ # if defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ >= 32 || __GLIBC__ > 2)
+ // get the other signal names from glibc 2.32
+ // (accessing the sys_sigabbrev variable causes linker warnings)
+ if (const char *p = sigabbrev_np(signum))
+ return p;
+ # endif
+ return "???";
+ }
+ static constexpr std::array fatalSignals = {
+ OUR_SIGNALS(ENUMERATE_SIGNALS)
+ };
+ # undef CASE_LABEL
+ # undef ENUMERATE_SIGNALS
+
+ static constexpr std::array crashingSignals = {
+ // Crash signals are special, because if we return from the handler
+ // without adjusting the machine state, the same instruction that
+ // originally caused the crash will get re-executed and will thus cause
+ // the same crash again. This is useful if our parent process logs the
+ // exit result or if core dumps are enabled: the core file will point
+ // to the actual instruction that crashed.
+ SIGILL, SIGBUS, SIGFPE, SIGSEGV
+ };
+ using OldActionsArray = std::array<struct sigaction, fatalSignals.size()>;
+
+ FatalSignalHandler();
+ ~FatalSignalHandler();
+
+ private:
+ Q_DISABLE_COPY_MOVE(FatalSignalHandler)
+
+ static OldActionsArray &oldActions();
+ auto alternateStackSize();
+ int setupAlternateStack();
+ void freeAlternateStack();
+
+ template <typename T> static
+ std::enable_if_t<sizeof(std::declval<T>().si_pid) + sizeof(std::declval<T>().si_uid) >= 1>
+ printSentSignalInfo(T *info)
+ {
+ writeToStderr(" sent by PID ", asyncSafeToString(info->si_pid),
+ " UID ", asyncSafeToString(info->si_uid));
+ }
+ static void printSentSignalInfo(...) {}
+
+ template <typename T> static
+ std::enable_if_t<sizeof(std::declval<T>().si_addr) >= 1> printCrashingSignalInfo(T *info)
+ {
+ using HexString = std::array<char, sizeof(quintptr) * 2>;
+ auto toHexString = [](quintptr u, HexString &&r = {}) {
+ int shift = sizeof(quintptr) * 8 - 4;
+ for (size_t i = 0; i < sizeof(quintptr) * 2; ++i, shift -= 4)
+ r[i] = QtMiscUtils::toHexLower(u >> shift);
+ struct iovec vec;
+ vec.iov_base = r.data();
+ vec.iov_len = r.size();
+ return vec;
+ };
+ writeToStderr(", code ", asyncSafeToString(info->si_code),
+ ", for address 0x", toHexString(quintptr(info->si_addr)));
+ }
+ static void printCrashingSignalInfo(...) {}
+ static void actionHandler(int signum, siginfo_t *info, void * /* ucontext */);
+
+ [[maybe_unused]] static void regularHandler(int signum)
+ {
+ actionHandler(signum, nullptr, nullptr);
+ }
+
+ void *alternateStackBase = MAP_FAILED;
+ static bool pauseOnCrash;
+ };
+#else // Q_OS_WASM or weird systems
+class Q_TESTLIB_EXPORT FatalSignalHandler {};
+inline void blockUnixSignals() {}
+#endif // Q_OS_* choice
+} // namespace CrashHandler
+} // namespace QTest
+QT_END_NAMESPACE
+
+#endif // QTESTCRASHHANDLER_H
diff --git a/src/testlib/qtestdata.cpp b/src/testlib/qtestdata.cpp
index 3293dfd3e5..c90a666c6b 100644
--- a/src/testlib/qtestdata.cpp
+++ b/src/testlib/qtestdata.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qmetaobject.h>
diff --git a/src/testlib/qtestdata.h b/src/testlib/qtestdata.h
index cf10fed8f3..4e2d072c2c 100644
--- a/src/testlib/qtestdata.h
+++ b/src/testlib/qtestdata.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTDATA_H
#define QTESTDATA_H
@@ -85,6 +49,14 @@ inline QTestData &operator<<(QTestData &data, const char * value)
return data;
}
+#ifdef __cpp_char8_t
+Q_WEAK_OVERLOAD
+inline QTestData &operator<<(QTestData &data, const char8_t *value)
+{
+ return data << reinterpret_cast<const char *>(value);
+}
+#endif
+
#ifdef QT_USE_QSTRINGBUILDER
template<typename A, typename B>
inline QTestData &operator<<(QTestData &data, const QStringBuilder<A, B> &value)
diff --git a/src/testlib/qtestelement.cpp b/src/testlib/qtestelement.cpp
index b468295917..ce85c4bb9f 100644
--- a/src/testlib/qtestelement.cpp
+++ b/src/testlib/qtestelement.cpp
@@ -1,63 +1,28 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qtestelement_p.h>
QT_BEGIN_NAMESPACE
-QTestElement::QTestElement(int type)
+QTestElement::QTestElement(QTest::LogElementType type)
: QTestCoreElement<QTestElement>(type)
{
}
QTestElement::~QTestElement()
{
- delete listOfChildren;
+ for (auto *child : listOfChildren)
+ delete child;
}
-bool QTestElement::addLogElement(QTestElement *element)
+bool QTestElement::addChild(QTestElement *element)
{
if (!element)
return false;
if (element->elementType() != QTest::LET_Undefined) {
- element->addToList(&listOfChildren);
+ listOfChildren.push_back(element);
element->setParent(this);
return true;
}
@@ -65,7 +30,7 @@ bool QTestElement::addLogElement(QTestElement *element)
return false;
}
-QTestElement *QTestElement::childElements() const
+const std::vector<QTestElement*> &QTestElement::childElements() const
{
return listOfChildren;
}
diff --git a/src/testlib/qtestelement_p.h b/src/testlib/qtestelement_p.h
index a6b2791a42..bed1ae927b 100644
--- a/src/testlib/qtestelement_p.h
+++ b/src/testlib/qtestelement_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTELEMENT_P_H
#define QTESTELEMENT_P_H
@@ -51,26 +15,27 @@
// We mean it.
//
+#include <QtTest/qttestglobal.h>
#include <QtTest/private/qtestcoreelement_p.h>
QT_BEGIN_NAMESPACE
-class QTestElement: public QTestCoreElement<QTestElement>
+class QTestElement : public QTestCoreElement<QTestElement>
{
public:
- QTestElement(int type = -1);
+ QTestElement(QTest::LogElementType type = QTest::LET_Undefined);
~QTestElement();
- bool addLogElement(QTestElement *element);
- QTestElement *childElements() const;
+ bool addChild(QTestElement *element);
+ const std::vector<QTestElement*> &childElements() const;
const QTestElement *parentElement() const;
void setParent(const QTestElement *p);
private:
- QTestElement *listOfChildren = nullptr;
- const QTestElement * parent = nullptr;
+ std::vector<QTestElement*> listOfChildren;
+ const QTestElement *parent = nullptr;
};
diff --git a/src/testlib/qtestelementattribute.cpp b/src/testlib/qtestelementattribute.cpp
index ec47ee1900..57e68fc40b 100644
--- a/src/testlib/qtestelementattribute.cpp
+++ b/src/testlib/qtestelementattribute.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qtestelementattribute_p.h>
#include <QtCore/qbytearray.h>
@@ -44,72 +8,6 @@
QT_BEGIN_NAMESPACE
-/*! \enum QTest::AttributeIndex
- This enum numbers the different tests.
-
- \value AI_Undefined
-
- \value AI_Name
-
- \value AI_Result
-
- \value AI_Tests
-
- \value AI_Failures
-
- \value AI_Errors
-
- \value AI_Type
-
- \value AI_Description
-
- \value AI_PropertyValue
-
- \value AI_QTestVersion
-
- \value AI_QtVersion
-
- \value AI_File
-
- \value AI_Line
-
- \value AI_Metric
-
- \value AI_Tag
-
- \value AI_Value
-
- \value AI_Iterations
-
- \value AI_Time
-
- \value AI_Timestamp
-*/
-
-/*! \enum QTest::LogElementType
- The enum specifies the kinds of test log messages.
-
- \value LET_Undefined
-
- \value LET_Property
-
- \value LET_Properties
-
- \value LET_Failure
-
- \value LET_Message
-
- \value LET_TestCase
-
- \value LET_TestSuite
-
- \value LET_Benchmark
-
- \value LET_SystemError
-
- \value LET_SystemOutput
-*/
-
QTestElementAttribute::QTestElementAttribute() = default;
QTestElementAttribute::~QTestElementAttribute()
@@ -127,23 +25,18 @@ const char *QTestElementAttribute::name() const
const char *AttributeNames[] =
{
"name",
- "result",
"tests",
"failures",
"errors",
"type",
- "description",
+ "message",
"value",
- "qtestversion",
- "qtversion",
- "file",
- "line",
- "metric",
- "tag",
"value",
- "iterations",
"time",
- "timestamp"
+ "timestamp",
+ "hostname",
+ "classname",
+ "skipped"
};
if (attributeIndex != QTest::AI_Undefined)
diff --git a/src/testlib/qtestelementattribute_p.h b/src/testlib/qtestelementattribute_p.h
index 523dd7435e..29e64079d2 100644
--- a/src/testlib/qtestelementattribute_p.h
+++ b/src/testlib/qtestelementattribute_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTELEMENTATTRIBUTE_P_H
#define QTESTELEMENTATTRIBUTE_P_H
@@ -51,7 +15,8 @@
// We mean it.
//
-#include <QtTest/private/qtestcorelist_p.h>
+#include <QtTest/qttestglobal.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -61,42 +26,38 @@ namespace QTest {
enum AttributeIndex
{
AI_Undefined = -1,
- AI_Name = 0,
- AI_Result = 1,
- AI_Tests = 2,
- AI_Failures = 3,
- AI_Errors = 4,
- AI_Type = 5,
- AI_Description = 6,
- AI_PropertyValue = 7,
- AI_QTestVersion = 8,
- AI_QtVersion = 9,
- AI_File = 10,
- AI_Line = 11,
- AI_Metric = 12,
- AI_Tag = 13,
- AI_Value = 14,
- AI_Iterations = 15,
- AI_Time = 16,
- AI_Timestamp = 17
+ AI_Name,
+ AI_Tests,
+ AI_Failures,
+ AI_Errors,
+ AI_Type,
+ AI_Message,
+ AI_PropertyValue,
+ AI_Value,
+ AI_Time,
+ AI_Timestamp,
+ AI_Hostname,
+ AI_Classname,
+ AI_Skipped
};
enum LogElementType
{
LET_Undefined = -1,
- LET_Property = 0,
- LET_Properties = 1,
- LET_Failure = 2,
- LET_Message = 3,
- LET_TestCase = 4,
- LET_TestSuite = 5,
- LET_Benchmark = 6,
- LET_SystemError = 7,
- LET_SystemOutput = 8
+ LET_Property,
+ LET_Properties,
+ LET_Failure,
+ LET_Error,
+ LET_TestCase,
+ LET_TestSuite,
+ LET_Text,
+ LET_SystemError,
+ LET_SystemOutput,
+ LET_Skipped
};
}
-class QTestElementAttribute: public QTestCoreList<QTestElementAttribute>
+class QTestElementAttribute
{
public:
QTestElementAttribute();
diff --git a/src/testlib/qtestevent.h b/src/testlib/qtestevent.h
index 50eff69edc..8f092cf8c9 100644
--- a/src/testlib/qtestevent.h
+++ b/src/testlib/qtestevent.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTEVENT_H
#define QTESTEVENT_H
@@ -168,7 +132,7 @@ class QTestEventList: public QList<QTestEvent *>
public:
inline QTestEventList() {}
inline QTestEventList(const QTestEventList &other): QList<QTestEvent *>()
- { for (int i = 0; i < other.count(); ++i) append(other.at(i)->clone()); }
+ { for (int i = 0; i < other.size(); ++i) append(other.at(i)->clone()); }
inline ~QTestEventList()
{ clear(); }
inline void clear()
@@ -218,7 +182,7 @@ public:
#ifdef QT_WIDGETS_LIB
inline void simulate(QWidget *w)
{
- for (int i = 0; i < count(); ++i)
+ for (int i = 0; i < size(); ++i)
at(i)->simulate(w);
}
#endif
diff --git a/src/testlib/qtestevent.qdoc b/src/testlib/qtestevent.qdoc
index af84f2ac2b..a7af6d5592 100644
--- a/src/testlib/qtestevent.qdoc
+++ b/src/testlib/qtestevent.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\class QTestEventList
@@ -142,25 +118,25 @@
For an example, please read the \l QTestEventList class documentation.
*/
-/*! \fn void QTestEventList::addMousePress(Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTestEventList::addMousePress(Qt::MouseButton button, Qt::KeyboardModifiers modifiers, QPoint pos = QPoint(), int delay=-1)
Add a mouse press to the list. The event will press the \a button with optional \a modifiers at the position \a pos with an optional \a delay. The default position is the center of the widget.
\sa QTest::mousePress()
*/
-/*! \fn void QTestEventList::addMouseRelease(Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTestEventList::addMouseRelease(Qt::MouseButton button, Qt::KeyboardModifiers modifiers, QPoint pos = QPoint(), int delay=-1)
Add a mouse release to the list. The event will release the \a button with optional \a modifiers at the position \a pos with an optional \a delay. The default position is the center of the widget.
\sa QTest::mouseRelease()
*/
-/*! \fn void QTestEventList::addMouseClick(Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTestEventList::addMouseClick(Qt::MouseButton button, Qt::KeyboardModifiers modifiers, QPoint pos = QPoint(), int delay=-1)
Add a mouse click to the list. The event will click the \a button with optional \a modifiers at the position \a pos with an optional \a delay. The default position is the center of the widget.
\sa QTest::mouseClick()
*/
-/*! \fn void QTestEventList::addMouseDClick(Qt::MouseButton button, Qt::KeyboardModifiers modifiers = 0, QPoint pos = QPoint(), int delay=-1)
+/*! \fn void QTestEventList::addMouseDClick(Qt::MouseButton button, Qt::KeyboardModifiers modifiers, QPoint pos = QPoint(), int delay=-1)
Add a double mouse click to the list. The event will double click the \a button with optional \a modifiers at the position \a pos with an optional \a delay. The default position is the center of the widget.
diff --git a/src/testlib/qtesteventloop.h b/src/testlib/qtesteventloop.h
index d576de4267..6e90880690 100644
--- a/src/testlib/qtesteventloop.h
+++ b/src/testlib/qtesteventloop.h
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTEVENTLOOP_H
#define QTESTEVENTLOOP_H
#include <QtTest/qttestglobal.h>
+#include <QtTest/qtestcase.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qeventloop.h>
@@ -60,8 +25,9 @@ public:
: QObject(parent), _timeout(false)
{}
- inline void enterLoopMSecs(int ms);
- inline void enterLoop(int secs) { enterLoopMSecs(secs * 1000); }
+ void enterLoopMSecs(int ms) { enterLoop(std::chrono::milliseconds{ms}); };
+ void enterLoop(int secs) { enterLoop(std::chrono::seconds{secs}); }
+ inline void enterLoop(std::chrono::milliseconds msecs);
inline void changeInterval(int secs)
{ killTimer(timerId); timerId = startTimer(secs * 1000); }
@@ -71,7 +37,7 @@ public:
inline static QTestEventLoop &instance()
{
- static QPointer<QTestEventLoop> testLoop;
+ Q_CONSTINIT static QPointer<QTestEventLoop> testLoop;
if (testLoop.isNull())
testLoop = new QTestEventLoop(QCoreApplication::instance());
return *static_cast<QTestEventLoop *>(testLoop);
@@ -90,15 +56,18 @@ private:
Q_DECL_UNUSED_MEMBER uint reserved :31;
};
-inline void QTestEventLoop::enterLoopMSecs(int ms)
+inline void QTestEventLoop::enterLoop(std::chrono::milliseconds msecs)
{
Q_ASSERT(!loop);
-
- QEventLoop l;
-
_timeout = false;
- timerId = startTimer(ms);
+ if (QTest::runningTest() && QTest::currentTestResolved())
+ return;
+
+ using namespace std::chrono_literals;
+ QEventLoop l;
+ // if tests want to measure sub-second precision, use a precise timer
+ timerId = startTimer(msecs, msecs < 1s ? Qt::PreciseTimer : Qt::CoarseTimer);
loop = &l;
l.exec();
diff --git a/src/testlib/qtesthelpers_p.h b/src/testlib/qtesthelpers_p.h
index 0e39f7aea2..d05d7cc660 100644
--- a/src/testlib/qtesthelpers_p.h
+++ b/src/testlib/qtesthelpers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTHELPERS_P_H
#define QTESTHELPERS_P_H
@@ -55,6 +19,7 @@
#include <QtCore/QString>
#include <QtCore/QChar>
#include <QtCore/QPoint>
+#include <QtCore/private/qglobal_p.h>
#ifdef QT_GUI_LIB
#include <QtGui/QGuiApplication>
@@ -103,6 +68,19 @@ static inline void setFrameless(QWidget *w)
| Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
w->setWindowFlags(flags);
}
+
+static inline void androidCompatibleShow(QWidget *widget)
+{
+ // On Android QWidget::show() shows the widget maximized, so if we need
+ // to move or resize the widget, we need to explicitly call
+ // QWidget::setVisible(true) instead, because that's what show() actually
+ // does on desktop platforms.
+#ifdef Q_OS_ANDROID
+ widget->setVisible(true);
+#else
+ widget->show();
+#endif
+}
#endif // QT_WIDGETS_LIB
} // namespace QTestPrivate
diff --git a/src/testlib/qtestjunitstreamer.cpp b/src/testlib/qtestjunitstreamer.cpp
index 28879c6e06..ee6fee2f2b 100644
--- a/src/testlib/qtestjunitstreamer.cpp
+++ b/src/testlib/qtestjunitstreamer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qtestjunitstreamer_p.h>
#include <QtTest/private/qjunittestlogger_p.h>
@@ -82,18 +46,8 @@ void QTestJUnitStreamer::formatStart(const QTestElement *element, QTestCharBuffe
char indent[20];
indentForElement(element, indent, sizeof(indent));
- // Messages/errors are written as CDATA within system-out, system-err,
- // respectively, comments elsewhere
- if (element->elementType() == QTest::LET_Message) {
- switch (element->parentElement()->elementType()) {
- case QTest::LET_SystemOutput:
- case QTest::LET_SystemError:
- QTest::qt_asprintf(formatted, "<![CDATA[");
- break;
- default:
- QTest::qt_asprintf(formatted, "%s<!--", indent);
- break;
- }
+ if (element->elementType() == QTest::LET_Text) {
+ QTest::qt_asprintf(formatted, "%s<![CDATA[", indent);
return;
}
@@ -105,7 +59,7 @@ void QTestJUnitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer
if (!element || !formatted )
return;
- if (!element->childElements()) {
+ if (element->childElements().empty()) {
formatted->data()[0] = '\0';
return;
}
@@ -116,38 +70,26 @@ void QTestJUnitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer
QTest::qt_asprintf(formatted, "%s</%s>\n", indent, element->elementName());
}
-void QTestJUnitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const
+bool QTestJUnitStreamer::formatAttributes(const QTestElement* element,
+ const QTestElementAttribute *attribute,
+ QTestCharBuffer *formatted) const
{
if (!attribute || !formatted )
- return;
+ return false;
QTest::AttributeIndex attrindex = attribute->index();
- // For messages/errors within system-out, system-err, respectively,
- // we only want to output `message'
- if (element && element->elementType() == QTest::LET_Message
- && (element->parentElement()->elementType() == QTest::LET_SystemOutput
- || element->parentElement()->elementType() == QTest::LET_SystemError)) {
-
- if (attrindex != QTest::AI_Description) return;
-
- QXmlTestLogger::xmlCdata(formatted, attribute->value());
- return;
+ if (element && element->elementType() == QTest::LET_Text) {
+ QTEST_ASSERT(attrindex == QTest::AI_Value);
+ return QXmlTestLogger::xmlCdata(formatted, attribute->value());
}
- char const* key = nullptr;
- if (attrindex == QTest::AI_Description)
- key = "message";
- else if (attrindex != QTest::AI_File && attrindex != QTest::AI_Line)
- key = attribute->name();
-
- if (key) {
- QTestCharBuffer quotedValue;
- QXmlTestLogger::xmlQuote(&quotedValue, attribute->value());
- QTest::qt_asprintf(formatted, " %s=\"%s\"", key, quotedValue.constData());
- } else {
- formatted->data()[0] = '\0';
+ QTestCharBuffer quotedValue;
+ if (QXmlTestLogger::xmlQuote(&quotedValue, attribute->value())) {
+ return QTest::qt_asprintf(formatted, " %s=\"%s\"",
+ attribute->name(), quotedValue.constData()) != 0;
}
+ return false;
}
void QTestJUnitStreamer::formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const
@@ -155,22 +97,12 @@ void QTestJUnitStreamer::formatAfterAttributes(const QTestElement *element, QTes
if (!element || !formatted )
return;
- // Messages/errors are written as CDATA within system-out, system-err,
- // respectively, comments elsewhere
- if (element->elementType() == QTest::LET_Message) {
- switch (element->parentElement()->elementType()) {
- case QTest::LET_SystemOutput:
- case QTest::LET_SystemError:
- QTest::qt_asprintf(formatted, "]]>\n");
- break;
- default:
- QTest::qt_asprintf(formatted, " -->\n");
- break;
- }
+ if (element->elementType() == QTest::LET_Text) {
+ QTest::qt_asprintf(formatted, "]]>\n");
return;
}
- if (!element->childElements())
+ if (element->childElements().empty())
QTest::qt_asprintf(formatted, "/>\n");
else
QTest::qt_asprintf(formatted, ">\n");
@@ -180,55 +112,39 @@ void QTestJUnitStreamer::output(QTestElement *element) const
{
QTEST_ASSERT(element);
- outputString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
- outputElements(element);
-}
+ if (!element->parentElement())
+ outputString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
-void QTestJUnitStreamer::outputElements(QTestElement *element, bool) const
-{
QTestCharBuffer buf;
- bool hasChildren;
-
- // Elements are in reverse order of occurrence, so
- // start from the end and work our way backwards.
- while (element && element->nextElement())
- element = element->nextElement();
- while (element) {
- hasChildren = element->childElements();
+ formatStart(element, &buf);
+ outputString(buf.data());
- if (element->elementType() != QTest::LET_Benchmark) {
- formatStart(element, &buf);
- outputString(buf.data());
+ outputElementAttributes(element, element->attributes());
- outputElementAttributes(element, element->attributes());
+ formatAfterAttributes(element, &buf);
+ outputString(buf.data());
- formatAfterAttributes(element, &buf);
- outputString(buf.data());
+ if (!element->childElements().empty())
+ outputElements(element->childElements());
- if (hasChildren)
- outputElements(element->childElements(), true);
+ formatEnd(element, &buf);
+ outputString(buf.data());
+}
- formatEnd(element, &buf);
- outputString(buf.data());
- }
- element = element->previousElement();
- }
+void QTestJUnitStreamer::outputElements(const std::vector<QTestElement*> &elements) const
+{
+ for (auto *element : elements)
+ output(element);
}
-void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const
+void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, const std::vector<QTestElementAttribute*> &attributes) const
{
QTestCharBuffer buf;
- // Attributes are in reverse order of occurrence, so
- // start from the end and work our way backwards.
- while (attribute && attribute->nextElement())
- attribute = attribute->nextElement();
-
- while (attribute) {
- formatAttributes(element, attribute, &buf);
- outputString(buf.data());
- attribute = attribute->previousElement();
+ for (auto *attribute : attributes) {
+ if (formatAttributes(element, attribute, &buf))
+ outputString(buf.data());
}
}
diff --git a/src/testlib/qtestjunitstreamer_p.h b/src/testlib/qtestjunitstreamer_p.h
index 7d91e2b66c..60cfa8be8e 100644
--- a/src/testlib/qtestjunitstreamer_p.h
+++ b/src/testlib/qtestjunitstreamer_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTJUNITSTREAMER_P_H
#define QTESTJUNITSTREAMER_P_H
@@ -51,7 +15,8 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
+#include <vector>
QT_BEGIN_NAMESPACE
@@ -70,14 +35,16 @@ class QTestJUnitStreamer
void formatStart(const QTestElement *element, QTestCharBuffer *formatted) const;
void formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const;
void formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const;
- void formatAttributes(const QTestElement *element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const;
void output(QTestElement *element) const;
- void outputElements(QTestElement *element, bool isChildElement = false) const;
- void outputElementAttributes(const QTestElement *element, QTestElementAttribute *attribute) const;
+ void outputElements(const std::vector<QTestElement*> &) const;
+ void outputElementAttributes(const QTestElement *element, const std::vector<QTestElementAttribute*> &attributes) const;
void outputString(const char *msg) const;
private:
+ [[nodiscard]] bool formatAttributes(const QTestElement *element,
+ const QTestElementAttribute *attribute,
+ QTestCharBuffer *formatted) const;
static void indentForElement(const QTestElement* element, char* buf, int size);
QJUnitTestLogger *testLogger;
diff --git a/src/testlib/qtestkeyboard.h b/src/testlib/qtestkeyboard.h
index 1c1b676e70..b051bd9124 100644
--- a/src/testlib/qtestkeyboard.h
+++ b/src/testlib/qtestkeyboard.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTKEYBOARD_H
#define QTESTKEYBOARD_H
@@ -93,7 +57,10 @@ namespace QTest
if (action == Click) {
+ QPointer<QWindow> ptr(window);
sendKeyEvent(Press, window, code, text, modifier, delay);
+ if (!ptr)
+ return;
sendKeyEvent(Release, window, code, text, modifier, delay);
return;
}
@@ -197,7 +164,7 @@ namespace QTest
if (press && qt_sendShortcutOverrideEvent(widget, a.timestamp(), code, modifier, text, repeat))
return;
if (!qApp->notify(widget, &a))
- QTest::qWarn("Keyboard event not accepted by receiving widget");
+ qWarning("Keyboard event not accepted by receiving widget");
}
static void sendKeyEvent(KeyAction action, QWidget *widget, Qt::Key code,
@@ -292,7 +259,7 @@ namespace QTest
inline static void keyClicks(QWidget *widget, const QString &sequence,
Qt::KeyboardModifiers modifier = Qt::NoModifier, int delay=-1)
{
- for (int i=0; i < sequence.length(); i++)
+ for (int i=0; i < sequence.size(); i++)
keyEvent(Click, widget, sequence.at(i).toLatin1(), modifier, delay);
}
diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp
index 7fbb3155fa..4b6df54e91 100644
--- a/src/testlib/qtestlog.cpp
+++ b/src/testlib/qtestlog.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/qtestassert.h>
@@ -60,6 +24,7 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qvariant.h>
#if QT_CONFIG(regularexpression)
#include <QtCore/QRegularExpression>
@@ -68,9 +33,15 @@
#include <stdlib.h>
#include <string.h>
#include <limits.h>
+#include <vector>
+
+#include <vector>
+#include <memory>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static void saveCoverageTool(const char * appname, bool testfailed, bool installedTestCoverage)
{
#ifdef __COVERAGESCANNER__
@@ -96,10 +67,10 @@ static void saveCoverageTool(const char * appname, bool testfailed, bool install
#endif
}
-static QElapsedTimer elapsedFunctionTime;
-static QElapsedTimer elapsedTotalTime;
+Q_CONSTINIT static QElapsedTimer elapsedFunctionTime;
+Q_CONSTINIT static QElapsedTimer elapsedTotalTime;
-#define FOREACH_TEST_LOGGER for (QAbstractTestLogger *logger : *QTest::loggers())
+#define FOREACH_TEST_LOGGER for (const auto &logger : std::as_const(*QTest::loggers()))
namespace QTest {
@@ -107,6 +78,7 @@ namespace QTest {
int passes = 0;
int skips = 0;
int blacklists = 0;
+ enum { Unresolved, Passed, Skipped, Suppressed, Failed } currentTestState;
struct IgnoreResultList
{
@@ -143,8 +115,8 @@ namespace QTest {
// ignore an optional whitespace at the end of str
// (the space was added automatically by ~QDebug() until Qt 5.3,
// so autotests still might expect it)
- if (expected.endsWith(QLatin1Char(' ')))
- return actual == QStringView{expected}.left(expected.length() - 1);
+ if (expected.endsWith(u' '))
+ return actual == QStringView{expected}.left(expected.size() - 1);
return false;
}
@@ -167,8 +139,11 @@ namespace QTest {
};
static IgnoreResultList *ignoreResultList = nullptr;
+ Q_CONSTINIT static QBasicMutex mutex;
- Q_GLOBAL_STATIC(QList<QAbstractTestLogger *>, loggers)
+ static std::vector<QVariant> failOnWarningList;
+
+ Q_GLOBAL_STATIC(std::vector<std::unique_ptr<QAbstractTestLogger>>, loggers)
static int verbosity = 0;
static int maxWarnings = 2002;
@@ -178,6 +153,8 @@ namespace QTest {
static bool handleIgnoredMessage(QtMsgType type, const QString &message)
{
+ const QMutexLocker mutexLocker(&QTest::mutex);
+
if (!ignoreResultList)
return false;
IgnoreResultList *last = nullptr;
@@ -187,10 +164,8 @@ namespace QTest {
// remove the item from the list
if (last)
last->next = list->next;
- else if (list->next)
- ignoreResultList = list->next;
else
- ignoreResultList = nullptr;
+ ignoreResultList = list->next;
delete list;
return true;
@@ -202,14 +177,40 @@ namespace QTest {
return false;
}
+ static bool handleFailOnWarning(const QMessageLogContext &context, const QString &message)
+ {
+ // failOnWarning can be called multiple times per test function, so let
+ // each call cause a failure if required.
+ for (const auto &pattern : failOnWarningList) {
+ if (pattern.metaType() == QMetaType::fromType<QString>()) {
+ if (message != pattern.toString())
+ continue;
+ }
+#if QT_CONFIG(regularexpression)
+ else if (pattern.metaType() == QMetaType::fromType<QRegularExpression>()) {
+ if (!message.contains(pattern.toRegularExpression()))
+ continue;
+ }
+#endif
+
+ const size_t maxMsgLen = 1024;
+ char msg[maxMsgLen] = {'\0'};
+ qsnprintf(msg, maxMsgLen, "Received a warning that resulted in a failure:\n%s",
+ qPrintable(message));
+ QTestResult::addFailure(msg, context.file, context.line);
+ return true;
+ }
+ return false;
+ }
+
static void messageHandler(QtMsgType type, const QMessageLogContext & context, const QString &message)
{
static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(QTest::maxWarnings);
- if (QTestLog::loggerCount() == 0) {
+ if (!QTestLog::hasLoggers()) {
// if this goes wrong, something is seriously broken.
qInstallMessageHandler(oldMessageHandler);
- QTEST_ASSERT(QTestLog::loggerCount() != 0);
+ QTEST_ASSERT(QTestLog::hasLoggers());
}
if (handleIgnoredMessage(type, message)) {
@@ -217,13 +218,16 @@ namespace QTest {
return;
}
+ if (type == QtWarningMsg && handleFailOnWarning(context, message))
+ return;
+
if (type != QtFatalMsg) {
if (counter.loadRelaxed() <= 0)
return;
if (!counter.deref()) {
FOREACH_TEST_LOGGER {
- logger->addMessage(QAbstractTestLogger::QSystem,
+ logger->addMessage(QAbstractTestLogger::Warn,
QStringLiteral("Maximum amount of warnings exceeded. Use -maxwarnings to override."));
}
return;
@@ -239,7 +243,7 @@ namespace QTest {
* this function, it will proceed with calling exit() and abort()
* and hence crash. Therefore, we call these logging functions such
* that we wrap up nicely, and in particular produce well-formed XML. */
- QTestResult::addFailure("Received a fatal error.", "Unknown file", 0);
+ QTestResult::addFailure("Received a fatal error.", context.file, context.line);
QTestLog::leaveTestFunction();
QTestLog::stopLogging();
}
@@ -268,6 +272,7 @@ void QTestLog::enterTestData(QTestData *data)
int QTestLog::unhandledIgnoreMessages()
{
+ const QMutexLocker mutexLocker(&QTest::mutex);
int i = 0;
QTest::IgnoreResultList *list = QTest::ignoreResultList;
while (list) {
@@ -288,14 +293,16 @@ void QTestLog::leaveTestFunction()
void QTestLog::printUnhandledIgnoreMessages()
{
+ const QMutexLocker mutexLocker(&QTest::mutex);
QString message;
QTest::IgnoreResultList *list = QTest::ignoreResultList;
while (list) {
if (list->pattern.userType() == QMetaType::QString) {
- message = QStringLiteral("Did not receive message: \"") + list->pattern.toString() + QLatin1Char('"');
+ message = "Did not receive message: \"%1\""_L1.arg(list->pattern.toString());
} else {
#if QT_CONFIG(regularexpression)
- message = QStringLiteral("Did not receive any message matching: \"") + list->pattern.toRegularExpression().pattern() + QLatin1Char('"');
+ message = "Did not receive any message matching: \"%1\""_L1.arg(
+ list->pattern.toRegularExpression().pattern());
#endif
}
FOREACH_TEST_LOGGER
@@ -307,17 +314,32 @@ void QTestLog::printUnhandledIgnoreMessages()
void QTestLog::clearIgnoreMessages()
{
+ const QMutexLocker mutexLocker(&QTest::mutex);
QTest::IgnoreResultList::clearList(QTest::ignoreResultList);
}
+void QTestLog::clearFailOnWarnings()
+{
+ QTest::failOnWarningList.clear();
+}
+
+void QTestLog::clearCurrentTestState()
+{
+ clearIgnoreMessages();
+ clearFailOnWarnings();
+ QTest::currentTestState = QTest::Unresolved;
+}
+
void QTestLog::addPass(const char *msg)
{
if (printAvailableTags)
return;
QTEST_ASSERT(msg);
+ Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
++QTest::passes;
+ QTest::currentTestState = QTest::Passed;
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::Pass, msg);
@@ -327,8 +349,18 @@ void QTestLog::addFail(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
- ++QTest::fails;
+ if (QTest::currentTestState == QTest::Unresolved) {
+ ++QTest::fails;
+ } else {
+ // After an XPASS/Continue, or fail or skip in a function the test
+ // calls, we can subsequently fail.
+ Q_ASSERT(QTest::currentTestState == QTest::Failed
+ || QTest::currentTestState == QTest::Skipped);
+ }
+ // It is up to particular loggers to decide whether to report such
+ // subsequent failures; they may carry useful information.
+ QTest::currentTestState = QTest::Failed;
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::Fail, msg, file, line);
}
@@ -336,7 +368,8 @@ void QTestLog::addFail(const char *msg, const char *file, int line)
void QTestLog::addXFail(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
- QTEST_ASSERT(file);
+
+ // Will be counted in addPass() if we get there.
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::XFail, msg, file, line);
@@ -345,10 +378,17 @@ void QTestLog::addXFail(const char *msg, const char *file, int line)
void QTestLog::addXPass(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
- QTEST_ASSERT(file);
- ++QTest::fails;
+ if (QTest::currentTestState == QTest::Unresolved) {
+ ++QTest::fails;
+ } else {
+ // After an XPASS/Continue, we can subsequently XPASS again.
+ // Likewise after a fail or skip in a function called by the test.
+ Q_ASSERT(QTest::currentTestState == QTest::Failed
+ || QTest::currentTestState == QTest::Skipped);
+ }
+ QTest::currentTestState = QTest::Failed;
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::XPass, msg, file, line);
}
@@ -356,8 +396,10 @@ void QTestLog::addXPass(const char *msg, const char *file, int line)
void QTestLog::addBPass(const char *msg)
{
QTEST_ASSERT(msg);
+ Q_ASSERT(QTest::currentTestState == QTest::Unresolved);
- ++QTest::blacklists;
+ ++QTest::blacklists; // Not passes ?
+ QTest::currentTestState = QTest::Suppressed;
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedPass, msg);
@@ -366,10 +408,17 @@ void QTestLog::addBPass(const char *msg)
void QTestLog::addBFail(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
- QTEST_ASSERT(file);
- ++QTest::blacklists;
+ if (QTest::currentTestState == QTest::Unresolved) {
+ ++QTest::blacklists;
+ } else {
+ // After a BXPASS/Continue, we can subsequently fail.
+ // Likewise after a fail or skip in a function called by a test.
+ Q_ASSERT(QTest::currentTestState == QTest::Suppressed
+ || QTest::currentTestState == QTest::Skipped);
+ }
+ QTest::currentTestState = QTest::Suppressed;
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedFail, msg, file, line);
}
@@ -377,10 +426,17 @@ void QTestLog::addBFail(const char *msg, const char *file, int line)
void QTestLog::addBXPass(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
- QTEST_ASSERT(file);
- ++QTest::blacklists;
+ if (QTest::currentTestState == QTest::Unresolved) {
+ ++QTest::blacklists;
+ } else {
+ // After a BXPASS/Continue, we may BXPASS again.
+ // Likewise after a fail or skip in a function called by a test.
+ Q_ASSERT(QTest::currentTestState == QTest::Suppressed
+ || QTest::currentTestState == QTest::Skipped);
+ }
+ QTest::currentTestState = QTest::Suppressed;
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedXPass, msg, file, line);
}
@@ -388,9 +444,8 @@ void QTestLog::addBXPass(const char *msg, const char *file, int line)
void QTestLog::addBXFail(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
- QTEST_ASSERT(file);
- ++QTest::blacklists;
+ // Will be counted in addBPass() if we get there.
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line);
@@ -399,18 +454,28 @@ void QTestLog::addBXFail(const char *msg, const char *file, int line)
void QTestLog::addSkip(const char *msg, const char *file, int line)
{
QTEST_ASSERT(msg);
- QTEST_ASSERT(file);
- ++QTest::skips;
+ if (QTest::currentTestState == QTest::Unresolved) {
+ ++QTest::skips;
+ QTest::currentTestState = QTest::Skipped;
+ } else {
+ // After an B?XPASS/Continue, we might subsequently skip.
+ // Likewise after a skip in a function called by a test.
+ Q_ASSERT(QTest::currentTestState == QTest::Suppressed
+ || QTest::currentTestState == QTest::Failed
+ || QTest::currentTestState == QTest::Skipped);
+ }
+ // It is up to particular loggers to decide whether to report such
+ // subsequent skips; they may carry useful information.
FOREACH_TEST_LOGGER
- logger->addMessage(QAbstractTestLogger::Skip, QString::fromUtf8(msg), file, line);
+ logger->addIncident(QAbstractTestLogger::Skip, msg, file, line);
}
-void QTestLog::addBenchmarkResult(const QBenchmarkResult &result)
+void QTestLog::addBenchmarkResults(const QList<QBenchmarkResult> &results)
{
FOREACH_TEST_LOGGER
- logger->addBenchmarkResult(result);
+ logger->addBenchmarkResults(results);
}
void QTestLog::startLogging()
@@ -427,7 +492,6 @@ void QTestLog::stopLogging()
qInstallMessageHandler(QTest::oldMessageHandler);
FOREACH_TEST_LOGGER {
logger->stopLogging();
- delete logger;
}
QTest::loggers()->clear();
saveCoverageTool(QTestResult::currentAppName(), failCount() != 0, QTestLog::installedTestCoverage());
@@ -488,12 +552,27 @@ void QTestLog::addLogger(LogMode mode, const char *filename)
void QTestLog::addLogger(QAbstractTestLogger *logger)
{
QTEST_ASSERT(logger);
- QTest::loggers()->append(logger);
+ QTest::loggers()->emplace_back(logger);
}
-int QTestLog::loggerCount()
+bool QTestLog::hasLoggers()
{
- return QTest::loggers()->size();
+ return !QTest::loggers()->empty();
+}
+
+/*!
+ \internal
+
+ Returns true if all loggers support repeated test runs
+*/
+bool QTestLog::isRepeatSupported()
+{
+ FOREACH_TEST_LOGGER {
+ if (!logger->isRepeatSupported())
+ return false;
+ }
+
+ return true;
}
bool QTestLog::loggerUsingStdout()
@@ -536,6 +615,7 @@ void QTestLog::ignoreMessage(QtMsgType type, const char *msg)
{
QTEST_ASSERT(msg);
+ const QMutexLocker mutexLocker(&QTest::mutex);
QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QString::fromUtf8(msg));
}
@@ -544,10 +624,25 @@ void QTestLog::ignoreMessage(QtMsgType type, const QRegularExpression &expressio
{
QTEST_ASSERT(expression.isValid());
+ const QMutexLocker mutexLocker(&QTest::mutex);
QTest::IgnoreResultList::append(QTest::ignoreResultList, type, QVariant(expression));
}
#endif // QT_CONFIG(regularexpression)
+void QTestLog::failOnWarning(const char *msg)
+{
+ QTest::failOnWarningList.push_back(QString::fromUtf8(msg));
+}
+
+#if QT_CONFIG(regularexpression)
+void QTestLog::failOnWarning(const QRegularExpression &expression)
+{
+ QTEST_ASSERT(expression.isValid());
+
+ QTest::failOnWarningList.push_back(QVariant::fromValue(expression));
+}
+#endif // QT_CONFIG(regularexpression)
+
void QTestLog::setMaxWarnings(int m)
{
QTest::maxWarnings = m <= 0 ? INT_MAX : m + 2;
diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h
index bdb22acbd3..f9bbfa158d 100644
--- a/src/testlib/qtestlog_p.h
+++ b/src/testlib/qtestlog_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTLOG_P_H
#define QTESTLOG_P_H
@@ -99,15 +63,23 @@ public:
static void addBXPass(const char *msg, const char *file, int line);
static void addBXFail(const char *msg, const char *file, int line);
static void addSkip(const char *msg, const char *file, int line);
- static void addBenchmarkResult(const QBenchmarkResult &result);
+ static void addBenchmarkResult(const QList<QBenchmarkResult> &result)
+ { return addBenchmarkResults({ result }); }
+ static void addBenchmarkResults(const QList<QBenchmarkResult> &result);
static void ignoreMessage(QtMsgType type, const char *msg);
#ifndef QT_NO_REGULAREXPRESSION
static void ignoreMessage(QtMsgType type, const QRegularExpression &expression);
#endif
+ static void failOnWarning(const char *msg);
+#ifndef QT_NO_REGULAREXPRESSION
+ static void failOnWarning(const QRegularExpression &expression);
+#endif
static int unhandledIgnoreMessages();
static void printUnhandledIgnoreMessages();
static void clearIgnoreMessages();
+ static void clearFailOnWarnings();
+ static void clearCurrentTestState();
static void warn(const char *msg, const char *file, int line);
static void info(const char *msg, const char *file, int line);
@@ -118,7 +90,8 @@ public:
static void addLogger(LogMode mode, const char *filename);
static void addLogger(QAbstractTestLogger *logger);
- static int loggerCount();
+ static bool hasLoggers();
+ static bool isRepeatSupported();
static bool loggerUsingStdout();
static void setVerboseLevel(int level);
diff --git a/src/testlib/qtestmouse.cpp b/src/testlib/qtestmouse.cpp
index 976908730a..e76a38ac84 100644
--- a/src/testlib/qtestmouse.cpp
+++ b/src/testlib/qtestmouse.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/QtGlobal>
#include <QtCore/qnamespace.h>
diff --git a/src/testlib/qtestmouse.h b/src/testlib/qtestmouse.h
index 3e43434a50..567d80c521 100644
--- a/src/testlib/qtestmouse.h
+++ b/src/testlib/qtestmouse.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTMOUSE_H
#define QTESTMOUSE_H
@@ -93,6 +57,12 @@ namespace QTest
event. We expect all event-handling code to rely on the event
timestamps, not the system clock; therefore tests can be run faster
than real-time.
+
+ If \a delay is not given, a default minimum mouse delay is used, and
+ unintended double-click events are prevented by incrementing the
+ timestamp by 500ms after each mouse release. Therefore, to test
+ double-clicks, it's necessary to give a realistic \a delay value (for
+ example, 10ms).
*/
static void mouseEvent(MouseAction action, QWindow *window, Qt::MouseButton button,
Qt::KeyboardModifiers stateKey, QPoint pos, int delay=-1)
@@ -103,20 +73,19 @@ namespace QTest
// pos is in window local coordinates
const QSize windowSize = window->geometry().size();
if (windowSize.width() <= pos.x() || windowSize.height() <= pos.y()) {
- QTest::qWarn(qPrintable(QString::fromLatin1("Mouse event at %1, %2 occurs outside of target window (%3x%4).")
- .arg(pos.x()).arg(pos.y()).arg(windowSize.width()).arg(windowSize.height())));
+ qWarning("Mouse event at %d, %d occurs outside target window (%dx%d).",
+ pos.x(), pos.y(), windowSize.width(), windowSize.height());
}
- if (delay == -1 || delay < defaultMouseDelay())
- delay = defaultMouseDelay();
- lastMouseTimestamp += qMax(1, delay);
+ int actualDelay = (delay == -1 || delay < defaultMouseDelay()) ? defaultMouseDelay() : delay;
+ lastMouseTimestamp += qMax(1, actualDelay);
if (pos.isNull())
pos = QPoint(window->width() / 2, window->height() / 2);
- QTEST_ASSERT(uint(stateKey) == 0 || stateKey & Qt::KeyboardModifierMask);
+ QTEST_ASSERT(!stateKey || stateKey & Qt::KeyboardModifierMask);
- stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
+ stateKey &= Qt::KeyboardModifierMask;
QPointF global = window->mapToGlobal(pos);
QPointer<QWindow> w(window);
@@ -144,7 +113,8 @@ namespace QTest
qtestMouseButtons.setFlag(button, false);
qt_handleMouseEvent(w, pos, global, qtestMouseButtons, button, QEvent::MouseButtonRelease,
stateKey, lastMouseTimestamp);
- lastMouseTimestamp += mouseDoubleClickInterval; // avoid double clicks being generated
+ if (delay == -1)
+ lastMouseTimestamp += mouseDoubleClickInterval; // avoid double clicks being generated
break;
case MouseMove:
qt_handleMouseEvent(w, pos, global, qtestMouseButtons, Qt::NoButton, QEvent::MouseMove,
@@ -201,34 +171,41 @@ namespace QTest
return;
}
- QTEST_ASSERT(stateKey == 0 || stateKey & Qt::KeyboardModifierMask);
+ QTEST_ASSERT(!stateKey || stateKey & Qt::KeyboardModifierMask);
- stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask);
+ stateKey &= Qt::KeyboardModifierMask;
- QEvent::Type meType;
- Qt::MouseButton meButton;
+ QEvent::Type meType = QEvent::None;
+ using namespace QTestPrivate;
switch (action)
{
case MousePress:
+ qtestMouseButtons.setFlag(button, true);
meType = QEvent::MouseButtonPress;
- meButton = button;
break;
case MouseRelease:
+ qtestMouseButtons.setFlag(button, false);
meType = QEvent::MouseButtonRelease;
- meButton = Qt::MouseButton();
break;
case MouseDClick:
+ qtestMouseButtons.setFlag(button, true);
meType = QEvent::MouseButtonDblClick;
- meButton = button;
break;
case MouseMove:
- QCursor::setPos(widget->mapToGlobal(pos));
- qApp->processEvents();
- return;
+ // ### Qt 7: compatibility with < Qt 6.3, we should not rely on QCursor::setPos
+ // for generating mouse move events, and code that depends on QCursor::pos should
+ // be tested using QCursor::setPos explicitly.
+ if (qtestMouseButtons == Qt::NoButton) {
+ QCursor::setPos(widget->mapToGlobal(pos));
+ qApp->processEvents();
+ return;
+ }
+ meType = QEvent::MouseMove;
+ break;
default:
QTEST_ASSERT(false);
}
- QMouseEvent me(meType, pos, widget->mapToGlobal(pos), button, meButton, stateKey, QPointingDevice::primaryPointingDevice());
+ QMouseEvent me(meType, pos, widget->mapToGlobal(pos), button, qtestMouseButtons, stateKey, QPointingDevice::primaryPointingDevice());
me.setTimestamp(lastMouseTimestamp);
if (action == MouseRelease) // avoid double clicks being generated
lastMouseTimestamp += mouseDoubleClickInterval;
@@ -237,8 +214,8 @@ namespace QTest
if (!qApp->notify(widget, &me)) {
static const char *const mouseActionNames[] =
{ "MousePress", "MouseRelease", "MouseClick", "MouseDClick", "MouseMove" };
- QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving widget");
- QTest::qWarn(warning.arg(QString::fromLatin1(mouseActionNames[static_cast<int>(action)])).toLatin1().data());
+ qWarning("Mouse event \"%s\" not accepted by receiving widget",
+ mouseActionNames[static_cast<int>(action)]);
}
#endif
}
diff --git a/src/testlib/qtestregistry.cpp b/src/testlib/qtestregistry.cpp
new file mode 100644
index 0000000000..ff1f8a57e6
--- /dev/null
+++ b/src/testlib/qtestregistry.cpp
@@ -0,0 +1,36 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <QtTest/private/qtestregistry_p.h>
+
+QT_REQUIRE_CONFIG(batch_test_support);
+
+QT_BEGIN_NAMESPACE
+
+namespace QTest {
+Q_GLOBAL_STATIC(TestRegistry, g_registry);
+
+TestRegistry *TestRegistry::instance()
+{
+ return g_registry;
+}
+
+void TestRegistry::registerTest(const QString& name, TestEntryFunction entry)
+{
+ m_tests.emplace(name, std::move(entry));
+}
+
+TestRegistry::TestEntryFunction
+TestRegistry::getTestEntryFunction(const QString& name) const
+{
+ const auto it = m_tests.find(name);
+ return it != m_tests.end() ? it.value() : nullptr;
+}
+
+QStringList TestRegistry::getAllTestNames() const
+{
+ return m_tests.keys();
+}
+}
+
+QT_END_NAMESPACE
diff --git a/src/testlib/qtestregistry_p.h b/src/testlib/qtestregistry_p.h
new file mode 100644
index 0000000000..85e236cd04
--- /dev/null
+++ b/src/testlib/qtestregistry_p.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTESTREGISTRY_P_H
+#define QTESTREGISTRY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+#include <QtCore/qhash.h>
+#include <QtTest/qttestglobal.h>
+
+QT_REQUIRE_CONFIG(batch_test_support);
+
+QT_BEGIN_NAMESPACE
+
+namespace QTest {
+class TestRegistry {
+public:
+ using TestEntryFunction = int(*)(int argv, char** argc);
+
+ static TestRegistry* instance();
+
+ void registerTest(const QString& name, TestEntryFunction data);
+ size_t total() const {
+ return m_tests.size();
+ }
+ TestEntryFunction getTestEntryFunction(const QString& name) const;
+ QStringList getAllTestNames() const;
+
+private:
+ QHash<QString, TestEntryFunction> m_tests;
+};
+} // namespace QTest
+
+QT_END_NAMESPACE
+
+#endif // QTESTREGISTRY_P_H
diff --git a/src/testlib/qtestresult.cpp b/src/testlib/qtestresult.cpp
index 96c7af29d5..7c5ce9ce54 100644
--- a/src/testlib/qtestresult.cpp
+++ b/src/testlib/qtestresult.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qtestresult_p.h>
#include <QtCore/qglobal.h>
@@ -46,6 +10,7 @@
#include <QtTest/qtestdata.h>
#include <QtTest/qtestcase.h>
#include <QtTest/qtestassert.h>
+#include <QtTest/qtesteventloop.h>
#include <stdlib.h>
#include <stdio.h>
@@ -162,23 +127,52 @@ static void clearExpectFail()
QTest::expectFailComment = nullptr;
}
+/*!
+ This function is called after completing each test function,
+ including test functions that are not data-driven.
+
+ For data-driven functions, this is called after each call to the test
+ function, with distinct data. Otherwise, this function is called once,
+ with currentTestData() and currentGlobalTestData() set to \nullptr.
+
+ The function is called before the test's cleanup(), if it has one.
+
+ For benchmarks, this will be called after each repeat of a function
+ (with the same data row), when the benchmarking code decides to
+ re-run one to get sufficient data.
+
+ \sa finishedCurrentTestDataCleanup()
+*/
void QTestResult::finishedCurrentTestData()
{
- if (QTest::expectFailMode) {
- addFailure("QEXPECT_FAIL was called without any subsequent verification statements",
- "Unknown File", 0);
- }
+ if (QTest::expectFailMode)
+ addFailure("QEXPECT_FAIL was called without any subsequent verification statements");
+
clearExpectFail();
+}
+
+/*!
+ This function is called after completing each test function,
+ including test functions that are not data-driven.
+
+ For data-driven functions, this is called after each call to the test
+ function, with distinct data. Otherwise, this function is called once,
+ with currentTestData() and currentGlobalTestData() set to \nullptr.
+ The function is called after the test's cleanup(), if it has one.
+
+ For benchmarks, this is called after all repeat calls to the function
+ (with a given data row).
+
+ \sa finishedCurrentTestData()
+*/
+void QTestResult::finishedCurrentTestDataCleanup()
+{
if (!QTest::hasFailed() && QTestLog::unhandledIgnoreMessages()) {
QTestLog::printUnhandledIgnoreMessages();
- addFailure("Not all expected messages were received", "Unknown File", 0);
+ addFailure("Not all expected messages were received");
}
- QTestLog::clearIgnoreMessages();
-}
-void QTestResult::finishedCurrentTestDataCleanup()
-{
// If the current test hasn't failed or been skipped, then it passes.
if (!QTest::hasFailed() && !QTest::skipCurrentTest) {
if (QTest::blacklistCurrentTest)
@@ -187,15 +181,25 @@ void QTestResult::finishedCurrentTestDataCleanup()
QTestLog::addPass("");
}
+ QTestLog::clearCurrentTestState();
QTest::resetFailed();
}
+/*!
+ This function is called after completing each test function,
+ including test functions that are data-driven.
+
+ For data-driven functions, this is called after after all data rows
+ have been tested, and the data table has been cleared, so both
+ currentTestData() and currentGlobalTestData() will be \nullptr.
+*/
void QTestResult::finishedCurrentTestFunction()
{
+ QTestLog::clearCurrentTestState(); // Needed if _data() skipped.
+ QTestLog::leaveTestFunction();
+
QTest::currentTestFunc = nullptr;
QTest::resetFailed();
-
- QTestLog::leaveTestFunction();
}
const char *QTestResult::currentTestFunction()
@@ -256,6 +260,7 @@ static bool checkStatement(bool statement, const char *msg, const char *file, in
QTestLog::addXPass(msg, file, line);
QTest::setFailed(true);
+ // Should B?XPass always (a) continue or (b) abort, regardless of mode ?
bool doContinue = (QTest::expectFailMode == QTest::Continue);
clearExpectFail();
return doContinue;
@@ -282,20 +287,27 @@ void QTestResult::fail(const char *msg, const char *file, int line)
checkStatement(false, msg, file, line);
}
+// QPalette's << operator produces 1363 characters. A comparison failure
+// involving two palettes can therefore require 2726 characters, not including
+// the other output produced by QTest. Users might also have their own types
+// with large amounts of output, so use a sufficiently high value here.
+static constexpr size_t maxMsgLen = 4096;
+
bool QTestResult::verify(bool statement, const char *statementStr,
const char *description, const char *file, int line)
{
QTEST_ASSERT(statementStr);
- char msg[1024] = {'\0'};
+ char msg[maxMsgLen];
+ msg[0] = '\0';
if (QTestLog::verboseLevel() >= 2) {
- qsnprintf(msg, 1024, "QVERIFY(%s)", statementStr);
+ qsnprintf(msg, maxMsgLen, "QVERIFY(%s)", statementStr);
QTestLog::info(msg, file, line);
}
if (statement == !!QTest::expectFailMode) {
- qsnprintf(msg, 1024,
+ qsnprintf(msg, maxMsgLen,
statement ? "'%s' returned TRUE unexpectedly. (%s)" : "'%s' returned FALSE. (%s)",
statementStr, description ? description : "");
}
@@ -303,39 +315,57 @@ bool QTestResult::verify(bool statement, const char *statementStr,
return checkStatement(statement, msg, file, line);
}
-// Format failures using the toString() template
-template <class Actual, class Expected>
-void formatFailMessage(char *msg, size_t maxMsgLen,
- const char *failureMsg,
- const Actual &val1, const Expected &val2,
- const char *actual, const char *expected)
+static const char *leftArgNameForOp(QTest::ComparisonOperation op)
{
- auto val1S = QTest::toString(val1);
- auto val2S = QTest::toString(val2);
-
- size_t len1 = mbstowcs(nullptr, actual, maxMsgLen); // Last parameter is not ignored on QNX
- size_t len2 = mbstowcs(nullptr, expected, maxMsgLen); // (result is never larger than this).
- qsnprintf(msg, maxMsgLen, "%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s",
- failureMsg,
- actual, qMax(len1, len2) - len1 + 1, ":", val1S ? val1S : "<null>",
- expected, qMax(len1, len2) - len2 + 1, ":", val2S ? val2S : "<null>");
+ return op == QTest::ComparisonOperation::CustomCompare ? "Actual " : "Computed ";
+}
- delete [] val1S;
- delete [] val2S;
+static const char *rightArgNameForOp(QTest::ComparisonOperation op)
+{
+ return op == QTest::ComparisonOperation::CustomCompare ? "Expected " : "Baseline ";
}
// Overload to format failures for "const char *" - no need to strdup().
void formatFailMessage(char *msg, size_t maxMsgLen,
const char *failureMsg,
const char *val1, const char *val2,
- const char *actual, const char *expected)
+ const char *actual, const char *expected,
+ QTest::ComparisonOperation op)
{
size_t len1 = mbstowcs(nullptr, actual, maxMsgLen); // Last parameter is not ignored on QNX
size_t len2 = mbstowcs(nullptr, expected, maxMsgLen); // (result is never larger than this).
- qsnprintf(msg, maxMsgLen, "%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s",
- failureMsg,
- actual, qMax(len1, len2) - len1 + 1, ":", val1 ? val1 : "<null>",
- expected, qMax(len1, len2) - len2 + 1, ":", val2 ? val2 : "<null>");
+ const int written = qsnprintf(msg, maxMsgLen, "%s\n", failureMsg);
+ msg += written;
+ maxMsgLen -= written;
+
+ if (val1 || val2) {
+ qsnprintf(msg, maxMsgLen, " %s(%s)%*s %s\n %s(%s)%*s %s",
+ leftArgNameForOp(op), actual, qMax(len1, len2) - len1 + 1, ":",
+ val1 ? val1 : "<null>",
+ rightArgNameForOp(op), expected, qMax(len1, len2) - len2 + 1, ":",
+ val2 ? val2 : "<null>");
+ } else {
+ // only print variable names if neither value can be represented as a string
+ qsnprintf(msg, maxMsgLen, " %s: %s\n %s: %s",
+ leftArgNameForOp(op), actual, rightArgNameForOp(op), expected);
+ }
+}
+
+// Format failures using the toString() template
+template <class Actual, class Expected>
+void formatFailMessage(char *msg, size_t maxMsgLen,
+ const char *failureMsg,
+ const Actual &val1, const Expected &val2,
+ const char *actual, const char *expected,
+ QTest::ComparisonOperation op)
+{
+ const char *val1S = QTest::toString(val1);
+ const char *val2S = QTest::toString(val2);
+
+ formatFailMessage(msg, maxMsgLen, failureMsg, val1S, val2S, actual, expected, op);
+
+ delete [] val1S;
+ delete [] val2S;
}
template <class Actual, class Expected>
@@ -345,8 +375,8 @@ static bool compareHelper(bool success, const char *failureMsg,
const char *file, int line,
bool hasValues = true)
{
- const size_t maxMsgLen = 1024;
- char msg[maxMsgLen] = {'\0'};
+ char msg[maxMsgLen];
+ msg[0] = '\0';
QTEST_ASSERT(expected);
QTEST_ASSERT(actual);
@@ -373,11 +403,44 @@ static bool compareHelper(bool success, const char *failureMsg,
return checkStatement(success, msg, file, line);
}
- formatFailMessage(msg, maxMsgLen, failureMsg, val1, val2, actual, expected);
+ formatFailMessage(msg, maxMsgLen, failureMsg, val1, val2, actual, expected,
+ QTest::ComparisonOperation::CustomCompare);
return checkStatement(success, msg, file, line);
}
+// A simplified version of compareHelper that does not use string
+// representations of the values, and prints only failureMsg when the
+// comparison fails.
+static bool compareHelper(bool success, const char *failureMsg,
+ const char *actual, const char *expected,
+ const char *file, int line)
+{
+ const size_t maxMsgLen = 1024;
+ char msg[maxMsgLen];
+ msg[0] = '\0';
+
+ QTEST_ASSERT(expected);
+ QTEST_ASSERT(actual);
+ // failureMsg can be null, if we do not use it
+ QTEST_ASSERT(success || failureMsg);
+
+ if (QTestLog::verboseLevel() >= 2) {
+ qsnprintf(msg, maxMsgLen, "QCOMPARE(%s, %s)", actual, expected);
+ QTestLog::info(msg, file, line);
+ }
+
+ if (success) {
+ if (QTest::expectFailMode) {
+ qsnprintf(msg, maxMsgLen, "QCOMPARE(%s, %s) returned TRUE unexpectedly.",
+ actual, expected);
+ }
+ return checkStatement(success, msg, file, line);
+ }
+
+ return checkStatement(success, failureMsg, file, line);
+}
+
bool QTestResult::compare(bool success, const char *failureMsg,
char *val1, char *val2,
const char *actual, const char *expected,
@@ -447,7 +510,7 @@ bool QTestResult::compare(bool success, const char *failureMsg,
}
bool QTestResult::compare(bool success, const char *failureMsg,
- QStringView val1, const QLatin1String &val2,
+ QStringView val1, const QLatin1StringView &val2,
const char *actual, const char *expected,
const char *file, int line)
{
@@ -455,16 +518,27 @@ bool QTestResult::compare(bool success, const char *failureMsg,
}
bool QTestResult::compare(bool success, const char *failureMsg,
- const QLatin1String & val1, QStringView val2,
+ const QLatin1StringView & val1, QStringView val2,
const char *actual, const char *expected,
const char *file, int line)
{
return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line);
}
+// Simplified version of compare() that does not take the values, because they
+// can't be converted to strings (or the user didn't want to do that).
+bool QTestResult::compare(bool success, const char *failureMsg,
+ const char *actual, const char *expeceted,
+ const char *file, int line)
+{
+ return compareHelper(success, failureMsg, actual, expeceted, file, line);
+}
+
void QTestResult::addFailure(const char *message, const char *file, int line)
{
clearExpectFail();
+ if (qApp && QThread::currentThread() == qApp->thread())
+ QTestEventLoop::instance().exitLoop();
if (QTest::blacklistCurrentTest)
QTestLog::addBFail(message, file, line);
@@ -510,4 +584,86 @@ const char *QTestResult::currentAppName()
return ::currentAppName;
}
+static const char *macroNameForOp(QTest::ComparisonOperation op)
+{
+ using namespace QTest;
+ switch (op) {
+ case ComparisonOperation::CustomCompare:
+ return "QCOMPARE"; /* not used */
+ case ComparisonOperation::Equal:
+ return "QCOMPARE_EQ";
+ case ComparisonOperation::NotEqual:
+ return "QCOMPARE_NE";
+ case ComparisonOperation::LessThan:
+ return "QCOMPARE_LT";
+ case ComparisonOperation::LessThanOrEqual:
+ return "QCOMPARE_LE";
+ case ComparisonOperation::GreaterThan:
+ return "QCOMPARE_GT";
+ case ComparisonOperation::GreaterThanOrEqual:
+ return "QCOMPARE_GE";
+ }
+ Q_UNREACHABLE_RETURN("");
+}
+
+static const char *failureMessageForOp(QTest::ComparisonOperation op)
+{
+ using namespace QTest;
+ switch (op) {
+ case ComparisonOperation::CustomCompare:
+ return "Compared values are not the same"; /* not used */
+ case ComparisonOperation::Equal:
+ return "The computed value is expected to be equal to the baseline, but is not";
+ case ComparisonOperation::NotEqual:
+ return "The computed value is expected to be different from the baseline, but is not";
+ case ComparisonOperation::LessThan:
+ return "The computed value is expected to be less than the baseline, but is not";
+ case ComparisonOperation::LessThanOrEqual:
+ return "The computed value is expected to be less than or equal to the baseline, but is not";
+ case ComparisonOperation::GreaterThan:
+ return "The computed value is expected to be greater than the baseline, but is not";
+ case ComparisonOperation::GreaterThanOrEqual:
+ return "The computed value is expected to be greater than or equal to the baseline, but is not";
+ }
+ Q_UNREACHABLE_RETURN("");
+}
+
+bool QTestResult::reportResult(bool success, const void *lhs, const void *rhs,
+ const char *(*lhsFormatter)(const void*),
+ const char *(*rhsFormatter)(const void*),
+ const char *lhsExpr, const char *rhsExpr,
+ QTest::ComparisonOperation op, const char *file, int line,
+ const char *failureMessage)
+{
+ char msg[maxMsgLen];
+ msg[0] = '\0';
+
+ QTEST_ASSERT(lhsExpr);
+ QTEST_ASSERT(rhsExpr);
+
+ if (QTestLog::verboseLevel() >= 2) {
+ qsnprintf(msg, maxMsgLen, "%s(%s, %s)", macroNameForOp(op), lhsExpr, rhsExpr);
+ QTestLog::info(msg, file, line);
+ }
+
+ if (success) {
+ if (QTest::expectFailMode) {
+ qsnprintf(msg, maxMsgLen, "%s(%s, %s) returned TRUE unexpectedly.",
+ macroNameForOp(op), lhsExpr, rhsExpr);
+ }
+ return checkStatement(success, msg, file, line);
+ }
+
+ const std::unique_ptr<const char[]> lhsPtr{ lhsFormatter(lhs) };
+ const std::unique_ptr<const char[]> rhsPtr{ rhsFormatter(rhs) };
+
+ if (!failureMessage)
+ failureMessage = failureMessageForOp(op);
+
+ formatFailMessage(msg, maxMsgLen, failureMessage, lhsPtr.get(), rhsPtr.get(),
+ lhsExpr, rhsExpr, op);
+
+ return checkStatement(success, msg, file, line);
+}
+
QT_END_NAMESPACE
diff --git a/src/testlib/qtestresult_p.h b/src/testlib/qtestresult_p.h
index 05ae764722..e94de64c06 100644
--- a/src/testlib/qtestresult_p.h
+++ b/src/testlib/qtestresult_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTRESULT_P_H
#define QTESTRESULT_P_H
@@ -52,12 +16,11 @@
//
#include <QtTest/qttestglobal.h>
+#include <QtCore/qstringfwd.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
-class QLatin1String;
-class QStringView;
-
class QTestResultPrivate;
class QTestData;
@@ -77,7 +40,10 @@ public:
static void reset();
static void setBlacklistCurrentTest(bool b);
- static void addFailure(const char *message, const char *file, int line);
+ static void addFailure(const char *message, const char *file = nullptr, int line = 0);
+ // ### TODO: Remove this overload when deprecated QTest::compare_overload
+ // is removed. Can't declare it deprecated, because it will unconditionally
+ // provide warnings.
static bool compare(bool success, const char *failureMsg,
char *val1, char *val2,
const char *actual, const char *expected,
@@ -109,13 +75,16 @@ public:
const char *actual, const char *expected,
const char *file, int line);
static bool compare(bool success, const char *failureMsg,
- const QLatin1String &val1, QStringView val2,
+ const QLatin1StringView &val1, QStringView val2,
const char *actual, const char *expected,
const char *file, int line);
static bool compare(bool success, const char *failureMsg,
- QStringView val1, const QLatin1String &val2,
+ QStringView val1, const QLatin1StringView &val2,
const char *actual, const char *expected,
const char *file, int line);
+ static bool compare(bool success, const char *failureMsg,
+ const char *actual, const char *expeceted,
+ const char *file, int line);
static void setCurrentGlobalTestData(QTestData *data);
static void setCurrentTestData(QTestData *data);
static void setCurrentTestFunction(const char *func);
@@ -132,6 +101,13 @@ public:
static void setCurrentAppName(const char *appName);
static const char *currentAppName();
+ static bool reportResult(bool success, const void *lhs, const void *rhs,
+ const char *(*lhsFormatter)(const void *),
+ const char *(*rhsFormatter)(const void *),
+ const char *lhsExpr, const char *rhsExpr,
+ QTest::ComparisonOperation op, const char *file, int line,
+ const char *failureMessage = nullptr);
+
private:
Q_DISABLE_COPY(QTestResult)
};
diff --git a/src/testlib/qtestspontaneevent.h b/src/testlib/qtestspontaneevent.h
index aac4397070..779c49f57b 100644
--- a/src/testlib/qtestspontaneevent.h
+++ b/src/testlib/qtestspontaneevent.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTSPONTANEEVENT_H
#define QTESTSPONTANEEVENT_H
diff --git a/src/testlib/qtestsystem.h b/src/testlib/qtestsystem.h
index 7a73bbb5d2..08f7522183 100644
--- a/src/testlib/qtestsystem.h
+++ b/src/testlib/qtestsystem.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTSYSTEM_H
#define QTESTSYSTEM_H
diff --git a/src/testlib/qtesttable.cpp b/src/testlib/qtesttable.cpp
index 85dff17017..2276365505 100644
--- a/src/testlib/qtesttable.cpp
+++ b/src/testlib/qtesttable.cpp
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtTest/private/qtesttable_p.h>
#include <QtTest/qtestdata.h>
#include <QtTest/qtestassert.h>
+#include <QtCore/private/qduplicatetracker_p.h>
#include <QtCore/qmetaobject.h>
#include <string.h>
@@ -71,6 +36,9 @@ public:
using DataList = std::vector<QTestData *>;
DataList dataList;
+ using TagSet = QDuplicateTracker<std::string>;
+ TagSet tagSet;
+
void addColumn(int elemType, const char *elemName) { elementList.push_back(Element(elemName, elemType)); }
void addRow(QTestData *data) { dataList.push_back(data); }
@@ -85,6 +53,8 @@ void QTestTable::addColumn(int type, const char *name)
{
QTEST_ASSERT(type);
QTEST_ASSERT(name);
+ if (indexOf(name) != -1)
+ qWarning() << "Duplicate data column" << name << "- please rename.";
d->addColumn(type, name);
}
@@ -106,6 +76,10 @@ bool QTestTable::isEmpty() const
QTestData *QTestTable::newData(const char *tag)
{
+ QTEST_ASSERT(tag);
+ if (d->tagSet.hasSeen(tag))
+ qWarning("Duplicate data tag \"%s\" - please rename.", tag);
+
QTestData *dt = new QTestData(tag, this);
d->addRow(dt);
return dt;
@@ -146,6 +120,9 @@ public:
bool operator()(const QTestTablePrivate::Element &e) const
{ return !strcmp(e.name, m_needle); }
+ bool operator()(const QTestData *e) const
+ { return !strcmp(e->dataTag(), m_needle); }
+
private:
const char *m_needle;
};
diff --git a/src/testlib/qtesttable_p.h b/src/testlib/qtesttable_p.h
index d2f389a134..bf67817526 100644
--- a/src/testlib/qtesttable_p.h
+++ b/src/testlib/qtesttable_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTTABLE_P_H
#define QTESTTABLE_P_H
@@ -52,6 +16,7 @@
//
#include <QtTest/qttestglobal.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/testlib/qtesttostring.h b/src/testlib/qtesttostring.h
new file mode 100644
index 0000000000..18262332ba
--- /dev/null
+++ b/src/testlib/qtesttostring.h
@@ -0,0 +1,499 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTESTTOSTRING_H
+#define QTESTTOSTRING_H
+
+#include <QtTest/qttestglobal.h>
+
+#if QT_CONFIG(itemmodel)
+# include <QtCore/qabstractitemmodel.h>
+#endif
+#include <QtCore/qbitarray.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qcborarray.h>
+#include <QtCore/qcborcommon.h>
+#include <QtCore/qcbormap.h>
+#include <QtCore/qcborvalue.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qurl.h>
+#include <QtCore/quuid.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QTest {
+namespace Internal {
+
+template<typename T> // Output registered enums
+inline typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, char*>::type toString(T e)
+{
+ QMetaEnum me = QMetaEnum::fromType<T>();
+ return qstrdup(me.valueToKey(int(e))); // int cast is necessary to support enum classes
+}
+
+template <typename T>
+inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && std::is_enum_v<T>, char*>::type toString(const T &e)
+{
+ return qstrdup(QByteArray::number(static_cast<std::underlying_type_t<T>>(e)).constData());
+}
+
+template <typename T> // Fallback; for built-in types debug streaming must be possible
+inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && !std::is_enum_v<T>, char *>::type toString(const T &t)
+{
+ char *result = nullptr;
+#ifndef QT_NO_DEBUG_STREAM
+ if constexpr (QTypeTraits::has_ostream_operator_v<QDebug, T>) {
+ result = qstrdup(QDebug::toString(t).toUtf8().constData());
+ } else {
+ static_assert(!QMetaTypeId2<T>::IsBuiltIn,
+ "Built-in type must implement debug streaming operator "
+ "or provide QTest::toString specialization");
+ }
+#endif
+ return result;
+}
+
+template<typename F> // Output QFlags of registered enumerations
+inline typename std::enable_if<QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f)
+{
+ const QMetaEnum me = QMetaEnum::fromType<F>();
+ return qstrdup(me.valueToKeys(int(f.toInt())).constData());
+}
+
+template <typename F> // Fallback: Output hex value
+inline typename std::enable_if<!QtPrivate::IsQEnumHelper<F>::Value, char*>::type toString(QFlags<F> f)
+{
+ const size_t space = 3 + 2 * sizeof(unsigned); // 2 for 0x, two hex digits per byte, 1 for '\0'
+ char *msg = new char[space];
+ qsnprintf(msg, space, "0x%x", unsigned(f.toInt()));
+ return msg;
+}
+
+template <typename T>
+constexpr bool is_suitable_type_helper_v = std::disjunction_v<std::is_same<T, char>,
+ std::is_same<T, void>,
+ std::is_same<T, QObject>
+ >;
+
+template <typename T>
+using is_suitable_type_v =
+ std::enable_if_t<!(std::is_pointer_v<T>
+ && is_suitable_type_helper_v<
+ std::remove_const_t<std::remove_pointer_t<T>>>),
+ bool>;
+
+} // namespace Internal
+
+Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual,
+ const char *expected, const char *file, int line);
+Q_TESTLIB_EXPORT char *formatString(const char *prefix, const char *suffix, size_t numArguments, ...);
+Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, qsizetype length);
+Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, qsizetype length);
+Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string);
+Q_TESTLIB_EXPORT char *toString(const char *);
+Q_TESTLIB_EXPORT char *toString(const volatile void *);
+Q_TESTLIB_EXPORT char *toString(const volatile QObject *);
+
+template<typename T, Internal::is_suitable_type_v<T> = true>
+inline char *toString(const T &t)
+{
+ return Internal::toString(t);
+}
+
+template <typename T1, typename T2>
+inline char *toString(const std::pair<T1, T2> &pair);
+
+template <class... Types>
+inline char *toString(const std::tuple<Types...> &tuple);
+
+template <typename Rep, typename Period>
+inline char *toString(std::chrono::duration<Rep, Period> duration);
+
+#define QTEST_COMPARE_DECL(KLASS)\
+ template<> Q_TESTLIB_EXPORT char *toString<KLASS >(const KLASS &);
+#ifndef Q_QDOC
+QTEST_COMPARE_DECL(short)
+QTEST_COMPARE_DECL(ushort)
+QTEST_COMPARE_DECL(int)
+QTEST_COMPARE_DECL(uint)
+QTEST_COMPARE_DECL(long)
+QTEST_COMPARE_DECL(ulong)
+QTEST_COMPARE_DECL(qint64)
+QTEST_COMPARE_DECL(quint64)
+
+QTEST_COMPARE_DECL(float)
+QTEST_COMPARE_DECL(double)
+QTEST_COMPARE_DECL(qfloat16)
+QTEST_COMPARE_DECL(char)
+QTEST_COMPARE_DECL(signed char)
+QTEST_COMPARE_DECL(unsigned char)
+QTEST_COMPARE_DECL(bool)
+#endif
+#undef QTEST_COMPARE_DECL
+
+template <> inline char *toString(const QStringView &str)
+{
+ return QTest::toPrettyUnicode(str);
+}
+
+template<> inline char *toString(const QString &str)
+{
+ return toString(QStringView(str));
+}
+
+template<> inline char *toString(const QLatin1StringView &str)
+{
+ return toString(QString(str));
+}
+
+template<> inline char *toString(const QByteArray &ba)
+{
+ return QTest::toPrettyCString(ba.constData(), ba.size());
+}
+
+template<> inline char *toString(const QBitArray &ba)
+{
+ qsizetype size = ba.size();
+ char *str = new char[size + 1];
+ for (qsizetype i = 0; i < size; ++i)
+ str[i] = "01"[ba.testBit(i)];
+ str[size] = '\0';
+ return str;
+}
+
+#if QT_CONFIG(datestring)
+template<> inline char *toString(const QTime &time)
+{
+ return time.isValid()
+ ? qstrdup(qPrintable(time.toString(u"hh:mm:ss.zzz")))
+ : qstrdup("Invalid QTime");
+}
+
+template<> inline char *toString(const QDate &date)
+{
+ return date.isValid()
+ ? qstrdup(qPrintable(date.toString(u"yyyy/MM/dd")))
+ : qstrdup("Invalid QDate");
+}
+
+template<> inline char *toString(const QDateTime &dateTime)
+{
+ return dateTime.isValid()
+ ? qstrdup(qPrintable(dateTime.toString(u"yyyy/MM/dd hh:mm:ss.zzz[t]")))
+ : qstrdup("Invalid QDateTime");
+}
+#endif // datestring
+
+template<> inline char *toString(const QCborError &c)
+{
+ // use the Q_ENUM formatting
+ return toString(c.c);
+}
+
+template<> inline char *toString(const QChar &c)
+{
+ const ushort uc = c.unicode();
+ if (uc < 128) {
+ char msg[32] = {'\0'};
+ qsnprintf(msg, sizeof(msg), "QChar: '%c' (0x%x)", char(uc), unsigned(uc));
+ return qstrdup(msg);
+ }
+ return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16))));
+}
+
+#if QT_CONFIG(itemmodel)
+template<> inline char *toString(const QModelIndex &idx)
+{
+ char msg[128];
+ qsnprintf(msg, sizeof(msg), "QModelIndex(%d,%d,%p,%p)", idx.row(), idx.column(), idx.internalPointer(), idx.model());
+ return qstrdup(msg);
+}
+#endif
+
+template<> inline char *toString(const QPoint &p)
+{
+ char msg[128] = {'\0'};
+ qsnprintf(msg, sizeof(msg), "QPoint(%d,%d)", p.x(), p.y());
+ return qstrdup(msg);
+}
+
+template<> inline char *toString(const QSize &s)
+{
+ char msg[128] = {'\0'};
+ qsnprintf(msg, sizeof(msg), "QSize(%dx%d)", s.width(), s.height());
+ return qstrdup(msg);
+}
+
+template<> inline char *toString(const QRect &s)
+{
+ char msg[256] = {'\0'};
+ qsnprintf(msg, sizeof(msg), "QRect(%d,%d %dx%d) (bottomright %d,%d)",
+ s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
+ return qstrdup(msg);
+}
+
+template<> inline char *toString(const QPointF &p)
+{
+ char msg[64] = {'\0'};
+ qsnprintf(msg, sizeof(msg), "QPointF(%g,%g)", p.x(), p.y());
+ return qstrdup(msg);
+}
+
+template<> inline char *toString(const QSizeF &s)
+{
+ char msg[64] = {'\0'};
+ qsnprintf(msg, sizeof(msg), "QSizeF(%gx%g)", s.width(), s.height());
+ return qstrdup(msg);
+}
+
+template<> inline char *toString(const QRectF &s)
+{
+ char msg[256] = {'\0'};
+ qsnprintf(msg, sizeof(msg), "QRectF(%g,%g %gx%g) (bottomright %g,%g)",
+ s.left(), s.top(), s.width(), s.height(), s.right(), s.bottom());
+ return qstrdup(msg);
+}
+
+template<> inline char *toString(const QUrl &uri)
+{
+ if (!uri.isValid())
+ return qstrdup(qPrintable(QLatin1StringView("Invalid URL: ") + uri.errorString()));
+ return qstrdup(uri.toEncoded().constData());
+}
+
+template <> inline char *toString(const QUuid &uuid)
+{
+ return qstrdup(uuid.toByteArray().constData());
+}
+
+template<> inline char *toString(const QVariant &v)
+{
+ QByteArray vstring("QVariant(");
+ if (v.isValid()) {
+ QByteArray type(v.typeName());
+ if (type.isEmpty()) {
+ type = QByteArray::number(v.userType());
+ }
+ vstring.append(type);
+ if (!v.isNull()) {
+ vstring.append(',');
+ if (v.canConvert<QString>()) {
+ vstring.append(v.toString().toLocal8Bit());
+ }
+ else {
+ vstring.append("<value not representable as string>");
+ }
+ }
+ }
+ vstring.append(')');
+
+ return qstrdup(vstring.constData());
+}
+
+template<> inline char *toString(const QPartialOrdering &o)
+{
+ if (o == QPartialOrdering::Less)
+ return qstrdup("Less");
+ if (o == QPartialOrdering::Equivalent)
+ return qstrdup("Equivalent");
+ if (o == QPartialOrdering::Greater)
+ return qstrdup("Greater");
+ if (o == QPartialOrdering::Unordered)
+ return qstrdup("Unordered");
+ return qstrdup("<invalid>");
+}
+
+namespace Internal {
+struct QCborValueFormatter
+{
+ enum { BufferLen = 256 };
+ static char *formatSimpleType(QCborSimpleType st)
+ {
+ char *buf = new char[BufferLen];
+ qsnprintf(buf, BufferLen, "QCborValue(QCborSimpleType(%d))", int(st));
+ return buf;
+ }
+
+ static char *formatTag(QCborTag tag, const QCborValue &taggedValue)
+ {
+ QScopedArrayPointer<char> hold(format(taggedValue));
+ char *buf = new char[BufferLen];
+ qsnprintf(buf, BufferLen, "QCborValue(QCborTag(%llu), %s)", tag, hold.get());
+ return buf;
+ }
+
+ static char *innerFormat(QCborValue::Type t, const char *str)
+ {
+ static const QMetaEnum typeEnum = []() {
+ int idx = QCborValue::staticMetaObject.indexOfEnumerator("Type");
+ return QCborValue::staticMetaObject.enumerator(idx);
+ }();
+
+ char *buf = new char[BufferLen];
+ const char *typeName = typeEnum.valueToKey(t);
+ if (typeName)
+ qsnprintf(buf, BufferLen, "QCborValue(%s, %s)", typeName, str);
+ else
+ qsnprintf(buf, BufferLen, "QCborValue(<unknown type 0x%02x>)", t);
+ return buf;
+ }
+
+ template<typename T> static char *format(QCborValue::Type type, const T &t)
+ {
+ QScopedArrayPointer<char> hold(QTest::toString(t));
+ return innerFormat(type, hold.get());
+ }
+
+ static char *format(const QCborValue &v)
+ {
+ switch (v.type()) {
+ case QCborValue::Integer:
+ return format(v.type(), v.toInteger());
+ case QCborValue::ByteArray:
+ return format(v.type(), v.toByteArray());
+ case QCborValue::String:
+ return format(v.type(), v.toString());
+ case QCborValue::Array:
+ return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toArray())).get());
+ case QCborValue::Map:
+ return innerFormat(v.type(), QScopedArrayPointer<char>(format(v.toMap())).get());
+ case QCborValue::Tag:
+ return formatTag(v.tag(), v.taggedValue());
+ case QCborValue::SimpleType:
+ break;
+ case QCborValue::True:
+ return qstrdup("QCborValue(true)");
+ case QCborValue::False:
+ return qstrdup("QCborValue(false)");
+ case QCborValue::Null:
+ return qstrdup("QCborValue(nullptr)");
+ case QCborValue::Undefined:
+ return qstrdup("QCborValue()");
+ case QCborValue::Double:
+ return format(v.type(), v.toDouble());
+ case QCborValue::DateTime:
+ case QCborValue::Url:
+ case QCborValue::RegularExpression:
+ return format(v.type(), v.taggedValue().toString());
+ case QCborValue::Uuid:
+ return format(v.type(), v.toUuid());
+ case QCborValue::Invalid:
+ return qstrdup("QCborValue(<invalid>)");
+ }
+
+ if (v.isSimpleType())
+ return formatSimpleType(v.toSimpleType());
+ return innerFormat(v.type(), "");
+ }
+
+ static char *format(const QCborArray &a)
+ {
+ QByteArray out(1, '[');
+ const char *comma = "";
+ for (QCborValueConstRef v : a) {
+ QScopedArrayPointer<char> s(format(v));
+ out += comma;
+ out += s.get();
+ comma = ", ";
+ }
+ out += ']';
+ return qstrdup(out.constData());
+ }
+
+ static char *format(const QCborMap &m)
+ {
+ QByteArray out(1, '{');
+ const char *comma = "";
+ for (auto pair : m) {
+ QScopedArrayPointer<char> key(format(pair.first));
+ QScopedArrayPointer<char> value(format(pair.second));
+ out += comma;
+ out += key.get();
+ out += ": ";
+ out += value.get();
+ comma = ", ";
+ }
+ out += '}';
+ return qstrdup(out.constData());
+ }
+};
+}
+
+template<> inline char *toString(const QCborValue &v)
+{
+ return Internal::QCborValueFormatter::format(v);
+}
+
+template<> inline char *toString(const QCborValueRef &v)
+{
+ return toString(QCborValue(v));
+}
+
+template<> inline char *toString(const QCborArray &a)
+{
+ return Internal::QCborValueFormatter::format(a);
+}
+
+template<> inline char *toString(const QCborMap &m)
+{
+ return Internal::QCborValueFormatter::format(m);
+}
+
+template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur)
+{
+ QString r;
+ QDebug d(&r);
+ d.nospace() << qSetRealNumberPrecision(9) << dur;
+ if constexpr (Period::num != 1 || Period::den != 1) {
+ // include the equivalent value in seconds, in parentheses
+ using namespace std::chrono;
+ d << " (" << duration_cast<duration<qreal>>(dur).count() << "s)";
+ }
+ return qstrdup(std::move(r).toUtf8().constData());
+}
+
+template <typename T1, typename T2>
+inline char *toString(const std::pair<T1, T2> &pair)
+{
+ const QScopedArrayPointer<char> first(toString(pair.first));
+ const QScopedArrayPointer<char> second(toString(pair.second));
+ return formatString("std::pair(", ")", 2, first.data(), second.data());
+}
+
+template <typename Tuple, std::size_t... I>
+inline char *tupleToString(const Tuple &tuple, std::index_sequence<I...>) {
+ using UP = std::unique_ptr<char[]>;
+ // Generate a table of N + 1 elements where N is the number of
+ // elements in the tuple.
+ // The last element is needed to support the empty tuple use case.
+ const UP data[] = {
+ UP(toString(std::get<I>(tuple)))..., UP{}
+ };
+ return formatString("std::tuple(", ")", sizeof...(I), data[I].get()...);
+}
+
+template <class... Types>
+inline char *toString(const std::tuple<Types...> &tuple)
+{
+ return tupleToString(tuple, std::make_index_sequence<sizeof...(Types)>{});
+}
+
+inline char *toString(std::nullptr_t)
+{
+ return toString(QStringView(u"nullptr"));
+}
+} // namespace QTest
+
+QT_END_NAMESPACE
+
+#endif // QTESTTOSTRING_H
diff --git a/src/testlib/qtesttouch.h b/src/testlib/qtesttouch.h
index 4b13d86054..71133ff3ec 100644
--- a/src/testlib/qtesttouch.h
+++ b/src/testlib/qtesttouch.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTTOUCH_H
#define QTESTTOUCH_H
@@ -65,7 +29,7 @@ QT_BEGIN_NAMESPACE
namespace QTest
{
-#if defined(QT_WIDGETS_LIB) || defined(Q_CLANG_QDOC)
+#if defined(QT_WIDGETS_LIB) || defined(Q_QDOC)
inline
QTouchEventWidgetSequence touchEvent(QWidget *widget,
QPointingDevice *device,
diff --git a/src/testlib/qtestutil_macos.mm b/src/testlib/qtestutil_macos.mm
index 880cd0f91f..74cfee70e9 100644
--- a/src/testlib/qtestutil_macos.mm
+++ b/src/testlib/qtestutil_macos.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qtestutil_macos_p.h"
@@ -53,8 +17,11 @@ namespace QTestPrivate {
to start with a clean slate and prevents the "previous restore failed"
dialog from showing if there was a test crash.
*/
- void disableWindowRestore() {
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"ApplePersistenceIgnoreState"];
+ void disableWindowRestore()
+ {
+ [NSUserDefaults.standardUserDefaults registerDefaults:@{
+ @"ApplePersistenceIgnoreState" : @YES
+ }];
}
bool macCrashReporterWillShowDialog()
@@ -85,7 +52,7 @@ namespace QTestPrivate {
/*! \internal
\class AppNapDisabler
- \brief Disables App Nap by registereing a bacground activity.
+ \brief Disables App Nap by registering a background activity.
App Nap remains disabled as long as the AppNapDisabler instance
exists.
diff --git a/src/testlib/qtestutil_macos_p.h b/src/testlib/qtestutil_macos_p.h
index c9a152dfdb..95025f7d7e 100644
--- a/src/testlib/qtestutil_macos_p.h
+++ b/src/testlib/qtestutil_macos_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTESTUTIL_MACOS_H
#define QTESTUTIL_MACOS_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <qglobal.h>
+#include <private/qglobal_p.h>
#import <objc/objc.h>
QT_BEGIN_NAMESPACE
diff --git a/src/testlib/qtestwheel.h b/src/testlib/qtestwheel.h
new file mode 100644
index 0000000000..564a586b4b
--- /dev/null
+++ b/src/testlib/qtestwheel.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTESTWHEEL_H
+#define QTESTWHEEL_H
+
+#if 0
+// inform syncqt
+#pragma qt_no_master_include
+#endif
+
+#include <QtTest/qttestglobal.h>
+#include <QtTest/qtestassert.h>
+#include <QtTest/qtestsystem.h>
+#include <QtTest/qtestspontaneevent.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qpointer.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qwindow.h>
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+Q_GUI_EXPORT void qt_handleWheelEvent(QWindow *window, const QPointF &local,
+ const QPointF &global, QPoint pixelDelta,
+ QPoint angleDelta, Qt::KeyboardModifiers mods, Qt::ScrollPhase phase);
+
+namespace QTest
+{
+ /*! \internal
+ This function creates a mouse wheel event and calls
+ QWindowSystemInterface::handleWheelEvent().
+ \a window is the window that should be receiving the event and \a pos
+ provides the location of the event in the window's local coordinates.
+ \a angleDelta contains the wheel rotation angle, while \a pixelDelta
+ contains the scrolling distance in pixels on screen.
+ The keyboard states at the time of the event are specified by \a stateKey.
+ The scrolling phase of the event is specified by \a phase.
+ */
+ [[maybe_unused]] static void wheelEvent(QWindow *window, QPointF pos,
+ QPoint angleDelta, QPoint pixelDelta = QPoint(0, 0),
+ Qt::KeyboardModifiers stateKey = Qt::NoModifier,
+ Qt::ScrollPhase phase = Qt::NoScrollPhase)
+ {
+ QTEST_ASSERT(window);
+
+ // pos is in window local coordinates
+ const QSize windowSize = window->geometry().size();
+ if (windowSize.width() <= pos.x() || windowSize.height() <= pos.y()) {
+ qWarning("Mouse event at %d, %d occurs outside target window (%dx%d).",
+ static_cast<int>(pos.x()), static_cast<int>(pos.y()), windowSize.width(), windowSize.height());
+ }
+
+ if (pos.isNull())
+ pos = QPoint(window->width() / 2, window->height() / 2);
+
+ QPointF global = window->mapToGlobal(pos);
+ QPointer<QWindow> w(window);
+
+ if (angleDelta.isNull() && pixelDelta.isNull())
+ qWarning("No angle or pixel delta specified.");
+
+ qt_handleWheelEvent(w, pos, global, pixelDelta, angleDelta, stateKey, phase);
+ qApp->processEvents();
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QTESTWHEEL_H
diff --git a/src/testlib/qttestglobal.h b/src/testlib/qttestglobal.h
index 70a14d2dd2..8ede78c2a2 100644
--- a/src/testlib/qttestglobal.h
+++ b/src/testlib/qttestglobal.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTTESTGLOBAL_H
#define QTTESTGLOBAL_H
@@ -59,6 +23,15 @@ QT_BEGIN_NAMESPACE
namespace QTest
{
enum TestFailMode { Abort = 1, Continue = 2 };
+ enum class ComparisonOperation {
+ CustomCompare, /* Used for QCOMPARE() */
+ Equal,
+ NotEqual,
+ LessThan,
+ LessThanOrEqual,
+ GreaterThan,
+ GreaterThanOrEqual,
+ };
}
QT_END_NAMESPACE
diff --git a/src/testlib/qxctestlogger.mm b/src/testlib/qxctestlogger.mm
index a40ee5e643..ed0411fb96 100644
--- a/src/testlib/qxctestlogger.mm
+++ b/src/testlib/qxctestlogger.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qxctestlogger_p.h"
@@ -46,6 +10,9 @@
#import <XCTest/XCTest.h>
+// This XCode logging integration has probably bit-rotted since it was written.
+// It is not even compiled as part of normal builds.
+
// ---------------------------------------------------------
@interface XCTestProbe (Private)
diff --git a/src/testlib/qxctestlogger_p.h b/src/testlib/qxctestlogger_p.h
index bd844e28e4..4d9476d33f 100644
--- a/src/testlib/qxctestlogger_p.h
+++ b/src/testlib/qxctestlogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QXCTESTLOGGER_P_H
#define QXCTESTLOGGER_P_H
diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp
index 763cea327b..27da73ba52 100644
--- a/src/testlib/qxmltestlogger.cpp
+++ b/src/testlib/qxmltestlogger.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <stdio.h>
#include <string.h>
@@ -53,32 +17,32 @@ QT_BEGIN_NAMESPACE
namespace QTest {
- static const char* xmlMessageType2String(QAbstractTestLogger::MessageTypes type)
+ static const char *xmlMessageType2String(QAbstractTestLogger::MessageTypes type)
{
switch (type) {
- case QAbstractTestLogger::Warn:
- return "warn";
- case QAbstractTestLogger::QSystem:
- return "system";
case QAbstractTestLogger::QDebug:
return "qdebug";
case QAbstractTestLogger::QInfo:
return "qinfo";
case QAbstractTestLogger::QWarning:
return "qwarn";
+ case QAbstractTestLogger::QCritical:
+ return "qcritical";
case QAbstractTestLogger::QFatal:
return "qfatal";
- case QAbstractTestLogger::Skip:
- return "skip";
case QAbstractTestLogger::Info:
return "info";
+ case QAbstractTestLogger::Warn:
+ return "warn";
}
return "??????";
}
- static const char* xmlIncidentType2String(QAbstractTestLogger::IncidentTypes type)
+ static const char *xmlIncidentType2String(QAbstractTestLogger::IncidentTypes type)
{
switch (type) {
+ case QAbstractTestLogger::Skip:
+ return "skip";
case QAbstractTestLogger::Pass:
return "pass";
case QAbstractTestLogger::XFail:
@@ -101,6 +65,26 @@ namespace QTest {
}
+/*! \internal
+ \class QXmlTestLogger
+ \inmodule QtTest
+
+ QXmlTestLogger implements two XML formats specific to Qt.
+
+ The two formats are distinguished by the XmlMode enum.
+*/
+/*! \internal
+ \enum QXmlTestLogger::XmlMode
+
+ This enumerated type selects the type of XML output to produce.
+
+ \value Complete A full self-contained XML document
+ \value Light XML content suitable for embedding in an XML document
+
+ The Complete form wraps the Light form in a <TestCase> element whose name
+ attribute identifies the test class whose private slots are to be run. It
+ also includes the usual <?xml ...> preamble.
+*/
QXmlTestLogger::QXmlTestLogger(XmlMode mode, const char *filename)
: QAbstractTestLogger(filename), xmlmode(mode)
@@ -116,46 +100,55 @@ void QXmlTestLogger::startLogging()
if (xmlmode == QXmlTestLogger::Complete) {
QTestCharBuffer quotedTc;
- xmlQuote(&quotedTc, QTestResult::currentTestObjectName());
- QTest::qt_asprintf(&buf,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<TestCase name=\"%s\">\n", quotedTc.constData());
+ QTest::qt_asprintf(&buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
outputString(buf.constData());
+ if (xmlQuote(&quotedTc, QTestResult::currentTestObjectName())) {
+ QTest::qt_asprintf(&buf, "<TestCase name=\"%s\">\n", quotedTc.constData());
+ outputString(buf.constData());
+ } else {
+ // Unconditional end-tag => omitting the start tag is bad.
+ Q_ASSERT_X(false, "QXmlTestLogger::startLogging",
+ "Insanely long test-case name or OOM issue");
+ }
}
QTestCharBuffer quotedBuild;
- xmlQuote(&quotedBuild, QLibraryInfo::build());
-
- QTest::qt_asprintf(&buf,
- "<Environment>\n"
- " <QtVersion>%s</QtVersion>\n"
- " <QtBuild>%s</QtBuild>\n"
- " <QTestVersion>" QTEST_VERSION_STR "</QTestVersion>\n"
- "</Environment>\n", qVersion(), quotedBuild.constData());
- outputString(buf.constData());
+ if (!QLibraryInfo::build() || xmlQuote(&quotedBuild, QLibraryInfo::build())) {
+ QTest::qt_asprintf(&buf,
+ " <Environment>\n"
+ " <QtVersion>%s</QtVersion>\n"
+ " <QtBuild>%s</QtBuild>\n"
+ " <QTestVersion>" QTEST_VERSION_STR "</QTestVersion>\n"
+ " </Environment>\n", qVersion(), quotedBuild.constData());
+ outputString(buf.constData());
+ }
}
void QXmlTestLogger::stopLogging()
{
QTestCharBuffer buf;
- QTest::qt_asprintf(&buf, "<Duration msecs=\"%s\"/>\n",
+ QTest::qt_asprintf(&buf, " <Duration msecs=\"%s\"/>\n",
QString::number(QTestLog::msecsTotalTime()).toUtf8().constData());
outputString(buf.constData());
- if (xmlmode == QXmlTestLogger::Complete) {
+ if (xmlmode == QXmlTestLogger::Complete)
outputString("</TestCase>\n");
- }
QAbstractTestLogger::stopLogging();
}
void QXmlTestLogger::enterTestFunction(const char *function)
{
- QTestCharBuffer buf;
QTestCharBuffer quotedFunction;
- xmlQuote(&quotedFunction, function);
- QTest::qt_asprintf(&buf, "<TestFunction name=\"%s\">\n", quotedFunction.constData());
- outputString(buf.constData());
+ if (xmlQuote(&quotedFunction, function)) {
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf, " <TestFunction name=\"%s\">\n", quotedFunction.constData());
+ outputString(buf.constData());
+ } else {
+ // Unconditional end-tag => omitting the start tag is bad.
+ Q_ASSERT_X(false, "QXmlTestLogger::enterTestFunction",
+ "Insanely long test-function name or OOM issue");
+ }
}
void QXmlTestLogger::leaveTestFunction()
@@ -163,7 +156,7 @@ void QXmlTestLogger::leaveTestFunction()
QTestCharBuffer buf;
QTest::qt_asprintf(&buf,
" <Duration msecs=\"%s\"/>\n"
- "</TestFunction>\n",
+ " </TestFunction>\n",
QString::number(QTestLog::msecsFunctionTime()).toUtf8().constData());
outputString(buf.constData());
@@ -181,45 +174,45 @@ static const char *incidentFormatString(bool noDescription, bool noTag)
{
if (noDescription) {
return noTag
- ? "<Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n"
- : "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
- " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
- "</Incident>\n";
+ ? " <Incident type=\"%s\" file=\"%s\" line=\"%d\" />\n"
+ : " <Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ " <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
+ " </Incident>\n";
}
return noTag
- ? "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
- " <Description><![CDATA[%s%s%s%s]]></Description>\n"
- "</Incident>\n"
- : "<Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
- " <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
- " <Description><![CDATA[%s]]></Description>\n"
- "</Incident>\n";
+ ? " <Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ " <Description><![CDATA[%s%s%s%s]]></Description>\n"
+ " </Incident>\n"
+ : " <Incident type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ " <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
+ " <Description><![CDATA[%s]]></Description>\n"
+ " </Incident>\n";
}
static const char *benchmarkResultFormatString()
{
- return "<BenchmarkResult metric=\"%s\" tag=\"%s\" value=\"%s\" iterations=\"%d\" />\n";
+ return " <BenchmarkResult metric=\"%s\" tag=\"%s\" value=\"%.6g\" iterations=\"%d\" />\n";
}
static const char *messageFormatString(bool noDescription, bool noTag)
{
if (noDescription) {
if (noTag)
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\" />\n";
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\" />\n";
else
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
" <DataTag><![CDATA[%s%s%s%s]]></DataTag>\n"
- "</Message>\n";
+ " </Message>\n";
} else {
if (noTag)
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
" <Description><![CDATA[%s%s%s%s]]></Description>\n"
- "</Message>\n";
+ " </Message>\n";
else
- return "<Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
+ return " <Message type=\"%s\" file=\"%s\" line=\"%d\">\n"
" <DataTag><![CDATA[%s%s%s]]></DataTag>\n"
" <Description><![CDATA[%s]]></Description>\n"
- "</Message>\n";
+ " </Message>\n";
}
}
@@ -239,42 +232,40 @@ void QXmlTestLogger::addIncident(IncidentTypes type, const char *description,
QTestCharBuffer cdataTag;
QTestCharBuffer cdataDescription;
- xmlQuote(&quotedFile, file);
- xmlCdata(&cdataGtag, gtag);
- xmlCdata(&cdataTag, tag);
- xmlCdata(&cdataDescription, description);
+ if (xmlQuote(&quotedFile, file)
+ && xmlCdata(&cdataGtag, gtag)
+ && xmlCdata(&cdataTag, tag)
+ && xmlCdata(&cdataDescription, description)) {
- QTest::qt_asprintf(&buf,
- QTest::incidentFormatString(QTest::isEmpty(description), notag),
- QTest::xmlIncidentType2String(type),
- quotedFile.constData(), line,
- cdataGtag.constData(),
- filler,
- cdataTag.constData(),
- cdataDescription.constData());
+ QTest::qt_asprintf(&buf,
+ QTest::incidentFormatString(QTest::isEmpty(description), notag),
+ QTest::xmlIncidentType2String(type),
+ quotedFile.constData(), line,
+ cdataGtag.constData(),
+ filler,
+ cdataTag.constData(),
+ cdataDescription.constData());
- outputString(buf.constData());
+ outputString(buf.constData());
+ }
}
void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
{
- QTestCharBuffer buf;
QTestCharBuffer quotedMetric;
QTestCharBuffer quotedTag;
- xmlQuote(&quotedMetric,
- benchmarkMetricName(result.metric));
- xmlQuote(&quotedTag, result.context.tag.toUtf8().constData());
-
- const qreal valuePerIteration = qreal(result.value) / qreal(result.iterations);
- QTest::qt_asprintf(
- &buf,
- QTest::benchmarkResultFormatString(),
- quotedMetric.constData(),
- quotedTag.constData(),
- QByteArray::number(valuePerIteration).constData(), //no 64-bit qsnprintf support
- result.iterations);
- outputString(buf.constData());
+ if (xmlQuote(&quotedMetric, benchmarkMetricName(result.measurement.metric))
+ && xmlQuote(&quotedTag, result.context.tag.toUtf8().constData())) {
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf,
+ QTest::benchmarkResultFormatString(),
+ quotedMetric.constData(),
+ quotedTag.constData(),
+ result.measurement.value / double(result.iterations),
+ result.iterations);
+ outputString(buf.constData());
+ }
}
void QXmlTestLogger::addMessage(MessageTypes type, const QString &message,
@@ -291,54 +282,52 @@ void QXmlTestLogger::addMessage(MessageTypes type, const QString &message,
QTestCharBuffer cdataTag;
QTestCharBuffer cdataDescription;
- xmlQuote(&quotedFile, file);
- xmlCdata(&cdataGtag, gtag);
- xmlCdata(&cdataTag, tag);
- xmlCdata(&cdataDescription, message.toUtf8().constData());
-
- QTest::qt_asprintf(&buf,
- QTest::messageFormatString(message.isEmpty(), notag),
- QTest::xmlMessageType2String(type),
- quotedFile.constData(), line,
- cdataGtag.constData(),
- filler,
- cdataTag.constData(),
- cdataDescription.constData());
+ if (xmlQuote(&quotedFile, file)
+ && xmlCdata(&cdataGtag, gtag)
+ && xmlCdata(&cdataTag, tag)
+ && xmlCdata(&cdataDescription, message.toUtf8().constData())) {
+ QTest::qt_asprintf(&buf,
+ QTest::messageFormatString(message.isEmpty(), notag),
+ QTest::xmlMessageType2String(type),
+ quotedFile.constData(), line,
+ cdataGtag.constData(),
+ filler,
+ cdataTag.constData(),
+ cdataDescription.constData());
- outputString(buf.constData());
+ outputString(buf.constData());
+ }
}
-/*
- Copy up to n characters from the src string into dest, escaping any special
- XML characters as necessary so that dest is suitable for use in an XML
- quoted attribute string.
-*/
-int QXmlTestLogger::xmlQuote(QTestCharBuffer* destBuf, char const* src, size_t n)
+int QXmlTestLogger::xmlQuote(QTestCharBuffer *destBuf, char const *src, qsizetype n)
{
- if (n == 0) return 0;
-
+ // QTestCharBuffer initially has size 512, with '\0' at the start of its
+ // data; and we only grow it.
+ Q_ASSERT(n >= 512 && destBuf->size() == n);
char *dest = destBuf->data();
- *dest = 0;
- if (!src) return 0;
- char* begin = dest;
- char* end = dest + n;
+ if (!src || !*src) {
+ Q_ASSERT(!dest[0]);
+ return 0;
+ }
+
+ char *begin = dest;
+ char *end = dest + n;
while (dest < end) {
switch (*src) {
-#define MAP_ENTITY(chr, ent) \
- case chr: \
- if (dest + sizeof(ent) < end) { \
- strcpy(dest, ent); \
- dest += sizeof(ent) - 1; \
- } \
- else { \
- *dest = 0; \
- return (dest+sizeof(ent)-begin); \
- } \
- ++src; \
- break;
+#define MAP_ENTITY(chr, ent) \
+ case chr: \
+ if (dest + sizeof(ent) < end) { \
+ strcpy(dest, ent); \
+ dest += sizeof(ent) - 1; \
+ } else { \
+ *dest = '\0'; \
+ return dest + sizeof(ent) - begin; \
+ } \
+ ++src; \
+ break;
MAP_ENTITY('>', "&gt;");
MAP_ENTITY('<', "&lt;");
@@ -346,64 +335,60 @@ int QXmlTestLogger::xmlQuote(QTestCharBuffer* destBuf, char const* src, size_t n
MAP_ENTITY('"', "&quot;");
MAP_ENTITY('&', "&amp;");
- // not strictly necessary, but allows handling of comments without
+ // Not strictly necessary, but allows handling of comments without
// having to explicitly look for `--'
MAP_ENTITY('-', "&#x002D;");
#undef MAP_ENTITY
- case 0:
- *dest = 0;
- return (dest-begin);
+ case '\0':
+ *dest = '\0';
+ return dest - begin;
- default:
- *dest = *src;
- ++dest;
- ++src;
- break;
+ default:
+ *dest = *src;
+ ++dest;
+ ++src;
+ break;
}
}
- // If we get here, dest was completely filled (dest == end)
- *(dest-1) = 0;
- return (dest-begin);
+ // If we get here, dest was completely filled:
+ Q_ASSERT(dest == end && end > begin);
+ dest[-1] = '\0'; // hygiene, but it'll be ignored
+ return n;
}
-/*
- Copy up to n characters from the src string into dest, escaping any
- special strings such that dest is suitable for use in an XML CDATA section.
-*/
-int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const* src, size_t n)
+int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const *src, qsizetype n)
{
- if (!n) return 0;
-
+ Q_ASSERT(n >= 512 && destBuf->size() == n);
char *dest = destBuf->data();
- if (!src || n == 1) {
- *dest = 0;
+ if (!src || !*src) {
+ Q_ASSERT(!dest[0]);
return 0;
}
static char const CDATA_END[] = "]]>";
static char const CDATA_END_ESCAPED[] = "]]]><![CDATA[]>";
+ const size_t CDATA_END_LEN = sizeof(CDATA_END) - 1;
- char* begin = dest;
- char* end = dest + n;
+ char *begin = dest;
+ char *end = dest + n;
while (dest < end) {
if (!*src) {
- *dest = 0;
- return (dest-begin);
+ *dest = '\0';
+ return dest - begin;
}
- if (!strncmp(src, CDATA_END, sizeof(CDATA_END)-1)) {
+ if (!strncmp(src, CDATA_END, CDATA_END_LEN)) {
if (dest + sizeof(CDATA_END_ESCAPED) < end) {
strcpy(dest, CDATA_END_ESCAPED);
- src += sizeof(CDATA_END)-1;
+ src += CDATA_END_LEN;
dest += sizeof(CDATA_END_ESCAPED) - 1;
- }
- else {
- *dest = 0;
- return (dest+sizeof(CDATA_END_ESCAPED)-begin);
+ } else {
+ *dest = '\0';
+ return dest + sizeof(CDATA_END_ESCAPED) - begin;
}
continue;
}
@@ -413,50 +398,61 @@ int QXmlTestLogger::xmlCdata(QTestCharBuffer *destBuf, char const* src, size_t n
++dest;
}
- // If we get here, dest was completely filled (dest == end)
- *(dest-1) = 0;
- return (dest-begin);
+ // If we get here, dest was completely filled; caller shall grow and retry:
+ Q_ASSERT(dest == end && end > begin);
+ dest[-1] = '\0'; // hygiene, but it'll be ignored
+ return n;
}
-typedef int (*StringFormatFunction)(QTestCharBuffer*,char const*,size_t);
+typedef int (*StringFormatFunction)(QTestCharBuffer *, char const *, qsizetype);
/*
A wrapper for string functions written to work with a fixed size buffer so they can be called
with a dynamically allocated buffer.
*/
-int allocateStringFn(QTestCharBuffer* str, char const* src, StringFormatFunction func)
+static bool allocateStringFn(QTestCharBuffer *str, char const *src, StringFormatFunction func)
{
- static const int MAXSIZE = 1024*1024*2;
-
+ constexpr int MAXSIZE = 1024 * 1024 * 2;
int size = str->size();
+ Q_ASSERT(size >= 512 && !str->data()[0]);
- int res = 0;
-
- for (;;) {
- res = func(str, src, size);
- str->data()[size - 1] = '\0';
- if (res < size) {
- // We succeeded or fatally failed
- break;
+ do {
+ const int res = func(str, src, size);
+ if (res < size) { // Success
+ Q_ASSERT(res > 0 || (!res && (!src || !src[0])));
+ return true;
}
- // buffer wasn't big enough, try again
+
+ // Buffer wasn't big enough, try again, if not too big:
size *= 2;
- if (size > MAXSIZE) {
- break;
- }
- if (!str->reset(size))
- break; // ran out of memory - bye
- }
+ } while (size <= MAXSIZE && str->reset(size));
- return res;
+ return false;
}
-int QXmlTestLogger::xmlQuote(QTestCharBuffer* str, char const* src)
+/*
+ Copy from \a src into \a destBuf, escaping any special XML characters as
+ necessary so that destBuf is suitable for use in an XML quoted attribute
+ string. Expands \a destBuf as needed to make room, up to a size of 2
+ MiB. Input requiring more than that much space for output is considered
+ invalid.
+
+ Returns 0 on invalid or empty input, the actual length written on success.
+*/
+bool QXmlTestLogger::xmlQuote(QTestCharBuffer *str, char const *src)
{
return allocateStringFn(str, src, QXmlTestLogger::xmlQuote);
}
-int QXmlTestLogger::xmlCdata(QTestCharBuffer* str, char const* src)
+/*
+ Copy from \a src into \a destBuf, escaping any special strings such that
+ destBuf is suitable for use in an XML CDATA section. Expands \a destBuf as
+ needed to make room, up to a size of 2 MiB. Input requiring more than that
+ much space for output is considered invalid.
+
+ Returns 0 on invalid or empty input, the actual length written on success.
+*/
+bool QXmlTestLogger::xmlCdata(QTestCharBuffer *str, char const *src)
{
return allocateStringFn(str, src, QXmlTestLogger::xmlCdata);
}
diff --git a/src/testlib/qxmltestlogger_p.h b/src/testlib/qxmltestlogger_p.h
index 04ed57d587..045da4d7bb 100644
--- a/src/testlib/qxmltestlogger_p.h
+++ b/src/testlib/qxmltestlogger_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtTest module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QXMLTESTLOGGER_P_H
#define QXMLTESTLOGGER_P_H
@@ -77,12 +41,12 @@ public:
void addMessage(MessageTypes type, const QString &message,
const char *file = nullptr, int line = 0) override;
- static int xmlCdata(QTestCharBuffer *dest, char const* src);
- static int xmlQuote(QTestCharBuffer *dest, char const* src);
- static int xmlCdata(QTestCharBuffer *dest, char const* src, size_t n);
- static int xmlQuote(QTestCharBuffer *dest, char const* src, size_t n);
-
+ [[nodiscard]] static bool xmlCdata(QTestCharBuffer *dest, char const *src);
+ [[nodiscard]] static bool xmlQuote(QTestCharBuffer *dest, char const *src);
private:
+ [[nodiscard]] static int xmlCdata(QTestCharBuffer *dest, char const *src, qsizetype n);
+ [[nodiscard]] static int xmlQuote(QTestCharBuffer *dest, char const *src, qsizetype n);
+
XmlMode xmlmode;
};
diff --git a/src/testlib/removed_api.cpp b/src/testlib/removed_api.cpp
new file mode 100644
index 0000000000..00a53735a0
--- /dev/null
+++ b/src/testlib/removed_api.cpp
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2024 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#define QT_TESTLIB_BUILD_REMOVED_API
+
+#include "qtest.h"
+
+#if QT_TESTLIB_REMOVED_SINCE(6, 8)
+
+QT_BEGIN_NAMESPACE
+
+namespace QTest {
+
+Q_TESTLIB_EXPORT char *toString(const void *p)
+{
+ const volatile void *ptr = p;
+ return toString(ptr);
+}
+
+} // namespace QTest
+
+QT_END_NAMESPACE
+
+// #include "qotherheader.h"
+// implement removed functions from qotherheader.h
+// order sections alphabetically to reduce chances of merge conflicts
+
+#endif // QT_TESTLIB_REMOVED_SINCE(6, 8)
diff --git a/src/testlib/selfcover.cmake b/src/testlib/selfcover.cmake
index 83af041951..9addaf645b 100644
--- a/src/testlib/selfcover.cmake
+++ b/src/testlib/selfcover.cmake
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
#
# Hand crafted file based on selfcover.pri
#
@@ -28,7 +31,7 @@ function(qt_internal_apply_testlib_coverage_options target)
--cs-mcc # enable Multiple Condition Coverage
--cs-mcdc # enable Multiple Condition / Decision Coverage
# (recommended for ISO 26262 ASIL A, B and C -- highly recommended for ASIL D)
- # https://doc.froglogic.com/squish-coco/4.1/codecoverage.html#sec%3Amcdc
+ # https://doc.qt.io/coco/code-coverage-analysis.html#mc-dc
)
target_compile_options(${target} PRIVATE
${testlib_coverage_options}